Ενότητα 6 · 6.3
Δικές σας υπηρεσίες και timers
Ενότητα 6: Υπηρεσίες και αρχεία καταγραφής · Χρόνος ανάγνωσης: 11 λεπτά
Στόχος μαθήματος: Μετά από αυτό το μάθημα μπορείτε να εκτελείτε ένα δικό σας πρόγραμμα ως υπηρεσία systemd με δικό του χρήστη και μερικές επιλογές ασφαλείας, καθώς και να προγραμματίζετε εργασίες με ένα systemd timer ή με cron.
Βασικές έννοιες: αρχείο unit, [Unit], [Service], [Install], ExecStart, User=, WorkingDirectory, EnvironmentFile, Restart=, WantedBy, NoNewPrivileges, ProtectSystem, ProtectHome, PrivateTmp, systemd-analyze security, timer, OnCalendar, Persistent, crontab
Γιατί όχι απλώς "nohup" ή "screen";
Όποιος βάζει ένα δικό του πρόγραμμα σε έναν server (μια μικρή διαδικτυακή εφαρμογή, ένα bot, ένα script που κάνει κάτι κάθε νύχτα), την πρώτη φορά συχνά το ξεκινά με το χέρι: node server.js & ή μέσα σε μια συνεδρία screen. Αυτό λειτουργεί μέχρι να αποσυνδεθείτε, να γίνει επανεκκίνηση του server ή να καταρρεύσει το πρόγραμμα. Τότε έχει χαθεί, και κανείς δεν το αντιλαμβάνεται.
Ένα πρόγραμμα που πρέπει να εκτελείται πάντα, πρέπει να είναι υπηρεσία systemd. Τότε:
- ξεκινά αυτόματα κατά την εκκίνηση του συστήματος·
- το systemd το επανεκκινεί αν καταρρεύσει·
- η έξοδός του καταγράφεται στο journal (μάθημα 6.2)·
- εκτελείται με έναν χρήστη που επιλέγετε εσείς, με τα δικαιώματα που επιτρέπετε εσείς.
Ένα δικό σας αρχείο unit
Ας υποθέσουμε ότι έχετε μια μικρή εφαρμογή Node.js στο /opt/factuurapp, η οποία ακούει στο 127.0.0.1:3000. Αργότερα το nginx ή το Caddy θα τη δημοσιεύσει προς τα έξω (ενότητα 7).
Βήμα 1: ένας δικός της χρήστης χωρίς shell (μάθημα 3.2). Η εφαρμογή δεν χρειάζεται να είναι root και δεν χρειάζεται να συνδέεται:
anna@web1:~$ sudo adduser --system --group --home /opt/factuurapp --no-create-home factuurapp
anna@web1:~$ sudo chown -R factuurapp:factuurapp /opt/factuurapp/dataΟ ίδιος ο κώδικας μπορεί να παραμείνει ιδιοκτησία του root (η εφαρμογή δεν χρειάζεται να τροποποιεί τον εαυτό της)· μόνο ο φάκελος στον οποίο γράφει ανήκει στον χρήστη της υπηρεσίας.
Βήμα 2: το αρχείο unit. Τα δικά σας units τα τοποθετείτε στο /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Τα τρία τμήματα:
- [Unit]: περιγραφή και σειρά εκκίνησης. Το
After=network-online.targetμαζί με τοWants=εξασφαλίζει ότι η υπηρεσία ξεκινά μόνο όταν το δίκτυο είναι διαθέσιμο. - [Service]: πώς εκτελείται το πρόγραμμα. Το
ExecStartμε την πλήρη διαδρομή (το systemd δεν χρησιμοποιεί διαδρομή αναζήτησης όπως το shell σας). ΤαUser=καιGroup=καθορίζουν με ποιον χρήστη. ΤοEnvironmentFileδιαβάζει ρυθμίσεις, όπως έναν κωδικό πρόσβασης βάσης δεδομένων, από ένα ξεχωριστό αρχείο, ώστε να μην βρίσκονται μέσα στο unit. - [Install]: τι κάνει το
enable. ΤοWantedBy=multi-user.targetσημαίνει: να ξεκινά μαζί με μια κανονική εκκίνηση του συστήματος.
Βήμα 3: τα μυστικά χωριστά, με αυστηρά δικαιώματα.
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Περιεχόμενο για παράδειγμα DATABASE_URL=postgres://factuur:...@127.0.0.1/factuur. Το systemd διαβάζει το αρχείο ως root πριν μεταβεί στον χρήστη της υπηρεσίας, επομένως αρκεί 600 με ιδιοκτήτη τον root.
Βήμα 4: φόρτωση, εκκίνηση, έλεγχος.
anna@web1:~$ sudo systemctl daemon-reload
anna@web1:~$ sudo systemctl enable --now factuurapp
anna@web1:~$ systemctl status factuurapp
anna@web1:~$ journalctl -u factuurapp -fΕπιλογές ασφαλείας: μικρός κόπος, μεγάλη διαφορά
Οι τέσσερις γραμμές κάτω από το RestartSec στο παράδειγμα περιορίζουν τι μπορεί να κάνει η υπηρεσία, ακόμη κι αν κάποιος βρει ένα κενό ασφαλείας στην εφαρμογή σας:
| Επιλογή | Αποτέλεσμα |
|---|---|
NoNewPrivileges=true | η διεργασία και οι θυγατρικές της δεν μπορούν ποτέ να αποκτήσουν περισσότερα δικαιώματα (ούτε μέσω sudo ή προγραμμάτων setuid) |
ProtectSystem=strict | ολόκληρο το σύστημα αρχείων είναι μόνο για ανάγνωση για την υπηρεσία, εκτός από όσα επιτρέπετε με το ReadWritePaths= |
ProtectHome=true | τα /home, /root και /run/user είναι αόρατα |
PrivateTmp=true | ένα δικό της, κενό /tmp που δεν βλέπουν άλλα προγράμματα |
Το systemd μπορεί να αξιολογήσει το unit σας:
anna@web1:~$ systemd-analyze security factuurapp
...
→ Overall exposure level for factuurapp.service: 4.9 OK 🙂Όσο χαμηλότερος ο αριθμός, τόσο καλύτερα. Μια υπηρεσία χωρίς επιλογές βαθμολογείται γύρω στο 9 ("UNSAFE"). Δεν χρειάζεται να φτάσετε στο μηδέν: κάθε επιλογή που προσθέτετε μπορεί να χαλάσει κάτι που χρειάζεται η εφαρμογή. Προσθέστε τις μία μία και δοκιμάστε μετά από κάθε βήμα. Αν κάτι σταματήσει να λειτουργεί, συνήθως θα το δείτε στο journal ("Read-only file system", "Permission denied").
Προγραμματισμός εργασιών: timers
Για κάτι που πρέπει να γίνεται σε σταθερές ώρες (ένα αντίγραφο ασφαλείας, ένα script καθαρισμού, μια αναφορά) χρειάζεστε ένα timer. Ένα systemd timer αποτελείται από δύο units με το ίδιο όνομα: ένα .service που λέει τι πρέπει να γίνει και ένα .timer που λέει πότε.
# /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 σημαίνει: το πρόγραμμα εκτελείται και μετά τελειώνει. Το OnCalendar είναι το χρονοδιάγραμμα: κάθε μέρα στις 03:15. Μπορείτε επίσης να γράψετε daily, weekly ή Mon *-*-* 02:00· το systemd-analyze calendar "Mon *-*-* 02:00" δείχνει πότε θα είναι η επόμενη φορά. Το Persistent=true είναι σημαντικό: αν ο server ήταν κλειστός στις 03:15, η εργασία εκτελείται αμέσως μετά την εκκίνηση. Το RandomizedDelaySec διασπείρει λίγο τη στιγμή έναρξης.
Την ενεργοποίηση την κάνετε με το timer, όχι με το 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 # μία χειροκίνητη δοκιμή τώρα
anna@web1:~$ journalctl -u opruimen.serviceΚαι το cron;
Το cron είναι το παλαιότερο, απλούστερο εργαλείο και εξακολουθεί να λειτουργεί μια χαρά. Μια γραμμή στο crontab ενός χρήστη (crontab -e):
15 3 * * * /usr/local/bin/opruimen.shΤα πέντε πεδία είναι λεπτό, ώρα, ημέρα του μήνα, μήνας και ημέρα της εβδομάδας. Πλεονεκτήματα και μειονεκτήματα δίπλα δίπλα:
| cron | systemd timer | |
|---|---|---|
| Ρύθμιση | μία γραμμή | δύο αρχεία |
| Χαμένη εκτέλεση (server κλειστός) | παραλείπεται | με Persistent=true εκτελείται αργότερα |
| Έξοδος και σφάλματα | μέσω email, αν ο server μπορεί να στείλει email· αλλιώς χάνονται | στο journal |
| Επιλογές ασφαλείας, χρήστης | μέσω του ίδιου του script | όλα όσα προσφέρει το systemd (βλ. παραπάνω) |
| Πότε εκτελέστηκε | δύσκολο να το δείτε | systemctl list-timers και το journal |
Για έναν server που διαχειρίζεστε σοβαρά, τα timers είναι η καλύτερη επιλογή, ιδίως για αντίγραφα ασφαλείας (μάθημα 8.2): θέλετε να ξέρετε με βεβαιότητα ότι εκτελέστηκαν και τι ανέφεραν. Μια γραμμή cron που αποτυγχάνει σιωπηλά, την αντιλαμβάνεστε μόνο όταν χρειαστείτε το αντίγραφο ασφαλείας.
Σε AlmaLinux και Rocky Linux
Τα units και τα timers λειτουργούν ακριβώς με τον ίδιο τρόπο. Μία διαφορά: το SELinux μπορεί να εμποδίσει μια δική σας υπηρεσία που διαβάζει ή γράφει αρχεία σε σημείο που το SELinux δεν περιμένει, ή που ακούει σε μια ασυνήθιστη θύρα. Το μήνυμα βρίσκεται στο /var/log/audit/audit.log. Μην απενεργοποιήσετε το SELinux για να λύσετε το πρόβλημα· βρείτε τη σωστή ετικέτα ή κανόνα (για παράδειγμα με το semanage).
Σύνοψη
- Ένα πρόγραμμα που πρέπει να εκτελείται πάντα, πρέπει να είναι υπηρεσία systemd: αυτόματη εκκίνηση, επανεκκίνηση σε περίπτωση κατάρρευσης, αρχεία καταγραφής στο journal.
- Δικά σας units στο
/etc/systemd/system/, μεUser=έναν δικό τους χρήστη χωρίς shell, μυστικά σε έναEnvironmentFileμε δικαιώματα 600. - Τα
NoNewPrivileges,ProtectSystem=strictμεReadWritePaths,ProtectHomeκαιPrivateTmpπεριορίζουν τη ζημιά σε περίπτωση κενού ασφαλείας· τοsystemd-analyze securityαξιολογεί το unit. - Ένα timer είναι ένα
.serviceσυν ένα.timerμεOnCalendar· τοPersistent=trueαναπληρώνει τις χαμένες εκτελέσεις. Ενεργοποιήστε το timer, όχι το service. - Το cron εξακολουθεί να λειτουργεί, αλλά τα timers δίνουν καλύτερα αρχεία καταγραφής και δεν χάνουν εκτελέσεις.
Κουίζ
Άσκηση
Άσκηση O6.3: Μια δική σας υπηρεσία με timer. Δημιουργήστε στον server σας μια μικρή δική σας υπηρεσία με timer, για παράδειγμα ένα script που κάθε νύχτα γράφει τον ελεύθερο χώρο στον δίσκο και το systemctl --failed σε ένα αρχείο ή στο journal. Χρησιμοποιήστε δικό της χρήστη, Type=oneshot, τουλάχιστον δύο επιλογές ασφαλείας και Persistent=true. Δοκιμάστε με systemctl start, δείτε την έξοδο στο journal και σημειώστε τη βαθμολογία του systemd-analyze security. Επικολλήστε το .service και το .timer σας και απαντήστε στις ερωτήσεις της λίστας ελέγχου.
Άσκηση O6.3: Μια δική σας υπηρεσία με timer
Αυτή την άσκηση την κάνετε μόνοι σας: κανείς δεν την ελέγχει. Σημειώστε τι έχετε κάνει και επισημάνετε την άσκηση ως ολοκληρωμένη.
Με Persistent=true η εργασία εκτελείται εκ των υστέρων μετά την εκκίνηση. Μια βαθμολογία κάτω από 6 είναι καλή για μια πρώτη υπηρεσία. Δικαιώματα εγγραφής μόνο μέσω ReadWritePaths.
Τα τικ και οι σημειώσεις σας παραμένουν μόνο σε αυτό το πρόγραμμα περιήγησης· δεν τα στέλνουμε πουθενά. Αν έχετε εγγραφεί, αποθηκεύουμε στο Τα μαθήματά μου μόνο ότι έχετε ολοκληρώσει την άσκηση.
Για περαιτέρω μελέτη
- Εγχειρίδιο DirectAdmin 15.1: Cron Jobs: εργασίες αυτόματα σε προκαθορισμένο χρόνο
- Εγχειρίδιο
systemd.exec(5)(User=, ProtectSystem=, NoNewPrivileges=): https://manpages.debian.org/trixie/systemd/systemd.exec.5.en.html - Εγχειρίδιο
systemd.timer(5)(OnCalendar=, Persistent=): https://manpages.debian.org/trixie/systemd/systemd.timer.5.en.html - Εγχειρίδιο
systemd.time(7)(χρονοδιαγράμματα): https://manpages.debian.org/trixie/systemd/systemd.time.7.en.html
