Comment utiliser ces fichiers
Ces fichiers sont des supports d'analyse liés à l'article Durcir un service systemd pour .NET 8+ : les arbitrages spécifiques au runtime. Ils regroupent la configuration minimale issue de la documentation Microsoft et la configuration durcie étudiée dans l'article, avec leurs commentaires inline.
Ces fichiers ne sont pas des configurations à copier en production. Chaque directive doit être validée dans votre modèle de menace, vos dépendances et vos contraintes d'exploitation.
WebApp-system-basic.service : configuration minimale basée sur la préconisation Microsoft
[Unit]
Description=ASP.NET Core Web Application
After=network.target
[Service]
WorkingDirectory=/var/www/myapp
ExecStart=/usr/bin/dotnet /var/www/myapp/myapp.dll
Restart=always
RestartSec=10
KillSignal=SIGINT
SyslogIdentifier=myapp
User=www-data
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false
[Install]
WantedBy=multi-user.target
WebApp-system-harden.service : configuration durcie
Configuration de travail reprenant les arbitrages de l'article.
Score systemd-analyze security : 1.7 (OK) (voir
conditions de mesure).
# =============================================================================
# Modèle d'unit systemd pour application ASP.NET Core (.NET 8+)
# Référence : https://www.ordanchesolutions.fr/articles/systemd-hardening-dotnet
#
# Convention de déploiement : /opt/myapp/ regroupé.
#
# Substitutions à effectuer :
# - myapp : nom du service
# - myapp-user : utilisateur dédié (créer avec : useradd --system
# --no-create-home --shell /usr/sbin/nologin myapp-user)
#
# Avant déploiement :
# sudo systemd-analyze verify /etc/systemd/system/myapp.service
# sudo systemd-analyze security myapp.service
# =============================================================================
[Unit]
Description=MyApp ASP.NET Core service
After=network.target
# -----------------------------------------------------------------------------
# Limitation du taux de redémarrage pour éviter les boucles de crash/restart
# -----------------------------------------------------------------------------
StartLimitBurst=3
StartLimitIntervalSec=60s
[Service]
# -----------------------------------------------------------------------------
# Journalisation
# -----------------------------------------------------------------------------
SyslogIdentifier=myapp-hardening
# -----------------------------------------------------------------------------
# Identité
# -----------------------------------------------------------------------------
User=myapp-user
Group=myapp-user
# -----------------------------------------------------------------------------
# Type / ProtectProc : arbitrage à choisir (voir article)
#
# Option A : priorité sécurité (par défaut ici).
# -----------------------------------------------------------------------------
Type=simple
ProtectProc=invisible
# -----------------------------------------------------------------------------
# Option B : priorité opérationnelle.
# Décommenter les deux lignes ci-dessous ET commenter les deux lignes Option A.
# -----------------------------------------------------------------------------
#Type=notify
#ProtectProc=default
# -----------------------------------------------------------------------------
# Exécution
# -----------------------------------------------------------------------------
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/dotnet /opt/myapp/MyApp.dll
Environment=ASPNETCORE_URLS=http://127.0.0.1:5000
# Variables d'environnement non sensibles. Pour les secrets, voir l'article
# dédié à l'intégration ASP.NET Core / KeyPerFile.
EnvironmentFile=-/opt/myapp/myapp.env
# -----------------------------------------------------------------------------
# Arrêt gracieux
# SIGTERM est le défaut depuis systemd 187, voir article pour le différentiel SIGINT/SIGTERM.
# TimeoutStopSec doit rester > HostOptions.ShutdownTimeout (30s par défaut).
# -----------------------------------------------------------------------------
KillSignal=SIGTERM
TimeoutStopSec=45
# -----------------------------------------------------------------------------
# Politique de redémarrage
# -----------------------------------------------------------------------------
Restart=on-failure
RestartSec=5s
# -----------------------------------------------------------------------------
# Système de fichiers
# ProtectSystem=strict met l'intégralité du FS en lecture seule.
# /opt/myapp est rouvert via ReadWritePaths sur les sous-chemins applicatifs
# qui ont besoin d'écrire (logs, données persistantes, cache).
# /home, /root, /run/user sont rendus inaccessibles par ProtectHome.
# -----------------------------------------------------------------------------
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
# gardé à titre explicite
PrivateMounts=yes
# /opt/ regroupé : ReadWritePaths ci-dessous.
ReadWritePaths=/opt/myapp/data
ReadWritePaths=/opt/myapp/logs
ReadWritePaths=/opt/myapp/cache
# FHS éclaté (voir article) : commenter ReadWritePaths,
# garder StateDirectory et utiliser $STATE_DIRECTORY côté app.
StateDirectory=myapp
# -----------------------------------------------------------------------------
# Capabilities et privilèges
# -----------------------------------------------------------------------------
CapabilityBoundingSet=
AmbientCapabilities=
NoNewPrivileges=true
LockPersonality=true
UMask=0077
ProcSubset=pid
RemoveIPC=true
# -----------------------------------------------------------------------------
# Mémoire et exécution de code
# MemoryDenyWriteExecute incompatible avec le JIT (.NET en mode standard).
# Décommenter UNIQUEMENT pour un déploiement Native AOT.
# -----------------------------------------------------------------------------
#MemoryDenyWriteExecute=true
# -----------------------------------------------------------------------------
# Noyau et système
# -----------------------------------------------------------------------------
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectKernelLogs=true
ProtectClock=true
ProtectControlGroups=true
ProtectHostname=true
RestrictSUIDSGID=true
RestrictRealtime=true
RestrictNamespaces=true
PrivateDevices=true
# -----------------------------------------------------------------------------
# Réseau
# AF_UNIX requis pour journald et NOTIFY_SOCKET (si Type=notify).
# AF_INET/AF_INET6 pour Kestrel.
# -----------------------------------------------------------------------------
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
# -----------------------------------------------------------------------------
# Appels système
# Base raisonnable. Pour resserrer, audit via strace/auditd nécessaire.
# -----------------------------------------------------------------------------
SystemCallArchitectures=native
SystemCallFilter=@system-service
SystemCallFilter=~@mount @swap @reboot @cpu-emulation @obsolete @debug @raw-io
SystemCallErrorNumber=EPERM
# -----------------------------------------------------------------------------
# Namespaces utilisateur (à tester attentivement dans le contexte de l'application cible)
# -----------------------------------------------------------------------------
#PrivateUsers=true
[Install]
WantedBy=multi-user.target