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
1926 lines
74 KiB
Markdown
1926 lines
74 KiB
Markdown
---
|
|
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](#etape-2-premiere-connexion) (constat et empreinte),
|
|
[4](#etape-4-sources-apt-et-mise-a-jour) (mise à jour),
|
|
[7](#etape-7-compte-administrateur-et-sudo) (compte admin),
|
|
[8](#etape-8-cles-ssh-et-durcissement) (clés SSH),
|
|
[10](#etape-10-pare-feu) (pare-feu),
|
|
[12](#etape-12-mises-a-jour-de-securite-automatiques) (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 :
|
|
|
|
```bash
|
|
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é.
|
|
|
|
```mermaid
|
|
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 :
|
|
|
|
```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*).
|
|
|
|
!!! 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`.
|
|
|
|
```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 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 :
|
|
|
|
```bash
|
|
# 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.
|
|
|
|
```bash
|
|
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 :
|
|
|
|
```bash
|
|
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 :
|
|
|
|
```bash
|
|
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.
|
|
|
|
```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
|
|
```
|
|
|
|
!!! 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`.
|
|
|
|
```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.
|
|
|
|
!!! 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
|
|
|
|
```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.
|
|
|
|
!!! 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](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
|
|
```
|
|
|
|
!!! 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
|
|
|
|
```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
|
|
|
|
**Recommandé** — de quoi diagnostiquer sans improviser.
|
|
|
|
```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
|
|
|
|
**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 :
|
|
|
|
```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
|
|
```
|
|
|
|
!!! 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.
|
|
|
|
```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
|
|
```
|
|
|
|
!!! 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.
|
|
|
|
```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
|
|
|
|
**Essentiel** — ne plus travailler avec le compte du fournisseur.
|
|
|
|
### 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
|
|
```
|
|
|
|
!!! 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 :
|
|
|
|
```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
|
|
```
|
|
|
|
!!! 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) :
|
|
|
|
```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`.
|
|
|
|
!!! 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)
|
|
|
|
```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.
|
|
|
|
!!! 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
|
|
|
|
```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.
|
|
|
|
Regardez d'abord ce qui s'y trouve déjà :
|
|
|
|
```bash
|
|
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.
|
|
|
|
```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
|
|
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 :
|
|
|
|
```bash
|
|
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 :
|
|
|
|
```bash
|
|
sudo sshd -t # syntaxe ; silence = OK
|
|
sudo sshd -T | grep -Ei 'permitrootlogin|passwordauth|allowgroups|^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.
|
|
|
|
!!! 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 :
|
|
|
|
```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é
|
|
```
|
|
|
|
**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 :
|
|
|
|
```bash
|
|
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 :
|
|
|
|
```bash
|
|
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 :
|
|
|
|
```bash
|
|
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
|
|
|
|
```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.
|
|
|
|
!!! 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
|
|
|
|
```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
|
|
|
|
**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)
|
|
|
|
```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' # $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.
|
|
|
|
```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
|
|
```
|
|
|
|
!!! 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 :**
|
|
|
|
```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
|
|
```
|
|
|
|
**Si vous avez choisi nftables** — `systemctl stop ufw` n'existe pas, il faut vider le
|
|
jeu de règles :
|
|
|
|
```bash
|
|
# 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.
|
|
|
|
```bash
|
|
# 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 :
|
|
|
|
```bash
|
|
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 :
|
|
|
|
```bash
|
|
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.
|
|
|
|
```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
|
|
|
|
# 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`.
|
|
|
|
```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
|
|
```
|
|
|
|
!!! 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.
|
|
|
|
```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.
|
|
|
|
### 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 :
|
|
|
|
```bash
|
|
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 :
|
|
|
|
```bash
|
|
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.
|
|
|
|
```bash
|
|
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
|
|
|
|
```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.
|
|
|
|
### 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 :
|
|
|
|
```bash
|
|
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 :
|
|
|
|
```bash
|
|
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).
|
|
|
|
```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
|
|
```
|
|
|
|
!!! 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](https://www.debian.org/releases/trixie/release-notes/issues.en.html)).
|
|
|
|
!!! 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.
|
|
|
|
```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
|
|
```
|
|
|
|
!!! 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.
|
|
|
|
```bash
|
|
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.
|
|
|
|
```bash
|
|
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
|
|
```
|
|
|
|
```bash
|
|
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
|
|
```
|
|
|
|
!!! 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 :
|
|
|
|
```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
|
|
```
|
|
|
|
### 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.
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```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 ./
|
|
```
|
|
|
|
!!! 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.
|
|
|
|
```bash
|
|
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 :
|
|
|
|
```bash
|
|
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 :
|
|
|
|
```bash
|
|
# 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.
|
|
|
|
```bash
|
|
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 :
|
|
|
|
```bash
|
|
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 :
|
|
|
|
```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.
|
|
|
|
## É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.
|
|
|
|
```bash
|
|
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
|
|
- [ ] `dig` confirme 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-id` vérifié
|
|
- [ ] Partition étendue à tout le volume facturé (`lsblk`, `growpart`)
|
|
- [ ] 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é, `AllowGroups` posé
|
|
- [ ] Groupe `ssh-users` créé et peuplé — **avec un second administrateur**
|
|
- [ ] `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`)
|
|
- [ ] `20auto-upgrades` contrôlé : `Unattended-Upgrade "1"`
|
|
- [ ] `needrestart` passé en mode non interactif
|
|
- [ ] `check-support-status` consulté (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 un `systemd-run`
|
|
- [ ] Sonde externe en place, sur une autre machine que celle-ci
|
|
- [ ] `lynis audit system` passé, suggestions lues et arbitrées
|
|
- [ ] 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|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 :
|
|
|
|
```bash
|
|
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 :
|
|
|
|
```bash
|
|
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 :
|
|
|
|
```bash
|
|
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](../guides/docker.md) — installer le moteur de conteneurs sur cette base ;
|
|
- [Nextcloud](nextcloud.md) et [Matrix](matrix.md) — deux services que l'association
|
|
héberge ;
|
|
- [Déploiement du wiki](deploiement-wiki.md) — 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 :
|
|
|
|
1. **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.
|
|
2. **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.
|
|
3. **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.
|
|
4. **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.
|
|
5. **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
|
|
|
|
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)
|