--- 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, mises à jour automatiques, supervision et sauvegarde. --- # Préparation d'un serveur Debian 13 (Trixie) !!! info "État du document" Rédigé le **19/09/2026** pour **Debian 13 « Trixie »**. 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 30 à 45 minutes. **Règle d'or** : tant que vous touchez à SSH ou au pare-feu, gardez une **deuxième session SSH ouverte** sur le serveur et ne la fermez jamais. Elle est votre filet de sécurité si une modification vous verrouille dehors. Vérifiez 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. ### 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) | | `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 | ### 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é. ```mermaid flowchart LR A[DNS + reverse] --> B[Identité
hostname, hosts] B --> C[Système
MAJ, paquets, locale] C --> D[Compte admin
+ sudo] D --> E[SSH
clés + durcissement] E --> F[Pare-feu
+ fail2ban] F --> G[Automatismes
MAJ, supervision] G --> H[Sauvegarde
+ instantané] ``` ## Étape 1 — DNS direct et reverse DNS 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 : ```bash 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*). > 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 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`. ```bash 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 ce qu'on a vraiment reçu Avant toute modification, prenez trois minutes pour constater l'état initial. ```bash 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 ``` 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 ### 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`. ```bash sudo hostnamectl set-hostname web01 ``` Vous pouvez aussi renseigner deux champs purement informatifs, utiles quand on gère plusieurs machines : ```bash 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**. ```bash 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. > ⚠️ 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 ```bash 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. > Sur certaines images cloud, `cloud-init` réécrit le nom d'hôte à chaque redémarrage. Si votre nom revient à sa valeur d'origine après un `reboot` : > > ```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 ### 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](https://dev.to/r3m8/debian-13-trixie-adoptez-le-nouveau-format-deb822-1ml6)). Autant partir sur le bon format tout de suite. ```bash 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](https://ostechnix.com/migrate-to-deb822-format-debian-13-trixie/)). Vérifiez le résultat : ```bash 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 ``` > 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 ```bash 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é ```bash 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é : ```bash 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 ```bash 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 ```bash 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. ```bash sudo apt-mark showmanual | head -50 # ce qui a été installé explicitement ``` ## Étape 6 — Locale, fuseau horaire et clavier ### 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 : ```bash 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 : ```bash 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 ``` > `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. ```bash sudo localectl set-locale LANG=fr_FR.UTF-8 LC_MESSAGES=C.UTF-8 ``` Reconnectez-vous, puis vérifiez : ```bash locale # aucune ligne "Cannot set LC_*" localectl status ``` ### 6.2 Fuseau horaire ```bash sudo timedatectl set-timezone Europe/Paris timedatectl list-timezones | grep Paris # pour trouver un autre fuseau ``` > 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. ```bash sudo timedatectl set-ntp true timedatectl status ``` Attendu : `System clock synchronized: yes` et `NTP service: active`. Pour utiliser les serveurs français : ```bash 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. ```bash sudo localectl set-keymap fr sudo localectl set-x11-keymap fr ``` ## Étape 7 — Compte administrateur et sudo ### 7.1 Créer l'utilisateur ```bash 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. ```bash sudo usermod -aG sudo alix id alix # vérifie que "sudo" apparaît dans les groupes ``` > 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 : ```bash 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 ``` ```bash sudo chmod 0440 /etc/sudoers.d/10-alix sudo visudo -c # contrôle global de la syntaxe ``` > ⚠️ `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) : ```bash 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) ```bash # 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`. Adaptez `Port` après l'étape 8 si vous le déplacez. ## Étape 8 — Clés SSH et durcissement 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) ```bash 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. ### 8.2 Déposer la clé publique ```bash ssh-copy-id -i ~/.ssh/id_ed25519.pub alix@203.0.113.10 ``` En manuel si `ssh-copy-id` n'est pas disponible : ```bash 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. ```bash 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 AllowUsers alix # Réduire la surface X11Forwarding no AllowAgentForwarding no AllowTcpForwarding no PermitTunnel no PermitUserEnvironment no # Sessions mortes ClientAliveInterval 300 ClientAliveCountMax 2 ``` > `AllowUsers alix` remplace avantageusement un `DenyUsers debian` : la liste blanche est explicite et couvre aussi les comptes que vous n'avez pas anticipés. Pensez à l'étendre si vous ajoutez un administrateur. > `PasswordAuthentication no` vous verrouille dehors si la clé de 8.2 ne fonctionne pas. Ne l'activez qu'après avoir vérifié la connexion par clé. Deux précautions : le premier fichier qui définit une directive gagne, et `sshd_config` charge souvent ses drop-ins **en première ligne**. Vérifiez donc la configuration réellement appliquée, jamais le fichier seul. ```bash sudo sshd -t # syntaxe ; silence = OK sudo sshd -T | grep -Ei 'permitrootlogin|passwordauth|allowusers|^port' ``` Puis rechargez et testez depuis une nouvelle session : ```bash 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. D'abord, constatez votre situation : ```bash 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 : ```bash 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. ```bash 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 : ```bash 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 : ```bash 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é ``` ## Étape 9 — Neutraliser le compte fournisseur 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é | `AllowUsers alix` (é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 : ```bash sudo passwd -l debian sudo usermod -s /usr/sbin/nologin debian sudo rm -f /home/debian/.ssh/authorized_keys ``` > ⚠️ 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 ```bash 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. > 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 ```bash # 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 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) ```bash 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' # 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. ```bash 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; } } ``` ```bash sudo nft -c -f /etc/nftables.conf # vérifie la syntaxe sans appliquer sudo systemctl enable --now nftables sudo nft list ruleset ``` > ⚠️ 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. ```bash # 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 ``` ### 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. ```bash # Depuis un autre poste : ce qui est réellement joignable nmap -Pn -p- web01.exemple.fr ``` ## Étape 11 — fail2ban 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. ```bash sudo apt install -y fail2ban ``` ### Configuration Ne modifiez jamais `jail.conf` : il est écrasé aux mises à jour. Créez `jail.local`. ```bash sudo nano /etc/fail2ban/jail.local ``` ```ini [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 ``` `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. > Si vous avez choisi ufw à l'étape 10, utilisez plutôt `banaction = ufw`. ```bash sudo systemctl enable --now fail2ban sudo systemctl restart fail2ban ``` ### Exploitation au quotidien ```bash 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 ``` > ⚠️ 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 Un serveur non mis à jour devient vulnérable en quelques semaines. `unattended-upgrades` applique les correctifs de sécurité sans intervention. ```bash sudo apt install -y unattended-upgrades apt-listchanges sudo dpkg-reconfigure -plow unattended-upgrades ``` ### Configuration ```bash 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. ### Vérifier que ça tourne ```bash 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 ```bash 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. ## Étape 13 — Swap, /tmp et paramètres 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). ```bash free -h # vérifier ce qui existe déjà swapon --show ``` Si la ligne Swap est à zéro : ```bash 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. ```bash # Réduire l'agressivité du swap sur un serveur echo 'vm.swappiness=10' | sudo tee /etc/sysctl.d/99-swappiness.conf ``` > 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](https://www.debian.org/releases/trixie/release-notes/issues.en.html)). ```bash df -h /tmp findmnt /tmp ``` Deux conséquences à connaître : 1. Écrire un gros fichier dans `/tmp` consomme de la RAM. Les traitements volumineux doivent utiliser `/var/tmp`, qui reste sur disque. 2. Sur une machine migrée depuis Debian 12, les anciens fichiers de `/tmp` sont toujours sur le disque, masqués par le montage. Ils occupent de l'espace invisible. ```bash # 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 : ```bash sudo systemctl mask tmp.mount sudo reboot ``` ### 13.3 Paramètres réseau et noyau ```bash 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 ``` ```bash sudo sysctl --system sudo sysctl -a --pattern 'tcp_syncookies|rp_filter' # contrôle ``` > N'appliquez pas `net.ipv4.ip_forward = 0` sans réfléchir : Docker, les conteneurs et les VPN en ont besoin. ## Étape 14 — Journaux, supervision et alertes ### 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. ```bash sudo nano /etc/systemd/journald.conf.d/00-limites.conf ``` ``` [Journal] Storage=persistent SystemMaxUse=500M SystemMaxFileSize=50M MaxRetentionSec=1month ``` ```bash sudo mkdir -p /etc/systemd/journald.conf.d sudo systemctl restart systemd-journald journalctl --disk-usage ``` ### 14.2 Commandes de lecture utiles ```bash 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. ```bash 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 ``` ```bash sudo chmod 600 /etc/msmtprc echo "Test depuis web01" | mail -s "Test alerte" alix@exemple.fr ``` > 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 : ```bash echo "root: alix@exemple.fr" | sudo tee -a /etc/aliases sudo newaliases 2>/dev/null || true ``` ### 14.4 Surveiller l'essentiel ```bash # 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 : ```bash 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 ``` ## Étape 15 — Sauvegarde et instantané de référence ### 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 ```bash 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 : ```bash scp -P 2222 alix@web01.exemple.fr:/root/config-initiale-*.tar.gz ./ ``` > 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. ```bash sudo apt install -y restic ``` ```bash # Initialisation vers un stockage distant (une seule fois) export RESTIC_REPOSITORY="sftp:sauvegarde@stockage.exemple.fr:/backups/web01" export RESTIC_PASSWORD_FILE="/root/.restic-pass" sudo restic init # Sauvegarde sudo restic backup /etc /home /var/www --exclude-caches # Vérification et purge sudo restic snapshots sudo 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`. ```bash sudo systemd-run --on-calendar="*-*-* 03:00:00" --unit=sauvegarde \ /usr/bin/restic backup /etc /home /var/www ``` ### 15.4 Tester la restauration À faire une fois, tout de suite, puis tous les trimestres : ```bash 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. ## 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 - [ ] `dig` confirme la correspondance dans les deux sens ### Identité et système - [ ] Première connexion réussie, état initial relevé (`ss -tulpn`) - [ ] Nom d'hôte court défini avec `hostnamectl set-hostname` - [ ] `/etc/hosts` corrigé : FQDN avant nom court sur la ligne `127.0.1.1` - [ ] `hostname -f` renvoie bien le FQDN - [ ] Sources APT converties en deb822, `trixie-security` présent - [ ] `apt full-upgrade` passé, machine redémarrée si le noyau a changé - [ ] `systemctl --failed` ne renvoie rien - [ ] Paquets de base installés, `vnstat` activé - [ ] Locale `fr_FR.UTF-8` générée et définie (avec un tiret) - [ ] Fuseau horaire posé, `timedatectl` indique l'horloge synchronisée ### Accès - [ ] Compte administrateur créé et ajouté au groupe `sudo` (avec `-aG`) - [ ] `sudo -v` et `sudo whoami` fonctionnent depuis le nouveau compte - [ ] Clé Ed25519 générée et déposée, connexion par clé vérifiée - [ ] Drop-in `sshd_config.d` créé : root refusé, mot de passe désactivé, `AllowUsers` posé - [ ] `sudo sshd -T` confirme la configuration réellement appliquée - [ ] Port SSH vérifié côté `ssh.socket` si vous l'avez changé - [ ] Compte fournisseur verrouillé, ses clés SSH retirées - [ ] Aucune clé inconnue dans les `authorized_keys` de 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-upgrades` actif, politique de redémarrage décidée - [ ] Paramètres sysctl appliqués (`sysctl --system`) - [ ] 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 - [ ] TTL DNS remonté à sa valeur normale ### Vérification finale d'un seul bloc ```bash hostname -f; echo '---' localectl status | head -3; echo '---' timedatectl | grep -E 'Time zone|synchronized'; echo '---' sudo sshd -T | grep -E '^(port|permitrootlogin|passwordauthentication|allowusers)'; 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 / ``` ## 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` » | `AllowUsers` en liste blanche + verrouillage du compte | Une liste blanche couvre aussi les comptes non anticipés ; le verrouillage ferme les autres voies que SSH | ### Ce que l'ancienne fiche ne couvrait pas 1. **Les clés SSH avant la désactivation du mot de passe.** C'est la seule séquence qui ne pardonne pas l'inversion. 2. **Le pare-feu.** Sans lui, chaque service déployé est exposé par défaut. 3. **Les mises à jour automatiques.** Un serveur à jour le jour de l'installation ne l'est plus trois semaines après. 4. **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) | ## Sources - [Format deb822 sous Trixie — dev.to](https://dev.to/r3m8/debian-13-trixie-adoptez-le-nouveau-format-deb822-1ml6) - [Migration vers deb822 — OSTechNix](https://ostechnix.com/migrate-to-deb822-format-debian-13-trixie/) - [Changer le port SSH sous Debian 13 — IONOS](https://www.ionos.com/help/server-cloud-infrastructure/getting-started/important-security-information-for-your-server/changing-the-ssh-port/) - [Notes de publication de Debian 13 — problèmes connus](https://www.debian.org/releases/trixie/release-notes/issues.en.html)