Modul 6 · 6.3
Egne tjenester og timere
Modul 6: Tjenester og logs · Læsetid: 11 minutter
Læringsmål: Efter denne lektion kan du køre dit eget program som systemd-tjeneste under en egen bruger, med et par sikkerhedsindstillinger, og planlægge opgaver med en systemd-timer eller cron.
Centrale begreber: unit-fil, [Unit], [Service], [Install], ExecStart, User=, WorkingDirectory, EnvironmentFile, Restart=, WantedBy, NoNewPrivileges, ProtectSystem, ProtectHome, PrivateTmp, systemd-analyze security, timer, OnCalendar, Persistent, crontab
Hvorfor ikke bare "nohup" eller "screen"?
Når man lægger et eget program på en server (en lille webapplikation, en bot, et script der gør noget hver nat), starter man det ofte i hånden første gang: node server.js &, eller i en screen-session. Det virker, indtil du logger ud, serveren genstarter eller programmet crasher. Så er det væk, og ingen opdager det.
Et program, der altid skal køre, bør være en systemd-tjeneste. Så:
- starter det af sig selv ved opstart;
- starter systemd det igen, hvis det crasher;
- havner outputtet i journalen (lektion 6.2);
- kører det under en bruger, du vælger, med de rettigheder, du tillader.
En egen unit-fil
Lad os sige, at du har en lille Node.js-applikation i /opt/factuurapp, som lytter på 127.0.0.1:3000. nginx eller Caddy gør den senere tilgængelig udefra (modul 7).
Trin 1: en egen bruger uden shell (lektion 3.2). Applikationen behøver ikke være root og behøver ikke at logge ind:
anna@web1:~$ sudo adduser --system --group --home /opt/factuurapp --no-create-home factuurapp
anna@web1:~$ sudo chown -R factuurapp:factuurapp /opt/factuurapp/dataSelve koden kan godt forblive ejet af root (applikationen behøver ikke ændre sig selv); kun mappen, den skriver i, ejes af tjenestebrugeren.
Trin 2: unit-filen. Egne units lægger du i /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.targetDe tre afsnit:
- [Unit]: beskrivelse og rækkefølge.
After=network-online.targetmedWants=sørger for, at tjenesten først starter, når netværket er klar. - [Service]: hvordan programmet kører.
ExecStartmed den fulde sti (systemd bruger ikke en søgesti som din shell).User=ogGroup=bestemmer, hvem det kører som.EnvironmentFilelæser indstillinger som en databaseadgangskode fra en separat fil, så de ikke står i unit-filen. - [Install]: hvad
enablegør.WantedBy=multi-user.targetbetyder: start med ved en normal opstart.
Trin 3: hemmeligheder for sig, med strenge rettigheder.
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/envIndholdet kunne for eksempel være DATABASE_URL=postgres://factuur:...@127.0.0.1/factuur. systemd læser filen som root, før den skifter til tjenestebrugeren, så 600 ejet af root er nok.
Trin 4: indlæs, start, kontrollér.
anna@web1:~$ sudo systemctl daemon-reload
anna@web1:~$ sudo systemctl enable --now factuurapp
anna@web1:~$ systemctl status factuurapp
anna@web1:~$ journalctl -u factuurapp -fSikkerhedsindstillinger: lille indsats, stor forskel
De fire linjer under RestartSec i eksemplet begrænser, hvad tjenesten kan, også hvis nogen finder et hul i din applikation:
| Indstilling | Effekt |
|---|---|
NoNewPrivileges=true | processen og dens børn kan aldrig få flere rettigheder (heller ikke via sudo eller setuid-programmer) |
ProtectSystem=strict | hele filsystemet er skrivebeskyttet for tjenesten, undtagen det, du tillader med ReadWritePaths= |
ProtectHome=true | /home, /root og /run/user er usynlige |
PrivateTmp=true | en egen, tom /tmp, som andre programmer ikke kan se |
systemd kan vurdere din unit:
anna@web1:~$ systemd-analyze security factuurapp
...
→ Overall exposure level for factuurapp.service: 4.9 OK 🙂Jo lavere tal, jo bedre. En tjeneste uden indstillinger får omkring 9 ("UNSAFE"). Du behøver ikke ned på nul: hver indstilling, du tilføjer, kan ødelægge noget, applikationen har brug for. Tilføj dem én ad gangen, og test efter hvert trin. Holder noget op med at virke, står det som regel i journalen ("Read-only file system", "Permission denied").
Planlæg opgaver: timere
Til noget, der skal ske på faste tidspunkter (en backup, et oprydningsscript, en rapport), har du brug for en timer. En systemd-timer består af to units med samme navn: en .service, der siger, hvad der skal ske, og en .timer, der siger hvornår.
# /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.targetType=oneshot betyder: programmet kører og er derefter færdigt. OnCalendar er tidsplanen: hver dag kl. 03:15. Du kan også skrive daily, weekly eller Mon *-*-* 02:00; systemd-analyze calendar "Mon *-*-* 02:00" viser, hvornår det er næste gang. Persistent=true er vigtig: var serveren slukket kl. 03:15, kører opgaven alligevel lige efter opstart. RandomizedDelaySec spreder starttidspunktet en smule.
Du slår det til med timeren, ikke med servicen:
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 # test nu én gang manuelt
anna@web1:~$ journalctl -u opruimen.serviceOg cron?
cron er det ældre, enklere værktøj og virker stadig fint. En linje i en brugers crontab (crontab -e):
15 3 * * * /usr/local/bin/opruimen.shDe fem felter er minut, time, dag i måneden, måned og ugedag. Fordele og ulemper side om side:
| cron | systemd-timer | |
|---|---|---|
| Opsætning | én linje | to filer |
| Mistet kørsel (server slukket) | springes over | med Persistent=true alligevel |
| Output og fejl | pr. mail, hvis serveren kan sende mail; ellers væk | i journalen |
| Sikkerhedsindstillinger, bruger | via selve scriptet | alt fra systemd (afsnittet ovenfor) |
| Se, hvornår den kørte | besværligt | systemctl list-timers og journalen |
På en server, du administrerer seriøst, er timere det bedste valg, især til backup (lektion 8.2): du vil vide med sikkerhed, at de kørte, og hvad de skrev. En cronlinje, der fejler i stilhed, opdager du først, når du får brug for backuppen.
På AlmaLinux og Rocky Linux
Units og timere virker præcis på samme måde. En forskel: SELinux kan blokere en egen tjeneste, der læser eller skriver filer et sted, SELinux ikke forventer, eller som lytter på en usædvanlig port. Meddelelsen står i /var/log/audit/audit.log. Slå ikke SELinux fra for at løse det; find den rigtige label eller regel (for eksempel med semanage).
Opsummering
- Et program, der altid skal køre, bør være en systemd-tjeneste: starter af sig selv, genstarter ved crash, logs i journalen.
- Egne units i
/etc/systemd/system/, medUser=en egen bruger uden shell, hemmeligheder i enEnvironmentFilemed rettigheder 600. NoNewPrivileges,ProtectSystem=strictmedReadWritePaths,ProtectHomeogPrivateTmpbegrænser skaden ved et hul;systemd-analyze securityvurderer unit-filen.- En timer er en
.serviceplus en.timermedOnCalendar;Persistent=trueindhenter mistede kørsler. Slå timeren til, ikke servicen. - cron virker stadig, men timere giver bedre logs og misser ingen kørsler.
Quiz
Opgave
Opgave O6.3: En egen tjeneste med timer. Lav en lille egen tjeneste med en timer på din server, for eksempel et script, der hver nat skriver den ledige diskplads og systemctl --failed til en fil eller journalen. Brug en egen bruger, Type=oneshot, mindst to sikkerhedsindstillinger og Persistent=true. Test med systemctl start, se outputtet i journalen, og notér scoren fra systemd-analyze security. Indsæt din .service og .timer, og besvar spørgsmålene i tjeklisten.
Opgave O6.3: En egen tjeneste med timer
Denne opgave laver du selv; ingen tjekker den. Afkryds hvad du har gjort, og markér opgaven som udført.
Med Persistent=true kører opgaven alligevel efter opstart. En score under 6 er god for en første tjeneste; skriverettigheder kun via ReadWritePaths.
Dine afkrydsninger og noter forbliver kun i denne browser; vi sender dem ingen steder hen. Er du logget ind, gemmer vi kun i Mine kurser, at du har udført opgaven.
Læs mere
- DirectAdmin vejledning 15.1: Cron-jobs: lad opgaver køre automatisk
- Manual
systemd.exec(5)(User=, ProtectSystem=, NoNewPrivileges=): https://manpages.debian.org/trixie/systemd/systemd.exec.5.en.html - Manual
systemd.timer(5)(OnCalendar=, Persistent=): https://manpages.debian.org/trixie/systemd/systemd.timer.5.en.html - Manual
systemd.time(7)(tidsplaner): https://manpages.debian.org/trixie/systemd/systemd.time.7.en.html
