Modulul 6 · 6.3
Servicii proprii și timere
Modulul 6: Servicii și jurnale · Timp de citire: 11 minute
Obiectivul lecției: După această lecție veți putea rula propriul program ca serviciu systemd sub un utilizator dedicat, cu câteva opțiuni de securitate, și veți putea planifica sarcini cu un timer systemd sau cu cron.
Noțiuni cheie: fișier unit, [Unit], [Service], [Install], ExecStart, User=, WorkingDirectory, EnvironmentFile, Restart=, WantedBy, NoNewPrivileges, ProtectSystem, ProtectHome, PrivateTmp, systemd-analyze security, timer, OnCalendar, Persistent, crontab
De ce nu pur și simplu "nohup" sau "screen"?
Cine pune un program propriu pe un server (o mică aplicație web, un bot, un script care face ceva în fiecare noapte) îl pornește adesea prima dată manual: node server.js & sau într-o sesiune screen. Acest lucru funcționează până când vă deconectați, serverul repornește sau programul se blochează. Atunci a dispărut, iar nimeni nu observă.
Un program care trebuie să ruleze permanent trebuie să fie un serviciu systemd. Astfel:
- pornește automat la inițializarea sistemului;
- systemd îl repornește dacă se blochează;
- ieșirea ajunge în journal (lecția 6.2);
- rulează sub un utilizator ales de dumneavoastră, cu permisiunile pe care le permiteți dumneavoastră.
Un fișier unit propriu
Să presupunem că aveți o mică aplicație Node.js în /opt/factuurapp, care ascultă pe 127.0.0.1:3000. Ulterior, nginx sau Caddy o va expune în exterior (modulul 7).
Pasul 1: un utilizator dedicat, fără shell (lecția 3.2). Aplicația nu are nevoie să fie root și nu trebuie să se autentifice:
anna@web1:~$ sudo adduser --system --group --home /opt/factuurapp --no-create-home factuurapp
anna@web1:~$ sudo chown -R factuurapp:factuurapp /opt/factuurapp/dataCodul în sine poate rămâne în proprietatea lui root (aplicația nu trebuie să se modifice singură); doar directorul în care scrie aparține utilizatorului serviciului.
Pasul 2: fișierul unit. Unit-urile proprii le puneți în /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.targetCele trei secțiuni:
- [Unit]: descrierea și ordinea.
After=network-online.targetîmpreună cuWants=asigură că serviciul pornește abia după ce rețeaua este disponibilă. - [Service]: modul în care rulează programul.
ExecStartcu calea completă (systemd nu folosește o cale de căutare precum shell-ul dumneavoastră).User=șiGroup=stabilesc sub ce utilizator.EnvironmentFilecitește setări, precum o parolă de bază de date, dintr-un fișier separat, astfel încât acestea să nu stea în unit. - [Install]: ce face
enable.WantedBy=multi-user.targetînseamnă: pornește odată cu o inițializare normală a sistemului.
Pasul 3: secretele separat, cu permisiuni stricte.
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/envConținutul poate fi, de exemplu, DATABASE_URL=postgres://factuur:...@127.0.0.1/factuur. systemd citește fișierul ca root înainte de a trece la utilizatorul serviciului, așa că 600 în proprietatea lui root este suficient.
Pasul 4: încărcați, porniți, verificați.
anna@web1:~$ sudo systemctl daemon-reload
anna@web1:~$ sudo systemctl enable --now factuurapp
anna@web1:~$ systemctl status factuurapp
anna@web1:~$ journalctl -u factuurapp -fOpțiuni de securitate: efort mic, diferență mare
Cele patru linii de sub RestartSec din exemplu limitează ce poate face serviciul, chiar și atunci când cineva găsește o vulnerabilitate în aplicația dumneavoastră:
| Opțiune | Efect |
|---|---|
NoNewPrivileges=true | procesul și copiii săi nu pot obține niciodată mai multe permisiuni (nici prin sudo sau prin programe setuid) |
ProtectSystem=strict | întregul sistem de fișiere este doar în citire pentru serviciu, cu excepția a ceea ce permiteți cu ReadWritePaths= |
ProtectHome=true | /home, /root și /run/user sunt invizibile |
PrivateTmp=true | un /tmp propriu, gol, pe care alte programe nu îl văd |
systemd vă poate evalua unit-ul:
anna@web1:~$ systemd-analyze security factuurapp
...
→ Overall exposure level for factuurapp.service: 4.9 OK 🙂Cu cât numărul este mai mic, cu atât mai bine. Un serviciu fără opțiuni are un scor în jur de 9 ("UNSAFE"). Nu trebuie să ajungeți la zero: fiecare opțiune adăugată poate strica ceva de care aplicația are nevoie. Adăugați-le pe rând și testați după fiecare pas. Dacă ceva nu mai funcționează, de obicei mesajul apare în journal ("Read-only file system", adică sistemul de fișiere este doar în citire, sau "Permission denied", adică permisiune refuzată).
Planificarea sarcinilor: timere
Pentru ceva ce trebuie să se întâmple la ore fixe (un backup, un script de curățare, un raport) aveți nevoie de un timer. Un timer systemd constă din două unit-uri cu același nume: un .service care spune ce trebuie să se întâmple și un .timer care spune când.
# /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 înseamnă: programul rulează și apoi se încheie. OnCalendar este programul orar: în fiecare zi la 03:15. Puteți scrie și daily, weekly sau Mon *-*-* 02:00; systemd-analyze calendar "Mon *-*-* 02:00" arată când va fi următoarea rulare. Persistent=true este important: dacă serverul era oprit la 03:15, sarcina rulează totuși imediat după pornire. RandomizedDelaySec distribuie puțin momentul de start.
Activarea se face cu timer-ul, nu cu serviciul:
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 # testați acum o dată manual
anna@web1:~$ journalctl -u opruimen.serviceȘi cron?
cron este instrumentul mai vechi și mai simplu și funcționează în continuare foarte bine. O linie în crontab-ul unui utilizator (crontab -e):
15 3 * * * /usr/local/bin/opruimen.shCele cinci câmpuri sunt minutul, ora, ziua din lună, luna și ziua din săptămână. Avantaje și dezavantaje, alăturate:
| cron | timer systemd | |
|---|---|---|
| Configurare | o singură linie | două fișiere |
| Rulare ratată (server oprit) | este sărită | cu Persistent=true rulează totuși |
| Ieșire și erori | prin e-mail, dacă serverul poate trimite e-mail; altfel se pierd | în journal |
| Opțiuni de securitate, utilizator | prin scriptul însuși | tot ce oferă systemd (lecția de mai sus) |
| Vedeți când a rulat | dificil | systemctl list-timers și journal-ul |
Pentru un server pe care îl administrați serios, timerele sunt alegerea mai bună, mai ales pentru backupuri (lecția 8.2): doriți să știți sigur că au rulat și ce au raportat. O linie cron care eșuează în tăcere o observați abia când aveți nevoie de backup.
Pe AlmaLinux și Rocky Linux
Unit-urile și timerele funcționează exact la fel. O diferență: SELinux poate bloca un serviciu propriu care citește sau scrie fișiere într-un loc la care SELinux nu se așteaptă ori care ascultă pe un port neobișnuit. Mesajul apare în /var/log/audit/audit.log. Nu dezactivați SELinux pentru a rezolva problema; căutați eticheta sau regula corectă (de exemplu cu semanage).
Rezumat
- Un program care trebuie să ruleze permanent trebuie să fie un serviciu systemd: pornire automată, repornire la blocare, jurnale în journal.
- Unit-urile proprii stau în
/etc/systemd/system/, cuUser=un utilizator dedicat fără shell, iar secretele într-unEnvironmentFilecu permisiunile 600. NoNewPrivileges,ProtectSystem=strictcuReadWritePaths,ProtectHomeșiPrivateTmplimitează pagubele în cazul unei vulnerabilități;systemd-analyze securityevaluează unit-ul.- Un timer înseamnă un
.serviceplus un.timercuOnCalendar;Persistent=truerecuperează rulările ratate. Activați timer-ul, nu serviciul. - cron funcționează în continuare, dar timerele oferă jurnale mai bune și nu ratează rulări.
Test (quiz)
Exercițiu
Exercițiul O6.3: Un serviciu propriu cu timer. Creați pe serverul dumneavoastră un mic serviciu propriu cu un timer, de exemplu un script care în fiecare noapte scrie spațiul liber pe disc și systemctl --failed într-un fișier sau în journal. Folosiți un utilizator dedicat, Type=oneshot, cel puțin două opțiuni de securitate și Persistent=true. Testați cu systemctl start, consultați ieșirea în journal și notați scorul obținut cu systemd-analyze security. Lipiți fișierele .service și .timer și răspundeți la întrebările din lista de verificare.
Exercițiul O6.3: Un serviciu propriu cu timer
Acest exercițiu îl faceți singur; nimeni nu îl verifică. Bifați ce ați făcut și marcați exercițiul ca finalizat.
Cu Persistent=true sarcina rulează totuși după pornire. Un scor sub 6 este bun pentru un prim serviciu; drepturi de scriere doar prin ReadWritePaths.
Bifele și notițele dumneavoastră rămân doar în acest browser; nu le trimitem nicăieri. Dacă sunteți autentificat, păstrăm în Cursurile mele doar faptul că ați finalizat exercițiul.
Lecturi suplimentare
- Manual DirectAdmin 15.1: Cron Jobs: rularea automată a sarcinilor
- Manualul
systemd.exec(5)(User=, ProtectSystem=, NoNewPrivileges=): https://manpages.debian.org/trixie/systemd/systemd.exec.5.en.html - Manualul
systemd.timer(5)(OnCalendar=, Persistent=): https://manpages.debian.org/trixie/systemd/systemd.timer.5.en.html - Manualul
systemd.time(7)(programări orare): https://manpages.debian.org/trixie/systemd/systemd.time.7.en.html
