Linux services draaien als unprivileged user
Voorkom dat een bug in uw software leidt tot volledige root-toegang. Leer hoe u services veilig isoleert met specifieke systeemaccounts of de DynamicUser optie in systemd.
Waarom niet alles als root uitvoeren
De root-gebruiker heeft volledige controle over de machine: elk bestand kan worden gelezen, elke instelling kan worden gewijzigd en het volledige systeem kan worden verwijderd. Wanneer u een service als root uitvoert, geeft u al deze bevoegdheden aan die service. Als de service een bug bevat die een aanvaller kan misbruiken, krijgt deze niet alleen toegang tot de service, maar direct tot root-rechten, wat neerkomt op de volledige server. Het uitvoeren van processen als een gebruiker zonder privileges beperkt de schade. Een bug in een service die onder een beperkt account draait, geeft een aanvaller alleen toegang tot wat dat account kan bereiken, wat idealiter vrijwel niets is.
Dit is het principe van de minste privileges: geef elk onderdeel van het systeem precies de toegang die nodig is om zijn taak uit te voeren, en niets meer. Dit is de meest effectieve gewoonte om de impact van een inbreuk te beperken, en op een moderne server kost het toepassen hiervan vrijwel geen moeite.
Een toegewezen account per service
De klassieke aanpak is om voor elke service een afzonderlijke systeemgebruiker aan te maken. Deze gebruiker is enkel eigenaar van de bestanden van die specifieke service en kan niet inloggen. Een systeemaccount voor een webapplicatie ziet er als volgt uit:
sudo useradd --system --no-create-home --shell /usr/sbin/nologin appsvcElke flag is van belang. --system maakt er een serviceaccount van, geen account voor een menselijke gebruiker. --no-create-home slaat een home-directory over die niet nodig is. --shell /usr/sbin/nologin zorgt ervoor dat, zelfs als een aanvaller op de een of andere manier toegang krijgt tot het account, deze geen shell kan openen. Het account bestaat uitsluitend om eigenaar te zijn van een proces en de bijbehorende bestanden.
Wijs vervolgens aan die gebruiker alleen de benodigde bestanden toe, en niets meer:
sudo chown -R appsvc:appsvc /opt/myappDe service leest en schrijft nu uitsluitend in de eigen directory en heeft nergens anders op de schijf iets te zoeken. Mocht de service ooit worden gecompromitteerd, dan zijn de bestanden die de aanvaller kan wijzigen beperkt tot /opt/myapp; het account kan nog steeds alles lezen wat voor iedereen toegankelijk is, maar het kan de rest van het systeem niet aanpassen.
Laat systemd het uitvoeren als die gebruiker
Zodra het account bestaat, geeft u aan systemd door dat de service onder dit account moet draaien. In het unit-bestand volstaat één regel:
[Service]
ExecStart=/opt/myapp/bin/server
User=appsvc
Group=appsvcUser=appsvc zorgt ervoor dat het proces start met de beperkte rechten van dat account in plaats van die van root. Dit is de standaard en beproefde methode om een applicatie onder systemd uit te voeren; het is aan te raden dit voor elke service waarvoor u een unit schrijft te doen. Het verlagen van rechten helpt echter niet als systemd het verkeerde proces in de gaten houdt. Als de unit dus nog steeds als actief wordt gerapporteerd nadat de daemon stilletjes is afgesloten, controleer dan of u het juiste Type= heeft gekozen voor de manier waarop uw proces start.
Of sla het account volledig over met DynamicUser
systemd kan nog een stap verder gaan en een tijdelijke gebruiker voor u aanmaken, die alleen bestaat zolang de service actief is. Stel DynamicUser=yes in en u hoeft helemaal geen account te beheren:
[Service]
ExecStart=/opt/myapp/bin/server
DynamicUser=yes
StateDirectory=myappBij het starten wijst systemd een ongebruikte user ID toe; bij het stoppen wordt deze weer vrijgegeven. De service krijgt ook een eigen /tmp, een alleen-lezen weergave van het grootste deel van het bestandssysteem, en een beschrijfbare state-directory onder /var/lib/myapp die door StateDirectory= wordt opgezet en toegewezen. Niets hiervan was mogelijk onder SysV init, waar het verlagen van privileges werd overgelaten aan wat het opstartscript van een service toevallig deed. Dat gat is een belangrijke reden waarom distributies in de eerste plaats naar systemd zijn overgestapt. Voor een zelfstandige service die alleen een eigen state-directory nodig heeft, is DynamicUser=yes de minst inspannende manier om sterke isolatie te verkrijgen, omdat er geen langdurig bestaand account is waarop een aanvaller zich kan richten.
Het handmatig schrijven van units is nauwkeurig werk, en het correct instellen van de hardening-richtlijnen vormt het grootste deel van de waarde. De generator in de systemd service and timer guide kan deze opties voor u invullen, zodat de unit direct correct is.
Hoe dit in het geheel past
Het principe van 'least privilege' is één laag en werkt samen met de andere lagen in plaats van ze te vervangen. Een default-deny firewall bepaalt wat de service kan bereiken; het draaien als een gebruiker zonder extra rechten bepaalt wat de service kan doen als deze wordt gecompromitteerd; en hardened SSH houdt aanvallers in de eerste plaats buiten de server. Geen van deze maatregelen is op zichzelf voldoende, maar samen zorgen ze ervoor dat een bug in één service niet leidt tot een compromis van de gehele server. Het hosten van applicaties die geheimen beheren laat zien waar deze lagen ophouden: een beperkt account beperkt wat een gecompromitteerd proces kan aanraken, maar een self-hosted wachtwoordmanager zoals Vaultwarden staat of valt nog steeds bij de manier waarop u het admin-token en het back-upbestand beveiligt; gebruikersisolatie dekt deze aspecten niet af.
Voordat u verdergaat, doorloopt u een hardening-checklist voor de gehele server en genereert u een persoonlijke kopie om mee te werken:
FAQ
Waarom zou ik een service niet als root uitvoeren?
Omdat root alles op de machine kan doen, geeft een service die als root draait en wordt gecompromitteerd de aanvaller de volledige server in handen, niet alleen de service. Door de service uit te voeren als een beperkt account zonder privileges, blijft de schade beperkt tot wat dat account kan benaderen. Reserveer root voor beheer en voer elke langlopende service uit als een beperkte gebruiker.
Hoe maak ik een gebruiker aan die niet kan inloggen?
Voer sudo useradd --system --no-create-home --shell /usr/sbin/nologin NAME uit. De nologin shell betekent dat het account geen interactieve sessie kan openen, zelfs niet als de inloggegevens worden gestolen, --system markeert het als een service-account en --no-create-home slaat een home-directory over die niet nodig is. Geef het alleen eigenaarschap over zijn eigen bestanden met chown.
Wat is systemd DynamicUser?
DynamicUser=yes vertelt systemd om een tijdelijke gebruiker voor de service aan te maken die alleen bestaat zolang de service draait, zodat u nooit een langdurig account hoeft te beheren. Het geeft de service ook een privé /tmp, een grotendeels alleen-lezen bestandssysteemweergave en een beheerde state-directory. Het is de minst inspannende manier om een op zichzelf staande service uit te voeren onder een tijdelijke identiteit met lage privileges.
Vervangt het draaien als een niet-root gebruiker een firewall?
Nee. Ze beschermen verschillende zaken. Draaien als een gebruiker zonder privileges beperkt wat een service kan doen als deze wordt gecompromitteerd, terwijl een firewall beperkt wat de service überhaupt kan bereiken. Gebruik beide, samen met beveiligde SSH, zodat elke laag de zaken dekt die de andere niet kunnen.
Welke bestanden moet de servicegebruiker bezitten?
Alleen de bestanden die de service daadwerkelijk nodig heeft, en niets meer. Geef het account eigenaarschap over zijn eigen werkdirectory en zijn data, en laat al het overige eigendom van root. Een goed patroon is sudo chown -R svc-app:svc-app /opt/svc-app voor de applicatiedirectory, terwijl configuratie onder /etc eigendom van root blijft en alleen leesbaar is voor de service. Het doel is dat als het proces ooit wordt gecompromitteerd, de bestanden die het kan wijzigen beperkt blijven tot zijn eigen data, en niet het rest van het systeem.