Read-only vs Read-write filesystemen

In onze omgeving maken wij onderscheid tussen read-only en read-write filesystemen. Voor de read-only filesystemen geldt dat deze vanaf de web, applicatie en database servers niet gewijzigd kunnen worden. Deze filesystemen worden gebruikt om de executeerbare delen en de configuratie van applicaties op te slaan.

In onze omgeving is het zo dat alle web-app en pages directories op read-only filesystemen liggen. Verder ligt alle configuratie (frontproxy, database, applicatieserver) op een read-only filesysteem. De read-write filesystemen worden alleen gebruikt voor logfiles, temp-directories en databases.

De reden hiervoor is dat als een site gehacked wordt, het voor de hacker onmogelijk is om b.v. jsp of php files te wijzigen. Concreet houdt dit in dat bij een geslaagde hack poging de applicatie zichzelf niet kan wijzigen (op instigatie van de hacker), en dat het dus bijzonder moeilijk zal zijn om b.v. aan de database inhoud te komen. Overigens is er bij ons nog nooit een geslaagde hackpoging geweest.

Dit houdt in dat het voor applicaties dus niet mogelijk is om self-modifying code te gebruiken (bv een jsp die een nieuwe versie van zichzelf neerzet). In het geval van MMBase houdt dit in dat er niet via het web-interface nieuwe delen van MMBase geinstalleerd kunnen worden (want de applicatieserver mag niet schrijven in z'n web-app directory) Zelfs als u bijvoorbeeld “chmod 777” (= unix speak voor world writable) op een jsp bestand zou doen, dan nog is het niet te wijzigen vanaf een applicatieserver. Dit willen we dan ook ten zeerste afraden.

De enige plek vanaf waar naar de read-only filesystemen geschreven mag worden is de upload server. Da's ook nodig, anders zouden er b.v. nooit nieuwe jsp bestanden geplaatst kunnen worden.

Users en Groups namen

In het web- app- en testcluster is i.h.a. een vrij stricte relatie tussen een usernaam en waar deze voor gebruikt wordt. B.v. een instantie die leeft onder /e/XX/YYYYYY draait bijna altijd onder het userid YYYYYYXX. Voorbeeld: de webserver instantie /e/fp/dmd1a draait onder als user dmd1afp, de postfix instantie /e/ml/dmd2b draait als dmd2bml.

Alle user accounts worden gecreeerd volgens het “all accounts are created equal” principe, wat inhoudt dat er nooit intrinsieke rechten aan een account hangen. De rechten volgen pas door het account toe te voegen aan specifieke groepen. Om dit af te dwingen worden accounts altijd gecreeerd met uid=gid (dit is unix terminologie, en betekent dat elk account in z'n eigen unieke groep geplaatst wordt). De naam van de groep is gelijk aan de naam van de user. Oftewel, als er een user X is met uid N, dan is z'n primary group-id ook N en de bijbehorende groepsnaam is weer X. Ook aan het uid hangen geen speciale rechten, da's gewoon het eerstvolgende vrije uid boven de 5000. Voorbeeld: de user “test1afp”. Deze heeft de volgende entry in /etc/passwd

test1afp:x:5000:5000:Test webserver:/e/fp/test1a:/sbin/nologin

Zie hoe het userid gelijk is aan het group-id (5000), dus in /etc/group komt de volgende regel voor:

test1afp:x:5000:

Als we nu rechten uit willen delen aan deze user, dan kan dat op basis van group-membership. Typisch voor een webserver is dat deze data moet kunnen wegschrijven en derhalve lid van een data writers (“dw”) group moet zijn:

dwtest:x:20000:test1afp,test1bfp,webtoko1

De groeps-id's hebben ook geen speciale betekenis; het eerste vrije uid boven de 20000. In de groeps namen zit wel enige codering, nl in de eerste twee letters:

  • rd - Reader

De groep voor users die ergens mogen lezen. Dit bestaat om ervoor te zorgen dat ongerelateerde entiteiten niet elkaars spullen mogen lezen. De readers omvat normaal gesproken alle user users die iets met een applicatie te maken hebben: webservers, applicatieservers, databases, uploaders en beheerders.

  • dw - Data writer

De groep voor users die data mogen plaatsen. Dit zijn de uploaders en de processen die zelf data wegschrijven (bv een webserver die ge-upload content ergens weg moet kunnen schrijven)

  • cw - Content Writer

De groep voor users die content (denk aan php code) mogen plaatsen. Dit zijn normaal gesproken alleen de uploaders.

Voorbeeld: stel dat er een applicatie <tt/foo/ is, bestaande uit een paar webservers en een paar uploaders, dan geeft dat aanleiding tot de volgende groepen:

rdfoo:x:20000:uploader1,uploader2,foo1afp,foo1bfp,beheerders
dwfoo:x:20001:uploader1,uploader2,foo1afp,foo1bfp
cwfoo:x:20002:uploader1,uploader2

Het probleem: afscherming en samen kunnen schrijven

Er zijn twee problemen die we op willen lossen:

  • Afscherming

Je wilt niet dat alles voor iedereen leesbaar is. De uploaders van de ene omroep moeten niet de code van een andere omroep kunnen zien.

  • Samen schrijven

Je wilt dat verschillende users dingen op het filesysteem met elkaar kunnen delen. Bijvoorbeeld meerdeer upload accounts die samen aan de code van 1 applicatie moeten kunnen werken, of meerdere geloadbalancede webservers die elk onder een gesharede /data spulletjes moeten kunnen wegschrijven. Ook wil je vaak dat zowel uploaders als webservers samen onder /data kunnens chrijven.

Oplossing voor Afscherming: Hekjes

Een hekje is een directory ergens bovenaan in een directory tree die niet world readable is, slechts group readable en dus alleen te passeren voor users die lid zijn van die groep.

Voorbeeld:

Beschouw de volgende groep:

readers:x:10000:uploader1,uploader2,webserver1,webserver2,beheerders

en deze directory:

/d/netapp/ro/00/ap/www.website.nl

Als we die directory nou owner root:readers, mode 750 geven dan is alles wat daaronder ligt niet meer te benaderen voor users die geen lid van de groep “readers” zijn. Onafhankelijk van de permissies daaronder!

Oplossing voor samen Schrijven: datawriters en contentwriters

Voor het schrijven roepen we ook 2 groepen in het leven: de datawriters en de contentwriters. Contentwriters zijn iha de users met een upload account, die vanuit de upload server de /pages (e.d.) directory mogen vullen. In de datawriters groep zitten de webservers en de uploaders samen. En dat geeft ze rechten om onder /data te schrijven.

Om dit goed te laten werken is het nodig dat zowel datawriters als contentwriters een umask 002 hanteren, zodat wat ze aanmaken default group writable is.

En om dat weer veilig te kunnen doen is het nodig dat ALLE users een uniek primary group ID hebben.

Dus, voorbeeld:

We hebben de users:

webserver1:x:5001:5001:Ik ben een webserver:/var/empty/home:/sbin/nologin
webserver1:x:5002:5002:Ik ben een webserver:/var/empty/home:/sbin/nologin
uploader1:x:5003:5003:Ik ben een uploader:/home/omroep/uploader1:/bin/bash
uploader2:x:5004:5004:Ik ben een uploader:/home/omroep/uploader1:/bin/bash

Dan hebben we de groepen:

readers:x:10000:uploader1,uploader2,webserver1,webserver2,beheerders
contentwriters:x:10001:uploader1,uploader2
datawriters:x:10002:uploader1,uploader2,webserver1,webserver2

En de permissies onder /e/ap/www.website.nl zouden dan zijn:

$ cd /e/ap/www.website.nl
# ls -Lla .
drwxr-x---    2 root     readers       4.0K Jan 14 23:12 .
drwxrwxr-x    2 root     contenwriters 4.0K Jan 14 23:12 pages
drwxrwxr-x    2 root     datawriters   4.0K Jan 14 23:12 data

Granulariteit van readers en content/data-writers

Readers

Laten we per omroep 1 readers groep aanmaken. Dat betekent dat als webtoko1 en webtoko2 allebei voor $omroep werken ze elkaars code kunnen lezen en de code die een omroep zelf opgehoest heeft. Maar, dat zou imo geen probleem moeten zijn.

Content/Data-writers

Je wilt niet dat webtoko1 en webtoko2 over elkaars code heen kunnen krassen. Ook wil je een asymmetrische relatie tussen $omroep en $webtoko, nl $omroep mag wel over de code van $webtoko heenkrassen, maar niet andersom. Dit is te regelen door per omroep-webtoko combinatie een groepje aan te maken:

readers:x:10000:omroep,webtoko1,webtoko2,webserver1,webserver2,beheerders
contentwriters:x:10001:omroep
toko1writers:x:10002:omroep,webtoko1
toko2writers:x:10003:omroep,webtoko2
datawriters:x:10004::omroep,webtoko1,webtoko2,webserver1,webserver2

Op die manier kan toko1 nog wel over de data van toko2 heenschrijven, maar dat konden ze toch al, als ze in dezelfde apache instantie draaien. (nl door het apache maar te laten doen)

$omroep dreigt wel lid van veel groepen te worden op die manier (net zoveel als dat ze gescheiden webtoko's in dienst hebben), dus die moeten wellicht van tijd tot tijd “newgrp toko1writers” intikken om over de code van toko1 heen te kunnen krassen.

Als tokoX nou maar 1 account heeft, en niet hoeft te delen met tokoY, dan kan het simpeler opgelost worden; nl tokoX geen lid te maken van enige contentwriter group en de pages directory owner tokoX. Dwz owner tokoX:contentwriters, mode 775.

Rechten goed houden met php

Bovenstaande rechtenstructuur is essentieel voor de juiste werking van een website. Wanneer er, bijvoorbeeld door de code van de website, wijzigingen in de rechten worden aangebracht kan hierdoor functionaliteit verloren gaan. Het is daarom belangrijk dat de code van de website de rechten niet veranderd wat helaas in php niet altijd voor de hand liggend is. De basis gedachte achter ons platform is: eea is zo opgezet dat de rechten vanzelf goed komen te staan. Wij zien dus het liefst dat er zo min mogelijk gebruik wordt gemaakt van chown/chmod en consorten. Voorwaarde is wel dat het zgn “umask” goed staat; dat moet op '002' staan. In onze web-, sshd- en ftpservers zorgen wij ervoor dat dat goed staat. In shell accounts adviseren wij om in een '.profile' een 'umask 002' op te nemen. Enige probleem geval zijn cronjobs. Cron draait met een verkeerd umask (022); indien er cronjobjes gebruikt worden die mogelijk files of directories zouden kunnen creeeren dan is het aan te raden om in het desbetreffende commando een umask op te nemen; bijvoorbeeld zoiets:

*/5 * * * * umask 002; doe_iets.php

Sommige php functies (b.v. mkdir) willen in sommige gevallen dat er permissies opgegeven worden. De mkdir functie werkt als volgt:

bool mkdir ( string $pathname [, int $mode = 0777 [, bool $recursive = false [, resource $context ]]] )

Dat houdt in dat als je recursive een directory aan wilt maken (“mkdir -p” zeg maar) je opeens verplicht bent om ook permissies op te geven. Gebruik in dat geval de default permissies (hier dus 0777) dat komt omdat de permissies die bij mkdir opgegeven worden, worden ge-bitmasked met je umask en op die manier toch de juiste rechten gebruikt worden.

Bepaalde CMS'en hebben extra settings nodig om directory's met juiste permissies aan te maken. Bijvoorbeeld Drupal; definieer in settings.php:

$settings['file_chmod_directory'] = 02775;
  • file-system-permissies.txt
  • Last modified: 2019/04/26 10:34
  • (external edit)