Exercice 12 : Planification avec cron et services systemd
🎯 Objectifs
À la fin de cet exercice, vous serez capable de :
- ✅ Écrire des expressions cron et configurer des tâches avec
crontab - ✅ Créer un fichier unit
.servicesystemd complet avec options de sécurité - ✅ Créer un timer
.timersystemd pour remplacer cron - ✅ Utiliser
journalctlpour diagnostiquer les problèmes de services - ✅ Appliquer les bonnes pratiques de sécurité des services (sandboxing)
Durée estimée : 40 minutes
Difficulté : ⭐⭐⭐☆☆ (Avancé)
Prérequis : Exercices 09, 10 complétés - scripts Bash
📖 Contexte
Cron est l'outil historique de planification sous Linux. Il est simple et universel. Systemd timers sont l'alternative moderne : plus puissants, mieux intégrés à journalctl, avec retry automatique et une granularité plus fine.
Pour les services longue durée (serveurs, daemons), systemd est incontournable. Pour les scripts périodiques, les deux approches fonctionnent.
📋 Énoncé
Planifiez des tâches de maintenance avec cron et créez un service systemd sécurisé.
🧭 Déroulement de l'exercice
Tâche 1 : Maîtriser la syntaxe cron
# Rappel : format cron
# ┌─────────── minute (0-59)
# │ ┌───────── heure (0-23)
# │ │ ┌─────── jour du mois (1-31)
# │ │ │ ┌───── mois (1-12 ou jan-dec)
# │ │ │ │ ┌─── jour de la semaine (0-7, 0 et 7 = dimanche)
# │ │ │ │ │
# * * * * * commande
# Voir les tâches cron actuelles
crontab -l 2>/dev/null || echo "Aucune tâche cron"
# Créer des tâches de démonstration
cat > mes-taches-cron.txt << 'EOF'
# Nettoyage des logs toutes les nuits à 2h30
30 2 * * * find /var/log -name "*.log" -mtime +30 -delete >> /var/log/nettoyage.log 2>&1
# Sauvegarde chaque jour à 3h, sauf le dimanche
0 3 * * 1-6 /opt/scripts/backup.sh >> /var/log/backup.log 2>&1
# Rapport hebdomadaire chaque lundi à 8h00
0 8 * * 1 /opt/scripts/rapport-semaine.sh | mail -s "Rapport semaine" admin@entreprise.com
# Vérification des disques toutes les 15 minutes
*/15 * * * * /opt/scripts/surveiller-disques.sh 85 >> /var/log/disques.log 2>&1
# Première tâche chaque 1er du mois à minuit
0 0 1 * * /opt/scripts/facturation-mensuelle.sh
# Toutes les 5 minutes, de 9h à 17h, en semaine
*/5 9-17 * * 1-5 /opt/scripts/healthcheck.sh
EOF
echo "Expressions cron créées dans mes-taches-cron.txt"
cat mes-taches-cron.txtTester une expression cron :
# Utiliser l'outil en ligne crontab.guru ou la commande suivante
# pour simuler la prochaine exécution
# Installer ncrontab (si disponible) ou tester manuellement
python3 -c "
import re
exprs = [
'30 2 * * *',
'*/15 * * * *',
'*/5 9-17 * * 1-5',
]
for e in exprs:
print(f'Expression: {e}')
"Tâche 2 : Créer un service systemd complet
Créez un script de monitoring que systemd va gérer :
sudo mkdir -p /opt/monitoring
sudo tee /opt/monitoring/monitor.sh << 'EOF'
#!/usr/bin/env bash
set -euo pipefail
LOG_FILE="/var/log/monitoring/system.log"
mkdir -p "$(dirname "$LOG_FILE")"
while true; do
TIMESTAMP=$(date '+%Y-%m-%dT%H:%M:%S')
CPU_IDLE=$(top -bn1 | grep "Cpu(s)" | awk '{print $8}' | tr -d '%id,')
MEM_TOTAL=$(free -m | awk '/^Mem/ {print $2}')
MEM_USED=$(free -m | awk '/^Mem/ {print $3}')
MEM_PCT=$(( MEM_USED * 100 / MEM_TOTAL ))
DISK_PCT=$(df / | awk 'NR==2 {print $5}' | tr -d '%')
echo "$TIMESTAMP cpu_idle=${CPU_IDLE}% mem=${MEM_PCT}% disk_root=${DISK_PCT}%" >> "$LOG_FILE"
# Alerte si mémoire > 90%
if [[ $MEM_PCT -gt 90 ]]; then
echo "$TIMESTAMP ALERTE: mémoire critique ${MEM_PCT}%" >> "$LOG_FILE"
fi
sleep 60 # Attendre 1 minute
done
EOF
sudo chmod +x /opt/monitoring/monitor.shMaintenant le fichier unit systemd :
sudo tee /etc/systemd/system/monitoring.service << 'EOF'
[Unit]
Description=Service de monitoring système
Documentation=https://wiki.entreprise.com/monitoring
# Démarrer après le réseau et les cibles essentielles
After=network.target
# Ne pas démarrer si le service de log n'est pas disponible
Wants=syslog.target
[Service]
Type=simple
ExecStart=/opt/monitoring/monitor.sh
# Utilisateur dédié (ne pas tourner en root)
User=monitoring
Group=monitoring
# Répertoire de travail
WorkingDirectory=/opt/monitoring
# Redémarrer automatiquement si le processus meurt
Restart=on-failure
RestartSec=10s
# Délai max pour le démarrage
TimeoutStartSec=30s
# Variables d'environnement
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
EnvironmentFile=-/etc/monitoring/config.env # Le - signifie : optionnel
# === SANDBOXING ET SÉCURITÉ ===
# Empêcher l'escalade de privilèges
NoNewPrivileges=true
# Protéger les répertoires système en lecture seule
ProtectSystem=strict
# Permettre l'écriture dans /var/log/monitoring
ReadWritePaths=/var/log/monitoring
# Interdire l'accès au répertoire home
ProtectHome=true
# Namespace réseau privé (ne pas utiliser pour les services réseau)
# PrivateNetwork=true
# Filesystem privé temporaire
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOFActiver et démarrer le service :
# Créer l'utilisateur dédié
sudo useradd --system --no-create-home --shell /usr/sbin/nologin monitoring 2>/dev/null || true
sudo mkdir -p /var/log/monitoring
sudo chown monitoring:monitoring /var/log/monitoring
# Recharger systemd et activer
sudo systemctl daemon-reload
sudo systemctl enable monitoring.service
sudo systemctl start monitoring.service
# Vérifier l'état
sudo systemctl status monitoring.serviceVérification :
# Le service doit être "active (running)"
systemctl is-active monitoring.service
# Voir les logs en temps réel
journalctl -u monitoring.service -f --no-pager
# Ctrl+C pour arrêter
# Voir les 10 dernières lignes de logs
journalctl -u monitoring.service -n 10Tâche 3 : Timer systemd (alternative moderne à cron)
# Script de nettoyage
sudo tee /opt/monitoring/nettoyage.sh << 'EOF'
#!/usr/bin/env bash
set -euo pipefail
echo "$(date) : Nettoyage des anciens logs..."
find /var/log/monitoring -name "*.log" -mtime +7 -delete
echo "$(date) : Nettoyage terminé"
EOF
sudo chmod +x /opt/monitoring/nettoyage.sh
# Service one-shot (s'exécute une fois et s'arrête)
sudo tee /etc/systemd/system/nettoyage-logs.service << 'EOF'
[Unit]
Description=Nettoyage des logs de monitoring
After=monitoring.service
[Service]
Type=oneshot
ExecStart=/opt/monitoring/nettoyage.sh
User=monitoring
[Install]
WantedBy=multi-user.target
EOF
# Timer associé au service
sudo tee /etc/systemd/system/nettoyage-logs.timer << 'EOF'
[Unit]
Description=Timer de nettoyage quotidien des logs
Requires=nettoyage-logs.service
[Timer]
# Exécuter chaque jour à 3h00
OnCalendar=*-*-* 03:00:00
# Exécuter au prochain boot si manqué (comme anacron)
Persistent=true
# Décalage aléatoire de 0 à 10 minutes (étaler la charge sur le cluster)
RandomizedDelaySec=10min
[Install]
WantedBy=timers.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now nettoyage-logs.timer
# Lister tous les timers actifs
systemctl list-timers --allVérification :
# Le timer doit apparaître dans la liste avec la prochaine exécution prévue
systemctl list-timers nettoyage-logs.timer
# Forcer l'exécution immédiate pour tester
sudo systemctl start nettoyage-logs.service
journalctl -u nettoyage-logs.service -n 20Tâche 4 : Diagnostic avec journalctl
# Logs du service depuis le démarrage
journalctl -u monitoring.service --since today
# Logs des 30 dernières minutes
journalctl -u monitoring.service --since "30 minutes ago"
# Filtrer les erreurs seulement (priorité err et plus)
journalctl -u monitoring.service -p err..emerg
# Format JSON pour le parsing
journalctl -u monitoring.service -o json-pretty | head -50
# Logs du boot courant
journalctl -u monitoring.service -b 0
# Logs du boot précédent (utile après un crash)
journalctl -u monitoring.service -b -1
# Nettoyer les vieux journaux (garder seulement les 7 derniers jours)
sudo journalctl --vacuum-time=7d✅ Vérification du résultat
mes-taches-cron.txtcontient 5 expressions cron validessystemctl is-active monitoring.serviceretourneactivejournalctl -u monitoring.service -n 5affiche des logs du monitoringsystemctl list-timersaffichenettoyage-logs.timeravec une prochaine exécution planifiée- Le service tourne avec l'utilisateur
monitoring(pas root)
💡 À retenir
| Outil | Usage | Avantage |
|---|---|---|
crontab | Tâches périodiques | Simple, universel |
| Systemd timer | Tâches périodiques | journalctl, retry, Persistent |
| Systemd service | Services continus | Restart auto, sandboxing |
Sandboxing systemd minimal :
[Service]
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=/chemin/dont/je/besoin✨ Solution Complète
# Créer un service + timer minimal
sudo tee /etc/systemd/system/mon-script.service << 'EOF'
[Unit]
Description=Mon script ponctuel
[Service]
Type=oneshot
ExecStart=/opt/mon-script.sh
User=www-data
NoNewPrivileges=true
ProtectSystem=strict
EOF
sudo tee /etc/systemd/system/mon-script.timer << 'EOF'
[Unit]
Description=Timer mon-script (quotidien à 2h)
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
[Install]
WantedBy=timers.target
EOF
sudo systemctl daemon-reload && sudo systemctl enable --now mon-script.timer