Aller au contenu

Paramètres des cookies

Choisissez, par catégorie, les cookies que vous autorisez. Vous pouvez modifier votre choix à tout moment via Paramètres des cookies, en bas de chaque page. Politique de confidentialité

Nécessaires

Toujours activés

Indispensables pour vous connecter, conserver votre panier et mémoriser vos choix. Ils sont toujours activés.

Quels cookies ?
  • weboke_sessie, weboke_klant, weboke_remember: votre session et le maintien de la connexion
  • weboke_basket: votre panier
  • wkBtwIncl: l'affichage des prix TTC ou HT
  • wok_cookies: votre choix concernant les cookies, 12 mois

Mesure quelle publicité mène à une visite ou à une commande, pour que nous ne payions pas des publicités inefficaces.

Quels cookies ?
  • Google Ads via Google Tag Manager (_gcl_au, _gcl_aw), jusqu'à 90 jours
  • OpenAI Ads, publicités dans ChatGPT
  • wok_aff: le partenaire qui vous a orienté vers nous (programme d'affiliation), 60 jours

4,6

Pays et langue

Chaque pays a son propre site, avec les prix et la TVA de ce pays.

Afficher

Module 6 · 6.3

Services personnalisés et timers

Module 6: Services et journaux · Temps de lecture : 11 minutes

Objectif pédagogique : À l'issue de cette leçon, vous saurez faire tourner votre propre programme comme service systemd sous un utilisateur dédié, avec quelques options de sécurité, et planifier des tâches avec un timer systemd ou cron.

Notions clés : fichier unit, [Unit], [Service], [Install], ExecStart, User=, WorkingDirectory, EnvironmentFile, Restart=, WantedBy, NoNewPrivileges, ProtectSystem, ProtectHome, PrivateTmp, systemd-analyze security, timer, OnCalendar, Persistent, crontab

Pourquoi ne pas simplement utiliser "nohup" ou "screen" ?

Lorsque l'on installe son propre programme sur un serveur (une petite application web, un bot, un script qui effectue une tâche chaque nuit), on le lance souvent à la main la première fois : node server.js &, ou dans une session screen. Cela fonctionne jusqu'à ce que vous vous déconnectiez, que le serveur redémarre ou que le programme plante. Il disparaît alors, et personne ne s'en aperçoit.

Un programme qui doit tourner en permanence doit être un service systemd. Ainsi :

  • il démarre automatiquement au démarrage du serveur ;
  • systemd le relance s'il plante ;
  • sa sortie est enregistrée dans le journal (leçon 6.2) ;
  • il tourne sous l'utilisateur de votre choix, avec les droits que vous lui accordez.

Votre propre fichier unit

Supposons que vous ayez une petite application Node.js dans /opt/factuurapp, qui écoute sur 127.0.0.1:3000. nginx ou Caddy l'exposera ensuite vers l'extérieur (module 7).

Étape 1 : un utilisateur dédié sans shell (leçon 3.2). L'application n'a pas besoin d'être root et n'a pas besoin de se connecter :

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

Le code lui-même peut rester la propriété de root (l'application n'a pas à se modifier elle-même) ; seul le dossier dans lequel elle écrit appartient à l'utilisateur du service.

Étape 2 : le fichier unit. Vos propres units se placent dans /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

Les trois blocs :

  • [Unit] : description et ordre de démarrage. After=network-online.target avec Wants= garantit que le service ne démarre qu'une fois le réseau disponible.
  • [Service] : la façon dont le programme tourne. ExecStart avec le chemin complet (systemd n'utilise pas de chemin de recherche comme votre shell). User= et Group= déterminent sous quelle identité. EnvironmentFile lit des paramètres, comme un mot de passe de base de données, depuis un fichier séparé, afin qu'ils ne figurent pas dans l'unit.
  • [Install] : ce que fait enable. WantedBy=multi-user.target signifie : démarrer avec un démarrage normal du système.

Étape 3 : les secrets à part, avec des droits stricts.

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

Contenu par exemple DATABASE_URL=postgres://factuur:...@127.0.0.1/factuur. systemd lit le fichier en tant que root avant de basculer vers l'utilisateur du service, donc 600 appartenant à root suffit.

Étape 4 : charger, démarrer, vérifier.

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

Options de sécurité : peu d'effort, une grande différence

Les quatre lignes sous RestartSec dans l'exemple limitent ce que le service peut faire, même si quelqu'un trouve une faille dans votre application :

OptionEffet
NoNewPrivileges=truele processus et ses enfants ne peuvent jamais obtenir davantage de droits (pas non plus via sudo ou des programmes setuid)
ProtectSystem=strictl'ensemble du système de fichiers est en lecture seule pour le service, sauf ce que vous autorisez avec ReadWritePaths=
ProtectHome=true/home, /root et /run/user sont invisibles
PrivateTmp=trueun /tmp propre et vide, que les autres programmes ne voient pas

systemd peut évaluer votre unit :

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

Plus le chiffre est bas, mieux c'est. Un service sans options obtient environ 9 ("UNSAFE"). Inutile de viser zéro : chaque option ajoutée peut casser quelque chose dont l'application a besoin. Ajoutez-les une par une et testez après chaque étape. Si quelque chose ne fonctionne plus, la raison figure généralement dans le journal ("Read-only file system", "Permission denied").

Planifier des tâches : les timers

Pour une tâche qui doit s'exécuter à heures fixes (une sauvegarde, un script de nettoyage, un rapport), il vous faut un timer. Un timer systemd se compose de deux units portant le même nom : un .service qui indique ce qui doit se passer, et un .timer qui indique quand.

# /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 signifie : le programme s'exécute puis se termine. OnCalendar est le calendrier : tous les jours à 03:15. Vous pouvez aussi écrire daily, weekly ou Mon *-*-* 02:00 ; systemd-analyze calendar "Mon *-*-* 02:00" indique quand aura lieu la prochaine exécution. Persistent=true est important : si le serveur était éteint à 03:15, la tâche s'exécute quand même juste après le démarrage. RandomizedDelaySec répartit légèrement le moment de démarrage.

On active le timer, pas le 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    # tester maintenant une fois manuellement
anna@web1:~$ journalctl -u opruimen.service

Et cron ?

cron est l'outil plus ancien et plus simple, et il fonctionne toujours très bien. Une ligne dans la crontab d'un utilisateur (crontab -e) :

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

Les cinq champs sont la minute, l'heure, le jour du mois, le mois et le jour de la semaine. Avantages et inconvénients côte à côte :

crontimer systemd
Mise en placeune lignedeux fichiers
Exécution manquée (serveur éteint)ignoréerattrapée avec Persistent=true
Sortie et erreurspar e-mail, si le serveur peut envoyer des e-mails ; sinon perduesdans le journal
Options de sécurité, utilisateurvia le script lui-mêmetout ce qu'offre systemd (voir plus haut)
Savoir quand il a tournédifficilesystemctl list-timers et le journal

Pour un serveur que vous administrez sérieusement, les timers sont le meilleur choix, en particulier pour les sauvegardes (leçon 8.2) : vous voulez être certain qu'elles ont tourné et savoir ce qu'elles ont signalé. Une ligne cron qui échoue en silence, vous ne le découvrez qu'au moment où vous avez besoin de la sauvegarde.

Sur AlmaLinux et Rocky Linux

Les units et les timers fonctionnent exactement de la même façon. Une différence : SELinux peut bloquer un service personnalisé qui lit ou écrit des fichiers à un emplacement auquel SELinux ne s'attend pas, ou qui écoute sur un port inhabituel. Le message se trouve dans /var/log/audit/audit.log. Ne désactivez pas SELinux pour résoudre le problème ; cherchez le bon label ou la bonne règle (par exemple avec semanage).

Résumé

  • Un programme qui doit tourner en permanence doit être un service systemd : démarrage automatique, redémarrage en cas de plantage, journaux dans le journal systemd.
  • Vos propres units dans /etc/systemd/system/, avec User= un utilisateur dédié sans shell, les secrets dans un EnvironmentFile avec les droits 600.
  • NoNewPrivileges, ProtectSystem=strict avec ReadWritePaths, ProtectHome et PrivateTmp limitent les dégâts en cas de faille ; systemd-analyze security évalue l'unit.
  • Un timer, c'est un .service plus un .timer avec OnCalendar ; Persistent=true rattrape les exécutions manquées. Activez le timer, pas le service.
  • cron fonctionne toujours, mais les timers offrent de meilleurs journaux et ne manquent aucune exécution.

Quiz

1. Votre application tourne comme service avec User=root. Pourquoi un utilisateur dédié est-il préférable ?



2. Vous avez créé un .service et un .timer. Qu'activez-vous avec enable --now ?



3. Que fait Persistent=true dans un timer ?



4. Vous ajoutez ProtectSystem=strict et votre application affiche ensuite "Read-only file system" lors de l'enregistrement des fichiers envoyés. Quelle est la bonne solution ?



5. Où placez-vous le mot de passe de base de données de votre service ?



Exercice

Exercice O6.3 : Un service personnalisé avec timer. Créez sur votre serveur un petit service personnalisé avec un timer, par exemple un script qui écrit chaque nuit l'espace disque libre et systemctl --failed dans un fichier ou dans le journal. Utilisez un utilisateur dédié, Type=oneshot, au moins deux options de sécurité et Persistent=true. Testez avec systemctl start, consultez la sortie dans le journal et notez le score de systemd-analyze security. Collez votre .service et votre .timer et répondez aux questions de la liste de contrôle.

Exercice O6.3: Un service personnalisé avec timer

Cet exercice, vous le faites vous-même ; personne ne le vérifie. Cochez ce que vous avez fait et marquez l'exercice comme terminé.




Vos cases cochées et vos notes restent uniquement dans ce navigateur ; nous ne les envoyons nulle part. Si vous êtes connecté, nous enregistrons dans Mes formations uniquement le fait que vous avez terminé l'exercice.

Pour aller plus loin

Un instant…

Configurez vos domaines

Pour chaque domaine, réglez les serveurs de noms, l'association à votre hébergement et le futur titulaire.

Chargement de vos domaines…