Revue croisée avec les guides de référence du domaine. Quatre commandes échouaient ou donnaient l'illusion de fonctionner : - journald : le drop-in était édité avant que son répertoire existe, nano refusait d'enregistrer ; - restic : les variables posées par « export » ne passent pas sudo, qui les efface — le dépôt n'était pas celui qu'on croyait ; - la sauvegarde reposait sur « systemd-run », dont l'unité transitoire disparaît au redémarrage : c'est la sauvegarde qui s'arrête sans que personne ne le voie. Remplacée par un .service + .timer persistant ; - le filet de sécurité du pare-feu n'existait que pour ufw ; l'équivalent nftables manquait. Ajouts de fond, par ordre d'importance : vérifier l'empreinte de la clé d'hôte à la première connexion — le seul moment où une interception est possible —, régénérer les clés des images clonées, AppArmor, l'audit Lynis en fin de parcours, la sonde de supervision externe (une alerte émise par la machine ne dit rien quand la machine est morte), la journalisation distante, le fichier 20auto-upgrades qui commande réellement l'automatisme, needrestart non interactif, growpart, la jail recidive, les clés FIDO2, et AllowGroups à la place d'AllowUsers pour qu'un second administrateur existe. Pédagogie : le modèle de menace en ouverture, un niveau par étape avec un parcours court de quinze minutes, des variables qu'on exporte au lieu de les remplacer à la main, quatre encadrés « pour comprendre » (FQDN/PTR, clé publique, activation par socket, tmpfs), et sur les trois étapes où l'on peut se verrouiller dehors : résultat attendu, si ça échoue, et comment revenir en arrière. Plus une annexe pour le jour où c'est raté — console, mode rescue, chroot. Les avertissements passent en admonitions typées : la couleur distingue enfin l'information du danger. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BE4rvHVETRoWnYNDTGssdo
74 KiB
| description |
|---|
| Préparer un serveur Debian 13 « Trixie » livré par un hébergeur — DNS, nom d'hôte, sources deb822, comptes, SSH par clé, pare-feu, fail2ban, AppArmor, mises à jour automatiques, supervision, sauvegarde restaurable et audit Lynis. |
Préparation d'un serveur Debian 13 (Trixie)
!!! info "État du document" Rédigé le 19/09/2026 pour Debian 13 « Trixie », révisé le 20/09/2026 après relecture croisée avec les guides de référence du domaine. Il remplace l'ancienne fiche Debian 11 : ce qui a changé est résumé en fin de page.
Avant de commencer
Cette procédure part d'une installation minimale de Debian 13 « Trixie » fraîchement livrée par un hébergeur, et s'arrête au moment où le serveur est prêt à recevoir ses services.
Comptez une heure en lisant vraiment, une demi-heure si vous connaissez déjà le terrain. Rien ici ne se rattrape en vitesse : les quatre étapes qui touchent à l'accès peuvent vous enfermer dehors.
Contre quoi vous vous protégez
Un serveur qui vient d'être livré n'est pas tranquille : son adresse IP appartient à des
plages publiques que des robots balaient en permanence. Les premières tentatives de
connexion arrivent dans les minutes qui suivent la mise en ligne, avant même que vous
ayez fini de le configurer. Elles ne vous visent pas : elles essaient root, admin,
test, avec les mots de passe des fuites connues, sur des millions d'adresses.
Trois familles d'ennuis, et ce que cette fiche oppose à chacune :
| Ce qui arrive vraiment | Ce qui l'en empêche |
|---|---|
| Un robot devine un mot de passe faible et ouvre une session | Authentification par clé seule, root refusé, comptes nominatifs (étapes 7 à 9) |
| Un service oublié écoute sur le réseau et se fait exploiter | Inventaire de ce qui écoute, pare-feu fermé par défaut, AppArmor (étapes 2, 10, 13) |
| Une faille publiée reste non corrigée pendant des semaines | Correctifs de sécurité automatiques, alerte de fin de support (étape 12) |
Et quand tout cela échoue quand même : un instantané, une sauvegarde restaurable et des journaux qui existent ailleurs (étapes 14 et 15).
!!! danger "Règle d'or : ne fermez jamais la seconde session" Tant que vous touchez à SSH ou au pare-feu, gardez une deuxième session SSH ouverte sur le serveur. Chaque modification se teste depuis une troisième session, jamais en refermant celle qui marche.
Vérifiez aussi, avant de commencer, que vous disposez d'un **accès de secours hors
SSH** : console KVM/VNC, console série ou mode rescue de l'hébergeur. Sans lui, une
erreur de pare-feu signifie une réinstallation. En cas de problème, la marche à suivre
est en fin de page : [Si vous êtes verrouillé dehors](#si-vous-etes-verrouille-dehors).
Les niveaux, et le parcours court
Chaque étape porte un niveau :
| Niveau | Ce que cela veut dire |
|---|---|
| Essentiel | À faire toujours, même en urgence. Sans cela, la machine est exposée ou inexploitable. |
| Recommandé | Ce qui distingue un serveur tenu d'un serveur qui tient par chance. À faire le jour même. |
| Avancé | Utile selon l'usage et l'hébergeur. À lire, à appliquer si le contexte s'y prête. |
Vous devez sécuriser un serveur ce soir, en un quart d'heure ? Faites, dans l'ordre : 2 (constat et empreinte), 4 (mise à jour), 7 (compte admin), 8 (clés SSH), 10 (pare-feu), 12 (mises à jour automatiques). Le reste peut attendre le lendemain — mais pas la semaine suivante.
Variables utilisées dans ce document
Remplacez systématiquement ces valeurs par les vôtres.
| Variable | Exemple | Signification |
|---|---|---|
SRV |
web01 |
Nom court du serveur (sans domaine) |
DOMAINE |
exemple.fr |
Le domaine qui porte le serveur |
FQDN |
web01.exemple.fr |
Nom complet, celui du DNS et du reverse |
IPV4 |
203.0.113.10 |
Adresse IPv4 publique |
IPV6 |
2001:db8::10 |
Adresse IPv6 publique, si fournie |
USER |
alix |
Votre futur compte d'administration |
VENDOR |
debian |
Compte créé par l'hébergeur, à neutraliser |
SSHPORT |
2222 |
Port SSH final, si vous le déplacez |
Plutôt que de remplacer web01 à la main dans quarante blocs — c'est là que naissent les
erreurs — posez-les une fois en début de session, sur le serveur comme sur votre poste :
export SRV=web01 DOMAINE=exemple.fr USER=alix SSHPORT=2222
export FQDN="$SRV.$DOMAINE"
export IPV4=203.0.113.10
Les commandes de cette page s'écrivent alors telles quelles, et echo $FQDN vérifie ce
que le shell a vraiment compris.
!!! warning "Deux pièges des variables d'environnement"
Elles disparaissent à la déconnexion : reposez-les à chaque nouvelle session, ou
écrivez-les dans votre ~/.bashrc le temps de l'installation.
Et **`sudo` ne les transmet pas** (`env_reset` est le défaut Debian) : une commande
comme `sudo echo $FQDN` fonctionne — le shell remplace avant d'appeler sudo — mais
`sudo sh -c 'echo $FQDN'` renvoie du vide. Utilisez `sudo -E` pour conserver
l'environnement, ou passez par `sudo tee` avec un *heredoc*.
Ordre des opérations
L'ordre compte : le DNS se propage pendant que vous travaillez, et le pare-feu ne se ferme qu'une fois le nouvel accès SSH prouvé.
flowchart LR
A[DNS + reverse] --> B[Identité<br/>hostname, hosts]
B --> C[Système<br/>MAJ, paquets, locale]
C --> D[Compte admin<br/>+ sudo]
D --> E[SSH<br/>clés + durcissement]
E --> F[Pare-feu<br/>+ fail2ban]
F --> G[Automatismes<br/>MAJ, supervision]
G --> H[Sauvegarde<br/>+ instantané]
Étape 1 — DNS direct et reverse DNS
Recommandé — le serveur tourne sans, mais ses mails partent en indésirables et ses journaux sont illisibles.
!!! info "Pour comprendre : FQDN, PTR et FCrDNS"
Le FQDN est le nom complet de la machine, web01.exemple.fr : un nom court
(web01) suivi du domaine. C'est lui, et lui seul, qui identifie le serveur pour le
reste d'Internet.
L'enregistrement **A** traduit ce nom en adresse IP — c'est l'aller. L'enregistrement
**PTR**, le *reverse*, fait le chemin retour : il traduit l'adresse IP en nom. Les
deux sont gérés à des endroits différents : le A chez le gestionnaire du domaine, le
PTR chez l'hébergeur qui possède l'adresse IP.
```
web01.exemple.fr ──(A)──▶ 203.0.113.10 ──(PTR)──▶ web01.exemple.fr
└──────────── la boucle doit se refermer ────────────┘
```
Quand la boucle se referme sur le même nom, on parle de **FCrDNS** valide. Les
serveurs de messagerie s'en servent comme premier filtre anti-spam : un expéditeur
dont le reverse ne correspond à rien est rejeté ou classé indésirable avant même
d'être lu.
Faites cette étape en premier : la propagation DNS prend de quelques minutes à quelques heures, et elle tournera pendant que vous configurez le reste. Un reverse DNS cohérent conditionne l'acceptation de vos mails et la lisibilité de vos journaux.
1.1 Enregistrements directs (zone DNS du domaine)
Dans l'interface DNS de votre registrar ou de votre hébergeur, créez :
| Type | Nom | Valeur | TTL |
|---|---|---|---|
A |
web01 |
203.0.113.10 |
300 pendant la mise en place, puis 3600 |
AAAA |
web01 |
2001:db8::10 |
idem, si IPv6 fournie |
Un TTL court (300 s) pendant l'installation vous laisse corriger une erreur sans attendre. Remontez-le une fois le serveur stabilisé.
1.2 Reverse DNS (PTR)
Le PTR ne se déclare pas dans la zone du domaine, mais chez le propriétaire de l'adresse IP, c'est-à-dire votre hébergeur. Cherchez dans son panneau une entrée nommée « reverse », « rDNS » ou « PTR », sur la fiche de l'IP et non sur celle du domaine.
La valeur doit être exactement le FQDN déclaré en 1.1 : web01.exemple.fr. Faites-le pour l'IPv4 et pour l'IPv6 ; un reverse IPv6 manquant est une cause classique de mails rejetés.
1.3 Vérification
Depuis votre poste, une fois la propagation faite :
dig +short web01.exemple.fr A
dig +short web01.exemple.fr AAAA
dig +short -x 203.0.113.10
dig +short -x 2001:db8::10
Les quatre réponses doivent se refermer sur elles-mêmes : le A pointe vers l'IP, le PTR de cette IP renvoie le même FQDN. C'est ce qu'on appelle un FCrDNS valide (forward-confirmed reverse DNS).
!!! note "Si dig manque sur votre poste"
Si dig n'est pas installé sur votre poste : sudo apt install dnsutils sous Debian/Ubuntu, brew install bind sous macOS.
Étape 2 — Première connexion
Essentiel — constater l'état de livraison et valider l'identité de la machine.
Connectez-vous avec le compte fourni par l'hébergeur. Sur les images Debian officielles, c'est debian ; d'autres utilisent admin, ubuntu ou root.
ssh debian@203.0.113.10
Utilisez l'IP et non le FQDN pour cette première connexion : le DNS n'est peut-être pas encore propagé.
Vérifier l'empreinte avant de répondre « yes »
SSH affiche l'empreinte de la clé d'hôte et demande confirmation. C'est le seul moment
de toute la vie du serveur où une interception est possible : une fois l'empreinte
enregistrée dans votre known_hosts, toute substitution déclenche une alerte. Répondre
« yes » sans regarder, c'est accepter n'importe quel serveur qui se présente à cette
adresse.
Comparez l'empreinte affichée avec celle que l'hébergeur publie — panneau d'administration, courriel de livraison, ou console KVM :
# Sur la console KVM de l'hébergeur, ou après une première connexion de confiance
for f in /etc/ssh/ssh_host_*_key.pub; do ssh-keygen -lf "$f"; done
Les deux empreintes doivent être identiques, caractère pour caractère. Si l'hébergeur ne publie rien et que vous avez une console, relevez-la là : c'est le seul canal qui ne passe pas par le réseau que vous cherchez à valider.
Régénérer les clés d'hôte d'une image clonée
Certains fournisseurs distribuent des images où les clés SSH ont été générées une seule fois, avant le clonage : des milliers de machines partagent alors la même clé privée, et qui possède cette image peut se faire passer pour votre serveur.
ls -l --time-style=full-iso /etc/ssh/ssh_host_*_key
Si la date est antérieure à la livraison de votre machine, régénérez-les :
sudo rm /etc/ssh/ssh_host_*
sudo dpkg-reconfigure openssh-server
sudo systemctl restart ssh
Votre prochaine connexion signalera un changement de clé, ce qui est normal ici :
ssh-keygen -R 203.0.113.10 sur votre poste, puis revérifiez la nouvelle empreinte.
Le même raisonnement vaut pour l'identifiant de la machine, qui sert de base au DUID DHCPv6 et à la corrélation des journaux :
cat /etc/machine-id
# S'il est identique sur deux machines issues de la même image :
sudo rm -f /etc/machine-id && sudo systemd-machine-id-setup
Vérifier ce qu'on a vraiment reçu
Avant toute modification, prenez trois minutes pour constater l'état initial.
cat /etc/os-release # confirme Debian 13 (trixie)
uname -r # version du noyau
ip -brief address # interfaces et adresses
lsblk # disques et partitions
free -h # RAM et swap
sudo systemctl list-units --type=service --state=running
sudo ss -tulpn # ce qui écoute déjà sur le réseau
!!! tip "Le disque est-il entier ?"
Beaucoup de VPS démarrent sur une image dont la partition est plus petite que le
volume facturé : lsblk montre alors un disque de 80 Go dont la partition n'en fait
que 20. L'espace est payé, mais inutilisable tant qu'on ne l'a pas réclamé.
```bash
sudo apt install -y cloud-guest-utils # fournit growpart
sudo growpart /dev/vda 1 # adaptez le disque et le numéro
sudo resize2fs /dev/vda1 # xfs_growfs si le système est en XFS
df -h /
```
Faites-le maintenant, avant d'avoir des données : l'opération est sûre mais se
déroule mieux sur une machine vide.
La dernière commande est la plus instructive : elle montre les services exposés dès la livraison. Tout ce qui écoute sur 0.0.0.0 ou :: et que vous ne reconnaissez pas mérite une décision explicite — désactiver ou garder.
Si la connexion est refusée
| Symptôme | Cause fréquente |
|---|---|
Permission denied (publickey) |
La clé publique n'a pas été injectée à la commande, ou mauvais nom d'utilisateur |
Connection refused |
Le serveur n'a pas fini de démarrer, ou SSH écoute sur un autre port |
Connection timed out |
Pare-feu amont côté hébergeur (security group, firewall network) |
REMOTE HOST IDENTIFICATION HAS CHANGED |
Réinstallation ou réattribution d'IP → ssh-keygen -R 203.0.113.10 |
Étape 3 — Nom d'hôte et /etc/hosts
Recommandé — une identité stable, sur laquelle s'appuient mail, journaux et certificats.
3.1 Définir le nom d'hôte
Debian attend ici le nom court, pas le FQDN. Le FQDN est reconstitué via /etc/hosts.
sudo hostnamectl set-hostname web01
Vous pouvez aussi renseigner deux champs purement informatifs, utiles quand on gère plusieurs machines :
sudo hostnamectl set-hostname "Front web — Paris" --pretty
sudo hostnamectl set-chassis vm # ou: server, desktop
3.2 Corriger /etc/hosts
C'est ce fichier, et non hostnamectl, qui donne au système son FQDN. La règle : sur la ligne 127.0.1.1, le FQDN vient en premier, le nom court ensuite.
sudo nano /etc/hosts
Ciblez ce résultat :
127.0.0.1 localhost
127.0.1.1 web01.exemple.fr web01
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
Certains hébergeurs pré-remplissent la ligne 127.0.1.1 avec le nom d'usine de la machine. Deux options : le supprimer, ou le laisser après vos deux entrées — la ligne compte alors quatre références.
127.0.1.1 web01.exemple.fr web01 vps-1a2b3c.exemple-hebergeur.net vps-1a2b3c
Les deux formes fonctionnent. Supprimer l'ancien nom est plus propre ; le conserver évite de casser un script de l'hébergeur qui s'y référerait encore.
!!! warning "Ne touchez pas à localhost"
Ne touchez jamais à la ligne 127.0.0.1 localhost. Et n'associez pas le FQDN à l'IP publique dans /etc/hosts : sur une machine à IP dynamique ou derrière NAT, cela crée des résolutions fantomatiques.
3.3 Vérifier
hostname # → web01
hostname -f # → web01.exemple.fr
hostname -d # → exemple.fr
hostnamectl status
Si hostname -f répond autre chose que le FQDN attendu, la ligne 127.0.1.1 est mal ordonnée. Le changement est immédiat, mais reconnectez-vous pour que l'invite de commande l'affiche.
!!! warning "Le nom d'hôte revient après un redémarrage ?"
Sur certaines images cloud, cloud-init le réécrit à chaque démarrage. Dites-lui de
respecter le vôtre :
```bash
sudo sed -i 's/^preserve_hostname:.*/preserve_hostname: true/' /etc/cloud/cloud.cfg
grep preserve_hostname /etc/cloud/cloud.cfg
```
Ajoutez la ligne si elle est absente.
Étape 4 — Sources APT et mise à jour
Essentiel — une machine livrée n'est jamais à jour.
4.1 Passer les sources au format deb822
Debian 13 introduit le format deb822 pour les dépôts. L'ancien format une-ligne est officiellement déprécié, même s'il restera supporté par Debian 14 « Forky » (détail du changement). Autant partir sur le bon format tout de suite.
sudo apt modernize-sources
La commande convertit /etc/apt/sources.list vers /etc/apt/sources.list.d/debian.sources et laisse au passage des sauvegardes /etc/apt/sources.list.bak et /etc/apt/sources.list.save, ce qui permet de revenir en arrière (procédure détaillée).
Vérifiez le résultat :
cat /etc/apt/sources.list.d/debian.sources
Vous devez y trouver trois blocs : trixie, trixie-updates et trixie-security, chacun avec un champ Signed-By.
Types: deb
URIs: http://deb.debian.org/debian/
Suites: trixie trixie-updates
Components: main contrib non-free-firmware
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
Types: deb
URIs: http://security.debian.org/debian-security/
Suites: trixie-security
Components: main contrib non-free-firmware
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
!!! danger "Sans trixie-security, aucun correctif"
Si le dépôt trixie-security manque, ajoutez-le à la main : sans lui, vous ne recevez aucun correctif de sécurité. C'est l'erreur la plus coûteuse de toute cette procédure.
non-free-firmware est nécessaire sur matériel physique (cartes réseau, RAID). contrib et non-free sont optionnels ; ne les ajoutez que si un paquet précis l'exige.
4.2 Mettre à jour
sudo apt update
sudo apt full-upgrade -y
sudo apt autoremove --purge -y
sudo apt clean
full-upgrade et non upgrade : le premier accepte d'installer ou de retirer des paquets quand une dépendance a changé, ce qui est exactement ce qu'on veut sur une machine neuve. upgrade bloquerait sur ces cas.
4.3 Redémarrer si le noyau a changé
ls /var/run/reboot-required 2>/dev/null && echo "Redémarrage nécessaire"
sudo reboot
Après redémarrage, reconnectez-vous et vérifiez que tout est remonté :
systemctl is-system-running # attendu : running
systemctl --failed # attendu : 0 loaded units listed
uptime
Un état degraded signale au moins un service en échec ; systemctl --failed vous dit lequel.
Étape 5 — Paquets de base
Recommandé — de quoi diagnostiquer sans improviser.
sudo apt install -y \
vnstat iftop htop nano git \
curl wget ca-certificates gnupg \
dnsutils net-tools lsof tcpdump \
rsync tree zip unzip \
ncdu sysstat needrestart
| Paquet | À quoi il sert |
|---|---|
vnstat |
Compteur de trafic réseau historisé, léger (vnstat -d, vnstat -m) |
iftop |
Trafic en temps réel par connexion |
htop |
Processus, CPU, mémoire, en interactif |
ncdu |
Trouve ce qui remplit le disque, en navigation |
sysstat |
sar, iostat, pidstat : historique de charge et d'E/S |
needrestart |
Signale après chaque apt les services à redémarrer |
dnsutils |
dig, nslookup pour déboguer la résolution |
lsof / tcpdump |
Qui tient un fichier, que passe-t-il sur le réseau |
Activer vnstat
sudo systemctl enable --now vnstat
vnstat --iflist # vérifie l'interface détectée
Il faut attendre quelques heures avant que vnstat -d affiche des données utiles : le compteur démarre à zéro.
Ce que vous ne voulez probablement pas
Sur un serveur, allez au plus maigre. Chaque paquet installé est une surface d'attaque et une mise à jour de plus. Évitez les méta-paquets task-*, les environnements de bureau, et tout serveur (apache2, nginx, mariadb) tant que vous n'êtes pas à l'étape de déploiement des services.
sudo apt-mark showmanual | head -50 # ce qui a été installé explicitement
Étape 6 — Locale, fuseau horaire et clavier
Recommandé — une heure juste est la condition d'un journal exploitable.
6.1 Locale
Attention à l'orthographe : c'est fr_FR.UTF-8 avec un tiret, pas fr_FR.UTF_8. Un underscore produit une locale invalide, silencieusement ignorée.
Générez d'abord la locale, puis déclarez-la comme défaut système :
sudo sed -i 's/^# *\(fr_FR.UTF-8\)/\1/' /etc/locale.gen
sudo locale-gen
sudo localectl set-locale LANG=fr_FR.UTF-8
Variante non interactive, adaptée à un script :
echo "locales locales/locales_to_be_generated multiselect fr_FR.UTF-8 UTF-8, en_US.UTF-8 UTF-8" | sudo debconf-set-selections
echo "locales locales/default_environment_locale select fr_FR.UTF-8" | sudo debconf-set-selections
sudo rm -f /etc/locale.gen
sudo dpkg-reconfigure --frontend=noninteractive locales
!!! note "Pourquoi localectl et pas LANG="
LANG=fr_FR.UTF-8 seul dans un terminal ne change que la session en cours. localectl écrit dans /etc/default/locale, lu par tous les services et toutes les futures connexions.
Recommandation : gardez les messages en anglais même avec une interface en français. Les messages d'erreur anglais sont cherchables et correspondent à la documentation ; les scripts qui parsent une sortie de commande cassent en français.
sudo localectl set-locale LANG=fr_FR.UTF-8 LC_MESSAGES=C.UTF-8
Reconnectez-vous, puis vérifiez :
locale # aucune ligne "Cannot set LC_*"
localectl status
6.2 Fuseau horaire
sudo timedatectl set-timezone Europe/Paris
timedatectl list-timezones | grep Paris # pour trouver un autre fuseau
!!! note "UTC ou heure locale ?"
Sur un parc international ou pour simplifier la lecture des journaux entre machines, UTC reste un choix défendable. Le seul vrai critère : que toutes vos machines aient le même fuseau.
6.3 Synchronisation de l'heure
Debian 13 utilise systemd-timesyncd par défaut. Une heure décalée casse TLS, les tokens et la corrélation des journaux.
sudo timedatectl set-ntp true
timedatectl status
Attendu : System clock synchronized: yes et NTP service: active.
Pour utiliser les serveurs français :
sudo mkdir -p /etc/systemd/timesyncd.conf.d
printf '[Time]\nNTP=0.fr.pool.ntp.org 1.fr.pool.ntp.org\nFallbackNTP=2.fr.pool.ntp.org 3.fr.pool.ntp.org\n' \
| sudo tee /etc/systemd/timesyncd.conf.d/pool-fr.conf
sudo systemctl restart systemd-timesyncd
timedatectl timesync-status
6.4 Clavier (console uniquement)
Utile seulement si vous passez par la console KVM de l'hébergeur ; sans effet sur SSH.
sudo localectl set-keymap fr
sudo localectl set-x11-keymap fr
Étape 7 — Compte administrateur et sudo
Essentiel — ne plus travailler avec le compte du fournisseur.
7.1 Créer l'utilisateur
sudo adduser alix
Choisissez un mot de passe long et unique. Les champs « Nom complet », « N° de bureau », etc. peuvent rester vides : validez avec Entrée.
7.2 Accorder les droits sudo
Sous Debian, l'appartenance au groupe sudo suffit. Nul besoin d'éditer les sudoers pour un administrateur standard.
sudo usermod -aG sudo alix
id alix # vérifie que "sudo" apparaît dans les groupes
!!! danger "Le -a n'est pas facultatif"
Le -a de usermod -aG est obligatoire : sans lui, l'utilisateur est retiré de tous ses autres groupes. C'est une erreur classique et difficile à diagnostiquer.
Si vous avez besoin d'une règle spécifique — par exemple autoriser un redémarrage de service sans mot de passe — n'éditez jamais /etc/sudoers directement. Créez un fichier dédié dans /etc/sudoers.d/, ce qui isole la panne en cas d'erreur de syntaxe :
sudo visudo -f /etc/sudoers.d/10-alix
# Redémarrage de nginx sans mot de passe
alix ALL=(root) NOPASSWD: /usr/bin/systemctl restart nginx
sudo chmod 0440 /etc/sudoers.d/10-alix
sudo visudo -c # contrôle global de la syntaxe
!!! danger "Toujours visudo, jamais l'éditeur seul"
visudo valide la syntaxe avant d'écrire, ce que nano /etc/sudoers ne fait pas. Une erreur dans les sudoers non validée rend sudo inutilisable pour tout le monde, y compris vous.
Évitez `NOPASSWD: ALL` : cela transforme toute exécution de code sous votre compte en accès root immédiat.
7.3 Tester avant de couper quoi que ce soit
Depuis une nouvelle session (en gardant l'ancienne ouverte) :
ssh alix@203.0.113.10
sudo -v # doit demander VOTRE mot de passe et rendre la main
sudo whoami # → root
Tant que ces trois commandes ne fonctionnent pas, n'allez pas plus loin : les étapes suivantes retirent votre accès actuel.
7.4 Confort de connexion (optionnel)
# Depuis votre poste : alias de connexion
cat >> ~/.ssh/config <<'EOF'
Host web01
HostName web01.exemple.fr
User alix
Port 22
IdentityFile ~/.ssh/id_ed25519
EOF
Vous vous connecterez ensuite avec ssh web01.
!!! note "Le port change en cours de route"
À ce stade, SSH écoute encore sur 22 : c'est le port à écrire ici. Si vous le
déplacez à l'étape 8.4, revenez mettre $SSHPORT — la suite de cette page emploie
2222 comme exemple de port final, y compris dans les règles de pare-feu de l'étape
10. Tant que la bascule n'est pas faite et vérifiée, gardez les deux ports
ouverts.
Étape 8 — Clés SSH et durcissement
Essentiel — c'est l'étape qui ferme la porte la plus attaquée.
C'est l'étape la plus risquée. Gardez une session ouverte du début à la fin.
8.1 Créer une paire de clés (sur votre poste, pas sur le serveur)
ssh-keygen -t ed25519 -C "alix@poste-bureau" -f ~/.ssh/id_ed25519
Ed25519 est le bon choix par défaut : court, rapide, sûr. Mettez une passphrase — elle protège la clé si votre poste est compromis, et ssh-agent évite de la retaper.
!!! info "Pour comprendre : pourquoi une clé plutôt qu'un mot de passe"
ssh-keygen produit deux fichiers : une clé privée, qui ne quitte jamais votre
poste, et une clé publique, que vous déposez sur tous les serveurs où vous allez.
À la connexion, le serveur envoie un défi que seule la clé privée sait résoudre — la
clé privée elle-même ne transite jamais.
Un mot de passe de douze caractères se devine en quelques milliards d'essais, et les
robots qui scannent le port 22 en font des milliers par heure. Une clé Ed25519 ne se
devine pas : il n'y a rien à essayer. C'est pour cela que l'on peut couper
l'authentification par mot de passe sans rien perdre.
!!! tip "Option avancée : une clé sur support matériel" Avec une clé de sécurité FIDO2 (YubiKey, Nitrokey, Token2), le secret ne quitte jamais le support physique — voler votre ordinateur ne suffit plus, il faut aussi la clé dans votre poche :
```bash
ssh-keygen -t ed25519-sk -C "alix@yubikey"
```
Le suffixe `-sk` (*security key*) demande une confirmation physique à chaque
connexion. Gardez une seconde clé matérielle, ou une clé logicielle de secours dans
`authorized_keys` : une clé FIDO2 perdue ne se duplique pas.
8.2 Déposer la clé publique
ssh-copy-id -i ~/.ssh/id_ed25519.pub alix@203.0.113.10
En manuel si ssh-copy-id n'est pas disponible :
ssh alix@203.0.113.10 "mkdir -p ~/.ssh && chmod 700 ~/.ssh"
cat ~/.ssh/id_ed25519.pub | ssh alix@203.0.113.10 "cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
Testez dans une nouvelle session : ssh alix@203.0.113.10 doit passer sans demander le mot de passe du compte.
8.3 Configurer sshd par fichier drop-in
Debian 13 lit /etc/ssh/sshd_config.d/*.conf. Écrivez votre configuration là plutôt que dans sshd_config : vos choix survivent aux mises à jour du paquet et se retirent en supprimant un seul fichier.
Regardez d'abord ce qui s'y trouve déjà :
ls -l /etc/ssh/sshd_config.d/
!!! warning "Le premier fichier qui parle a raison"
Pour une directive donnée, la première valeur rencontrée l'emporte, et les
fichiers sont lus dans l'ordre alphabétique. Sous Debian, l'Include est en tête de
sshd_config, donc n'importe quel drop-in gagne contre le corps du fichier — mais un
50-cloud-init.conf, fréquent sur les images d'hébergeurs, gagne contre votre
99-durcissement.conf. Or il force souvent PasswordAuthentication yes.
Si un tel fichier est présent : nommez le vôtre `00-durcissement.conf`, ou corrigez
directement le fichier fautif. Dans tous les cas, c'est `sshd -T` qui tranche, pas la
lecture des fichiers.
sudo nano /etc/ssh/sshd_config.d/99-durcissement.conf
# Authentification
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey
MaxAuthTries 3
MaxSessions 5
LoginGraceTime 30
# Limiter qui peut se connecter
AllowGroups ssh-users
# Réduire la surface
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding no
PermitTunnel no
PermitUserEnvironment no
# Sessions mortes
ClientAliveInterval 300
ClientAliveCountMax 2
Le groupe doit exister avant le rechargement, sans quoi plus personne ne peut se connecter :
sudo groupadd -f ssh-users
sudo usermod -aG ssh-users alix
id alix # ssh-users doit apparaître
!!! note "Pourquoi un groupe plutôt qu'une liste de noms"
Une liste blanche vaut mieux qu'un DenyUsers debian : elle couvre aussi les comptes
que vous n'avez pas anticipés. Mais AllowUsers alix désigne une personne, et un
serveur dont une seule personne détient l'accès est un serveur perdu le jour où cette
personne ne répond plus.
Avec un groupe, ouvrir l'accès à quelqu'un devient `sudo usermod -aG ssh-users bea`,
et le fermer `sudo gpasswd -d bea ssh-users` — sans jamais rouvrir la configuration de
sshd. **Prévoyez un second administrateur** dès maintenant : c'est le moment où cela
coûte le moins cher.
!!! danger "L'ordre des opérations n'est pas négociable"
PasswordAuthentication no vous verrouille dehors si la clé de l'étape 8.2 ne
fonctionne pas. Vérifiez la connexion par clé dans une session séparée avant de
recharger sshd, et gardez la session courante ouverte jusqu'au succès du test.
Vérifiez maintenant la configuration réellement appliquée, jamais le fichier seul :
sudo sshd -t # syntaxe ; silence = OK
sudo sshd -T | grep -Ei 'permitrootlogin|passwordauth|allowgroups|^port'
Puis rechargez et testez depuis une nouvelle session :
sudo systemctl reload ssh
8.4 Changer le port : la spécificité Debian 13
Depuis Debian 12, SSH est démarré par activation à la demande via systemd (ssh.socket). Conséquence : une directive Port dans sshd_config peut rester sans effet, car c'est le socket systemd qui décide de l'écoute.
!!! info "Pour comprendre : l'activation par socket"
Dans le modèle classique, sshd démarre au boot, ouvre lui-même le port 22 et attend.
Avec l'activation par socket, c'est systemd qui ouvre le port et écoute à sa
place ; sshd ne démarre qu'à l'arrivée d'une connexion, et systemd lui passe la
main.
Le port n'appartient donc plus à `sshd` mais à l'unité `ssh.socket`. C'est pourquoi
`Port 2222` dans `sshd_config` est lu, accepté, et sans effet : au moment où `sshd`
s'exécute, l'écoute est déjà établie par quelqu'un d'autre. Le gain — quelques
mégaoctets de mémoire tant que personne ne se connecte — explique ce choix par
défaut ; c'est aussi le piège le plus déroutant de Debian 13.
D'abord, constatez votre situation :
systemctl is-active ssh.socket # active → c'est le socket qui commande
systemctl is-active ssh.service
ss -tlpn | grep -E 'ssh|:22'
Si ssh.socket est actif, modifiez le socket :
sudo systemctl edit ssh.socket
[Socket]
ListenStream=
ListenStream=0.0.0.0:2222
ListenStream=[::]:2222
La ligne ListenStream= vide est indispensable : elle efface le port 22 hérité. Sans elle, vous ajoutez 2222 sans retirer 22.
sudo systemctl daemon-reload
sudo systemctl restart ssh.socket
ss -tlpn | grep 2222
Alternative — revenir au modèle classique, où sshd_config reprend la main :
sudo systemctl disable --now ssh.socket
sudo systemctl enable --now ssh.service
8.5 Faut-il vraiment changer de port ?
Ce n'est pas une mesure de sécurité, seulement de réduction du bruit : elle élimine les scans automatisés des journaux, pas un attaquant ciblé qui scannera vos 65 535 ports. Avec PasswordAuthentication no et fail2ban, le port 22 est déjà très défendable. Un port non standard complique en revanche le pare-feu, les sondes de supervision et les collaborateurs.
8.6 Vérification finale
Ouvrez une session neuve, sans fermer l'ancienne :
ssh -p 2222 alix@web01.exemple.fr
ssh -p 2222 debian@web01.exemple.fr # doit être refusé
ssh -p 2222 root@web01.exemple.fr # doit être refusé
Résultat attendu — la première commande ouvre une session sans demander de mot de
passe ; les deux autres échouent en Permission denied (publickey). Et la configuration
appliquée confirme les quatre directives qui comptent :
sudo sshd -T | grep -Ei '^port|permitrootlogin|passwordauthentication|allowgroups'
Si ça échoue
| Symptôme | Cause la plus fréquente |
|---|---|
Permission denied (publickey) sur votre compte |
La clé n'est pas dans authorized_keys, ou les droits sont trop larges (chmod 700 ~/.ssh, 600 authorized_keys) |
Connection refused après un changement de port |
ssh.socket n'a pas été rechargé, ou le pare-feu n'ouvre pas le nouveau port |
| Le mot de passe est toujours demandé | Un drop-in antérieur gagne : relisez ls /etc/ssh/sshd_config.d/ et tranchez avec sshd -T |
| Plus personne ne peut entrer | AllowGroups pointe un groupe vide ou inexistant |
Revenir en arrière, depuis la session restée ouverte :
sudo rm /etc/ssh/sshd_config.d/99-durcissement.conf
sudo systemctl reload ssh
# Si le port avait été déplacé :
sudo rm -f /etc/systemd/system/ssh.socket.d/override.conf
sudo systemctl daemon-reload && sudo systemctl restart ssh.socket
Étape 9 — Neutraliser le compte fournisseur
Essentiel — un compte connu de tous, au nom prévisible.
Le compte debian livré par l'hébergeur est connu de tous et ciblé en premier par les scans. Une fois votre accès personnel prouvé, retirez-lui l'accès SSH.
9.1 Choisir le niveau de neutralisation
| Niveau | Commande | Effet | Quand |
|---|---|---|---|
| SSH bloqué | AllowGroups ssh-users (étape 8.3) |
Le compte existe, ne peut plus se connecter par SSH | Cas général |
| Mot de passe verrouillé | sudo passwd -l debian |
Plus d'authentification par mot de passe | À combiner avec le précédent |
| Shell retiré | sudo usermod -s /usr/sbin/nologin debian |
Plus aucune session interactive | Compte purement technique |
| Compte supprimé | sudo deluser --remove-home debian |
Disparu | Seulement si rien ne dépend de lui |
La combinaison recommandée est la plus conservatrice :
sudo passwd -l debian
sudo usermod -s /usr/sbin/nologin debian
sudo rm -f /home/debian/.ssh/authorized_keys
!!! warning "Ne supprimez pas le compte du fournisseur trop vite"
Ne supprimez pas le compte avant d'avoir vérifié qu'aucun processus ne tourne sous son identité (ps -u debian) et qu'il n'est pas utilisé par l'outillage de l'hébergeur (agent de sauvegarde, cloud-init, console de secours). Certains hébergeurs recréent le compte à chaque redémarrage.
9.2 Le cas de root
sudo passwd -l root # verrouille le mot de passe root
sudo rm -f /root/.ssh/authorized_keys
Sur Debian, root n'a généralement pas de mot de passe utilisable en SSH dès lors que PermitRootLogin no est posé. Le verrouillage est une ceinture en plus des bretelles.
!!! danger "Ne retirez pas le shell de root"
N'utilisez pas usermod -s /usr/sbin/nologin root : vous perdriez l'accès par la console de secours de l'hébergeur, votre dernier recours.
9.3 Inventaire des comptes
# Comptes capables de se connecter
awk -F: '$3 >= 1000 && $3 < 65534 {print $1, $7}' /etc/passwd
# Membres du groupe sudo
getent group sudo
# Comptes sans mot de passe (doit être vide)
sudo awk -F: '$2 == "" {print $1}' /etc/shadow
# Clés SSH autorisées sur toute la machine
sudo find /home /root -name authorized_keys -exec ls -l {} \; -exec cat {} \;
La dernière commande mérite un passage attentif : une clé publique inconnue dans un authorized_keys est une porte dérobée.
Étape 10 — Pare-feu
Essentiel — tout ce qui écoute est joignable tant que rien ne filtre.
Debian 13 utilise nftables comme moteur de filtrage. Deux approches : nftables directement, ou ufw qui l'utilise en coulisse avec une syntaxe plus simple.
10.1 Option A — ufw (recommandé si vous débutez)
sudo apt install -y ufw
# Politique par défaut : tout entrant bloqué, tout sortant autorisé
sudo ufw default deny incoming
sudo ufw default allow outgoing
# SSH EN PREMIER — sinon vous vous coupez l'accès à l'activation
sudo ufw limit 2222/tcp comment 'SSH' # $SSHPORT : le port réellement en écoute
# Pendant une bascule de port, autorisez les deux le temps de vérifier :
# sudo ufw limit 22/tcp comment 'SSH ancien port'
# Services web, à n'ouvrir que le jour où vous les déployez
# sudo ufw allow 80/tcp comment 'HTTP'
# sudo ufw allow 443/tcp comment 'HTTPS'
sudo ufw enable
sudo ufw status verbose
limit plutôt que allow pour SSH : ufw bloque une IP qui dépasse six tentatives de connexion en 30 secondes. C'est une première barrière avant fail2ban.
10.2 Option B — nftables directement
Plus verbeux, mais sans couche intermédiaire et lisible d'un seul coup d'œil.
sudo nano /etc/nftables.conf
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept
ct state invalid drop
iif lo accept
ip protocol icmp accept
ip6 nexthdr icmpv6 accept
tcp dport 2222 ct state new limit rate 6/minute accept
# tcp dport { 80, 443 } accept
counter
}
chain forward {
type filter hook forward priority filter; policy drop;
}
chain output {
type filter hook output priority filter; policy accept;
}
}
sudo nft -c -f /etc/nftables.conf # vérifie la syntaxe sans appliquer
sudo systemctl enable --now nftables
sudo nft list ruleset
!!! danger "Jamais de policy drop sans règle SSH"
Ne laissez jamais policy drop sur input sans règle SSH au-dessus. Testez toujours avec nft -c -f avant d'appliquer.
10.3 Filet de sécurité pendant les tests
Programmez une désactivation automatique du pare-feu dans 10 minutes. Si vous vous verrouillez dehors, l'accès revient seul ; si tout va bien, vous annulez la tâche.
Si vous avez choisi ufw :
# Avant d'activer les règles
sudo systemd-run --on-active=10min --unit=ufw-panic systemctl stop ufw
# Une fois la connexion reconfirmée depuis une nouvelle session
sudo systemctl stop ufw-panic.timer
Si vous avez choisi nftables — systemctl stop ufw n'existe pas, il faut vider le
jeu de règles :
# Avant d'activer les règles
sudo systemd-run --on-active=10min --unit=nft-panic nft flush ruleset
# Une fois la connexion reconfirmée depuis une nouvelle session
sudo systemctl stop nft-panic.timer
Ici, l'unité transitoire est exactement ce qu'on veut : elle doit disparaître d'elle-même.
10.4 Le pare-feu de l'hébergeur
Beaucoup d'hébergeurs interposent leur propre filtrage en amont (security group, « firewall network »). Un port ouvert sur la machine et fermé chez eux reste inaccessible — et l'inverse est vrai. Tenez les deux couches alignées, et documentez-les ensemble.
# Depuis un autre poste : ce qui est réellement joignable
nmap -Pn -p- web01.exemple.fr
10.5 Contrôler, réparer, annuler
Résultat attendu — la politique d'entrée est en refus, SSH est la seule exception, et une nouvelle session s'ouvre toujours :
sudo ufw status verbose # ou : sudo nft list ruleset
ss -tlpn # ce qui écoute, à comparer avec ce qui est ouvert
Si ça échoue
| Symptôme | Cause la plus fréquente |
|---|---|
| Session en cours figée juste après l'activation | Règle SSH absente ou placée après la politique de refus |
| Le port est ouvert, le service reste injoignable | Le pare-feu de l'hébergeur ne l'ouvre pas de son côté (§10.4) |
ufw semble sans effet |
Des règles nftables concurrentes existent : sudo nft list ruleset pour voir qui filtre réellement |
| Tout tombe après un redémarrage | Le pare-feu n'est pas activé au démarrage : systemctl is-enabled ufw ou nftables |
Revenir en arrière — depuis la session restée ouverte, ou par la console de l'hébergeur :
sudo ufw disable # option A
sudo nft flush ruleset # option B, vide tout le filtrage
sudo systemctl disable --now nftables # et empêche son retour au redémarrage
!!! danger "Un pare-feu vidé est un serveur nu"
nft flush ruleset laisse la machine sans aucun filtrage. C'est une manœuvre de
dépannage, pas un état dans lequel on laisse un serveur : remettez les règles dans la
foulée, une fois l'accès retrouvé.
Étape 11 — fail2ban
Recommandé — écarte le bruit de fond avant qu'il ne remplisse vos journaux.
fail2ban lit les journaux et bannit temporairement les IP qui échouent trop souvent. Avec PasswordAuthentication no, son utilité sur SSH est surtout de nettoyer les journaux ; elle redevient essentielle dès que vous exposez un service authentifié par mot de passe.
sudo apt install -y fail2ban
Configuration
Ne modifiez jamais jail.conf : il est écrasé aux mises à jour. Créez jail.local.
sudo nano /etc/fail2ban/jail.local
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 4
backend = systemd
banaction = nftables-multiport
# Ne bannissez jamais votre propre IP fixe
ignoreip = 127.0.0.1/8 ::1 198.51.100.7
[sshd]
enabled = true
port = 2222
mode = aggressive
# Bannit longuement ceux qui reviennent après chaque bannissement
[recidive]
enabled = true
bantime = 1w
findtime = 1d
maxretry = 3
La jail recidive lit les bannissements de fail2ban lui-même : une adresse bannie trois
fois en vingt-quatre heures l'est ensuite pour une semaine. C'est ce qui sépare le robot
opportuniste, qui repasse toutes les heures, du visiteur qui s'est trompé de mot de passe.
backend = systemd est important sous Debian 13 : les journaux passent par journald, pas par un fichier /var/log/auth.log garanti. banaction = nftables-multiport évite un conflit avec le moteur nftables du système.
!!! note "Avec ufw plutôt que nftables"
Si vous avez choisi ufw à l'étape 10, utilisez plutôt banaction = ufw.
sudo systemctl enable --now fail2ban
sudo systemctl restart fail2ban
Exploitation au quotidien
sudo fail2ban-client status # prisons actives
sudo fail2ban-client status sshd # IP bannies
sudo fail2ban-client set sshd unbanip 198.51.100.7 # débannir
sudo fail2ban-client set sshd banip 203.0.113.99 # bannir à la main
!!! warning "Votre IP dans ignoreip, avant de démarrer"
Mettez votre IP fixe dans ignoreip avant de démarrer le service. Sinon, une passphrase mal tapée quatre fois vous bannit une heure de votre propre serveur.
Étape 12 — Mises à jour de sécurité automatiques
Essentiel — un serveur à jour aujourd'hui ne l'est plus dans trois semaines.
Un serveur non mis à jour devient vulnérable en quelques semaines. unattended-upgrades applique les correctifs de sécurité sans intervention.
sudo apt install -y unattended-upgrades apt-listchanges
sudo dpkg-reconfigure -plow unattended-upgrades
Configuration
sudo nano /etc/apt/apt.conf.d/50unattended-upgrades
Lignes à vérifier ou décommenter :
Unattended-Upgrade::Origins-Pattern {
"origin=Debian,codename=${distro_codename},label=Debian-Security";
"origin=Debian,codename=${distro_codename}-security,label=Debian-Security";
};
Unattended-Upgrade::Mail "alix@exemple.fr";
Unattended-Upgrade::MailReport "on-change";
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Automatic-Reboot "false";
Unattended-Upgrade::Automatic-Reboot-Time "04:00";
Le choix structurant est Automatic-Reboot :
| Valeur | Conséquence |
|---|---|
"false" |
Les correctifs noyau attendent un redémarrage manuel. Le serveur reste vulnérable en attendant, mais ne tombe jamais seul. |
"true" |
Redémarrage automatique à 04:00 si nécessaire. Correctifs réellement actifs, au prix d'une coupure imprévisible. |
Pour un serveur de production isolé, "true" avec une heure creuse est souvent le meilleur compromis. Pour un serveur critique sans redondance, gardez "false" et redémarrez lors d'une fenêtre planifiée.
Le fichier qui décide vraiment
dpkg-reconfigure écrit dans /etc/apt/apt.conf.d/20auto-upgrades, et c'est ce
fichier qui commande l'automatisation — pas 50unattended-upgrades, qui n'en règle que
le détail. Vérifiez-le explicitement :
cat /etc/apt/apt.conf.d/20auto-upgrades
Il doit contenir ces quatre directives :
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "7";
APT::Periodic::Unattended-Upgrade "1";
Sans la dernière à "1", tout le reste est configuré pour rien : les index se
rafraîchissent, les paquets se téléchargent, et rien ne s'installe.
Rendre needrestart non interactif
Installé à l'étape 5, needrestart pose une question quand des services doivent être
relancés. Sur un serveur, cette question bloque unattended-upgrades au lieu d'être
posée à quelqu'un :
echo "\$nrconf{restart} = 'a';" | sudo tee /etc/needrestart/conf.d/50-auto.conf
'a' redémarre automatiquement les services concernés. Gardez 'l' (lister seulement)
si vous préférez décider vous-même — mais alors n'activez pas le redémarrage automatique
d'unattended-upgrades, vous cumuleriez le pire des deux.
Savoir ce qui n'est plus suivi
Tous les paquets d'une version stable ne sont pas couverts par l'équipe de sécurité jusqu'au bout — certains sortent du périmètre en cours de vie.
sudo apt install -y debian-security-support
check-support-status
À relancer après chaque gros apt : la commande liste ce qui est installé chez vous et
n'est plus, ou bientôt plus, suivi.
Vérifier que ça tourne
sudo unattended-upgrade --dry-run --debug # simulation détaillée
systemctl list-timers | grep -E 'apt|unattended'
cat /var/log/unattended-upgrades/unattended-upgrades.log
Deux minuteries doivent apparaître : apt-daily.timer (rafraîchissement des index) et apt-daily-upgrade.timer (installation).
Savoir quand redémarrer
cat /var/run/reboot-required.pkgs 2>/dev/null # paquets concernés
sudo needrestart -b # services à relancer
Pensez à intégrer cette vérification à votre routine hebdomadaire.
Contrôler, réparer, annuler
Résultat attendu — les deux minuteries APT sont actives, et le journal montre des installations réelles et non un simulacre :
systemctl list-timers apt-daily.timer apt-daily-upgrade.timer
grep -c "Packages that will be upgraded" /var/log/unattended-upgrades/unattended-upgrades.log
Si ça échoue
| Symptôme | Cause la plus fréquente |
|---|---|
| Le journal reste vide des jours durant | APT::Periodic::Unattended-Upgrade est à "0" dans 20auto-upgrades |
| « No packages found that can be upgraded unattended » en permanence | Origins-Pattern ne couvre pas trixie-security, ou le dépôt de sécurité manque (étape 4) |
| Les mises à jour s'installent mais rien ne redémarre | needrestart en mode interactif : il attend une réponse que personne ne donnera |
| Le serveur redémarre à des heures imprévues | Automatic-Reboot "true" sans Automatic-Reboot-Time adapté |
Revenir en arrière — pour suspendre l'automatisme sans désinstaller :
sudo sed -i 's/^APT::Periodic::Unattended-Upgrade.*/APT::Periodic::Unattended-Upgrade "0";/' \
/etc/apt/apt.conf.d/20auto-upgrades
sudo systemctl disable --now apt-daily-upgrade.timer
!!! warning "Suspendre, c'est accepter de faire à la main" Un serveur sans mises à jour automatiques redevient vulnérable en quelques semaines. Si vous coupez l'automatisme, inscrivez la mise à jour manuelle dans une routine datée — pas dans vos bonnes intentions.
Étape 13 — Swap, /tmp et paramètres noyau
Recommandé — la stabilité au quotidien, et quelques durcissements noyau.
13.1 Swap
Beaucoup de VPS sont livrés sans swap. Un peu de swap évite qu'un pic de mémoire déclenche le tueur de processus du noyau (OOM killer).
free -h # vérifier ce qui existe déjà
swapon --show
Si la ligne Swap est à zéro :
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
Dimensionnement raisonnable : 2 Go jusqu'à 4 Go de RAM, puis 4 Go au-delà. Inutile d'aller au-delà sur un serveur : si vous swappez beaucoup, il faut de la RAM, pas du disque.
# Réduire l'agressivité du swap sur un serveur
echo 'vm.swappiness=10' | sudo tee /etc/sysctl.d/99-swappiness.conf
!!! note "Le coût du swap dépend du stockage" Sur SSD NVMe, le swap est peu coûteux. Sur un disque réseau facturé aux IOPS, il peut coûter cher et ralentir fortement.
13.2 /tmp est désormais en RAM
Changement notable de Debian 13 : /tmp est monté en tmpfs, donc en mémoire, et vidé à chaque redémarrage (note de version).
!!! info "Pour comprendre : un système de fichiers en mémoire" Un tmpfs ressemble à un répertoire ordinaire, mais son contenu n'est jamais écrit sur le disque : il vit dans la RAM. D'où ses deux propriétés, qui expliquent tout le reste — il est très rapide, et il disparaît intégralement à l'extinction.
Cela veut aussi dire qu'un fichier de 3 Go déposé dans `/tmp` occupe 3 Go de mémoire
vive, au détriment des services. Sur un serveur à 2 Go de RAM, quelques fichiers
temporaires volumineux suffisent à provoquer un arrêt par manque de mémoire.
df -h /tmp
findmnt /tmp
Deux conséquences à connaître :
- Écrire un gros fichier dans
/tmpconsomme de la RAM. Les traitements volumineux doivent utiliser/var/tmp, qui reste sur disque. - Sur une machine migrée depuis Debian 12, les anciens fichiers de
/tmpsont toujours sur le disque, masqués par le montage. Ils occupent de l'espace invisible.
# Retrouver et nettoyer l'ancien /tmp masqué
sudo mkdir -p /mnt/racine
sudo mount --bind / /mnt/racine
ls -lha /mnt/racine/tmp/
# ... supprimez ce qui doit l'être ...
sudo umount /mnt/racine && sudo rmdir /mnt/racine
Pour repasser /tmp sur disque si votre usage le demande :
sudo systemctl mask tmp.mount
sudo reboot
13.3 Paramètres réseau et noyau
sudo nano /etc/sysctl.d/99-durcissement.conf
# Anti-usurpation d'adresse
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Ignorer le routage par la source et les redirections ICMP
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
# Journaliser les paquets manifestement usurpés
net.ipv4.conf.all.log_martians = 1
# Résistance aux SYN flood
net.ipv4.tcp_syncookies = 1
# Limiter la lecture des informations noyau
kernel.dmesg_restrict = 1
kernel.kptr_restrict = 2
# Protection des liens en répertoires partagés
fs.protected_hardlinks = 1
fs.protected_symlinks = 1
sudo sysctl --system
sudo sysctl -a --pattern 'tcp_syncookies|rp_filter' # contrôle
!!! warning "Ne coupez pas le routage à l'aveugle"
N'appliquez pas net.ipv4.ip_forward = 0 sans réfléchir : Docker, les conteneurs et
les VPN en ont besoin.
13.4 AppArmor : vérifier qu'il travaille
AppArmor est actif par défaut depuis Debian 12, et personne ne le regarde jamais. Il
confine chaque programme profilé à ce dont il a besoin : un nginx compromis ne peut pas
lire /home, même en tant que root, parce que le noyau le lui refuse.
sudo apt install -y apparmor-utils
sudo aa-status
Résultat attendu — des profils chargés, et surtout des profils en mode enforce :
apparmor module is loaded.
XX profiles are loaded.
XX profiles are in enforce mode.
0 profiles are in complain mode.
Un profil en complain journalise les violations sans les bloquer : c'est le mode de mise
au point, pas un mode d'exploitation. Repassez-le en application avec
sudo aa-enforce /etc/apparmor.d/<profil>.
Retenez surtout que les services installés plus tard apportent leurs profils : après avoir
déployé nginx ou MariaDB, un aa-status confirme qu'ils sont bien confinés.
Étape 14 — Journaux, supervision et alertes
Recommandé — savoir ce qui se passe, et être prévenu quand plus rien ne se passe.
14.1 Plafonner les journaux
Par défaut, journald peut occuper jusqu'à 10 % du système de fichiers. Sur un petit VPS, c'est une cause classique de disque plein.
Le répertoire des drop-ins n'existe pas par défaut : créez-le avant d'ouvrir l'éditeur, sinon l'enregistrement échoue.
sudo mkdir -p /etc/systemd/journald.conf.d
sudo nano /etc/systemd/journald.conf.d/00-limites.conf
[Journal]
Storage=persistent
SystemMaxUse=500M
SystemMaxFileSize=50M
MaxRetentionSec=1month
sudo systemctl restart systemd-journald
journalctl --disk-usage
14.2 Commandes de lecture utiles
journalctl -p err -b # erreurs depuis le démarrage
journalctl -u ssh --since "1 hour ago" # un service, une fenêtre de temps
journalctl -f # en direct
journalctl --since yesterday | grep -i "failed password"
last -20 # dernières connexions réussies
lastb -20 # dernières tentatives échouées
14.3 Recevoir les alertes par mail
Sans MTA, les rapports de unattended-upgrades, cron et fail2ban sont écrits mais jamais lus. Un relais léger suffit — n'installez pas un serveur de mail complet.
sudo apt install -y msmtp-mta bsd-mailx
sudo nano /etc/msmtprc
defaults
auth on
tls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
logfile /var/log/msmtp.log
account default
host smtp.exemple.fr
port 587
from web01@exemple.fr
user web01@exemple.fr
password VOTRE_MOT_DE_PASSE
sudo chmod 600 /etc/msmtprc
echo "Test depuis web01" | mail -s "Test alerte" alix@exemple.fr
!!! warning "Un mot de passe en clair sur le disque"
Le fichier contient un mot de passe en clair : chmod 600 est obligatoire. Préférez un mot de passe d'application dédié, révocable, plutôt que votre mot de passe principal.
Redirigez ensuite le courrier de root vers vous :
echo "root: alix@exemple.fr" | sudo tee -a /etc/aliases
sudo newaliases 2>/dev/null || true
14.4 Surveiller l'essentiel
# Trafic réseau
vnstat -d # par jour
vnstat -m # par mois, utile face à un quota
# Disque
df -h
sudo ncdu / # exploration interactive
# Charge et E/S
uptime
iostat -x 2 5
sar -u 1 5
Une alerte simple sur le disque, via cron, vaut mieux qu'une supervision compliquée jamais installée :
sudo tee /etc/cron.daily/alerte-disque >/dev/null <<'EOF'
#!/bin/sh
SEUIL=85
USAGE=$(df / --output=pcent | tail -1 | tr -dc '0-9')
[ "$USAGE" -ge "$SEUIL" ] && echo "Disque / à ${USAGE}% sur $(hostname -f)" \
| mail -s "ALERTE disque $(hostname)" root
exit 0
EOF
sudo chmod +x /etc/cron.daily/alerte-disque
14.5 Sortir les journaux de la machine
Tout ce qui précède repose sur des journaux locaux. Or un attaquant qui obtient root
efface /var/log et l'historique de journald : il ne reste rien pour comprendre ce qui
s'est passé. Un journal n'a de valeur d'enquête que s'il existe ailleurs.
Le principe : journald recopie vers syslog, et rsyslog expédie vers un collecteur
distant.
# Sur la machine : autoriser le relais vers syslog
sudo mkdir -p /etc/systemd/journald.conf.d
echo -e "[Journal]\nForwardToSyslog=yes" \
| sudo tee /etc/systemd/journald.conf.d/10-syslog.conf
sudo systemctl restart systemd-journald
# Puis, dans rsyslog, une ligne de destination (TCP, port 514 par défaut)
# *.* @@collecteur.exemple.fr:514
La mise en place complète — collecteur, chiffrement TLS, rétention — dépasse cette fiche. Le minimum utile, si vous n'avez pas de collecteur : envoyer les alertes par courriel (§14.3), dont la copie sort de la machine.
14.6 Une sonde depuis l'extérieur
L'alerte disque ci-dessus part de la machine. Le jour où celle-ci est éteinte, saturée ou injoignable, elle n'envoie plus rien — et le silence ressemble à s'y méprendre au bon fonctionnement.
C'est la brique qui manque à presque tous les petits serveurs : une sonde externe, qui teste depuis ailleurs et vous prévient quand la réponse ne vient pas.
- Uptime Kuma, auto-hébergé sur une autre machine — idéalement chez un autre hébergeur, sans quoi une panne de datacentre emporte la sonde avec le serveur ;
- ou un service de supervision gratuit, si vous n'avez qu'une machine.
Surveillez au minimum le port SSH et, dès qu'ils existent, les ports 80 et 443 avec la date d'expiration du certificat.
Étape 15 — Sauvegarde et instantané de référence
Essentiel — la seule étape qui rattrape l'échec de toutes les autres.
15.1 Instantané immédiat
Vous venez de construire une base propre. Prenez-en un instantané (snapshot) chez votre hébergeur maintenant, avant de déployer le moindre service. C'est votre point de retour : une mauvaise manipulation plus tard se résoudra en trois minutes au lieu d'une réinstallation complète.
15.2 Archiver la configuration
sudo tar czf /root/config-initiale-$(date +%F).tar.gz \
/etc/ssh/sshd_config.d /etc/hosts /etc/hostname \
/etc/apt/sources.list.d /etc/sudoers.d \
/etc/fail2ban/jail.local /etc/nftables.conf \
/etc/sysctl.d /etc/default/locale 2>/dev/null
Récupérez l'archive sur votre poste :
scp -P 2222 alix@web01.exemple.fr:/root/config-initiale-*.tar.gz ./
!!! tip "Mieux : versionner la configuration"
Mieux encore : versionnez ces fichiers dans un dépôt Git privé — sans les secrets. git est déjà installé à l'étape 5.
15.3 Mettre en place une vraie sauvegarde
Une sauvegarde qu'on n'a jamais restaurée n'est pas une sauvegarde. Principe des 3-2-1 : trois copies, deux supports, une hors site.
sudo apt install -y restic
Les paramètres de restic se passent par variables d'environnement. Comme sudo les
efface (env_reset est le défaut sous Debian), déposez-les dans un fichier lu par root
plutôt que de compter sur un export dans votre session :
sudo install -m 600 /dev/null /root/.restic-env
sudo tee /root/.restic-env >/dev/null <<'EOF'
RESTIC_REPOSITORY=sftp:sauvegarde@stockage.exemple.fr:/backups/web01
RESTIC_PASSWORD_FILE=/root/.restic-pass
EOF
sudo install -m 600 /dev/null /root/.restic-pass
sudo nano /root/.restic-pass # la phrase de passe du dépôt, seule sur sa ligne
!!! danger "Cette phrase de passe n'est récupérable nulle part" Un dépôt restic dont on a perdu la phrase de passe est définitivement illisible. Recopiez-la hors du serveur — gestionnaire de mots de passe ou coffre de l'association — avant d'aller plus loin.
Initialisation et premières commandes, en chargeant explicitement ce fichier :
# Initialisation du dépôt distant (une seule fois)
sudo env $(cat /root/.restic-env | xargs) restic init
# Sauvegarde
sudo env $(cat /root/.restic-env | xargs) restic backup /etc /home /var/www --exclude-caches
# Vérification et purge
sudo env $(cat /root/.restic-env | xargs) restic snapshots
sudo env $(cat /root/.restic-env | xargs) restic forget \
--keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
Automatisez avec une minuterie systemd plutôt qu'un cron : les échecs remontent dans
journalctl.
!!! warning "N'utilisez pas systemd-run pour une sauvegarde"
systemd-run --on-calendar crée une unité transitoire : elle disparaît au
redémarrage, et la sauvegarde cesse silencieusement. On ne s'en aperçoit que le jour
où l'on en a besoin. Écrivez de vraies unités.
sudo tee /etc/systemd/system/sauvegarde.service >/dev/null <<'EOF'
[Unit]
Description=Sauvegarde restic
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
EnvironmentFile=/root/.restic-env
ExecStart=/usr/bin/restic backup /etc /home /var/www --exclude-caches
ExecStartPost=/usr/bin/restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
EOF
sudo tee /etc/systemd/system/sauvegarde.timer >/dev/null <<'EOF'
[Unit]
Description=Sauvegarde restic quotidienne
[Timer]
OnCalendar=*-*-* 03:00:00
RandomizedDelaySec=30m
Persistent=true
[Install]
WantedBy=timers.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now sauvegarde.timer
Persistent=true rattrape l'exécution manquée si la machine était éteinte à 3 h.
EnvironmentFile règle le problème des variables : le service les lit lui-même, sans
dépendre de votre session.
Résultat attendu — la minuterie apparaît avec sa prochaine échéance, et la première exécution se déclenche à la demande :
systemctl list-timers sauvegarde.timer
sudo systemctl start sauvegarde.service
journalctl -u sauvegarde.service -n 20
15.4 Tester la restauration
À faire une fois, tout de suite, puis tous les trimestres :
sudo restic restore latest --target /tmp/test-restauration --include /etc/hosts
cat /tmp/test-restauration/etc/hosts
Notez la date du dernier test réussi quelque part de visible. C'est la seule métrique qui compte vraiment.
Étape 16 — Auditer ce qui a été fait
Recommandé — vérifier que ce qui est écrit ici a réellement pris.
Vous avez appliqué quinze étapes ; un outil d'audit vous dira lesquelles ont réellement pris. Lynis parcourt la machine et note ce qu'il trouve.
sudo apt install -y lynis
sudo lynis audit system
Le rapport se termine par un index de durcissement sur 100, et surtout par une liste de suggestions numérotées, chacune avec sa justification.
Résultat attendu — sur une Debian fraîchement installée, l'index tourne autour de 55 à 60 ; après un parcours comme celui-ci, il monte typiquement vers 70 à 75.
!!! warning "Ne courez pas après le 100" Les points restants dépendent de ce que la machine fait : durcir un noyau au-delà du raisonnable, ou désactiver des modules dont vos services auront besoin, se paie en pannes incompréhensibles six mois plus tard. Lisez les suggestions, appliquez celles qui ont un sens pour votre usage, et notez pourquoi vous écartez les autres.
Le rapport complet reste dans /var/log/lynis-report.dat. Relancez l'audit après chaque
déploiement de service : c'est la meilleure façon de voir ce qu'un apt install a ouvert.
Checklist récapitulative
Préparatifs
- Accès de secours hors SSH vérifié (console KVM, mode rescue)
- Enregistrement A (et AAAA) créé vers le FQDN
- Reverse DNS (PTR) aligné sur le FQDN, IPv4 et IPv6
digconfirme la correspondance dans les deux sens
Identité et système
- Première connexion réussie, état initial relevé (
ss -tulpn) - Empreinte de la clé d'hôte comparée avec celle publiée par l'hébergeur
- Clés d'hôte régénérées si l'image était clonée,
machine-idvérifié - Partition étendue à tout le volume facturé (
lsblk,growpart) - Nom d'hôte court défini avec
hostnamectl set-hostname /etc/hostscorrigé : FQDN avant nom court sur la ligne127.0.1.1hostname -frenvoie bien le FQDN- Sources APT converties en deb822,
trixie-securityprésent apt full-upgradepassé, machine redémarrée si le noyau a changésystemctl --failedne renvoie rien- Paquets de base installés,
vnstatactivé - Locale
fr_FR.UTF-8générée et définie (avec un tiret) - Fuseau horaire posé,
timedatectlindique l'horloge synchronisée
Accès
- Compte administrateur créé et ajouté au groupe
sudo(avec-aG) sudo -vetsudo whoamifonctionnent depuis le nouveau compte- Clé Ed25519 générée et déposée, connexion par clé vérifiée
- Drop-in
sshd_config.dcréé : root refusé, mot de passe désactivé,AllowGroupsposé - Groupe
ssh-userscréé et peuplé — avec un second administrateur sudo sshd -Tconfirme la configuration réellement appliquée- Port SSH vérifié côté
ssh.socketsi vous l'avez changé - Compte fournisseur verrouillé, ses clés SSH retirées
- Aucune clé inconnue dans les
authorized_keysde la machine
Protection
- Pare-feu actif, SSH autorisé avant activation
- Ports ouverts alignés entre la machine et le pare-feu de l'hébergeur
- fail2ban actif, votre IP fixe dans
ignoreip unattended-upgradesactif, politique de redémarrage décidée- Paramètres sysctl appliqués (
sysctl --system) 20auto-upgradescontrôlé :Unattended-Upgrade "1"needrestartpassé en mode non interactifcheck-support-statusconsulté (paquets hors support sécurité)aa-status: profils AppArmor chargés et en mode enforce- Swap présent si la RAM est limitée
Exploitation
- Journaux plafonnés (
journalctl --disk-usage) - Envoi de mail testé, courrier de root redirigé
- Sauvegarde configurée et restauration testée une fois
- Instantané de référence pris chez l'hébergeur
- Archive de configuration récupérée hors du serveur
- Minuterie de sauvegarde persistante (
.timer+Persistent=true), pas unsystemd-run - Sonde externe en place, sur une autre machine que celle-ci
lynis audit systempassé, suggestions lues et arbitrées- TTL DNS remonté à sa valeur normale
Vérification finale d'un seul bloc
hostname -f; echo '---'
localectl status | head -3; echo '---'
timedatectl | grep -E 'Time zone|synchronized'; echo '---'
sudo sshd -T | grep -E '^(port|permitrootlogin|passwordauthentication|allowgroups)'; echo '---'
sudo ufw status verbose 2>/dev/null || sudo nft list ruleset | head -20; echo '---'
sudo fail2ban-client status; echo '---'
systemctl --failed; echo '---'
free -h; df -h /
Si vous êtes verrouillé dehors
Cela arrive, malgré la session de secours — elle finit par expirer, ou l'on referme la mauvaise fenêtre. Voici l'ordre dans lequel chercher.
1. Est-ce vraiment le serveur ? Depuis un autre réseau (partage de connexion du téléphone), testez le port :
nc -vz 203.0.113.10 2222
Si la connexion passe d'ailleurs mais pas de chez vous, c'est fail2ban ou le pare-feu de l'hébergeur qui a banni votre adresse, pas la configuration du serveur.
2. La console de l'hébergeur. KVM, VNC ou console série : elle donne un accès clavier indépendant de SSH et du réseau. C'est la voie la plus rapide, et la raison pour laquelle cette fiche demande de vérifier son existence avant de commencer.
Une fois connecté, retirez ce qui bloque :
sudo rm /etc/ssh/sshd_config.d/99-durcissement.conf
sudo systemctl restart ssh
sudo ufw disable # ou : sudo nft flush ruleset
sudo fail2ban-client unban --all
3. Le mode rescue. Si même la console ne répond plus, l'hébergeur propose un démarrage sur un système de secours. Le disque de la machine s'y monte, et l'on répare de l'extérieur :
lsblk # identifier la partition racine
sudo mount /dev/vda1 /mnt
sudo mount --bind /dev /mnt/dev
sudo mount --bind /proc /mnt/proc
sudo mount --bind /sys /mnt/sys
sudo chroot /mnt /bin/bash
# Dans le chroot : annuler la modification fautive
rm /etc/ssh/sshd_config.d/99-durcissement.conf
sed -i 's/^Port .*/Port 22/' /etc/ssh/sshd_config
systemctl disable nftables
exit
sudo umount -R /mnt
Redémarrez sur le disque, reconnectez-vous, et reprenez la configuration en gardant cette fois deux sessions ouvertes.
!!! tip "Ce qui verrouille, dans l'ordre de fréquence"
Un port SSH changé sans ouvrir le nouveau dans le pare-feu ; un AllowGroups posé
avant que le groupe n'existe ; PasswordAuthentication no avec une clé qui n'a jamais
été testée ; une politique drop activée sans règle SSH au-dessus. Les quatre se
préviennent en testant depuis une seconde session avant de fermer la première.
Et après ?
Le serveur est prêt à recevoir ses services — c'est là que cette fiche s'arrête et que les autres commencent :
- Docker — installer le moteur de conteneurs sur cette base ;
- Nextcloud et Matrix — deux services que l'association héberge ;
- Déploiement du wiki — l'exemple d'un service statique, webhook compris.
Ouvrez les ports au moment du déploiement, pas avant, et repassez un lynis audit system
derrière.
!!! tip "Le deuxième serveur ne se prépare pas à la main" Cette checklist a exactement la forme d'un playbook. Le jour où vous installez une deuxième machine, transposez-la dans Ansible : chaque étape devient une tâche idempotente, l'ordre est écrit noir sur blanc, et la configuration cesse de vivre dans la mémoire de celui qui l'a faite. Des rôles publics existent pour Debian 13 — lisez-les avant de les appliquer, ils durcissent parfois plus que vous ne le souhaitez.
Ce que vous avez construit
En cinq lignes, pour consolider ce que la checklist vérifie point par point :
- Une identité cohérente — un FQDN qui se résout dans les deux sens, un nom d'hôte stable, une heure juste. Sans cela, les journaux mentent et les mails partent en indésirables.
- Un accès nominatif et robuste — une clé par personne, un groupe qui décide qui entre, le compte du fournisseur neutralisé, root inaccessible depuis le réseau.
- Une surface réduite et surveillée — un pare-feu qui ferme par défaut, fail2ban qui écarte les robots, AppArmor qui confine les services, des journaux plafonnés et lisibles.
- Une machine qui se maintient seule — les correctifs de sécurité s'installent, ce qui n'est plus suivi se signale, et vous êtes prévenu de ce qui demande un redémarrage.
- Une porte de sortie — un instantané de référence, une sauvegarde dont la restauration a été testée, et une sonde extérieure pour vous dire que la machine vit.
Le reste — les services, leurs ports, leurs bases de données — se construit par-dessus.
Ce qui change par rapport à l'ancienne fiche Debian 11
L'ancienne fiche couvrait l'essentiel de la mise en route. Voici ce qui y était inexact ou incomplet, et pourquoi c'est corrigé ici.
| Ancienne fiche | Correction | Pourquoi |
|---|---|---|
LANG=fr_FR.UTF_8 |
fr_FR.UTF-8 |
Tiret et non underscore ; sinon la locale est invalide. Et LANG= seul ne dure que le temps de la session : utilisez localectl set-locale |
Ordre dans /etc/hosts « avant les noms déjà renseignés » |
FQDN puis nom court, dans cet ordre | hostname -f prend le premier nom de la ligne. Inversé, il renvoie le nom court |
adduser nom |
sudo adduser nom |
La création d'utilisateur demande les privilèges root |
Droits supplémentaires via visudo |
usermod -aG sudo d'abord ; visudo -f /etc/sudoers.d/10-nom si besoin spécifique |
L'appartenance au groupe suffit dans 95 % des cas. Un fichier dédié isole une erreur de syntaxe au lieu de casser sudo pour tout le monde |
apt upgrade -y |
apt full-upgrade -y |
upgrade refuse d'installer ou de retirer des paquets, donc bloque sur des transitions courantes d'une machine neuve |
« Désactiver ssh pour l'utilisateur debian » |
AllowGroups ssh-users + verrouillage du compte |
Une liste blanche couvre aussi les comptes non anticipés ; un groupe survit à l'arrivée d'un second administrateur ; le verrouillage ferme les autres voies que SSH |
Ce que l'ancienne fiche ne couvrait pas
- Les clés SSH avant la désactivation du mot de passe. C'est la seule séquence qui ne pardonne pas l'inversion.
- Le pare-feu. Sans lui, chaque service déployé est exposé par défaut.
- Les mises à jour automatiques. Un serveur à jour le jour de l'installation ne l'est plus trois semaines après.
- La sauvegarde. Y compris le test de restauration, qui est la partie qu'on saute toujours.
Pièges spécifiques à Debian 13
| Piège | Symptôme | Solution |
|---|---|---|
| SSH en activation par socket | Port 2222 dans sshd_config ignoré, SSH reste sur 22 |
systemctl edit ssh.socket avec un ListenStream= vide puis le nouveau port (étape 8.4) |
/tmp en tmpfs |
Espace disque « disparu » après migration ; gros fichiers qui saturent la RAM | Nettoyer l'ancien /tmp masqué, utiliser /var/tmp pour le volumineux (étape 13.2) |
| Sources au format deb822 | Un script qui écrit dans /etc/apt/sources.list semble sans effet |
Écrire dans /etc/apt/sources.list.d/*.sources |
cloud-init |
Le nom d'hôte revient à sa valeur d'origine après redémarrage | preserve_hostname: true (étape 3.3) |