Gå direkte til indholdet

Cookieindstillinger

Vælg for hver type, hvilke cookies du tillader. Du kan altid ændre dit valg via Cookieindstillinger nederst på hver side. Privatlivspolitik

Nødvendige

Altid slået til

Nødvendige for at logge ind, gemme din kurv og huske dine valg. De er altid slået til.

Hvilke cookies?
  • weboke_sessie, weboke_klant, weboke_remember: din session og at forblive logget ind
  • weboke_basket: din kurv
  • wkBtwIncl: vise priser med eller uden moms
  • wok_cookies: dit cookievalg, 12 måneder

Måler, hvilken annonce der fører til et besøg eller en bestilling, så vi ikke betaler for annoncer, der ikke virker.

Hvilke cookies?
  • Google Ads via Google Tag Manager (_gcl_au, _gcl_aw), op til 90 dage
  • OpenAI Ads, annoncer i ChatGPT
  • wok_aff: hvilken partner der henviste dig til os (affiliateprogram), 60 dage

4,6

Land og sprog

Hvert land har sin egen side med landets priser og moms.

Se

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/data

Selve 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.target

De tre afsnit:

  • [Unit]: beskrivelse og rækkefølge. After=network-online.target med Wants= sørger for, at tjenesten først starter, når netværket er klar.
  • [Service]: hvordan programmet kører. ExecStart med den fulde sti (systemd bruger ikke en søgesti som din shell). User= og Group= bestemmer, hvem det kører som. EnvironmentFile læser indstillinger som en databaseadgangskode fra en separat fil, så de ikke står i unit-filen.
  • [Install]: hvad enable gør. WantedBy=multi-user.target betyder: 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/env

Indholdet 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 -f

Sikkerhedsindstillinger: 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:

IndstillingEffekt
NoNewPrivileges=trueprocessen og dens børn kan aldrig få flere rettigheder (heller ikke via sudo eller setuid-programmer)
ProtectSystem=stricthele filsystemet er skrivebeskyttet for tjenesten, undtagen det, du tillader med ReadWritePaths=
ProtectHome=true/home, /root og /run/user er usynlige
PrivateTmp=trueen 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.target

Type=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.service

Og 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.sh

De fem felter er minut, time, dag i måneden, måned og ugedag. Fordele og ulemper side om side:

cronsystemd-timer
Opsætningén linjeto filer
Mistet kørsel (server slukket)springes overmed Persistent=true alligevel
Output og fejlpr. mail, hvis serveren kan sende mail; ellers væki journalen
Sikkerhedsindstillinger, brugervia selve scriptetalt fra systemd (afsnittet ovenfor)
Se, hvornår den kørtebesværligtsystemctl 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/, med User= en egen bruger uden shell, hemmeligheder i en EnvironmentFile med rettigheder 600.
  • NoNewPrivileges, ProtectSystem=strict med ReadWritePaths, ProtectHome og PrivateTmp begrænser skaden ved et hul; systemd-analyze security vurderer unit-filen.
  • En timer er en .service plus en .timer med OnCalendar; Persistent=true indhenter mistede kørsler. Slå timeren til, ikke servicen.
  • cron virker stadig, men timere giver bedre logs og misser ingen kørsler.

Quiz

1. Din applikation kører som tjeneste med User=root. Hvorfor er en egen bruger bedre?



2. Du har lavet en .service og en .timer. Hvad slår du til med enable --now?



3. Hvad gør Persistent=true i en timer?



4. Du tilføjer ProtectSystem=strict, og bagefter giver din applikation "Read-only file system", når den gemmer uploads. Hvad er den rigtige løsning?



5. Hvor lægger du databaseadgangskoden til din egen tjeneste?



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.




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

Et øjeblik…

Opsæt dine domæner

Angiv for hvert domæne navneserverne, tilknytningen til din hosting, og hvem der bliver ejer.

Indlæser dine domæner…