Mappen, bestanden & rechten - 2020 (WordPress)

Uit De Vliegende Brigade
(wijz) ← Oudere versie | Huidige versie (wijz) | Nieuwere versie → (wijz)
Naar navigatie springen Naar zoeken springen

Hoe stel je rechten op bestanden en mappen rondom een WordPress-installatie, op de juiste manier in?

Na jarenlang experimenteren, dacht ik in mei 2020 de perfecte oplossing te hebben gevonden. Zie onderaan dit artikel waarom dat niet zo is. De rest van dit artikel gaat over de implementatie hiervan.

Samenvatting

  1. www-data (Apache) is de eigenaar van alle mappen en bestanden. Rechten zijn beperkt tot lezen van bestanden en executeren van directories, muv. recursieve schrijfrechten op de map wp-content/uploads
  2. www-admin is de groep van systeembeheerders. Apache zit hier niet in. Deze groep mag alles (muv. executierechten op bestanden - nergens nodig)
  3. Je kunt WordPress-sites als vanouds updaten met scripts, want dan is www-admin de eigenaar - WordPress mag niet zichzelf updaten - Perfect!
  4. In Apache Virtual Host-files instellen dat executie van PHP in diverse mappen niet mag (bv. upload-mappen).

Zie script wp_sr onderaan dit artikel. Geschreven op 2 mei 2020. Nog niet op alle servers beschikbaar.

Let op

Dit systeem is inderdaad bijna perfect, maar in de praktijk werkt het niet. Zie onderaan dit artikel voor details.

Vraagstukken

Dit is een checklist van dingen die ik niet zeker weet, en die ik regelmatig tegenkom als het over dit onderwerp gaat:

  • Plugins update ik middels de WP CLI. Brengt dat een risico met zich mee dat het Apache-proces te veel rechten krijgt?
  • Hoe verhelp ik foutmeldingen rondom WordFence?
  • Hoe zorg ik ervoor dat rechten veilig zijn ingesteld?
  • Wie moet de eigenaar van bestanden en mappen worden? Ikzelf? (liever niet, want ik ben binnen afzienbare termijn niet meer de enige die de betreffende webservers onderhoud) www_data? Iets anders?
  • Hoe zit het met groups? Moet ik daar iets mee?
  • Waarom vindt de WP CLI het zo'n slechte zaak als je een commando als root wilt geven?
  • Wat zijn ACL's, en moet ik daar iets mee [1]?
  • Als www-data eigenaar is, hoe zorg ik ervoor dat ik zelf nog mormaal met die bestanden kan werken? Iets met groups? Iets met su? Iets met ACL's?
  • Moet ik iets met setuid, setgid en/of sticky bits? Ik vermoed dat dit is, hoe je met FTP bestanden upload naar een account op een shared server, zonder dat er problemen ontstaan tav. de rechten op die ge-uploade bestanden.

Vereisten

Veilig

Belangrijk dat rechten op een veilige manier zijn ingesteld. Met veilig wordt bedoeld, dat hackers zo min mogelijk kans maken, en dat incidenten niet escaleren.

  • Doorgaans kunnen hackers een systeem binnendringen dankzij zwakheden in een onderdeel van WordPress, en daar ben ik machteloos over
  • Waar ik niét machteloos over ben: Maatregelen treffen zodat zo'n incident niet escaleert. Dat is waar dit hoofdstuk over gaat.

Werkbaar

Ik had ooit (2018?) een ingenieus systeem bedacht, maar het bleek niet te werken, omdat ik dan steeds met su dingen moest doen, ofzo.

Zelf kunnen updaten

Ik zou het erg prettig vinden als ik sites nog steeds kan updaten met een eigen script dat WP CLI aanroept.

Wat - Principes

Verschillende instellingen gedurende installatie & operationale activiteiten?

  • Iets wat ik al langer vermoedde: Tijdens installatie van een site, heb je andere instellingen nodig dan wanneer de site operationeel is.
  • Dit kun je een stap verder nemen: Verschillende instellingen gedurende bijwerken van een site, en wanneer de site operationeel is.

Gebruiker: Apache

Er zijn maar twee gebruikers. De eerste daarvan is Apache. Dat is hetzelfde als de site-bezoeker, of PHP. Wat Apache wel of niet moet kunnen, is afhankelijk van wie je het vraagt en de gekozen oplossing. Mijn indruk:

  • Alle mappen & bestanden kunnen lezen
  • Onder Uploads mappen aamaken, lezen & schrijven
  • Misschien kunnen schrijven (bv. voor automatische updates en configureren van WordFence).

Gebruiker: Beheerders

De tweede groep van gebruikers, betreft beheerders of developers. Zij loggen in via SSH. Ze hebben beiden sudo-rechten, maar su root is niet mogelijk. Eventueel su naar een ander (niet-root)-account, zou geen probleem zijn. Deze developers/beheerders hebben toegang tot alle virtuele hosts.

Ze moeten ongeveer alles kunnen, zonder dat dat heel ingewikkeld wordt (bv. als je cd niet mag gebruiken, worden dingen heel lastig, omdat je cd niet kunt doen als sudo cd, omdat je daarna als gebruiker 'opeens' op een verboden plek bent aangeland).

Owner vs. group

Nu komt de truuk [2]: Je hebt twee soorten accounts (Apache & Beheerders), en die moeten verschillende rechten krijgen. De standaardmanier om dat te doen:

  • Eigenaar: Maak de ene soort eigenaar van de bestanden en mappen
  • Groep: Maak de andere soort lid van een groep waar deze bestanden en mappen bijhoren.

Maar wie wordt wat? Daar zijn verschillende ideeën over. Het speelt ook een rol als je bv. Plesk gebruikt. Zie het volgende hoofdstuk voor details.

Websites mogen zichzelf niet updaten

Tegenwoordig update WordPress zichzelf standaard automatisch. Dat impliceert dat WordPress (eigenlijk uiteraard Apache) schrijfrechten heeft op z'n eigen installatie. Dat vind ik een slecht idee: Een beveiligingsincident zou daardoor gemakkelijk kunnen escaleren.

Online kon ik daar niets over vinden. Ik vond daar wel een hoop mensen die dit een slecht idee vinden om een hele andere reden: Ze willen de controle behouden wanneer WordPress (of bv. WooCommerce, of welk onderdeel van WordPress dan ook) wordt bijgewerkt. JaapdeWit.com heeft om die reden automatische updates uitgeschakeld.

Oplossing

Apache is eigenaar - Beheerders zitten in een groep

Mijn oplossing:

Entiteit Wat?
Apache Eigenaar (www-data)
Beheerders Groep (www-admin)

Redenen voor deze keuzes:

  • Het is vrijwel onvermijdelijk dat Apache submappen gaat aanmaken onder uploads, dus een systeem waarbij Apache niet eigenaar is van bestanden, wordt inconsistent
  • Op het moment dat er meerdere beheerders zijn, wordt het raar als verschillende beheerders eigenaar worden van verschillende mappen en bestanden
  • Deze aanpak werd hier gesuggereerd voor systemen met meerdere virtuele hosts.

Rechten Apache

Operatie Opmerkingen
Bestanden & mappen lezen
  • Dit geldt in beginsel voor alle mappen & bestanden
  • Dit geldt ook voor PHP-bestanden: Een PHP-script geldt niet als een executable (omdat er een interpretator aan te pas komt ofzo). PHP-scripts hoeven dus niet de eigenschap executable te hebben
  • In bepaalde mappen mogen PHP-bestanden niet geëxecuteerd worden. Daar heeft Apache oplossingen voor (zie verderop).
  • Onduidelijk of dit ook geldt voor de 'eigen' mappen die ik soms aanmaak binnen een installatie om locale upload-bestanden te parkeren of database-kopiën tijdelijk naar toe te schrijven: Er wordt alleen maar iets gedaan met die mappen en bestanden nav. scripts die door mezelf worden aangeroepen en die niet door Apache worden aangeroepen → Testen.
Mappen lezen & executeren
  • Dit geldt in beginsel voor alle mappen binnen de virtual host
  • Zie opmerking over 'eigen' mappen hierboven.
Uploads:
  • Submappen aanmaken
  • Bestanden lezen & schrijven
Ook schrijven, ivm. uploaden nieuwe bestanden
Overige schrijfrechten? Ik ben er geen fan van, dat Apache ook onder meer omstandigheden kan schrijven. Bv.:
  • Tijdens installatie WordFence, wil deze een PHP-bestand aanmaken
  • WordPress kunnen updaten via de site.

Ik denk dat dit het risico nogal verhoogt op escalatie bij een incident. Een tussenoplossing: Tijdens rechten verruimen, en daarna weer dichtzetten.

Rechten beheerders

Beheerders kunnen alles. Dus zoiets als sudo chmod -R g+rwxX vanuit de root van een installatie. Als je per se wilt, kun je hiervan x nog weglaten: Doorgaans heb je geen bash-bestanden in een WordPress-installatie (en voor PHP heb je x ook niet nodig).

Complicaties ivm. beperkte rechten

Probleem (2): Zo uit het probleem zich
Probleem (2): Eigenaar+groep is strompf, en other mag nix, niet eens lezen

Oh oh:

Probleem (1): Ik kan niet uploaden naar Apache-mappen

De eerste complicatie: Regelmatig maakt Apache nieuwe upload-submappen aan, bv. bij de eerste update in een nieuwe maand. Voorbeeld:

drwxrwx---  5 www-data www-admin    4096 mrt  3 11:07 ./
drwxrwx--- 12 www-data www-admin    4096 feb 29 18:55 ../
drwxrwx---  2 www-data www-admin   65536 jan 27 11:34 01/
drwxrwx---  2 www-data www-admin 2293760 feb 29 23:10 02/
drwxrwx---  2 www-data www-data     4096 mrt  3 11:07 03/

Nu kan ik zelf niet meer via scripts bestanden uploaden, want ik heb geen schrijfrechten tot map 03. Voorbeeld van een foutmelding:

   Line 287 - Error returned while sideloading image 01 http://example.com/hiero/blub.jpg
   The error: WP_Error Object
(
    [errors] => Array
        (
            [upload_error] => Array
                (
                    [0] => The uploaded file could not be moved to wp-content/uploads/2020/03.
                )
        )
    [error_data] => Array
        (
        )
)

Probleem (2): Apache kan mijn uploads niet lezen

Dit probleem is er ook de andere kant op: Als ik mbv. een script via de API afbeeldingen upload, dan kan Apache die afbeeldingen niet lezen: Eigenaar en groep zijn steeds strompf

Probleem (3): Updates maken mij eigenaar & groep

Dit is wat er gebeurt in de map wp-content/plugins na een update van Yoast via de CLI (dus door mij, niet door Apache):

dr-xrwx---  6 www-data www-admin 4096 dec 12 14:27 woocommerce-pdf-ips-templates/
dr-xrwx---  8 www-data www-admin 4096 dec 12 14:27 woocommerce-pip/
dr-xrwx---  5 www-data www-admin 4096 dec 12 14:27 woocommerce-quantity-increment/
dr-xrwx--- 14 www-data www-admin 4096 feb 29 23:15 wordfence/
drwxrwxr-x 16 strompf  strompf   4096 mrt  3 17:00 wordpress-seo/
dr-xrwx---  5 www-data www-admin 4096 jan 16 16:25 wp-mail-smtp/
dr-xrwx---  7 www-data www-admin 4096 dec 12 14:27 wp-sync-db-1.6/
dr-xrwx---  6 www-data www-admin 4096 dec 12 14:27 wp-sync-db-media-files-master/

De site ondervindt er geen last van, want others kan lezen+executeren, maar een schoonheidsprijs verdient het niet.

Oplossingen

  1. Maak beheerders ook lid van www-data - Simpele oplossing voor het eerste probleem, het maakt het leven iets gemakkelijker, en het verzwakt de beveiliging niet
  2. Incorporeer in upload-scripts code om eigenaar+groep aan te passen na upload
  3. Extra (niet toegepast): Gebruik su in upload-scripts, zodat Apache het upload.
  4. Voer update-scripts uit met su, zodat Apache het upload. Probleem: Groep moet dan alsnog worden aangepast
  5. Voer na updates een script uit om eigenaarschap+groep bij te werken

De eerste twee dingen hierboven heb ik toegepast. Hoe de tweede oplossing eruit ziet in m'n ImProc-routine:

Ergens bovenaan het script staat dit. De reden dat ik het jaar selecteer, en niet in een keer de hele map uploads doe: Daar staan onduidelijke dingen van WordFence in, en ik heb geen zin in complicaties:

upload_path_this_year=$site_path"/wp-content/uploads/"$(date +'%Y')

De eigenlijke code:

################################################################################################
# Upload images & associate with products as thumbnails
################################################################################################
#
echo "Upload images..."
#
# Upload!
##########################################################
#
php -f ./Subroutines/images-import-and-associate-with-products.php "$site_path" "$site_url" "$product_table" "quiet"


# Update owner + group
##########################################################
#
# * Otherwise, Apache can't read stuff
# * Can't do this without "sudo"
#
sudo chown -R www-data "$upload_path_this_year"
sudo chgrp -R www-admin "$upload_path_this_year"

Casus: Ontwikkelomgeving (feb. 2020)

Groep aanmaken & configureren

Groep aanmaken en mezelf aan die groep toevoegen:

sudo groupadd www-admin
sudo usermod -a -G www-admin strompf

Waarbij:

  • -a - Add
  • -G - Groups

Verificatie (voorbeeld):

groups
strompf adm cdrom sudo dip plugdev lxd lpadmin sambashare www-admin

PHP disablen in uploads

Dat kan door in .htaccess te zetten: php_flag engine off, maar ik vind het prettiger om dit dit de virtuele host-definitie te doen. Dit werkt trouwens recursief (dus in alle submappen is php ook uitgeschakeld). Voorbeeld:

<VirtualHost *:80>

        ServerName example.com
        ServerAlias www.example.com
        DirectoryIndex index.php index.html index.htm
        DocumentRoot /var/www/example.com

        <Directory /var/www/example.com>
                AllowOverride All
                Require all granted
                RewriteEngine on
                RewriteBase /
                RewriteCond %{REQUEST_FILENAME} !-f
                RewriteCond %{REQUEST_FILENAME} !-d
                RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]
        </Directory>

        # HIER ONTBREEKT EEN PRIVÉ-MAP

        <Directory /var/www/example.com/wp-admin/css>
                php_admin_value engine Off
        </Directory>


        <Directory /var/www/example.com/wp-admin/images>
                php_admin_value engine Off
        </Directory>


        <Directory /var/www/example.com/wp-admin/js>
                php_admin_value engine Off
        </Directory>


        <Directory /var/www/example.com/wp-content/languages>
                php_admin_value engine Off
        </Directory>


        <Directory /var/www/example.com/wp-content/updraft>
                php_admin_value engine Off
        </Directory>


        <Directory /var/www/example.com/wp-content/uploads>
                php_admin_value engine Off
        </Directory>


        LogLevel warn
        ErrorLog /var/log/apache2/error.log
        CustomLog /var/log/apache2/access.log vhost_combined

</VirtualHost>

Updaten via CLI geen probleem!

En nu de uitsmijter: In tegenstelling tot wat ik verwachtte, kan ik de site nog steeds prima via de CLI bijwerken (wp plugin update --all && wp theme update --all && wp language core update && wp core update && wp wc update). Waarschijnlijk is dat, omdat ik degene ben die die commando's geeft, en niet de site en dus Apache: Ik kan dit wel (want www-admin-groep), maar Apache niet - Heel goed.

Script wp_sr (Mei 2020)

Het blijft weerbarstige materie. Dit is m'n script op dit moment. Oa. houdt het nog geen rekening met WordFence:

#!/bin/bash

echo " "
echo " "
echo "############################################################"
echo "wp_sr - Set website file and directory rights 2020"
echo "############################################################"
#
# * http://wiki.devliegendebrigade.nl/Mappen,_bestanden_%26_rechten_-_2020_(WordPress)
# * Jeroen Strompf - May 2020
# * Not sure if these settings will be ok for Wordfence
#
#
###################################################################################
# Process input argument (a domain name)
###################################################################################
#
#
if [ -n "$1" ]; then	# Argument provided
	#
	echo "   Path provided"
	path=$1
	#
	# Check if path starts with /var/www/
	########################################
	#
	if [ "${path:0:9}" = "/var/www/" ]; then
		#
		echo " "
		echo "   Path starts with /var/www/ - Good!"
		#
	# If not, add /var/www/
	########################################
	#
	else
		#
		echo "   Path doesn't start with /var/www/ - Added"
		path="/var/www/$path"
		#
	fi


else	# No argument provided
	#
	# Use PWD
	########################################
	#
	path=$PWD
	#
	echo "   No argument provided - Use PWD"
	#
	#
	# Check if path starts with /var/www/
	########################################
	#
	if [ "${path:0:9}" = "/var/www/" ]; then
		#
		echo "   PWD starts with /var/www/ - Good!"
		#
	else
		#
		echo "   PWD doesn't start with /var/www/ - Exiting"
		exit
	fi
fi

# Check if this is the root of a WordPress site
###############################################
#
if [ -f "$path/wp-config.php" ]; then
	#
	echo "   Path points to the root of a WordPress-site - Good"
	#
else
	#
	echo "   Path doesn't point to the root of a WordPress-site - Exiting"
	exit	
fi
#
echo " "
echo "   Path: $path"
echo " "


###################################################################################
# Set owner & group
###################################################################################
#
sudo chown -R www-data $path
sudo chgrp -R www-admin $path


###################################################################################
# Set basic rights
###################################################################################
#
# * Thanks to this command, only one find-chmod-command is needed. Otherwise, an
#   additional find-chmod-command would have been needed to disable execution for
#   files, and that takes quite some time
# * Drawback of this approach: You can't ls before the whole procedure is finished.
#   You can however, use "sudo ls" or "sudo ls -alF" if really needed: In order to 
#   do an "ls" command, you need execution right on the directory and read-rights
#   on something ;)
#
#
# Settings
#############################################
#
# * User:  Read (4) - no write, no execute → 4
# * Group: Read (4) + write(2), no execute → 6
# * Others: Nothing → 0
#
sudo chmod -R 460 $path
#
# sudo ls -alF $path


###################################################################################
# Set directory rights for user & group
###################################################################################
#
# * This command is quite slow. Hence do it for user and group at once
# * Grant execute rights for user & group on directories - Not on files
# * "sudo" is needed for "find", as otherwise, you can't yet read this direcotry
#
sudo find $path -type d -exec sudo chmod ug+x {} \;
#
# sudo ls -alF $path


###################################################################################
# Allow user to write to "upload"
###################################################################################
#
# "user" = Apache = www-data
#
sudo chmod -R u+w $path"/wp-content/uploads"


###################################################################################
# Check
###################################################################################
#
ls -alF

Waarom dit geen goede aanpak is

WordPress kan zichzelf niet updaten

WordPress en WordPress-onderdelen kunnen zichzelf niet updaten. Oorspronkelijk was dat juist de aanleiding voor dit systeem, want beveiligingsissue. In de praktijk is dit echter vaak onwerkbaar. In Mappen, bestanden & rechten - 2021 (WordPress) worden hier oplossingen voor geboden.

Migratie is problematisch

Sites kunnen niet zomaar gekopiëerd worden: Tijdens kopiëren wordt wat eerst de groep was, daarna de eigenaar. Daardoor kunnen mappen-van-mappen niet gekopiëerd worden, want op de doellocatie mag er niet geschreven worden in die mappen.

Er zijn in ieder geval twee relatief gemakkelijke oplossingen.

  1. Kopiëer op de bronlocatie de site eerst naar een tijdelijke locatie. Pas daar de rechten aan, zodat eigenaar alles mag lezen en schrijven. Migreer de site daarna.
  2. Pas de rechten direct aan op de bestaande site en migreer vandaar. Zet daarna de rechten weer terug naar de oude situatie.

In beide gevallen is dit het commando om de rechten aan te passen vóór migratie - En 't werkt in een fractie van een seconde:

sudo chmod -R u+rw /var/www/site

Casus: Migratie (WordPress)#Casus: Rechten belemmeren migratie (mei 2021), Rsync#Keep permissions - p-switch.

Uploader is niet eigenaar

Dit is de achterliggende oorzaak dat migraties ingewikkeld maakt: De uploader is niet de bedoelde eigenaar van de bestanden. Zie paragraaf hierboven voor details.

Alleen ik kan updaten

Als ik samenwerk met anderen, kunnen zij plugins niet bijwerken, tenzij ze SSH-toegang hebben.

Rechten aanpassen is bewerkelijk

Ik heb een script wp_sr geschreven dat rechten aanpast. Dat script doet er echter nogal lang over en ik heb het gevoel dat het leven er niet gemakkelijker op wordt.

Sommige mappen & bestanden zijn toch écht van mij

Soms parkeer ik scripts en bv. databasedumps ergens in een WordPress-boom. Van die bestanden ben ik de eigenaar. En dat maakt het hele systeem gelijk een stuk ingewikkelder en minder consistent. Het maakt de scripts er ook niet efficiënter op.

Zie ook

Bronnen

Complete oplossingen

Overig