Direct naar de inhoud

Cookie-instellingen

Kies per soort welke cookies je toestaat. Je kunt je keuze altijd wijzigen via Cookie-instellingen onderaan elke pagina. Privacyverklaring

Noodzakelijk

Altijd aan

Nodig om in te loggen, je winkelwagen te bewaren en je keuzes te onthouden. Deze staan altijd aan.

Welke cookies?
  • weboke_sessie, weboke_klant, weboke_remember: je sessie en ingelogd blijven
  • weboke_basket: je winkelwagen
  • wkBtwIncl: prijzen met of zonder btw tonen
  • wok_cookies: je cookiekeuze, 12 maanden

Meet welke advertentie tot een bezoek of bestelling leidt, zodat we niet betalen voor advertenties die niets doen.

Welke cookies?
  • Google Ads via Google Tag Manager (_gcl_au, _gcl_aw), tot 90 dagen
  • OpenAI Ads, advertenties in ChatGPT
  • wok_aff: via welke partner je bij ons kwam (affiliateprogramma), 60 dagen

4,6

Land en taal

Elk land heeft zijn eigen site, met prijzen en btw van dat land.

Bekijken

Module 6 · 6.3

Eigen diensten en timers

Module 6: Diensten en logs · Leestijd: 11 minuten

Leerdoel: Na deze les kun je een eigen programma als systemd-dienst laten draaien onder een eigen gebruiker, met een paar beveiligingsopties, en taken plannen met een systemd-timer of cron.

Kernbegrippen: unit-bestand, [Unit], [Service], [Install], ExecStart, User=, WorkingDirectory, EnvironmentFile, Restart=, WantedBy, NoNewPrivileges, ProtectSystem, ProtectHome, PrivateTmp, systemd-analyze security, timer, OnCalendar, Persistent, crontab

Waarom niet gewoon "nohup" of "screen"?

Wie een eigen programma op een server zet (een kleine webapplicatie, een bot, een script dat elke nacht iets doet), start het de eerste keer vaak met de hand: node server.js &, of in een screen-sessie. Dat werkt tot je uitlogt, de server herstart of het programma crasht. Dan is het weg, en niemand merkt het.

Een programma dat altijd moet draaien, hoort een systemd-dienst te zijn. Dan:

  • start het vanzelf bij het opstarten;
  • start systemd het opnieuw als het crasht;
  • komt de uitvoer in de journal (les 6.2);
  • draait het onder een gebruiker die je kiest, met de rechten die jij toestaat.

Een eigen unit-bestand

Stel: je hebt een kleine Node.js-applicatie in /opt/factuurapp, die op 127.0.0.1:3000 luistert. nginx of Caddy zet hem straks naar buiten (module 7).

Stap 1: een eigen gebruiker zonder shell (les 3.2). De applicatie hoeft geen root te zijn en hoeft niet in te loggen:

anna@web1:~$ sudo adduser --system --group --home /opt/factuurapp --no-create-home factuurapp
anna@web1:~$ sudo chown -R factuurapp:factuurapp /opt/factuurapp/data

De code zelf mag van root blijven (de applicatie hoeft zichzelf niet aan te passen); alleen de map waarin hij schrijft, is van de dienstgebruiker.

Stap 2: het unit-bestand. Eigen units zet je in /etc/systemd/system/:

# /etc/systemd/system/factuurapp.service
[Unit]
Description=Factuurapp (Node.js)
After=network-online.target
Wants=network-online.target

[Service]
User=factuurapp
Group=factuurapp
WorkingDirectory=/opt/factuurapp
EnvironmentFile=/etc/factuurapp/env
ExecStart=/usr/bin/node /opt/factuurapp/server.js
Restart=on-failure
RestartSec=5s

NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=/opt/factuurapp/data

[Install]
WantedBy=multi-user.target

De drie blokken:

  • [Unit]: beschrijving en volgorde. After=network-online.target met Wants= zorgt dat de dienst pas start als het netwerk er is.
  • [Service]: hoe het programma draait. ExecStart met het volledige pad (systemd gebruikt geen zoekpad zoals je shell). User= en Group= bepalen onder wie. EnvironmentFile leest instellingen zoals een databasewachtwoord uit een apart bestand, zodat ze niet in de unit staan.
  • [Install]: wat enable doet. WantedBy=multi-user.target betekent: start mee bij een normale opstart.

Stap 3: geheimen apart, met strenge rechten.

anna@web1:~$ sudo mkdir /etc/factuurapp
anna@web1:~$ sudo install -m 600 -o root -g root /dev/null /etc/factuurapp/env
anna@web1:~$ sudoedit /etc/factuurapp/env

Inhoud bijvoorbeeld DATABASE_URL=postgres://factuur:...@127.0.0.1/factuur. systemd leest het bestand als root vóór het overschakelt naar de dienstgebruiker, dus 600 van root is genoeg.

Stap 4: laden, starten, controleren.

anna@web1:~$ sudo systemctl daemon-reload
anna@web1:~$ sudo systemctl enable --now factuurapp
anna@web1:~$ systemctl status factuurapp
anna@web1:~$ journalctl -u factuurapp -f

Beveiligingsopties: kleine moeite, groot verschil

De vier regels onder RestartSec in het voorbeeld beperken wat de dienst kan, ook als iemand een lek in je applicatie vindt:

OptieEffect
NoNewPrivileges=truehet proces en zijn kinderen kunnen nooit meer rechten krijgen (ook niet via sudo of setuid-programma's)
ProtectSystem=stricthet hele bestandssysteem is alleen-lezen voor de dienst, behalve wat je met ReadWritePaths= toestaat
ProtectHome=true/home, /root en /run/user zijn onzichtbaar
PrivateTmp=trueeen eigen, lege /tmp die andere programma's niet zien

systemd kan je unit beoordelen:

anna@web1:~$ systemd-analyze security factuurapp
...
→ Overall exposure level for factuurapp.service: 4.9 OK 🙂

Hoe lager het getal, hoe beter. Een dienst zonder opties scoort rond de 9 ("UNSAFE"). Je hoeft niet naar nul: elke optie die je toevoegt, kan iets breken wat de applicatie nodig heeft. Voeg ze één voor één toe en test na elke stap. Werkt iets niet meer, dan staat het meestal in de journal ("Read-only file system", "Permission denied").

Taken plannen: timers

Voor iets wat op vaste tijden moet gebeuren (een back-up, een opruimscript, een rapport) heb je een timer nodig. Een systemd-timer bestaat uit twee units met dezelfde naam: een .service die zegt wat er moet gebeuren, en een .timer die zegt wanneer.

# /etc/systemd/system/opruimen.service
[Unit]
Description=Oude exportbestanden opruimen

[Service]
Type=oneshot
User=factuurapp
ExecStart=/usr/bin/find /opt/factuurapp/data/export -type f -mtime +30 -delete
# /etc/systemd/system/opruimen.timer
[Unit]
Description=Dagelijks oude exportbestanden opruimen

[Timer]
OnCalendar=*-*-* 03:15:00
RandomizedDelaySec=10m
Persistent=true

[Install]
WantedBy=timers.target

Type=oneshot betekent: het programma draait en is daarna klaar. OnCalendar is het tijdschema: elke dag om 03:15. Je kunt ook daily, weekly of Mon *-*-* 02:00 schrijven; systemd-analyze calendar "Mon *-*-* 02:00" laat zien wanneer dat de volgende keer is. Persistent=true is belangrijk: stond de server om 03:15 uit, dan draait de taak meteen na het opstarten alsnog. RandomizedDelaySec spreidt het startmoment een beetje.

Aanzetten doe je met de timer, niet met de service:

anna@web1:~$ sudo systemctl daemon-reload
anna@web1:~$ sudo systemctl enable --now opruimen.timer
anna@web1:~$ systemctl list-timers opruimen
NEXT                        LEFT     LAST PASSED UNIT           ACTIVATES
Tue 2026-11-10 03:19:42 CET 15h left -    -      opruimen.timer opruimen.service
anna@web1:~$ sudo systemctl start opruimen.service    # nu één keer met de hand testen
anna@web1:~$ journalctl -u opruimen.service

En cron?

cron is het oudere, eenvoudigere gereedschap en werkt nog prima. Een regel in de crontab van een gebruiker (crontab -e):

15 3 * * * /usr/local/bin/opruimen.sh

De vijf velden zijn minuut, uur, dag van de maand, maand en dag van de week. Voor- en nadelen naast elkaar:

cronsystemd-timer
Opzettenéén regeltwee bestanden
Gemiste run (server uit)wordt overgeslagenmet Persistent=true alsnog
Uitvoer en foutenper mail, als de server kan mailen; anders wegin de journal
Beveiligingsopties, gebruikervia het script zelfalles van systemd (les hierboven)
Zien wanneer hij lieplastigsystemctl list-timers en de journal

Voor een server die je serieus beheert, zijn timers de betere keus, vooral voor back-ups (les 8.2): je wilt zeker weten dat ze liepen en wat ze zeiden. Een cronregel die stil faalt, merk je pas als je de back-up nodig hebt.

Op AlmaLinux en Rocky Linux

Units en timers werken precies hetzelfde. Een verschil: SELinux kan een eigen dienst tegenhouden die bestanden leest of schrijft op een plek die SELinux niet verwacht, of die op een ongebruikelijke poort luistert. De melding staat in /var/log/audit/audit.log. Zet SELinux niet uit om het op te lossen; zoek de juiste label of regel (bijvoorbeeld met semanage).

Samenvatting

  • Een programma dat altijd moet draaien, hoort een systemd-dienst te zijn: vanzelf starten, herstarten bij een crash, logs in de journal.
  • Eigen units in /etc/systemd/system/, met User= een eigen gebruiker zonder shell, geheimen in een EnvironmentFile met rechten 600.
  • NoNewPrivileges, ProtectSystem=strict met ReadWritePaths, ProtectHome en PrivateTmp beperken de schade bij een lek; systemd-analyze security beoordeelt de unit.
  • Een timer is een .service plus een .timer met OnCalendar; Persistent=true haalt gemiste runs in. Zet de timer aan, niet de service.
  • cron werkt nog, maar timers geven betere logs en missen geen runs.

Quiz

1. Je applicatie draait als dienst met User=root. Waarom is een eigen gebruiker beter?



2. Je hebt een .service en een .timer gemaakt. Wat zet je aan met enable --now?



3. Wat doet Persistent=true in een timer?



4. Je voegt ProtectSystem=strict toe en je applicatie geeft daarna "Read-only file system" bij het opslaan van uploads. Wat is de juiste oplossing?



5. Waar zet je het databasewachtwoord van je eigen dienst?



Opdracht

Opdracht O6.3: Een eigen dienst met timer. Maak op je server een kleine eigen dienst met een timer, bijvoorbeeld een script dat elke nacht de vrije schijfruimte en systemctl --failed in een bestand of de journal schrijft. Gebruik een eigen gebruiker, Type=oneshot, minstens twee beveiligingsopties en Persistent=true. Test met systemctl start, bekijk de uitvoer in de journal, en noteer de score van systemd-analyze security. Plak je .service en .timer en beantwoord de vragen in de checklist.

Opdracht O6.3: Een eigen dienst met timer

Deze opdracht doe je zelf; niemand kijkt hem na. Vink af wat je gedaan hebt en markeer de opdracht als gedaan.




Je vinkjes en aantekeningen blijven alleen in deze browser; we sturen ze nergens heen. Ben je aangemeld, dan bewaren we in Mijn cursussen alleen dat je de opdracht gedaan hebt.

Verder lezen

Even geduld…

Stel je domeinen in

Regel per domein de nameservers, de koppeling aan je hosting en wie de eigenaar wordt.

Je domeinen laden…