# nas-host — hôte Incus et serveur NAS _Guide de reconstruction. Il décrit comment rebâtir nas-host tel qu'il fonctionne, du système nu jusqu'aux conteneurs en service._ ⚠️ **Treize lignes de ce document dépassent 84 caractères et s'enrouleront dans le PDF sans signe visible : douze des treize lignes de cron, et la ligne `mp3fs` d'`/etc/fstab`.** Ni cron ni `fstab` n'acceptent de continuation — **les recopier depuis le fichier `.md`, jamais depuis le PDF.** Toutes les autres commandes longues ont été coupées par continuation `\`. **Le fichier `.md` de cette page :** [ouvrir le `.md`](https://blog.infolaf.ca/wp-content/uploads/fichiers/guide-host1-hote-et-conteneurs-public.md) **Ce que cette page couvre :** rebâtir de bout en bout une machine qui cumule deux rôles — serveur de fichiers pour un réseau domestique, et hôte de conteneurs système. Du matériel nu jusqu'aux conteneurs en service, avec les sauvegardes qui vont avec. **Contexte :** Ubuntu Server 24.04 sur un Supermicro X11SAE-M, Incus 6.0 LTS du dépôt Zabbly, cinq conteneurs en macvlan, un disque de données de 12,7 To partagé en SMB pour les postes Windows et en NFS pour les postes Linux. Les adresses, noms d'hôtes et identifiants de disques sont des exemples : les transposer à son propre parc. **Trois choses valent le détour même sans suivre le guide de bout en bout.** Pourquoi un hôte ne peut pas joindre ses propres conteneurs macvlan, et le montage à deux cartes réseau qui lève la difficulté — section *Réseau*. Le réglage Samba d'une seule ligne sans lequel les fichiers de verrouillage des suites bureautiques s'accumulent indéfiniment, jusqu'à rendre des documents illisibles en écriture pendant des années — section *Partages Samba*. Et le fait que le filtrage des modules rsync s'applique côté serveur et non côté client, ce qui décide de ce qu'un mot de passe compromis donnerait réellement — section *Sauvegardes*. **Convention : chaque bloc de commandes indique la machine où il s'exécute.** Un bloc unique se copie tel quel ; des blocs séparés signifient qu'une édition ou une décision intervient entre eux. --- ## Ce que cette machine fait nas-host porte deux rôles qu'on aurait pu séparer sur deux machines, et qui partagent ici le même matériel. **Serveur de fichiers.** Un disque de 12,7 To, partagé en SMB pour les postes Windows et en NFS pour les postes Linux, avec un miroir MP3 transcodé à la volée pour les lecteurs qui ne lisent pas le FLAC. **Hôte de conteneurs.** Cinq conteneurs Incus en macvlan, chacun avec sa propre adresse sur le réseau domestique : un serveur web, un Pi-hole, un mandataire Squid, un serveur de musique et un correcteur linguistique. S'y ajoutent des services propres à l'hôte : diffusion audio par Icecast, serveur de musique Lyrion, indexation MusicIP, partage DLNA, surveillance de l'onduleur, et la synchronisation hors site vers Proton Drive. **Cette cohabitation a une conséquence structurelle** : l'hôte ne peut pas devenir un système immuable de type IncusOS, puisque la moitié de ce qu'il fait vit en dehors des conteneurs. --- ## Matériel et contraintes | Élément | Valeur | |---|---| | Carte mère | Supermicro X11SAE-M, chipset C236 | | Processeur | Intel Xeon E3-1230 v5, 4 cœurs / 8 threads | | Mémoire | 31 Gio | | Système | NVMe Samsung 970 EVO Plus, 465 Go | | Données | WD 12,7 To en SATA | | Disque web | Seagate 1,8 To en SATA, dédié au conteneur web | | Démarrage | **BIOS hérité**, `/boot` sur une clé USB | **La contrainte de démarrage gouverne toute l'installation.** La carte mère possède un connecteur M.2 mais **ne sait pas démarrer dessus** en BIOS hérité, et sa mise à jour de BIOS est impossible. La solution est une clé USB rapide qui porte `/boot` et GRUB ; le système lui-même vit sur le NVMe. Cette clé doit rester branchée en permanence. **C'est le point de fragilité de la machine** : si elle lâche, nas-host ne démarre plus. Elle est pour cette raison imagée et répliquée **hors site** — et non sur un disque de la machine qui, elle, ne démarrerait plus pour aller le lire. **L'image couvre le périphérique entier**, table de partition et secteur d'amorçage compris : copier seulement `/boot` donnerait une clé pleine de fichiers justes qui ne démarrerait pas. La procédure, le rafraîchissement automatique au changement de noyau et la compression sont décrits dans la page [Sauvegarde](https://blog.infolaf.ca/wiki/sauvegarde-strategie-methode/). **Prérequis pour rebâtir :** - une carte NVMe pour le système - une clé USB 3.1 de 16 Go pour `/boot` et GRUB - une clé USB d'installation d'Ubuntu Server 24.04 --- ## Installer le système Au partitionnement, trois décisions : 1. Une partition unique sur le NVMe, montée sur `/` 2. La clé USB configurée comme `/boot` 3. GRUB installé **sur la clé USB**, pas sur le NVMe Ajouter le serveur OpenSSH quand l'installateur le propose : la machine n'a pas d'écran en service normal. Dans le BIOS ensuite, régler l'ordre de démarrage sur la clé USB. ### Neutraliser cloud-init **Sur `[nas-host]`** : ```bash sudo touch /etc/cloud/cloud-init.disabled ``` **Sur `[nas-host]`** : ```bash echo "network: {config: disabled}" | sudo tee \ /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg ``` **Pourquoi.** cloud-init réécrit la configuration réseau quand il croit voir une instance neuve. Sur une machine dont les adresses sont fixes et dont les conteneurs dépendent, une réécriture silencieuse au démarrage est exactement ce qu'on ne veut pas. --- ## Réseau — deux cartes, et c'est délibéré ``` eno1 192.168.0.2/24 parent du macvlan, porte la route par défaut eno2 192.168.0.11/24 adresse d'administration de l'hôte ``` **Un hôte ne peut pas joindre ses propres conteneurs macvlan à travers l'interface parente.** Les paquets ne remontent jamais la pile réseau locale : c'est une propriété du macvlan, pas un défaut de configuration. Ici, les conteneurs sont enfants d'`eno1`, et l'hôte leur parle depuis `eno2`. Le trafic sort par une carte, traverse le commutateur, revient par l'autre. **C'est ce montage qui rend les sauvegardes possibles.** Toutes les sauvegardes nocturnes sont des tirages rsync de l'hôte vers les conteneurs. Sans la seconde carte, elles ne fonctionneraient pas. ⚠️ **Conséquence : `eno2` n'est pas disponible pour un passthrough PCI vers une machine virtuelle.** La céder supprimerait le 192.168.0.11 de l'hôte et couperait sa voie vers les conteneurs. ### Configuration **Sur `[nas-host]`** : ```bash sudo nano /etc/netplan/50-static-public-ip.yaml ``` Contenu : ```yaml network: version: 2 renderer: networkd ethernets: eno1: dhcp4: no dhcp6: no addresses: [192.168.0.2/24] nameservers: addresses: [192.168.0.5, 192.168.0.1] routes: - to: default via: 192.168.0.1 eno2: dhcp4: no dhcp6: no addresses: [192.168.0.11/24] nameservers: addresses: [192.168.0.5, 192.168.0.1] ``` **Sur `[nas-host]`** : ```bash sudo chmod 600 /etc/netplan/50-static-public-ip.yaml sudo netplan apply ip -br addr show eno1 eno2 ``` **Le second résolveur est le routeur, et il n'est pas décoratif.** Le premier est le Pi-hole, qui tourne dans un conteneur de cette machine : l'arrêter priverait l'hôte de résolution de noms, y compris pour se réparer. 🔴 **Le repli ne peut pas être un résolveur public.** Le routeur détourne vers le filtre toute requête DNS adressée hors du réseau local : une adresse extérieure renverrait donc à la machine en panne. Seule une adresse locale replie réellement. ⚠️ **Ce repli ne sert que les machines à adresse fixe.** Les appareils qui reçoivent leurs résolveurs par DHCP n'en ont qu'un seul et n'ont aucune seconde adresse à essayer. Pour eux, la manœuvre de dépannage est de couper la redirection DNS au routeur — *DNS Director → Enable → OFF → Appliquer* —, ce qui leur rend le résolveur du routeur. --- ## Base du système **Sur `[nas-host]`** : ```bash sudo apt install -y acl ntpsec zfsutils-linux rsync ``` | Paquet | Rôle | |---|---| | `acl` | permissions étendues sur les partages | | `ntpsec` | synchronisation horaire | | `zfsutils-linux` | le pool de stockage d'Incus | | `rsync` | le démon qui sert les sauvegardes | **Sur `[nas-host]`** — fuseau horaire : ```bash sudo timedatectl set-timezone America/Toronto timedatectl ``` ⚠️ **Changer le fuseau d'une machine déplace ses tâches cron**, dont les heures s'interprètent dans ce fuseau. Sur nas-host qui porte treize lignes de tâches planifiées, toutes nocturnes, ce réglage se fait à l'installation et ne se retouche plus. `America/Montreal` est un lien de compatibilité vers `America/Toronto`, avec les mêmes règles d'heure avancée. Le nom canonique est préférable : il existe même dans une image allégée. --- ## Stockage ### Structure Le disque de 12,7 To est monté **une seule fois**, sur `/media/nas1-14TB`. Les deux points de partage `/media/nas1` et `/media/nas2` sont des **montages liés** de deux de ses sous-répertoires. ``` /dev/sda1 ────► /media/nas1-14TB ├── nas1 ──(bind)──► /media/nas1 └── nas2 ──(bind)──► /media/nas2 ``` ⚠️ **Espace et sort communs.** Les deux « disques » partagent la même capacité et disparaissent ensemble si le disque tombe. Cette structure vient du remplacement de deux disques plus anciens par un seul grand, en conservant les chemins que les scripts et les partages connaissaient. ⚠️ **Ne jamais écrire un `rsync` de `/media/nas1` vers `/media/nas1-14TB`.** rsync ajoute le nom du répertoire source à la destination : la copie écrirait dans `/media/nas1-14TB/nas1`, **qui est la source elle-même**. Deux scripts de migration écrits du temps des disques séparés sont devenus des pièges le jour de cette réorganisation. ### Points de montage **Sur `[nas-host]`** : ```bash sudo mkdir -p /media/nas1 /media/nas2 sudo chmod 770 /media/nas1 /media/nas2 sudo mkdir -p /media/nas1/Audio/sub_mp3-128 ``` ### Relever l'identifiant du disque **Sur `[nas-host]`** : ```bash sudo blkid /dev/sda1 ``` L'UUID relevé est propre au système de fichiers : il change à chaque reformatage. C'est lui qu'on inscrit dans `fstab`, jamais `/dev/sda1`, qui dépend de l'ordre de détection des disques. ### Le fichier `fstab` **Sur `[nas-host]`** : ```bash sudo nano /etc/fstab ``` Les lignes qui concernent les données : ``` UUID= /media/nas1-14TB ext4 defaults,acl 0 2 /media/nas1-14TB/nas1 /media/nas1 none bind /media/nas1-14TB/nas2 /media/nas2 none bind ``` ⚠️ **L'ordre compte.** Le montage lié ne peut pas précéder le montage du disque qui le porte. `fstab` est traité dans l'ordre du fichier. L'option `acl` sur le montage physique est indispensable : les permissions étendues des partages en dépendent, et un montage sans elle les rendrait inopérantes sans message d'erreur. **Sur `[nas-host]`** : ```bash sudo mount -a df -hT | grep media ``` --- ## Miroir MP3 transcodé à la volée `mp3fs` présente une arborescence FLAC comme une arborescence MP3, transcodée à la lecture. Rien n'est stocké : le miroir ne coûte que du calcul, au moment où un fichier est lu. **Sur `[nas-host]`** : ```bash sudo apt install -y mp3fs ``` Ligne à ajouter dans `/etc/fstab`, **après** celles des montages liés : ``` mp3fs#/media/nas1/Audio/Musique/ /media/nas1/Audio/sub_mp3-128/ -ogainmode=1,fuse allow_other,ro,bitrate=128 0 0 ``` ⚠️ **Cette ligne ne se coupe pas** — une entrée de `fstab` tient sur une seule ligne. Elle s'enroule donc dans le PDF sans signe visible : **la recopier depuis le fichier `.md`, jamais depuis le PDF.** **Ce montage apparaît deux fois dans `mount`**, sous `/media/nas1/Audio/sub_mp3-128` et sous `/media/nas1-14TB/nas1/Audio/sub_mp3-128`. C'est normal et non un défaut : la racine est en propagation partagée, donc un montage effectué sous un chemin lié se voit aussi par l'autre. Il n'y a bien qu'un seul processus `mp3fs`. --- ## Partages NFS — pour les postes Linux **Sur `[nas-host]`** : ```bash sudo apt install -y nfs-kernel-server ``` **Sur `[nas-host]`** : ```bash sudo nano /etc/exports ``` Contenu : ``` /media/nas1 \ 192.168.0.0/24(rw,sync,no_subtree_check,all_squash,anonuid=1000,anongid=150) /media/nas2 \ 192.168.0.0/24(rw,sync,no_subtree_check,all_squash,anonuid=1000,anongid=150) /home/hostadmin \ 192.168.0.0/24(rw,sync,no_subtree_check,all_squash,anonuid=1000,anongid=150) ``` `/etc/exports` accepte la continuation par barre oblique inverse. Les trois entrées s'écrivent aussi bien sur une ligne chacune ; la forme coupée est retenue ici pour qu'elles se recopient sans risque depuis un PDF. **Sur `[nas-host]`** : ```bash sudo exportfs -ra sudo systemctl restart nfs-kernel-server sudo exportfs -v ``` **Le rôle d'`all_squash`.** Toute écriture venant du réseau devient propriété de l'utilisateur `hostadmin` (1000) et du groupe `sambashare` (150), quel que soit l'utilisateur du poste client. C'est ce qui garantit que les fichiers créés depuis un portable Linux portent la même identité que ceux créés par les services de la machine — et qu'ils restent accessibles aux partages Samba. **Les attributs étendus `user.*` ne traversent pas NFS.** Sans conséquence pour l'usage courant, mais tout outil qui les lit ou les écrit doit tourner sur nas-host, pas sur un poste où le partage est monté. **Le côté client est décrit ailleurs.** Monter ces exports sur un poste, puis relier les dossiers du profil au partage : [Accéder aux partages du NAS depuis un poste Linux](https://blog.infolaf.ca/wiki/acceder-aux-partages-du-nas-depuis-un-poste-linux/). --- ## Partages Samba — pour les postes Windows ### Le groupe commun Toutes les machines du réseau doivent employer **le même identifiant de groupe** pour que les permissions se correspondent d'une machine à l'autre. Le choix est ici `150`. **Sur `[nas-host]`** : ```bash sudo apt install -y samba ``` **Sur `[nas-host]`** — vérifier le GID attribué puis le corriger : ```bash getent group sambashare sudo groupmod -g 150 sambashare ``` Si le groupe portait un autre numéro et que des fichiers l'utilisaient déjà, il faut les reprendre : ```bash sudo find / -xdev -group -exec chgrp -h sambashare {} \; ``` ### Le compte générique Windows **Sur `[nas-host]`** : ```bash sudo useradd -s /bin/false -d /dev/null -g sambashare virtuel ``` `-s /bin/false` et `-d /dev/null` : ce compte sert uniquement à Samba, il ne doit jamais pouvoir ouvrir une session. **Sur `[nas-host]`** — les mots de passe Samba, qui sont distincts de ceux du système : ```bash sudo smbpasswd -a hostadmin ``` ```bash sudo smbpasswd -a virtuel ``` ### Les services qui accèdent aux fichiers Chaque service qui lit ou écrit dans les partages doit appartenir au groupe commun. **Sur `[nas-host]`** : ```bash for u in hostadmin virtuel user squeezeboxserver minidlna; do sudo usermod -a -G sambashare "$u" done getent group sambashare ``` ⚠️ **Le `-a` est essentiel.** Sans lui, `usermod -G` **remplace** tous les groupes secondaires de l'utilisateur au lieu d'en ajouter un. ### Configuration **Sur `[nas-host]`** : ```bash sudo nano /etc/samba/smb.conf ``` Section globale : ``` [global] workgroup = WORKGROUP server string = %h server (Samba, Ubuntu) server role = standalone server server min protocol = SMB2_02 delete readonly = yes obey pam restrictions = yes unix password sync = yes passwd program = /usr/bin/passwd %u pam password change = yes map to guest = bad user log file = /var/log/samba/log.%m max log size = 1000 logging = file panic action = /usr/share/samba/panic-action %d usershare allow guests = yes vfs objects = acl_xattr map acl inherit = yes store dos attributes = yes ``` ⚠️ **`server min protocol = SMB2_02` n'est pas facultatif.** Le réglage par défaut des installations anciennes autorisait SMB1 et l'authentification LANMAN — le protocole qu'ont exploité EternalBlue et WannaCry, et un algorithme qui découpe le mot de passe en deux moitiés de sept caractères sans sel. Tout appareil en service aujourd'hui négocie SMB2 ou SMB3. ⚠️ **`delete readonly = yes` répare un défaut qui met des années à se voir.** LibreOffice et Word créent leurs fichiers de verrouillage avec l'attribut DOS « lecture seule ». Sans ce réglage, Samba refuse de les effacer à la fermeture du document — fidèle à la sémantique DOS, où un fichier en lecture seule ne se supprime pas. Le verrou devient alors permanent, et le document s'ouvre ensuite en lecture seule ou en copie. Les verrous s'accumulent indéfiniment. `store dos attributes = yes` range les attributs DOS dans un attribut étendu de chaque fichier plutôt que dans les bits de permission Unix. C'est ce qui préserve la date de création Windows. ### Les partages ``` [Serveur - Dossier home] comment = Répertoire home de nas-host path = /home/hostadmin read only = no write list = hostadmin create mask = 0775 directory mask = 0770 [Documents des parents] comment = Documents des parents path = /media/nas1/Documents/ write list = hostadmin, user, virtuel create mask = 0775 directory mask = 0775 [Documents des enfants] comment = Documents des enfants path = /media/nas1/Documents/LesEnfants write list = @sambashare create mask = 0775 directory mask = 0775 [Scan] comment = Numérisations path = /media/nas1/Documents/Numérisations read list = @sambashare write list = hostadmin, user, virtuel create mask = 0775 directory mask = 0775 [Downloads sur nas1] comment = Téléchargements path = /media/nas1/Download write list = @sambashare create mask = 0775 directory mask = 0775 [nas1] comment = Disque nas1 path = /media/nas1 read list = @sambashare write list = hostadmin create mask = 0775 directory mask = 0775 [nas2] comment = Disque nas2 path = /media/nas2 write list = @sambashare create mask = 0775 directory mask = 0775 [submp3_128] comment = Miroir MP3 path = /media/nas1/Audio/sub_mp3-128 read list = @sambashare write list = hostadmin create mask = 0775 directory mask = 0775 ``` ⚠️ **Ces partages se chevauchent, et le droit obtenu dépend de la porte empruntée.** `[nas1]` expose `/media/nas1` en entier, ce qui contient déjà `Documents/`, `Download` et le miroir MP3. Un même fichier atteint par `[nas1]` n'est modifiable que par `hostadmin`, alors qu'il l'est aussi par `user` et `virtuel` s'il est atteint par `[Documents des parents]`. C'est un héritage à connaître avant de diagnostiquer un refus d'écriture : **vérifier d'abord par quel partage le poste est entré.** Un partage sans `read only` explicite est en lecture seule — c'est le défaut de Samba. C'est la `write list` qui rouvre l'écriture, nominativement. La `read list`, elle, n'apporte rien dans cette configuration : le partage étant déjà en lecture seule pour tous, elle ne peut que retirer ce que la `write list` accorde. **Sur `[nas-host]`** — vérifier avant de recharger, sans exception : ```bash testparm -s ``` **Sur `[nas-host]`** : ```bash sudo systemctl restart smbd nmbd systemctl is-active smbd nmbd ``` **`testparm -s` n'affiche que ce qui diffère du défaut.** Une sortie vide après un changement est donc un bon signe, pas un échec. Pour voir les valeurs réellement en vigueur, il faut `testparm -s -v`. ### Permissions étendues **Sur `[nas-host]`** : ```bash sudo setfacl -Rm g:sambashare:rwX /media/nas1 /media/nas2 sudo setfacl -Rm d:g:sambashare:rwX /media/nas1 /media/nas2 sudo setfacl -Rm d:o:rx /media/nas1 /media/nas2 getfacl /media/nas1 ``` | Option | Effet | |---|---| | `-R` | récursif | | `g:sambashare:rwX` | le groupe commun peut lire et écrire | | `X` majuscule | droit d'exécution sur les **répertoires seulement**, pas sur les fichiers | | `d:` | règle **par défaut**, héritée par tout ce qui sera créé ensuite | ⚠️ **Le `d:` est ce qui fait tenir l'ensemble dans la durée.** Sans les règles par défaut, la commande corrige l'existant et rien de plus : chaque fichier créé le lendemain échapperait au groupe commun. **En cas de refus d'écriture inexpliqué, lire `getfacl` et non `ls -l`.** Une ACL comporte une entrée `mask::` qui plafonne les droits effectifs de toutes les entrées de groupe. Un `mask::r-x` laisse `group:sambashare:rwx` s'afficher intact tout en le ramenant à la lecture — `getfacl` le signale par un `#effective:` en bout de ligne, `ls -l` ne le montre jamais. ### Le groupe qui se perd, et le bit qui l'en empêche Les ACL décident de **qui a le droit**, pas de **quel groupe porte un fichier neuf**. Or un fichier créé prend par défaut le groupe principal de celui qui l'écrit — et le groupe principal d'un compte de service n'est pas `sambashare`. Chaque logiciel qui réécrit un fichier le fait donc sortir du groupe commun, en silence. 🎯 **Le bit `setgid` sur les dossiers renverse la règle** : un fichier créé dans un dossier qui le porte prend le groupe **du dossier**, quel que soit son auteur. Le groupe cesse de se perdre, et la remise en état périodique devient inutile. **Sur `[nas-host]`** : ```bash sudo find /media/nas1 -xdev -type d -exec chmod 2775 {} + ``` Le `2` de tête est le `setgid` ; `775` est le mode habituel d'un dossier partagé. L'épreuve tient en trois commandes, et elle est sans appel : le fichier doit sortir au groupe du dossier, pas au groupe principal du compte. **Sur `[nas-host]`** : ```bash cd /media/nas1 touch essai.tmp; stat -c '%a %U:%G %n' essai.tmp; rm -f essai.tmp ``` ⚠️ **Le `setgid` agit à la création, donc il ne couvre pas tout.** Une **copie** crée un fichier et en hérite. Un **déplacement sur le même système de fichiers** n'est qu'un renommage — même inode, groupe d'origine conservé, `setgid` sans effet. Et `cp -p`, `cp -a` et `rsync -a` réimposent délibérément le propriétaire et le groupe de la source, donc ils l'annulent aussi. Un dossier de préparation d'où les fichiers **migrent** vers le partage doit donc porter le `setgid` lui aussi, sans quoi tout ce qui en sort arrive avec le mauvais groupe. ### L'ordre des trois opérations n'est pas libre 🔴 **`chmod` réécrit le masque des ACL.** Sur un fichier qui en porte, les bits du groupe ne désignent pas le droit du groupe propriétaire mais le **masque** — le plafond de toutes les entrées nommées. Un `chmod` passé **après** un `setfacl` rabote donc en silence les droits qu'on vient d'accorder : l'entrée reste inscrite, visible dans `getfacl`, et sans effet. L'ordre est donc **propriétaire, puis modes, puis ACL**, jamais l'inverse. Et `chmod` ne peut pas tout : il n'atteint **jamais** l'entrée `group::`. Un `group::rwx` hérité d'un ancien `chmod -R 774` survit à un `chmod 664` et fait remonter le masque au `setfacl` suivant. Seul `setfacl` corrige cette entrée. **Sur `[nas-host]`** — remettre une arborescence d'aplomb, dans cet ordre : ```bash cd /media/nas1 sudo find . -xdev -name '.Trash*' -prune -o -exec chown -h hostadmin:sambashare {} + sudo find . -xdev -name '.Trash*' -prune -o -type d -exec chmod 2775 {} + sudo find . -xdev -name '.Trash*' -prune -o -type f -exec chmod 664 {} + sudo find . -xdev -name '.Trash*' -prune -o -type d \ -exec setfacl -m g:sambashare:rwX -m d:g:sambashare:rwX {} + sudo find . -xdev -name '.Trash*' -prune -o -type f \ -exec setfacl -m g::rw,g:sambashare:rw {} + ``` ⚠️ **`-xdev` n'est pas une précaution de style.** Il empêche `find` de franchir une frontière de système de fichiers, ce qui écarte d'un coup le miroir transcodé et tout autre montage placé dans l'arborescence — un montage en lecture seule ferait échouer les commandes, et le parcourir coûterait des heures. Les corbeilles `.Trash-1000` sont écartées séparément : leur mode `700` est voulu. Le `-h` de `chown` porte sur les liens symboliques eux-mêmes plutôt que sur leurs cibles, qui peuvent être ailleurs. ### Sauvegarder les droits avant d'y toucher 🔴 **Sur une arborescence de dizaines de milliers de fichiers, une erreur de mode ne se rattrape pas à la main.** Un relevé complet se prend en une commande et restaure propriétaires, modes et ACL à l'identique. **Sur `[nas-host]`** : ```bash sudo sh -c "find /media/nas1 -xdev -print0 \ | xargs -0 getfacl -p > /root/acl-nas1.txt" ``` La redirection passe par `sh -c` parce qu'un `sudo commande > /root/fichier` échoue : c'est le shell appelant, sans droits sur `/root`, qui traite le `>`. Le retour arrière : ```bash sudo setfacl --restore=/root/acl-nas1.txt ``` ### Retirer des ACL ```bash sudo setfacl -bR /media/nas1/dossier sudo setfacl -x g:sambashare /media/nas1/dossier sudo setfacl -k /media/nas1/dossier ``` `-b` retire toutes les entrées étendues, `-x` une entrée nommée, `-k` les seules règles par défaut. C'est ce qu'il faut pour remettre en état un sous-dossier qu'un traitement récursif a touché par erreur — une corbeille, par exemple. --- ## Sauvegardes ### Le principe Toutes les sauvegardes sont des **tirages** : nas-host va chercher les données là où elles sont, par le protocole du démon rsync, sur le port 873. Rien n'est poussé, et aucun accès au stockage des conteneurs depuis l'hôte n'est nécessaire. ``` nas-host ──rsync://──► conteneur web ──► /media/nas1/Backups/ nas-host ──rsync://──► lui-même ──► /media/nas1/Backups/ nas-host ──rsync://──► poste de travail ──► /media/nas1/Backups/ ``` ⚠️ **Le protocole du démon rsync n'est pas chiffré.** Le mot de passe est protégé par défi-réponse, mais les données circulent en clair. Compromis assumé sur un réseau domestique ; à ne pas reproduire au-delà. ### Le démon **Sur `[nas-host]`** : ```bash sudo nano /etc/rsyncd.conf ``` Le fichier complet, onze modules : ``` log file = /var/log/rsync.log [scripts_nas-host] uid = hostadmin gid = hostadmin path = /home/hostadmin/scripts hosts allow = 192.168.0.11 comment = Scripts de nas-host read only = true auth users = test secrets file = /etc/rsyncd.scrt [musicip_nas-host] uid = hostadmin gid = hostadmin path = /home/hostadmin/logiciels/MusicIP exclude = lost+found/ hosts allow = 192.168.0.11 comment = Configuration de MusicIP read only = true auth users = test secrets file = /etc/rsyncd.scrt [MusicMagic_nas-host] uid = hostadmin gid = hostadmin path = /home/hostadmin/.MusicMagic exclude = lost+found/ hosts allow = 192.168.0.11 comment = Base MusicMagic read only = true auth users = test secrets file = /etc/rsyncd.scrt [icecast2_config_nas-host] uid = root gid = root path = /etc/icecast2/ hosts allow = 192.168.0.11 comment = Configuration d Icecast read only = true auth users = test secrets file = /etc/rsyncd.scrt [ices2_metadata_nas-host] uid = hostadmin gid = hostadmin path = /home/hostadmin/ices2/ hosts allow = 192.168.0.11 comment = Metadonnees ices2 read only = true auth users = test secrets file = /etc/rsyncd.scrt [ices2_config_nas-host] uid = root gid = root path = /etc/ exclude = lost+found/ ** include = ices2*config.xml hosts allow = 192.168.0.11 comment = Configuration des flux ices2 read only = true auth users = test secrets file = /etc/rsyncd.scrt [bliss_nas-host] uid = hostadmin gid = hostadmin path = /home/hostadmin/.bliss include= regex-rules/*** settings exclude=lost+found/ ** hosts allow = 192.168.0.11 comment = Configuration de BlissHQ read only = true auth users = test secrets file = /etc/rsyncd.scrt [rsync_nas-host] uid = root gid = root path = /etc/ exclude = lost+found/ ** include = rsyncd.conf hosts allow = 192.168.0.11 comment = Configuration du demon rsync read only = true auth users = test secrets file = /etc/rsyncd.scrt [samba_nas-host] uid = root gid = root path = /etc/samba/ exclude = lost+found/ ** include = smb.conf hosts allow = 192.168.0.11 comment = Configuration de Samba read only = true auth users = test secrets file = /etc/rsyncd.scrt [nfs_nas-host] uid = root gid = root path = /etc/ exclude = lost+found/ ** include = exports hosts allow = 192.168.0.11 comment = Exports NFS read only = true auth users = test secrets file = /etc/rsyncd.scrt [crontab_nas-host] uid = root gid = root path = /var/spool/cron/crontabs/ exclude = lost+found/ ** include = hostadmin hosts allow = 192.168.0.11 comment = Crontab de hostadmin read only = true auth users = test secrets file = /etc/rsyncd.scrt ``` ⚠️ **Le filtre serveur n'est pas décoratif.** Cinq modules pointent sur `/etc` ou sur un répertoire plus large que ce dont on a besoin, et six tournent avec `uid = root`. Le couple `exclude = lost+found/ **` et `include = ` restreint ce que le démon accepte de servir. Il vit côté serveur, n'est **pas** transmis au client, et ne se contourne pas. ⚠️ **Ne jamais écrire `include = ***` en pensant filtrer.** Les `include` étant évalués avant les `exclude`, `***` retient absolument tout — y compris ce que la ligne `exclude` suivante prétend écarter. Un module ainsi configuré n'a **aucun filtre**, alors qu'il en affiche deux. Pour ne retirer qu'un répertoire, un `exclude` seul suffit ; c'est la forme employée ci-dessus pour `musicip_nas-host` et `MusicMagic_nas-host`. **`bliss_nas-host` emploie pourtant `regex-rules/***`, et ce n'est pas la même chose.** Le motif interdit est `include = ***` **seul**, qui retient tout. Appliqué à un sous-répertoire nommé, `***` veut dire « ce répertoire et tout son contenu » — c'est la forme correcte pour servir une arborescence précise et rien d'autre. ⚠️ Ce module écrit par ailleurs ses deux directives **sans espace autour du `=`** et dans l'ordre inverse des autres : rsyncd lit les deux formes de façon identique, et le fichier est reproduit ici tel qu'il est sur la machine. ⚠️ **Pas de commentaire en fin de ligne dans ce fichier.** rsyncd ne reconnaît `#` comme marque de commentaire qu'en **début** de ligne. Un `hosts allow = 192.168.0.11 #ici votre IP` fait lire `#ici`, `votre` et `IP` comme trois noms d'hôtes de plus. Ils ne résolvent pas, donc ils sont ignorés et le filtrage semble fonctionner — par accident, pas par conception. Le `comment` de chaque module s'affiche dans la liste que rend `rsync --list-only rsync://192.168.0.11/`. Le recopier d'un module à l'autre sans l'adapter produit un inventaire trompeur le jour où on cherche ce qui est sauvegardé. **rsyncd évalue les `include` avant les `exclude`**, quel que soit leur ordre dans le fichier. Le `**` exclut donc tout ce que l'`include` n'a pas retenu. ⚠️ **Ne pas confondre avec les `--include` des scripts.** Ceux-là sont côté client : ils décident de ce qu'on demande, pas de ce que le serveur accepte de donner. Un module sans filtre serveur reste entièrement lisible par quiconque atteint le démon avec le mot de passe. ✅ **La démonstration se fait sur ce module même.** Qu'un script réclame `apache2/` au module `rsync_nas-host`, et rien n'arrive : le répertoire de destination ne contient toujours que `rsyncd.conf`. Le client a demandé, **le démon n'a pas servi** — son `include` ne le permet pas. ⚠️ **Le revers, et il coûte du temps** : élargir le filtre du module ne suffit pas non plus. Le script porte le sien, et **les deux doivent être élargis ensemble** — sinon le démon sert le fichier, le client le jette, et le tirage se termine à `échecs : 0`. Le cas est détaillé dans [Sauvegarde](https://blog.infolaf.ca/wiki/sauvegarde-strategie-methode/). ### Le mot de passe **Sur `[nas-host]`** : ```bash sudo nano /etc/rsyncd.scrt ``` Une ligne, `utilisateur:motdepasse` : ``` test: ``` **Sur `[nas-host]`** : ```bash sudo chmod 600 /etc/rsyncd.scrt sudo systemctl enable --now rsync ``` Le fichier jumeau côté client, sans nom d'utilisateur, ne contient que le mot de passe : ```bash nano /home/hostadmin/scripts/rsync/auto/rsync_pass ``` ```bash chmod 600 /home/hostadmin/scripts/rsync/auto/rsync_pass ``` **rsync refuse un fichier de mots de passe lisible par d'autres.** C'est une protection, pas une chicane. **Aucun redémarrage n'est nécessaire après modification de `rsyncd.conf`** : le démon relit sa configuration à chaque connexion entrante. C'est aussi pourquoi un module cassé n'empêche jamais le démon de tourner — et pourquoi il peut échouer en silence pendant des années. ### Durcir le démon **Sur `[nas-host]`** : ```bash sudo mkdir -p /etc/systemd/system/rsync.service.d ``` **Sur `[nas-host]`** : ```bash printf '%s\n' '[Service]' 'ProtectSystem=full' 'PrivateDevices=yes' \ 'NoNewPrivileges=yes' \ | sudo tee /etc/systemd/system/rsync.service.d/durcissement.conf ``` **Sur `[nas-host]`** : ```bash sudo systemctl daemon-reload && sudo systemctl restart rsync systemctl show rsync -p ProtectSystem -p PrivateDevices -p NoNewPrivileges ``` | Option | Effet | |---|---| | `ProtectSystem=full` | `/usr`, `/boot` et `/etc` en lecture seule pour ce service | | `PrivateDevices=yes` | aucun accès aux périphériques physiques | | `NoNewPrivileges=yes` | interdit d'acquérir des privilèges en cours d'exécution | ⚠️ **Ne jamais ajouter `ProtectHome=`.** C'est la première option que suggèrent tous les guides de durcissement, et elle masquerait `/home` au service — or cinq modules servent `/home/hostadmin`. Ils échoueraient tous d'un coup. Un *drop-in* plutôt qu'une modification de l'unité fournie par le paquet : celle-ci serait remplacée à la prochaine mise à jour. ### La forme des scripts Chaque script de sauvegarde suit le même moule : ```bash #!/bin/bash . /home/hostadmin/scripts/rsync/auto/verifier_montages.sh ERREURS=0 r() { rsync "$@" || { echo "ÉCHEC : ${*: -2:1} -> ${*: -1}"; ERREURS=$((ERREURS+1)); } } echo "=== $(basename "$0") — début $(date -Is)" r -avz --stats --delete \ --password-file /home/hostadmin/scripts/rsync/auto/rsync_pass \ test@192.168.0.11::scripts_nas-host/ \ /media/nas1/Backups/Scripts/nas-host echo "=== $(basename "$0") — fin $(date -Is) — échecs : $ERREURS" exit $ERREURS ``` 🔴 **La deuxième ligne est un veto, pas une formalité.** `/media/nas1` et `/media/nas2` sont des points de montage : **démontés, les chemins existent toujours** comme simples répertoires du disque système, et rsync y écrirait sans se plaindre — remplissant `/` et fabriquant une sauvegarde qui n'est pas sur le disque de sauvegarde. Le fichier chargé vérifie les deux montages et **arrête le script appelant** s'il en manque un. Son texte complet est dans [Sauvegarde](https://blog.infolaf.ca/wiki/sauvegarde-strategie-methode/). **La fonction `r()` corrige un défaut structurel.** Le code de sortie d'un script est celui de sa **dernière** commande. Un script qui enchaîne treize `rsync` dont le douzième échoue se termine malgré tout en succès. Le compteur rend l'échec visible et exploitable. ⚠️ **Un compteur d'échecs ne voit pas l'absence de tentative.** Si la liste sur laquelle une boucle travaille est vide — parce que le service interrogé ne répond pas — la boucle ne s'exécute pas, le compteur reste à zéro, et le script annonce un succès complet. Tout script qui construit sa liste dynamiquement doit vérifier qu'elle n'est pas vide avant de commencer. **Les marqueurs de début et de fin ne sont pas cosmétiques.** Les tâches écrivent toutes dans le même journal quotidien ; sans eux, rien ne permet de savoir où commence et finit chacune, ni d'attribuer une suppression au bon script. ### La crontab **Sur `[nas-host]`** : ```bash sudo mkdir -p /var/log/backup sudo chown hostadmin:hostadmin /var/log/backup ``` ⚠️ **Le `chown` est indispensable.** Les tâches tournent sous `hostadmin`, qui ne peut pas écrire dans `/var/log`. Un répertoire resté à `root` produirait un « Permission denied » **dans la redirection elle-même** — donc une erreur sans destination, invisible. **Sur `[nas-host]`** : ```bash crontab -e ``` ``` MAILTO="" 15 00 * * * bash /home/hostadmin/scripts/rsync/auto/backup_mysql-ns2.sh >> /var/log/backup/$(date +\%F).log 2>&1 20 00 * * * bash /home/hostadmin/scripts/rsync/auto/backup_web-ns2.sh >> /var/log/backup/$(date +\%F).log 2>&1 35 00 * * * /usr/bin/incus exec pi-hole -- bash /root/scripts/backup_pihole.sh >> /var/log/backup/$(date +\%F).log 2>&1 40 00 * * * bash /home/hostadmin/scripts/rsync/auto/backup_scripts.sh >> /var/log/backup/$(date +\%F).log 2>&1 45 00 * * * bash /home/hostadmin/scripts/rsync/auto/backup_config.sh >> /var/log/backup/$(date +\%F).log 2>&1 50 00 * * * bash /home/hostadmin/scripts/rsync/auto/backup_scripts_poste-bureau.sh >> /var/log/backup/$(date +\%F).log 2>&1 55 00 * * * bash /home/hostadmin/scripts/rsync/auto/backup_config_poste-bureau.sh >> /var/log/backup/$(date +\%F).log 2>&1 00 01 * * * bash /home/hostadmin/scripts/rsync/auto/backup_logiciels.sh >> /var/log/backup/$(date +\%F).log 2>&1 30 01 * * * bash /home/hostadmin/scripts/rsync/auto/upload_photos_photos-webadmin-ca.sh >> /var/log/backup/$(date +\%F).log 2>&1 00 02 1 * * bash /home/hostadmin/scripts/rsync/auto/backup_virtualbox_poste-bureau.sh >> /var/log/backup/$(date +\%F).log 2>&1 00 03 1 * * bash /home/hostadmin/scripts/rsync/auto/backup_incus_export.sh >> /var/log/backup/$(date +\%F).log 2>&1 30 03 1 * * find /var/log/backup/ -name '*.log' -mtime +180 -delete 30 04 * * * cd /home/hostadmin/scripts/tableau-de-bord && bash releve-tableau-de-bord.sh >/dev/null && python3 build_tableau-de-bord.py >/dev/null ``` 🎯 **Les scripts que ces lignes appellent ne sont pas décrits ici.** Ils vivent dans `/home/hostadmin/scripts/rsync/auto/` et sont **écrits en entier** dans [Sauvegarde](https://blog.infolaf.ca/wiki/sauvegarde-strategie-methode/), section *Les scripts de tirage*, avec le garde-fou commun que tous chargent en deuxième ligne. **Cette page porte la machine et ses tâches planifiées ; l'autre porte ce qu'elles exécutent.** 🔴 **Cette crontab n'est pas l'inventaire des tâches de la machine, et c'est un piège.** Deux tâches de plus, absentes de cette liste, tournent chaque nuit **depuis `/etc/cron.d/`, sous root**, parce qu'elles exigent des privilèges que le compte `hostadmin` n'a pas : ``` /etc/cron.d/backup-lyrion 01:05 configuration du serveur de musique /etc/cron.d/image-cle-amorcage 04:00 image de la clé d'amorçage ``` Elles n'apparaissent nulle part ici, et ne se révèlent que **dans le journal**. **Pour inventorier ce qui s'exécute réellement, il faut regarder les deux endroits :** ```bash sudo ls -l /var/spool/cron/crontabs/ ls -l /etc/cron.d/ ``` ⚠️ **Et root apporte une contrainte que rien ne rappelle : aucune tâche sous root ne doit être la première de la nuit.** Le journal quotidien est créé par la première tâche qui écrit dedans. Créé par root, les tâches suivantes sous `hostadmin` ne pourraient plus y ajouter leurs lignes, et perdraient leur trace sans erreur visible — la redirection échouant là où personne ne la lit. La première tâche est à 00:15 sous `hostadmin` : l'ordre tient, et c'est à vérifier avant d'avancer l'heure d'une tâche root. **L'ordre des lignes n'a aucune importance pour cron**, qui lit le fichier entier et déclenche selon l'heure. Elles sont rangées ici par heure d'exécution, ce qui rend lisible l'enchaînement de la nuit ; le fichier réel peut porter le même contenu dans un autre ordre. **Deux de ces tâches ne sont pas des sauvegardes.** À 1 h 30, `upload_photos_photos-webadmin-ca.sh` **pousse** les photos du NAS vers la galerie du serveur web — c'est le seul transfert de cette machine qui va dans ce sens, et le seul qui écrive chez un autre. Son fonctionnement, son garde-fou et le module inscriptible qu'il emploie sont décrits dans [Piwigo](https://blog.infolaf.ca/wiki/piwigo/). Il partage le journal des autres tâches, d'où sa présence ici. **À 4 h 30, la dernière ligne ne sauvegarde rien : elle lit.** Elle relève l'état de la machine — âge des sauvegardes sur les deux disques, tâches de la nuit retrouvées ou manquantes, services, montages, espace disque, températures — et en fabrique une page HTML déposée sur le disque de sauvegarde. C'est le tableau de bord, décrit en entier dans `guide-tableau-de-bord-nas-host.md` : les deux scripts y figurent verbatim. ⚠️ **L'heure n'est pas arbitraire.** 4 h 30 vient **après** la dernière tâche de la nuit et après l'image de la clé d'amorçage de 4 h : le relevé lit une nuit terminée. Avancé avant 4 h, il signalerait chaque matin comme manquantes des tâches qui n'ont simplement pas encore tourné. ℹ️ **Le `&&` entre les deux commandes est délibéré.** Si le relevé échoue, le formatage ne s'exécute pas et la page conserve le contenu de la veille — visiblement daté d'hier — plutôt que d'afficher une journée à demi mesurée qui aurait l'air d'un relevé valide. Le `cd` préalable est nécessaire parce que les deux scripts écrivent leurs fichiers à côté d'eux-mêmes. Cette ligne n'écrit rien dans `/var/log/backup/`, d'où le `>/dev/null` : sa sortie est un rapport de terminal, pas un journal, et la page HTML est elle-même le résultat conservé. ⚠️ **Une ligne de cron ne se coupe pas.** Cron n'accepte aucune continuation par barre oblique inverse : chaque tâche tient sur une seule ligne, aussi longue soit-elle. Dans un PDF elle s'enroule sans signe visible — **la recopier depuis le fichier `.md`, jamais depuis le PDF.** ⚠️ **Le `\%` est obligatoire.** Dans une crontab, `%` marque une fin de ligne et bascule le reste vers l'entrée standard de la commande. Un `date +%F` non échappé casse la ligne de façon parfaitement déroutante. ⚠️ **`bash` et non `sh`.** Le préfixe `sh` court-circuite le `#!/bin/bash` du script et l'exécute sous `dash`. Un script écrit pour bash mais lancé par `sh` échoue uniquement en cron, jamais à la main — le pire des modes de défaillance. ⚠️ **L'ordre de `>> fichier 2>&1` compte.** Le `2>&1` renvoie le canal d'erreur là où pointe **déjà** le canal de sortie. Inversé, les erreurs partiraient vers l'ancienne destination — or ce sont elles qui importent. ### Tester une tâche dans les conditions de cron Un script qui fonctionne dans un shell interactif peut échouer en cron, dont l'environnement est presque vide. **Sur `[nas-host]`** : ```bash env -i HOME=/home/hostadmin PATH=/usr/bin:/bin \ /home/hostadmin/scripts/rsync/auto/backup_config.sh ``` Le point sensible est `HOME` : sans lui, tout outil qui cherche un fichier de configuration dans le répertoire personnel échoue. --- ## Incus ### Choisir la branche | Question | Choix | Raison | |---|---|---| | Dépôt | Zabbly `lts-6.0` | Ubuntu fige sa base en 6.0.0 ; Zabbly suit les correctives amont sur la même ligne LTS | | Version | **6.0**, pas 7.0 | Incus 7.0 exige un noyau ≥ 6.12 ; Ubuntu 24.04 est en 6.8 | | Nom du pool | `local` | | | Emplacement | fichier creux de 100 Go sur le NVMe | Le pool porte le système de fichiers des conteneurs, donc MySQL — pas sur un disque mécanique | **Ce que la 7.0 apporte** — conteneurs OCI, écouteur S3, sauvegardes incrémentales de VM, fonctions de grappe — ne concerne pas un hôte unique portant cinq conteneurs système en macvlan. ### Vérifier la clé du dépôt **Sur `[nas-host]`** — cette commande n'importe rien, elle affiche : ```bash curl -fsSL https://pkgs.zabbly.com/key.asc | gpg --show-keys --fingerprint ``` Empreinte attendue : ``` 4EFC 5906 96CB 15B8 7C73 A3AD 82CC 8797 C838 DCFD ``` **Pourquoi.** On s'apprête à autoriser un dépôt tiers dont les paquets s'installeront en root. La vérification n'a de valeur que parce que l'empreinte de référence vient d'une **source indépendante du téléchargement** — elle est publiée dans le dépôt GitHub de Zabbly. Comparer l'empreinte à elle-même ne vérifierait rien. ### Ajouter le dépôt **Sur `[nas-host]`** : ```bash sudo mkdir -p /etc/apt/keyrings/ sudo curl -fsSL https://pkgs.zabbly.com/key.asc -o /etc/apt/keyrings/zabbly.asc ``` **Sur `[nas-host]`** : ```bash sudo nano /etc/apt/sources.list.d/zabbly-incus-lts-6.0.sources ``` ``` Enabled: yes Types: deb URIs: https://pkgs.zabbly.com/incus/lts-6.0 Suites: noble Components: main Architectures: amd64 Signed-By: /etc/apt/keyrings/zabbly.asc ``` ### Donner la priorité au dépôt ⚠️ **C'est l'étape qu'on ne devine pas.** Ubuntu distribue son propre paquet `incus`, et un abonnement Ubuntu Pro donne au dépôt ESM une priorité de **510**, supérieure au 500 par défaut d'un dépôt tiers. Sans épinglage, `apt install incus` installerait la version d'Ubuntu — **silencieusement, sans erreur**. **Sur `[nas-host]`** : ```bash sudo nano /etc/apt/preferences.d/zabbly-incus ``` ``` Package: * Pin: origin pkgs.zabbly.com Pin-Priority: 600 ``` - `Pin: origin` désigne le **nom d'hôte** du dépôt, pas son champ « Origin » — confusion classique de la documentation d'apt. - `600` dépasse le 510 d'ESM. **Ne pas dépasser 1000** : au-delà, apt s'autorise à *rétrograder* des paquets déjà installés. **Sur `[nas-host]`** — vérifier d'où viendra le paquet, avant de l'installer : ```bash sudo apt update apt policy incus ``` Le candidat doit venir de `pkgs.zabbly.com` avec la priorité 600. **Prendre l'habitude de `apt policy` avant toute installation depuis un dépôt tiers.** ### Installer **Sur `[nas-host]`** — simuler d'abord : ```bash sudo apt install --dry-run incus incus-ui-canonical ``` Trois choses à regarder : les paquets Zabbly qui seront installés, la ligne **« 0 à enlever »** — s'arrêter si apt propose de retirer quoi que ce soit — et l'absence de `qemu-system-x86`, le support des machines virtuelles n'étant pas installé par défaut. **Sur `[nas-host]`** : ```bash sudo apt install incus incus-ui-canonical ``` **Sur `[nas-host]`** : ```bash systemctl status incus.socket --no-pager ``` **`incus.service` apparaît `inactive (dead)`, et c'est normal.** Incus utilise l'activation par socket : systemd écoute sur `/var/lib/incus/unix.socket` et ne démarre le démon qu'à la première connexion d'un client. C'est `incus.socket` qui doit être `active (listening)`. ### Autoriser son utilisateur **Sur `[nas-host]`** : ```bash sudo usermod -aG incus-admin hostadmin ``` Le paquet crée deux groupes : `incus` pour un accès restreint, `incus-admin` pour le contrôle complet. **Sur `[nas-host]`** — se déconnecter et se reconnecter, le changement de groupe n'étant lu qu'à l'ouverture de session : ```bash exit ``` **Sur `[nas-host]`** : ```bash id incus version ``` Les versions client et serveur doivent correspondre. ### Initialiser **Sur `[nas-host]`** : ```bash incus admin init ``` | Question | Réponse | |---|---| | Clustering | `no` | | Nouveau pool de stockage | `yes` | | Nom du pool | **`local`** | | Moteur | `zfs` | | Nouvel agrégat ZFS | `yes` | | Périphérique bloc existant | `no` | | Taille en Gio | **`100`** | | Créer un pont réseau local | **`no`** | | Utiliser un pont existant | `no` | | Serveur disponible sur le réseau | `no` | | Mettre à jour les images en cache | `yes` | | Imprimer un preseed YAML | `yes` | ⚠️ **Refuser le pont réseau.** Sinon Incus fabrique un `incusbr0` qui ne servira jamais, les conteneurs étant tous en macvlan. Le preseed imprimé est à conserver : `incus admin init --preseed < fichier.yaml` reproduit cette configuration sans aucune question. **Sur `[nas-host]`** : ```bash incus storage info local incus profile show default ``` Le profil `default` ne doit contenir que le périphérique `root` pointant vers `pool: local`, sans carte réseau. ⚠️ **`incus storage info`, et non `incus storage list`.** La liste ne montre **aucune colonne d'espace** dans cette version : on peut la lire dix fois sans jamais voir combien il reste. C'est `info` qui donne l'espace total et l'espace employé du pool. **Et pour savoir où un conteneur est réellement monté, ne pas demander à ZFS.** `zfs list -o mountpoint` rend **`legacy`** pour ces datasets — Incus les monte lui-même, sans point de montage enregistré. ⚠️ Une variable qui contient le mot `legacy` ne le dit pas : les commandes suivantes portent alors sur un chemin relatif inexistant, et un `find | wc -l` rend **zéro**, qui ressemble trait pour trait au résultat attendu. Le chemin réel se lit dans `/proc/mounts` : ```bash sudo grep -n 'nom-du-conteneur' /proc/mounts ``` ### Le profil réseau **Sur `[nas-host]`** : ```bash incus profile create macvlan incus profile device add macvlan eth0 nic nictype=macvlan parent=eno1 incus profile show macvlan ``` | Élément | Rôle | |---|---| | `eth0` | le nom que le conteneur verra — celui que son netplan configure | | `nictype=macvlan` | le conteneur obtient sa propre MAC sur le réseau physique | | `parent=eno1` | **l'interface hôte à laquelle il s'accroche** | ⚠️ **`parent=eno1` est le point critique.** Avec `eno2`, les conteneurs resteraient joignables depuis le réseau, mais **l'hôte ne pourrait plus les atteindre** — et les sauvegardes nocturnes tomberaient sans erreur visible. ### L'interface web **Sur `[nas-host]`** : ```bash incus config set core.https_address 192.168.0.11:8443 ``` ⚠️ **Lier à l'adresse d'administration, jamais à `:8443` tout court**, sur une machine dont le port 443 est redirigé depuis Internet. L'authentification se fait par **certificat client**, pas par mot de passe : l'API donne un contrôle équivalent à root. Le client en ligne de commande le fait sans qu'on s'en aperçoive ; le navigateur exige une manipulation manuelle, et **son ordre n'est pas évident** : 1. Onglet **« Browser certificate »** → **Generate**. C'est cette page qui *crée* le certificat. 2. Télécharger le `.pfx`, l'importer dans Firefox : `about:preferences#privacy` → Certificats → Afficher les certificats → **Vos certificats** → Importer. Mot de passe vide si aucun n'a été défini. 3. **Redémarrer Firefox** complètement. 4. Rouvrir l'URL et répondre aux **deux** dialogues : le choix du certificat, puis l'avertissement `SELF_SIGNED_CERT` qui concerne le certificat *du serveur*. 5. Seulement maintenant, l'onglet « Identity trust token » devient utilisable. Générer un jeton et le déclarer : ```bash incus config trust add incus-ui ``` Un jeton est **à usage unique** : la première soumission consomme l'opération en attente, qu'elle aboutisse ou non. En cas d'échec, regénérer plutôt que réessayer. --- ## Créer un conteneur **Sur `[nas-host]`** : ```bash incus launch images:ubuntu/24.04 mon-conteneur --profile default --profile macvlan ``` **Sur `[nas-host]`** : ```bash incus config set mon-conteneur boot.autostart true ``` ### Les limites de mémoire et de processeur Un conteneur sans limite déclarée peut prendre **toute la mémoire de la machine**, et rien ne le signale : les autres ralentissent, puis le noyau tue un processus, chez eux ou chez lui. Le plafond se pose d'après une mesure, jamais d'après l'intuition. **Sur `[nas-host]`** — relever ce que chaque conteneur consomme : ```bash for c in $(incus list --format csv -c n); do printf "%-14s limite %-10s courante %s\n" "$c" \ "$(incus config get $c limits.memory)" \ "$(incus info $c | awk '/Mémoire \(courante\)/ {print $NF}')" done free -h ``` ⚠️ **Une consommation courante est une photographie, pas un maximum.** Le plafond se pose à trois ou cinq fois la valeur relevée : il sert à empêcher qu'un emballement affame les autres, pas à serrer au plus près. Trop juste, il fait tuer le service par le noyau sans un mot dans le journal de ce service — c'est l'hôte qui tue, pas l'application qui échoue. Deux précisions évitent de sous-estimer le besoin : un plafond n'est **pas** une réservation, donc la somme des plafonds peut dépasser sans risque la mémoire de la machine ; et **ce qu'un service lit par projection en mémoire y est compté**, ce qui porte le besoin d'un service à gros index bien au-delà de la taille de son propre processus. **Sur `[nas-host]`** — poser les plafonds : ```bash incus config set mon-conteneur limits.memory 1GiB incus config set mon-conteneur limits.cpu 2 ``` ℹ️ **La limite s'applique à chaud, et se vérifie de l'intérieur** : `incus exec mon-conteneur -- free -h` doit annoncer le plafond comme mémoire totale. C'est ce qui distingue un plafond réellement appliqué d'une valeur seulement inscrite dans la configuration. ### Retirer ce que l'image apporte sans qu'on le demande **`open-vm-tools` est installé sur toutes les images Ubuntu Server**, par recommandation du métapaquet `ubuntu-server`. C'est l'agent invité des machines virtuelles VMware : **il n'a aucun objet dans un conteneur**, et c'est lui qui déclenche le `udevadm trigger` d'où vient tout le bruit des installations. **Sur `[nas-host]`** : ```bash incus exec mon-conteneur -- apt-get -y purge open-vm-tools incus exec mon-conteneur -- apt-get -s autoremove --purge | grep -E "^(Purg|Remv)" ``` **Lire la liste avant d'appliquer** — elle diffère d'un conteneur à l'autre : ```bash incus exec mon-conteneur -- apt-get -y autoremove --purge ``` ⚠️ **`--purge` n'est pas optionnel sur un `autoremove`** : sans lui, apt garde les fichiers de configuration et fabrique autant de paquets en état `rc`. ⚠️ **Une montée de version ne retire rien.** `do-release-upgrade` met à niveau ce qui est présent ; `open-vm-tools` ressortirait simplement dans sa version suivante. **Le retrait est un geste explicite, et il vaut mieux le faire tôt** : ce qui est retiré n'a pas à être migré. ### Trois pièges d'`incus exec` ⚠️ **`incus exec` ne lance pas d'interpréteur de commandes.** Un `*`, un tube ou une redirection écrits après `incus exec --` sont traités par le shell de **l'hôte**, pas par celui du conteneur. `incus exec web -- du -sh /var/www/*` cherche un fichier littéralement nommé `*`, puisque `/var/www` n'existe pas sur l'hôte. **Pour donner un motif au conteneur : `bash -c "…"`.** **Les `Failed to write 'change' to '/sys/…/uevent': Permission denied` ne sont pas des erreurs.** Ils viennent d'un `udevadm trigger` lancé par un script post-installation : dans un conteneur non privilégié, `/sys` est en lecture seule. **Les chemins le disent mieux que n'importe quelle explication** — ils nomment le contrôleur SATA et les disques **de l'hôte**. C'est un message d'erreur qui prouve que le confinement fonctionne. ⚠️ **`incus file push` ne dépose pas le fichier en `root:root`.** Sans `--uid 0 --gid 0`, il arrive avec le propriétaire par défaut du conteneur — `ubuntu:ubuntu` — et le service qui devait le lire l'ignore. **Les deux options font partie de la commande, pas de ses raffinements :** ```bash incus file push /tmp/fichier.conf --uid 0 --gid 0 \ mon-conteneur/etc/quelque-part/fichier.conf incus file pull mon-conteneur/etc/quelque-part/fichier.conf - ``` **La relecture par `incus file pull` n'est pas décorative.** Si le chemin de dépôt était fautif, le dépôt **réussirait quand même** — en créant un fichier ailleurs. ### L'adresse fixe, dans le conteneur **Les adresses vivent dans le netplan de chaque conteneur, pas dans Incus.** C'est ce qui les rend indépendantes de l'hôte : elles voyagent avec le conteneur, et la MAC n'a aucune importance. **Sur `[nas-host]`** : ```bash incus exec mon-conteneur -- nano /etc/netplan/50-static-public-ip.yaml ``` ```yaml network: version: 2 renderer: networkd ethernets: eth0: dhcp4: no dhcp6: no addresses: [192.168.0.X/24] nameservers: addresses: [192.168.0.5, 192.168.0.1] routes: - to: default via: 192.168.0.1 ``` **Sur `[nas-host]`** : ```bash incus exec mon-conteneur -- chmod 600 /etc/netplan/50-static-public-ip.yaml incus exec mon-conteneur -- netplan apply ``` ⚠️ **Neutraliser cloud-init dans le conteneur**, faute de quoi un fichier `50-cloud-init.yaml` peut réapparaître et reprendre la main sur l'adresse : ```bash echo "network: {config: disabled}" | incus exec mon-conteneur -- \ tee /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg ``` **Sur `[nas-host]`** — aligner le fuseau horaire, les images arrivant en `Etc/UTC` : ```bash incus exec mon-conteneur -- timedatectl set-timezone America/Toronto ``` Sans cela, un même fichier porte deux heures différentes selon qu'on le regarde depuis l'hôte ou depuis le conteneur — quatre heures d'écart qui font perdre du temps quand on corrèle deux journaux. ⚠️ **Ne jamais tester un service macvlan depuis l'hôte.** Un `curl` lancé depuis nas-host vers un conteneur peut **geler sans erreur** : les paquets partent par l'interface parente, où le macvlan interdit précisément le dialogue hôte↔conteneur, et disparaissent sans message de refus. **Tester depuis une autre machine du réseau.** --- ## Copier un conteneur C'est le geste à faire avant toute opération risquée : la copie sert de répétition, là où une erreur ne coûte rien. **Sur `[nas-host]`** : ```bash incus copy mon-conteneur essai incus config set essai boot.autostart false incus config device remove essai incus config get essai boot.autostart incus config device list essai incus config device list mon-conteneur ``` ⚠️ **L'ordre n'est pas négociable.** `incus copy` produit une copie **arrêtée** — rien ne démarre tout seul —, mais elle porte **l'adresse de l'original dans son netplan** et **la déclaration de ses périphériques**. Les deux gestes qui suivent la copie viennent donc avant tout démarrage. 🔴 **Deux instances qui montent le même disque ne produisent pas une erreur, mais une corruption.** C'est la raison du `device remove`. ⚠️ **Et `boot.autostart false` se relit après coup.** Prescrit une fois sans être exécuté, il a déjà laissé deux conteneurs démarrer sur la même adresse au redémarrage de l'hôte. **Les trois relectures ci-dessus ne sont pas du zèle** — la troisième vérifie qu'on a retiré le disque de la *copie* et non de la machine en service. **Changer l'adresse d'un conteneur arrêté** — on ne peut pas y ouvrir d'éditeur : le fichier se prépare sur l'hôte, puis se dépose. **Sur `[nas-host]`** : ```bash nano /tmp/netplan-essai.yaml ``` Contenu, identique à celui de l'original **sauf l'adresse** — à coller dans l'éditeur ouvert ci-dessus : ```yaml network: version: 2 renderer: networkd ethernets: eth0: dhcp4: no dhcp6: no addresses: [192.168.0.4/24] nameservers: addresses: [192.168.0.5, 192.168.0.1] routes: - to: default via: 192.168.0.1 ``` **Sur `[nas-host]`** : ```bash incus file push /tmp/netplan-essai.yaml --uid 0 --gid 0 \ essai/etc/netplan/50-static-public-ip.yaml --mode 0600 incus file pull essai/etc/netplan/50-static-public-ip.yaml - ``` `--mode 0600` préserve les permissions ; sans lui, netplan se plaint d'un fichier trop ouvert. ### Deux choses qu'une copie ne reproduit pas ⚠️ **Le nom d'hôte.** Incus pose le nom de l'**instance** comme nom d'hôte **à chaque démarrage**. Une copie nommée autrement ne résout plus son propre nom, Apache s'en plaint, et **ISPConfig s'en sert**. Il faut le reposer après chaque redémarrage de la copie : ```bash incus exec essai -- hostnamectl set-hostname mon-conteneur ``` ⚠️ **L'état volatil.** Drapeaux, verrous, sockets, processus lancés à la main : tout ce qui vit dans `/run` — un système de fichiers **en mémoire** — ne traverse pas la copie, qui démarre avec un `/run` vierge. 🎯 **Une répétition sur copie ne peut donc pas révéler un obstacle de cette nature** : elle part d'un état plus propre que l'original. **Le renommage de l'instance n'est pas nécessaire** pour s'y retrouver : tant qu'on passe par `incus exec -- …` depuis l'hôte, il n'y a pas d'invite à confondre. --- ## Un conteneur avec un disque de l'hôte Le conteneur web monte une partition entière comme `/var/www`. **Sur `[nas-host]`** — identifier le disque par son identifiant matériel, jamais par `/dev/sdX` : ```bash ls -l /dev/disk/by-id/ ``` **Sur `[nas-host]`** : ```bash incus config device add conteneur-web web disk \ source=/dev/disk/by-id/scsi-SATA_-part2 \ path=/var/www ``` ### Le faire fonctionner sans privilèges Un conteneur non privilégié décale les identifiants : ce qui est l'utilisateur 0 à l'intérieur est l'utilisateur 1000000 sur l'hôte. Les fichiers d'un disque écrit avant cette bascule portent des numéros qui ne tombent plus dans la plage attendue. **Sur `[nas-host]`** : ```bash sudo apt install -y incus-extra ``` **Sur `[nas-host]`** — le conteneur arrêté, décaler les propriétaires du disque : ```bash sudo fuidshift /point/de/montage/du/disque b:0:1000000:1000000 ``` ⚠️ **La taille de plage `1000000` — et non `1000000000` — est ce qui rend l'opération rejouable.** Avec la grande plage, les fichiers déjà décalés à 1000000 tombent encore dans la plage source et seraient décalés une seconde fois, vers 2000000. Avec la petite, ils en sortent et l'outil les marque « ne pas changer ». **C'est la forme à retenir, y compris pour une première passe.** ⚠️ **Un attribut immuable interrompt le décalage.** ISPConfig pose `chattr +i` sur la racine de certains sites, et **un fichier immuable refuse `chown` même à root**. L'outil s'arrête net en laissant l'arborescence à moitié traitée — d'où l'importance de la forme rejouable ci-dessus. Retirer l'attribut avec `chattr -i` avant de reprendre. **Le mode essai de `fuidshift` imprime une ligne par entrée parcourue, pas par entrée modifiée.** Compter les lignes ne mesure rien ; il faut compter celles qui ne se terminent pas par `to -1 -1`. **Pourquoi cette manœuvre n'est pas nécessaire pour le système de fichiers racine du conteneur.** Incus n'écrit plus les propriétaires décalés sur le disque : il utilise les **montages idmappés**, une fonction du noyau qui applique la translation au moment du montage. Le fichier reste tel quel sur le disque ; seul le conteneur le voit décalé. Mais cette fonction ne s'applique qu'aux sources de type **répertoire**, pas aux périphériques bloc — d'où le décalage manuel ici. ⚠️ **Piège de lecture qui en découle** : un `ls -n` de l'hôte montrant des numéros bas dans un système de fichiers de conteneur **ne signifie pas** que ce conteneur est privilégié. Seule la carte fait foi : ```bash incus config get conteneur-web volatile.idmap.current ``` ### Accéder aux fichiers de l'hôte avec la bonne identité Quand un conteneur doit lire des fichiers appartenant à l'utilisateur de l'hôte, la projection d'identifiants se règle sur l'instance : ```yaml raw.idmap: | uid 1000 1000 gid 150 150 ``` Cette ligne découpe la correspondance en trois segments : 0 à 999 vont vers 1000000, **l'identifiant 1000 traverse tel quel**, et 1001 et au-delà reprennent plus loin. Le conteneur porte alors l'identité du compte de l'utilisateur pour ces fichiers-là seulement. ⚠️ **Monter en lecture seule tout ce qui n'a pas besoin d'être écrit.** Un conteneur exposé sur Internet qui porte l'identité de l'utilisateur de l'hôte et monte un répertoire en écriture offre, en cas de faille applicative, un chemin d'écriture vers les données de l'hôte — **sans aucune évasion de conteneur**. Ce n'est pas un défaut de confinement mais le fonctionnement prévu. ```bash incus config device set conteneur-web readonly true ``` **Un lien symbolique ne suit pas hors de l'espace de montage.** Si le répertoire monté ne contient que des liens vers des fichiers situés ailleurs, il faut monter aussi leur destination, **au même chemin que sur l'hôte**, sinon les liens pointent dans le vide. ### Deux limites du conteneur non privilégié, à connaître avant d'en avoir besoin ⚠️ **Il ne peut pas créer de fichier de périphérique**, même en root. Les capacités sont évaluées contre l'espace de noms propriétaire du système de fichiers — même cause que le `chattr +i` impossible. Conséquence concrète : **restaurer une arborescence par `tar` échoue sur les nœuds de `/dev`** que contient un environnement confiné jailkit : ``` tar: ./web6/dev/urandom: Cannot mknod: Operation not permitted ``` `tar` **signale et poursuit** : le reste arrive bien, mais il sort en échec. À savoir avant de conclure qu'une restauration a échoué. ✅ **Incus refuse de démarrer un conteneur dont la source d'un disque a disparu.** Éprouvé : source supprimée, démarrage refusé. **Le scénario redouté — `/var/www` non monté et un tirage nocturne `--delete` propageant le vide jusqu'à la sauvegarde — n'existe pas.** ⚠️ **Il reste un cas, et il n'est pas couvert de ce côté : une source présente mais vide.** Incus démarre alors sans rien dire. Seul un contrôle « source non vide » dans le script de tirage l'attrape — il est décrit dans [Sauvegarde](https://blog.infolaf.ca/wiki/sauvegarde-strategie-methode/). **L'expérience elle-même a un piège** : `incus config device add` valide la source **au moment de la déclaration** et refuse tout de suite. Pour éprouver le *démarrage*, il faut déclarer le disque vers une source existante, la retirer ensuite, puis tenter de démarrer. --- ## Sauvegarde des conteneurs Les tirages nocturnes sauvegardent les **données** des services. Ils ne sauvegardent pas les conteneurs eux-mêmes. Un script mensuel s'en charge le 1er du mois : **`backup_incus_export.sh`, écrit en entier dans [Sauvegarde](https://blog.infolaf.ca/wiki/sauvegarde-strategie-methode/), section *Les scripts de tirage*.** Il vit avec les dix autres, et c'est là qu'on le corrige — **un seul endroit, pour qu'il n'existe pas deux versions qui divergent.** **Ce qu'il faut en comprendre, et qui ne se lit pas dans son code :** - **Découverte dynamique** — un conteneur ajouté plus tard est sauvegardé sans qu'on y pense. - **Un répertoire par mois** — le tri alphabétique équivaut au tri chronologique, ce qui rend la purge triviale. - **Purge par comptage** et non par âge — garantit exactement deux mois même si une exécution a été manquée. - ⚠️ **Aucune purge si un export a échoué.** Supprimer l'ancienne sauvegarde quand la nouvelle vient d'échouer est la façon classique de se retrouver sans rien. - **Sans `--optimized-storage`** — plus lent et plus gros, mais restaurable vers n'importe quel stockage, y compris le jour où le pool ZFS serait lui-même le problème. - **Vérification de liste vide** — sans elle, un Incus qui ne répond pas produirait un succès avec zéro conteneur sauvegardé. ⚠️ **Les conteneurs sont exportés en marche.** Le système de fichiers peut être saisi en pleine écriture et une base de données n'y sera pas nécessairement cohérente. C'est acceptable **parce que la copie cohérente existe ailleurs** : le tirage nocturne des données. **L'export reconstruit le conteneur, le tirage restaure les données** — ni l'un ni l'autre ne suffit seul. ⚠️ **Un export ne contient pas les disques montés depuis l'hôte.** Il capture le volume du conteneur, pas ce qui y est monté. Un serveur web restauré depuis son export démarre, mais ses sites sont vides tant que leur propre sauvegarde n'a pas été replacée. --- ## Ce que cette machine porte, et ce qu'elle ne protège pas **nas-host est le point de convergence de toutes les sauvegardes du parc** : les copies quotidiennes et les exports mensuels atterrissent sur son disque de données, et c'est de là que part la synchronisation hors site. ``` /media/nas1/Backups/ copies quotidiennes /media/nas1/Backups/incus-exports/ exports mensuels, deux mois conservés ``` **Et c'est exactement pourquoi ce disque ne suffit pas.** Tout converge au même endroit, sur la même machine, sur **un seul système de fichiers** — `/media/nas1` et `/media/nas2` sont deux montages liés d'un même disque. Une panne matérielle les emporte ensemble. **Les quatre niveaux de protection, leurs angles morts et le raisonnement qui les rend complémentaires sont décrits dans [Sauvegarde](https://blog.infolaf.ca/wiki/sauvegarde-strategie-methode/)** — un seul endroit où les tenir à jour. --- ## Services de l'hôte Ces services ne vivent pas dans des conteneurs. Chacun a son propre article ; l'ordre ci-dessous est celui de leur installation. | Service | Rôle | |---|---| | Lyrion Music Server | serveur de musique du foyer | | MusicIP | analyse acoustique et listes de lecture | | BlissHQ | métadonnées et pochettes | | minidlna | partage DLNA vers les téléviseurs | | Icecast et ices2 | diffusion audio et repli d'alarme | | Onduleur CyberPower | arrêt propre en cas de coupure | | Syncthing | propagation de fichiers entre appareils | | Synchronisation Proton Drive | copie hors site | **BlissHQ est un programme Java lancé depuis son répertoire**, sans paquet ni unité systemd. Il n'apparaît donc ni dans `dpkg -l` ni dans `systemctl` — ce qui ne veut pas dire qu'il est absent. **Et une chose présente sur la machine n'est pas ce qu'elle paraît : `/etc/apache2`.** Aucun serveur web ne tourne ici et aucun paquet apache n'est installé, pas même en état `rc`. Le répertoire ne contient qu'un `conf-available/javascript-common.conf`, déposé par le paquet **`javascript-common`** — lui-même tiré par `libjs-jquery` et `libjs-underscore`. Un paquet qui ne dépend pas d'Apache y dépose un fragment **au cas où** un serveur web existerait. **Rien à retirer, rien à surveiller** — et trois commandes pour le savoir, alors qu'une phrase suffit à ne pas refaire l'enquête. --- ## Quand une mise à jour refuse de commencer Un jour, `apt upgrade` s'arrête avant d'avoir traité le moindre paquet : ``` dpkg: erreur fatale irréversible, abandon : l'utilisateur système « untel » inconnu dans le fichier des permissions ``` 🔴 **Le logiciel nommé dans le message n'a rien à voir avec ce qu'on met à jour**, et il peut avoir été retiré de la machine des années plus tôt. `dpkg` tient une liste de **dérogations de permissions** — des propriétaires et des droits qu'il impose à certains fichiers, différents de ceux que le paquet déclare. Quand un service est purgé, son compte système disparaît, mais la dérogation qui le nomme peut rester. `dpkg` refuse alors de travailler tant qu'il ne peut pas résoudre ce nom — et il refuse **avant** de traiter quoi que ce soit, bloquant donc l'ensemble des mises à jour, correctifs de sécurité compris. **Voir de quoi il s'agit** : ```bash sudo dpkg-statoverride --list | grep -i ``` Chaque ligne donne le propriétaire, le groupe, les droits, puis le **chemin** concerné. C'est ce chemin qui sert à retirer l'entrée, une par une : ```bash sudo dpkg-statoverride --remove /chemin/du/fichier ``` Retirer une dérogation ne touche à aucun fichier : elle ne fait qu'effacer une consigne. Les répertoires du service disparu restent donc en place, et sont à traiter séparément. ⚠️ **La règle qui en découle vaut au retrait, pas à la panne** : après avoir purgé un service qui tournait sous son propre compte système, vérifier qu'aucune dérogation ne le nomme encore. Rien ne le signale sur le moment — le défaut dort jusqu'à la mise à jour suivante, où il se présente sous un message qui désigne le mauvais coupable. --- ## Vérifications après reconstruction **Sur `[nas-host]`** — le système : ```bash uname -r ip -br addr show eno1 eno2 df -hT | grep media systemctl --failed ``` **Sur `[nas-host]`** — les partages : ```bash testparm -s -v | grep -iE 'min protocol|delete readonly' sudo exportfs -v systemctl is-active smbd nmbd nfs-kernel-server rsync ``` **Sur `[nas-host]`** — les conteneurs : ```bash incus list sudo zpool list ``` **Sur `[nas-host]`** — les sauvegardes, en conditions réelles : ```bash bash /home/hostadmin/scripts/rsync/auto/backup_config.sh | tail -3 ``` La dernière ligne doit se terminer par **« échecs : 0 »**. ⚠️ **Ne jamais juger une sauvegarde à la date de ses fichiers.** `rsync -a` préserve la date de la source : le fichier le plus récent d'une sauvegarde porte la date qu'il avait à l'origine, pas celle de la copie. La question se pose au journal de cron, pas aux fichiers. ⚠️ **Redémarrer la machine avant de considérer l'installation terminée.** C'est ce test qui révèle ce qui a été fait à la main sans être inscrit : une unité activée mais non `enable`, un montage absent de `fstab`, un service qui ne démarre que parce qu'on l'a lancé. Rien de tout cela ne se voit tant qu'on ne redémarre pas. --- ## Ce que ce guide ne couvre pas - l'installation de chaque service de l'hôte — un article par service - le conteneur web et sa pile Apache multi-PHP — article dédié - [le Pi-hole](https://blog.infolaf.ca/wiki/pi-hole-filtrage-resolution-noms-reseau-local/), le mandataire Squid, le correcteur linguistique — articles dédiés - la montée d'un conteneur vers la prochaine version LTS — article dédié : rebâtir une machine et en monter une existante sont deux travaux distincts - la sécurisation de l'accès distant — article dédié