<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>blog.folf.fr</title>
 <link href="https://blog.folf.fr/atom.xml" rel="self"/>
 <link href="https://blog.folf.fr/"/>
 <updated>2026-10-06T14:59:13+02:00</updated>
 <id>https://blog.folf.fr</id>
 <author>
   <name>Tristan BRINGUIER</name>
   <email>blog@folf.fr</email>
 </author>

 
 
 
 
 <entry>
   <title>Windows 11 Tips &amp; Tricks</title>
   <link href="https://blog.folf.fr/windows/"/>
   <updated>2026-08-24T13:51:12+02:00</updated>
   <published>2026-08-24T13:45:23+02:00</published>
   <id>https://blog.folf.fr/windows/</id>
   <content type="html">Sous Windows 11, Hyper-V et la **sécurité basée sur la virtualisation** (VBS) accaparent `VT-x`/`AMD-V` dès le démarrage. Résultat : VirtualBox et VMware Workstation tombent en mode dégradé — la fameuse **« tortue verte »** dans la barre d&apos;état de VirtualBox — et la virtualisation imbriquée (`nested virt`) devient tout bonnement impossible. Tant que l&apos;hyperviseur Windows tient `VT-x`, l&apos;hyperviseur tiers ne peut pas l&apos;exposer à son tour à une VM invitée.

Cette page rassemble ce qu&apos;il faut désactiver pour rendre `VT-x` à VirtualBox / VMware, et plus largement pour dégager les couches de sécurité de Windows 11 dont on ne veut pas sur une machine de lab.

&gt; [!CAUTION]
&gt; On retire ici des protections système **réelles**. Conséquences directes :
&gt;
&gt; - **WSL2, Docker Desktop (backend WSL2 ou Hyper-V), Windows Sandbox** ne fonctionneront plus.
&gt; - **Credential Guard, HVCI (intégrité mémoire), Windows Hello / PIN** sont désactivés — la machine est nettement plus exposée au vol de credentials et aux rootkits noyau.
&gt; - Combiné à la suppression de Defender (plus bas), il ne reste **aucune défense de base**.
&gt; - À réserver à un poste de test / lab, jamais à une machine avec des données sensibles.
&gt;
&gt; Sauf mention contraire, les commandes qui suivent sont à lancer dans un **PowerShell administrateur**, VM éteinte, suivies d&apos;un **redémarrage** (parfois deux ou trois).

## Comprendre qui vole VT-x

Contrairement à une idée répandue, **Windows Defender n&apos;y est pour rien** : un antivirus ne prend jamais `VT-x`. Ce qui accapare la virtualisation matérielle, c&apos;est l&apos;**hyperviseur Windows** (Hyper-V) et la pile **VBS / VTL1** qui tourne au-dessus (Credential Guard, HVCI, System Guard…).

Seul bémol : la *protection contre les falsifications* de Defender est une cause classique de clés `DeviceGuard` qui **repassent à 1 après chaque reboot**. Si Defender est déjà parti, tant mieux pour la persistance des réglages ci-dessous.

## Diagnostic

Avant de tout casser, savoir quelle brique est active.

- ```powershell
  Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard |
    Select-Object VirtualizationBasedSecurityStatus, SecurityServicesConfigured, SecurityServicesRunning
  ```

  - État de VBS et des services associés. `VirtualizationBasedSecurityStatus` : `0` = VBS éteinte, `1` = activée mais pas en cours, `2` = activée **et en cours**. Dans `SecurityServicesRunning` : `1` = Credential Guard, `2` = HVCI / intégrité mémoire, `3` = System Guard, `5` = protection de pile matérielle noyau, `7` = Hypervisor-Enforced Paging Translation. C&apos;est ce tableau qui dit quelle brique cibler.

- ```powershell
  bcdedit /enum {current} | findstr /i &quot;hypervisorlaunchtype vsmlaunchtype loadoptions&quot;
  systeminfo | findstr /i &quot;hypervis&quot;
  ```

  - Indique si l&apos;hyperviseur est lancé au boot et pourquoi.

- ```powershell
  CiTool --list-policies
  ```

  - Sur 24H2 / 25H2, révèle les politiques **WDAC / Code Integrity** (`.cip` / `.p7b`) qui maintiennent parfois l&apos;hyperviseur en vie même après avoir tout désactivé. Une « Microsoft Windows Virtualization Based Security Policy » ici = VBS verrouillée par politique.

## Désactiver VBS, Device Guard, Credential Guard et HVCI

Tout passe par le registre. La clé `WindowsHello` est celle que la plupart des tutos oublient : c&apos;est elle qui rend la désactivation **persistante** sur 24H2+, au prix du PIN / Windows Hello.

- ```powershell
  $scen = &quot;HKLM:\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios&quot;
  foreach ($s in &apos;HypervisorEnforcedCodeIntegrity&apos;,&apos;CredentialGuard&apos;,&apos;SystemGuard&apos;,&apos;KernelShadowStacks&apos;,&apos;WindowsHello&apos;) {
      New-Item -Path &quot;$scen\$s&quot; -Force | Out-Null
      Set-ItemProperty -Path &quot;$scen\$s&quot; -Name Enabled -Value 0 -Type DWord
  }
  ```

  - Coupe HVCI (intégrité mémoire), Credential Guard, System Guard, les *kernel shadow stacks* et Windows Hello. Retirer `&apos;WindowsHello&apos;` de la liste si l&apos;on veut garder le PIN — mais alors HVCI a tendance à revenir après un reboot.

- ```powershell
  $dg = &quot;HKLM:\SYSTEM\CurrentControlSet\Control\DeviceGuard&quot;
  Set-ItemProperty -Path $dg -Name EnableVirtualizationBasedSecurity -Value 0 -Type DWord
  Set-ItemProperty -Path $dg -Name RequirePlatformSecurityFeatures  -Value 0 -Type DWord
  Set-ItemProperty -Path &quot;HKLM:\SYSTEM\CurrentControlSet\Control\Lsa&quot; -Name LsaCfgFlags -Value 0 -Type DWord
  ```

  - Désactive VBS elle-même et l&apos;isolation LSA. `LsaCfgFlags = 0` neutralise Credential Guard côté LSA.

- ```powershell
  $gpo = &quot;HKLM:\SOFTWARE\Policies\Microsoft\Windows\DeviceGuard&quot;
  New-Item -Path $gpo -Force | Out-Null
  Set-ItemProperty -Path $gpo -Name EnableVirtualizationBasedSecurity -Value 0 -Type DWord
  Set-ItemProperty -Path $gpo -Name RequirePlatformSecurityFeatures  -Value 0 -Type DWord
  Set-ItemProperty -Path $gpo -Name HypervisorEnforcedCodeIntegrity  -Value 0 -Type DWord
  Set-ItemProperty -Path $gpo -Name LsaCfgFlags                      -Value 0 -Type DWord
  ```

  - Miroir côté stratégie de groupe (GPO). Sans lui, une politique peut re-forcer VBS au démarrage suivant. Équivalent graphique : `gpedit.msc` → *Configuration ordinateur → Modèles d&apos;administration → Système → Device Guard → « Activer la sécurité basée sur la virtualisation » → Désactivé*.

## Retirer Hyper-V et les fonctionnalités liées

- ```powershell
  Get-WindowsOptionalFeature -Online |
    Where-Object { $_.State -eq &apos;Enabled&apos; -and $_.FeatureName -match &apos;Hyper|Virtual|Container|Linux|Hypervisor&apos; }
  ```

  - Liste ce qui est réellement activé avant de couper — utile pour n&apos;oublier aucune distribution WSL ni conteneur.

- ```powershell
  $features = &apos;Microsoft-Hyper-V-All&apos;,&apos;HypervisorPlatform&apos;,&apos;VirtualMachinePlatform&apos;,
              &apos;Containers-DisposableClientVM&apos;,&apos;Microsoft-Windows-Subsystem-Linux&apos;
  foreach ($f in $features) {
      Disable-WindowsOptionalFeature -Online -FeatureName $f -NoRestart -ErrorAction SilentlyContinue | Out-Null
  }
  ```

  - Désactive Hyper-V, la *Plateforme d&apos;hyperviseur Windows*, la *Plateforme d&apos;ordinateur virtuel*, Windows Sandbox et WSL. Variante DISM avec purge du payload : `dism /online /disable-feature /featurename:VirtualMachinePlatform /Remove`.

## Couper l&apos;hyperviseur au démarrage

- ```powershell
  bcdedit /set hypervisorlaunchtype off
  bcdedit /set vsmlaunchtype off
  ```

  - Empêche le lancement de l&apos;hyperviseur et du *Secure Kernel* au boot. C&apos;est nécessaire mais **pas toujours suffisant du premier coup** : il faut parfois deux ou trois redémarrages avant que la tortue verte disparaisse.

## Supprimer Windows Defender

Là où Windows réactive Defender sans arrêt, l&apos;outil open-source **`windows-defender-remover`** de `ionuttbara` le retire proprement (installeur hors-ligne, prise en charge des versions récentes) :

**→ [github.com/ionuttbara/windows-defender-remover](https://github.com/ionuttbara/windows-defender-remover)**

- **Étape 1 — désactiver la protection contre les falsifications AVANT tout.** `Paramètres → Sécurité Windows → Protection contre les virus et menaces → Gérer les paramètres → Protection contre les falsifications = Désactivé`. Sans ça, la suppression échoue.
- **Étape 2** — télécharger le binaire depuis l&apos;onglet **Releases** du dépôt, puis le lancer **en administrateur**.
- **Étape 3** — choisir **Remove Windows Defender**, laisser l&apos;installeur hors-ligne faire son travail.
- **Étape 4** — redémarrer.

&gt; [!WARNING]
&gt; Outil non signé par Microsoft : SmartScreen et Defender lui-même vont tenter de le bloquer (d&apos;où l&apos;étape 1). Ne le télécharger **que** depuis le dépôt officiel `ionuttbara/windows-defender-remover`, jamais depuis un mirror. La suppression est réversible via l&apos;outil, mais laisse la machine sans antivirus tant que rien d&apos;autre n&apos;est installé.

## Vérifier après redémarrage

- ```powershell
  Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard |
    Select-Object VirtualizationBasedSecurityStatus, SecurityServicesRunning
  ```

  - Objectif : `VirtualizationBasedSecurityStatus = 0` et `SecurityServicesRunning` vide (`{}`). Vérifiable aussi dans `msinfo32` → *« Sécurité basée sur la virtualisation : non activée »*.

- ```powershell
  bcdedit /enum {current} | findstr /i &quot;hypervisorlaunchtype&quot;
  ```

  - Doit afficher `hypervisorlaunchtype  Off`.

- Côté VirtualBox, le `VBox.log` de la VM doit contenir `HM: HMR3Init: VT-x w/ nested paging and unrestricted guest execution hw support` — et surtout **pas** `HMR3Init: Attempting fall back to NEM (Hyper-V is active)`. Plus de tortue verte dans la barre d&apos;état = c&apos;est bon.

## Cas particulier : VBS verrouillée en UEFI

Si, après reboot, `SecurityServicesRunning` contient encore `1` (Credential Guard), c&apos;est un verrou firmware. Il faut alors la procédure `SecConfig.efi`.

&gt; [!CAUTION]
&gt; Cette manip touche à la séquence de boot EFI. À ne tenter qu&apos;en dernier recours, en sachant restaurer un environnement de récupération.

- ```bat
  mountvol X: /s
  copy %WINDIR%\System32\SecConfig.efi X:\EFI\Microsoft\Boot\SecConfig.efi /Y
  bcdedit /create {0cb3b571-2f2e-4343-a879-d86a476d7215} /d &quot;DebugTool&quot; /application osloader
  bcdedit /set {0cb3b571-2f2e-4343-a879-d86a476d7215} path &quot;\EFI\Microsoft\Boot\SecConfig.efi&quot;
  bcdedit /set {bootmgr} bootsequence {0cb3b571-2f2e-4343-a879-d86a476d7215}
  bcdedit /set {0cb3b571-2f2e-4343-a879-d86a476d7215} loadoptions DISABLE-LSA-ISO,DISABLE-VBS
  bcdedit /set {0cb3b571-2f2e-4343-a879-d86a476d7215} device partition=X:
  mountvol X: /d
  ```

  - À lancer dans une **invite de commandes (cmd) administrateur** — la syntaxe `copy` / `%WINDIR%` ne s&apos;interprète pas sous PowerShell. Crée une entrée de boot `DebugTool` qui désactive l&apos;isolation LSA et VBS au niveau firmware. Au redémarrage, appuyer sur **F3** pour confirmer. Alternative officielle : le [Device Guard and Credential Guard hardware readiness tool](https://www.microsoft.com/en-us/download/details.aspx?id=53337) de Microsoft (`DG_Readiness_Tool_v3.6.ps1 -Disable`), puis F3 aux deux invites.

&gt; [!NOTE]
&gt; Sur **Windows 11 25H2**, certaines builds refusent toujours de lâcher VTL1 : `DG_Readiness_Tool` ne fait plus rien et le *Secure Kernel* reste actif à cause des politiques WDAC (visibles via `CiTool --list-policies`). Si c&apos;est le cas, la seule solution fiable pour du nested virt régulier reste un **hôte Linux** (KVM/libvirt), qui n&apos;a aucune de ces contraintes.

## Activer la virtualisation imbriquée

Une fois VBS et Hyper-V hors du chemin, exposer `VT-x` à la VM :

- ```powershell
  VBoxManage modifyvm &quot;MaVM&quot; --nested-hw-virt on --nested-paging on --cpus 4
  VBoxManage showvminfo &quot;MaVM&quot; | Select-String -Pattern &quot;Nested&quot;
  ```

  - Active la virtualisation imbriquée pour VirtualBox. Le support côté hôte est arrivé avec **VirtualBox 6.0 pour les CPU AMD et 6.1 pour Intel** ; `--nested-paging` (actif par défaut) est un prérequis de `--nested-hw-virt`. VM éteinte, pas en *saved state*. La case « Enable Nested VT-x/AMD-V » de l&apos;interface reste grisée sur hôte Intel — la ligne de commande l&apos;active quand même.

&gt; [!TIP]
&gt; VMware Workstation profite du même nettoyage : sans Hyper-V pour lui prendre `VT-x`, il repasse en mode VMM natif au lieu de la couche WHP dégradée. Aucune commande spécifique — le simple fait d&apos;avoir coupé l&apos;hyperviseur suffit.
</content>
 </entry>
 
 
 
 <entry>
   <title>YubiKey — OpenPGP, agent SSH &amp; signature Git</title>
   <link href="https://blog.folf.fr/yubikeys/"/>
   <updated>2026-09-04T16:10:18+02:00</updated>
   <published>2026-04-22T16:16:25+02:00</published>
   <id>https://blog.folf.fr/yubikeys/</id>
   <content type="html">J&apos;utilise deux YubiKeys : une au quotidien, une en secours dans un tiroir fermé. Elles me servent pour du **FIDO2/WebAuthn** (connexion aux sites), du **OpenPGP** (signature Git, chiffrement) et comme **clé SSH** (le tout via `gpg-agent`).

Voici la marche à suivre complète, de la sortie du blister jusqu&apos;aux commits signés.

&gt; [!IMPORTANT]
&gt; Les clés privées OpenPGP générées **sur** la YubiKey ne peuvent jamais en ressortir. C&apos;est tout l&apos;intérêt, mais cela veut dire qu&apos;une clé perdue ou cassée est une identité perdue. D&apos;où le fait qu&apos;il faut avoir deux clés avant de générer quoi que ce soit !

## Réception et prise en main

Yubico expédie ses clés dans un blister scellé. Avant toute chose, vérifier qu&apos;il n&apos;a pas été ouvert, puis contrôler l&apos;authenticité de la clé sur [yubico.com/genuine](https://www.yubico.com/genuine/) (le site interroge l&apos;attestation matérielle de la clé via WebAuthn).

Toute la configuration se fait avec **YubiKey Manager CLI** (`ykman`).

- ```shell
  # Fedora / RHEL
  dnf install yubikey-manager
  # Debian / Ubuntu
  apt install yubikey-manager
  ```

  - Installe `ykman`.

- ```shell
  ykman list
  ```

  - Liste les clés branchées avec leur **numéro de série**. À noter quelque part : c&apos;est ce numéro qui permet de savoir laquelle des deux clés est insérée. Il est également inscrit physiquement sur la YubiKey.

- ```shell
  ykman info
  ```

  - Affiche le modèle, la **version de firmware** et les applets activés sur chaque interface (USB / NFC).
  - Il faut au minimum le **firmware 5.2.3** pour utiliser Ed25519 / Curve25519 en OpenPGP. En dessous, on est limité au RSA.

- ```shell
  ykman config usb --list
  ykman config usb --disable OTP --force
  ```

  - Désactive l&apos;applet OTP. Sans ça, un appui involontaire sur la clé tape une longue chaîne `cccccc…` dans le champ actif — dans un terminal ou un formulaire de mot de passe, c&apos;est pénible.
  - Applets disponibles : `OTP`, `U2F`, `FIDO2`, `OATH`, `PIV`, `OPENPGP`, `HSMAUTH`. Ne garder que ce qu&apos;on utilise.
  - La clé redémarre après la commande : la débrancher / rebrancher.

## Réinitialiser l&apos;applet OpenPGP

Sur une clé neuve comme sur une clé récupérée, on repart d&apos;un applet OpenPGP propre.

- ```shell
  ykman openpgp info
  ```

  - État actuel : version de l&apos;applet, compteurs d&apos;essais restants, politiques de touch, présence de clés.

- ```shell
  ykman openpgp reset
  ```

  - Remet l&apos;applet OpenPGP à zéro. **Détruit irrémédiablement les clés qui s&apos;y trouvent** et remet les PIN par défaut. N&apos;affecte pas les autres applets (FIDO2, PIV, OATH).

Après un reset, les valeurs d&apos;usine sont :

| Secret | Valeur par défaut | Longueur mini | Rôle |
|---|---|---|---|
| **User PIN** | `123456` | 6 caractères | Usage quotidien : signer, déchiffrer, s&apos;authentifier |
| **Admin PIN** | `12345678` | 8 caractères | Administration : générer des clés, changer les politiques, débloquer le User PIN |
| **Reset Code** | *(non défini)* | 8 caractères | Optionnel : débloque le User PIN sans connaître l&apos;Admin PIN |

## PIN, KDF et compteurs d&apos;essais

L&apos;ordre des opérations compte : `kdf-setup` et `set-retries` réinitialisent les PIN sur certains firmwares. On les fait **avant** de définir ses propres secrets.

- ```shell
  gpg --card-edit
  
  gpg/card&gt; admin
  gpg/card&gt; kdf-setup
  gpg/card&gt; quit
  ```

  - Active le **Key Derived Format** : le PIN est haché côté machine avant d&apos;être envoyé à la clé, il ne transite donc jamais en clair sur le bus USB.
  - À faire **immédiatement après le reset**, sur un applet vierge.

- ```shell
  ykman openpgp access set-retries 5 5 5
  ```

  - Passe les compteurs d&apos;essais à 5 pour le User PIN, le Reset Code et l&apos;Admin PIN (3 par défaut). Demande l&apos;Admin PIN.

- ```shell
  ykman openpgp access change-pin
  ykman openpgp access change-admin-pin
  ykman openpgp access change-reset-code
  ```

  - Définit les trois secrets. Ou, de façon équivalente, en interactif :

    ```shell
    gpg --card-edit

    gpg/card&gt; admin
    gpg/card&gt; passwd
    # 1 = User PIN, 2 = débloquer le User PIN, 3 = Admin PIN, 4 = Reset Code
    ```

  - La YubiKey accepte jusqu&apos;à **127 caractères** : autant utiliser une vraie phrase de passe plutôt qu&apos;un code numérique, d&apos;autant que `gpg-agent` la met en cache.

&gt; [!CAUTION]
&gt; Si le **User PIN** est bloqué, on le débloque avec l&apos;Admin PIN (`passwd` → `2`) ou le Reset Code.
&gt; Si l&apos;**Admin PIN** est bloqué, il n&apos;y a **aucun recours** : seul `ykman openpgp reset` remet la clé en service, et il détruit les clés OpenPGP au passage.

## Politique de présence (*touch*)

Par défaut, dès que le PIN est en cache, la clé signe et déchiffre **sans aucune action physique** : un malware sur la machine peut s&apos;en servir silencieusement. La politique de touch corrige ça en exigeant un contact sur le disque doré.

| Politique | Comportement |
|---|---|
| `off` | Aucun contact requis *(défaut)* |
| `on` | Contact requis à **chaque** opération |
| `cached` | Contact requis, puis **mis en cache 15 secondes** par la clé |
| `fixed` | Comme `on`, mais **irréversible** sans reset de l&apos;applet |
| `cached-fixed` | Comme `cached`, mais **irréversible** sans reset de l&apos;applet |

&gt; [!NOTE]
&gt; Même avec la politique sur `off`, la clé réclame le **User PIN** à la première opération suivant chaque branchement : le cache de `gpg-agent` est vidé dès que la carte est retirée. Ce que `off` supprime, c&apos;est le contact physique, pas la saisie du PIN.

- ```shell
  ykman openpgp keys set-touch sig cached
  ykman openpgp keys set-touch enc cached
  ykman openpgp keys set-touch aut cached
  ykman openpgp keys set-touch att cached
  ```

  - Applique la politique aux quatre emplacements : `sig` (signature), `enc` (chiffrement), `aut` (authentification SSH) et `att` (attestation). Demande l&apos;Admin PIN.
  - `cached` est le bon compromis au quotidien : un `git rebase` qui signe dix commits ne demande qu&apos;un seul contact, mais rien ne se signe pendant qu&apos;on est parti chercher un café.

&gt; [!WARNING]
&gt; Ne jamais utiliser `fixed` ou `cached-fixed` sans en mesurer la portée : la politique ne peut plus **jamais** être assouplie, y compris par soi-même. Le seul retour en arrière est `ykman openpgp reset`, qui efface les clés.

- ```shell
  gpg --card-edit
  
  gpg/card&gt; admin
  gpg/card&gt; forcesig
  ```

  - Bascule le drapeau *Signature PIN* entre `forced` (la clé réclame le PIN à chaque signature, valeur par défaut) et `not forced`. Le laisser sur `forced` : le cache de `gpg-agent` rend la contrainte invisible à l&apos;usage.

## Générer l&apos;identité OpenPGP sur la clé

C&apos;est l&apos;approche que je retiens : les trois clés sont générées **par le processeur de la YubiKey** et n&apos;en sortent jamais.

&gt; [!NOTE]
&gt; Avec cette méthode, la clé de signature devient la **clé maître** (celle qui certifie les identités). Elle vit sur la YubiKey : sans elle, impossible d&apos;ajouter une adresse e-mail, de signer une autre clé ou de prolonger une date d&apos;expiration.

- ```shell
  gpg --card-edit
  
  gpg/card&gt; admin
  gpg/card&gt; name
  # DUPONT
  # Jean
  gpg/card&gt; lang
  # fr
  gpg/card&gt; url
  # https://github.com/jeandupont.gpg
  ```

  - Renseigne les métadonnées du porteur. Le champ `url` est le plus utile : il indique où récupérer la clé publique, ce qui permet plus tard un `fetch` en une commande sur n&apos;importe quelle machine.

- ```shell
  gpg/card&gt; key-attr
  # Signature key  → (2) ECC → (1) Curve 25519
  # Encryption key → (2) ECC → (1) Curve 25519
  # Authentication → (2) ECC → (1) Curve 25519
  ```

  - Choisit l&apos;algorithme des trois emplacements **avant** génération. Curve 25519 donne Ed25519 pour la signature et l&apos;authentification, et Curve25519 (cv25519) pour le chiffrement : plus rapide et bien plus court que du RSA 4096, pour une sécurité au moins équivalente.
  - Rester sur **RSA 4096** si le firmware est antérieur à 5.2.3, ou si l&apos;on doit interopérer avec des outils anciens.

- ```shell
  gpg/card&gt; generate
  # Sauvegarde hors carte de la clé de chiffrement ? → n
  # La clé est-elle valable jusqu&apos;à ? → 0 (pas d&apos;expiration)
  # Nom réel        : Jean DUPONT
  # Adresse e-mail  : jean.dupont@folf.fr
  # Commentaire     : YubiKey Principale de Jean DUPONT !!
  gpg/card&gt; quit
  ```

  - Génère les trois paires de clés sur la puce. Compter une à deux minutes en RSA 4096, quelques secondes en Curve 25519. Demande l&apos;Admin PIN puis le User PIN.
  - Répondre `n` à la sauvegarde hors carte : la YubiKey ne laissera de toute façon pas sortir la clé.
  - Mettre le **numéro de série de la clé dans le commentaire** évite bien des hésitations quand on jongle entre deux YubiKeys.
  
- ```shell
  gpg --card-status
  ```

  - Vérifie le résultat : empreintes des trois clés, politiques de touch, compteur de signatures, numéro de série. C&apos;est aussi cette commande qui crée les *stubs* (références locales vers la carte) dans le trousseau.

## Gérer plusieurs adresses e-mail

Une identité OpenPGP peut porter autant d&apos;**UID** que nécessaire — pro, perso, alias — tous couverts par la même clé.

- ```shell
  gpg --edit-key jean.dupont@folf.fr
  
  gpg&gt; adduid
  # Nom réel       : Jean DUPONT
  # Adresse e-mail : jean.dupont@email-pro.com
  gpg&gt; adduid
  # Adresse e-mail : jean.dupont@email-associatif.be
  gpg&gt; save
  ```

  - Ajoute des identités. Chaque `adduid` est **certifié par la clé maître** : la YubiKey doit être branchée, elle demandera le User PIN et un contact.

- ```shell
  gpg --edit-key jean.dupont@folf.fr
  
  gpg&gt; uid 2
  gpg&gt; primary
  gpg&gt; save
  ```

  - Désigne l&apos;UID n° 2 comme identité principale (celle affichée par défaut par les outils). `uid 2` sélectionne, `uid 2` à nouveau désélectionne.

- ```shell
  gpg --edit-key jean.dupont@email-pro.com
  
  gpg&gt; uid 3
  gpg&gt; revuid
  gpg&gt; save
  ```

  - Révoque un UID devenu obsolète (changement d&apos;employeur, domaine abandonné). On ne **supprime** pas un UID déjà publié — on le révoque, pour que les personnes ayant la clé sachent qu&apos;il ne faut plus l&apos;utiliser.

## Certificat de révocation {#certificat-de-revocation}

À faire **le jour même de la génération**, tant que la clé est disponible.

- ```shell
  gpg --output revocation-0xABCDEF.asc --gen-revoke 0xABCDEF0123456789
  ```

  - Produit un certificat qui invalidera publiquement la clé. Choisir le motif `1` (clé compromise) ou `3` (clé plus utilisée) — un certificat par motif utile, tant qu&apos;à faire.
  - Sans ce fichier, une clé perdue reste indéfiniment valide aux yeux du monde.

&gt; [!IMPORTANT]
&gt; Ce fichier permet à **quiconque le détient** de tuer l&apos;identité. Il se stocke hors ligne : clé USB chiffrée dans un coffre, ou imprimé sur papier (`paperkey`, ou un QR code).

## Deux YubiKeys, deux identités croisées

Puisque chaque clé génère sa propre identité, il faut que les deux se reconnaissent mutuellement : une **signature croisée** permet à qui fait confiance à l&apos;une de faire confiance à l&apos;autre.

- ```shell
  # YubiKey A branchée
  gpg --armor --export 0xAAAA1111 &gt; cle-a.asc
  
  # YubiKey B branchée
  gpg --card-status
  gpg --armor --export 0xBBBB2222 &gt; cle-b.asc
  ```

  - Exporte les deux clés publiques. Le `gpg --card-status` après changement de clé crée les stubs de la nouvelle carte.

- ```shell
  # YubiKey A branchée
  gpg -u 0xAAAA1111 --sign-key 0xBBBB2222
  
  # YubiKey B branchée
  gpg -u 0xBBBB2222 --sign-key 0xAAAA1111
  ```

  - Chaque clé certifie l&apos;autre. `-u` force la clé qui signe, ce qui est indispensable quand le trousseau en contient plusieurs.

- ```shell
  gpg --armor --export 0xAAAA1111 0xBBBB2222 &gt; cles-publiques.asc
  ```

  - Agrège les deux clés publiques, signatures croisées comprises, dans un seul fichier à distribuer.

## Exporter et publier son identité

- ```shell
  gpg --list-secret-keys --keyid-format=long --with-subkey-fingerprints
  ```

  - Affiche les empreintes complètes des clés et sous-clés. Le `&gt;` devant `ssb` signale une sous-clé qui vit sur une carte. C&apos;est ici qu&apos;on récupère l&apos;identifiant de la **sous-clé de signature** pour Git.

- ```shell
  gpg --armor --export 0xAAAA1111 &gt; cle-publique.asc
  ```

  - Exporte la clé publique au format texte. C&apos;est le seul fichier à diffuser librement.

- ```shell
  gpg --export-ssh-key 0xAAAA1111
  ```

  - Sort la sous-clé d&apos;authentification au **format OpenSSH**, prête à coller dans un `~/.ssh/authorized_keys` ou dans GitHub. Ajouter un `!` après l&apos;identifiant (`0xCCCC3333!`) pour désigner une sous-clé précise.

- ```shell
  gpg --export-ownertrust &gt; ownertrust.txt
  ```

  - Exporte la base de confiance. Utile pour rejouer à l&apos;identique sur une autre machine, sans repasser par le menu `trust`.

- ```shell
  gpg --keyserver hkps://keys.openpgp.org --send-keys 0xAAAA1111
  ```

  - Publie sur un serveur de clés. `keys.openpgp.org` demande une validation par e-mail pour chaque UID, et **retire les signatures tierces** : les signatures croisées n&apos;y survivront pas. D&apos;où l&apos;intérêt de diffuser aussi le fichier agrégé.

Je publie surtout mes clés via GitHub, qui les expose automatiquement :

- ```shell
  curl https://github.com/jeandupont.gpg   # clés OpenPGP
  curl https://github.com/jeandupont.keys  # clés SSH publiques
  ```

  - GitHub sert les clés déclarées dans *Settings → SSH and GPG keys*. C&apos;est cette URL que je mets dans le champ `url` de la carte, ce qui rend le déploiement sur une nouvelle machine trivial.

## Redéployer son identité sur une nouvelle machine

Le scénario du quotidien : machine fraîche, YubiKey en poche.

- ```shell
  gpg -k
  ```

  - Crée l&apos;arborescence `~/.gnupg` avec les fichiers par défaut. Sans ça, les commandes suivantes râlent.

- ```shell
  gpg --import cle-publique.asc
  ```

  - Importe l&apos;identité publique. Sans clé publique dans le trousseau, `gpg` ne saura pas quoi faire de la carte : **c&apos;est bien la clé publique qui arrive en premier**.

  - Alternative sans transfert de fichier, si le champ `url` a été renseigné sur la carte :

    ```shell
    gpg --card-edit
    
    gpg/card&gt; fetch
    ```

    - Télécharge la clé publique depuis l&apos;URL stockée sur la YubiKey.

- ```shell
  gpg --edit-key jean.dupont@folf.fr
  
  gpg&gt; trust
  # 5 = confiance ultime
  # y
  gpg&gt; quit
  ```

  - Déclare la clé comme étant la sienne. Sans confiance ultime, `gpg` affiche un avertissement à chaque signature et Git refuse de considérer les commits comme vérifiés.

- ```shell
  gpg --card-status
  ```

  - YubiKey branchée, crée les stubs de clés secrètes qui pointent vers la carte. À partir d&apos;ici, `gpg --list-secret-keys` montre l&apos;identité complète.

## Configurer `gpg-agent` (et l&apos;agent SSH)

`gpg-agent` sait se faire passer pour un agent SSH. Une seule brique gère alors le PIN, le cache et les deux protocoles.

- ```shell
  ~/.gnupg/gpg-agent.conf
  
  pinentry-program /usr/bin/pinentry-qt
  enable-ssh-support
  default-cache-ttl 28800
  max-cache-ttl 28800
  default-cache-ttl-ssh 28800
  max-cache-ttl-ssh 28800
  ```

  - `pinentry-qt` sous KDE Plasma ; `pinentry-gnome3` sous GNOME, `pinentry-curses` en TTY pur.
  - `enable-ssh-support` expose la socket `S.gpg-agent.ssh`.
  - Les TTL sont en secondes : 28800 = 8 heures, soit une journée de travail avec une seule saisie de PIN. `max-cache-ttl` plafonne le cache même en cas d&apos;usage continu, il doit donc être au moins égal à `default-cache-ttl`.

- ```shell
  gpgconf --kill gpg-agent
  gpgconf --launch gpg-agent
  ```

  - Applique la configuration. `gpgconf --reload gpg-agent` suffit pour la plupart des options, mais pas pour `enable-ssh-support` qui exige un vrai redémarrage.

- ```shell
  mkdir -p ~/.config/environment.d
  ```
  ```shell
  ~/.config/environment.d/10-gpg-ssh.conf
  
  SSH_AUTH_SOCK=${XDG_RUNTIME_DIR}/gnupg/S.gpg-agent.ssh
  ```

  - Déclare la socket auprès du gestionnaire de session `systemd --user`. C&apos;est ce qui rend la variable disponible aux applications graphiques lancées depuis le menu (VS Code, IDE JetBrains, clients Git graphiques) et pas seulement aux terminaux.
  - `environment.d` ne fait **pas** de substitution de commande : `$(gpgconf …)` n&apos;y fonctionnerait pas, seule l&apos;expansion de variables déjà définies comme `${XDG_RUNTIME_DIR}` est supportée.

- ```shell
  mkdir -p ~/.config/plasma-workspace/env
  ```
  ```shell
  ~/.config/plasma-workspace/env/10-gpg-agent.sh
  
  #!/bin/sh
  export SSH_AUTH_SOCK=&quot;$(gpgconf --list-dirs agent-ssh-socket)&quot;
  gpgconf --launch gpg-agent
  ```
  ```shell
  chmod +x ~/.config/plasma-workspace/env/10-gpg-agent.sh
  ```

  - Filet de sécurité pour Plasma, qui exécute ces scripts au démarrage de la session. Redondant avec `environment.d` sur une session Plasma démarrée par systemd, mais inoffensif et utile si la session bascule sur le mode de démarrage historique.

- ```shell
  ~/.bashrc
  
  export GPG_TTY=$(tty)
  
  if [ -z &quot;$SSH_CONNECTION&quot; ]; then
      export SSH_AUTH_SOCK=&quot;$(gpgconf --list-dirs agent-ssh-socket)&quot;
  fi
  
  gpg-connect-agent updatestartuptty /bye &gt;/dev/null
  ```

  - `GPG_TTY` indique à `pinentry` sur quel terminal afficher la demande de PIN. Indispensable en console.
  - Le garde `SSH_CONNECTION` évite d&apos;**écraser l&apos;agent transféré** par `ssh -A` : sans lui, se connecter à un serveur distant casserait le rebond par agent forwarding.
  - `updatestartuptty` recale l&apos;agent déjà lancé sur le terminal courant. Sans ça, le second terminal d&apos;une session hérite du TTY du premier et le prompt de PIN part dans le vide.

Après quoi, on redémarre pour que tout soit correctement appliqué.

## Utiliser sa YubiKey en SSH

- ```shell
  ssh-add -L
  ```

  - Liste les clés proposées par l&apos;agent. La sous-clé d&apos;authentification de la carte insérée doit apparaître, suivie d&apos;un commentaire `cardno:…`. `gpg-agent` expose automatiquement les clés d&apos;authentification des cartes, sans passer par `~/.gnupg/sshcontrol`.

- ```shell
  ssh-add -L | ssh utilisateur@serveur &apos;cat &gt;&gt; ~/.ssh/authorized_keys&apos;
  ```

  - Autorise la clé sur un serveur distant. `ssh-copy-id utilisateur@serveur` fait la même chose en allant chercher la clé publique dans l&apos;agent — à condition d&apos;y avoir déjà accès autrement (mot de passe, autre clé).

- ```shell
  ssh -T git@github.com
  ```

  - Vérifie l&apos;accès Git par SSH. La YubiKey clignote : contact requis.

- ```shell
  gpg --export-ssh-key 0xAAAA1111 &gt; ~/.ssh/yubikey-a.pub
  ```
  - Exporte la clé publique SSH de la yubikey.

## Signer ses commits Git

- ```shell
  gpg --list-secret-keys --keyid-format=long --with-subkey-fingerprints
  ```

  - Récupère l&apos;identifiant de la sous-clé portant la capacité `[S]` — c&apos;est elle qui signe, pas la clé maître.

- ```shell
  git config --global user.name  &quot;Jean DUPONT&quot;
  git config --global user.email &quot;jean.dupont@folf.fr&quot;
  git config --global user.signingkey &apos;0xCCCC3333DDDD4444&apos;
  git config --global commit.gpgsign true
  git config --global tag.gpgSign true
  git config --global gpg.format openpgp
  git config --global gpg.program /usr/bin/gpg
  ```

  - `gpg.format openpgp` est la valeur par défaut, mais l&apos;expliciter évite les surprises si la configuration a un jour basculé sur `ssh`.
  - L&apos;adresse de `user.email` doit correspondre à un **UID de la clé**, sinon GitHub et consorts affichent la signature comme non vérifiée.
  
- ```shell
  git commit --allow-empty -m &quot;test signature&quot;
  git log --show-signature -1
  ```

  - Vérifie la chaîne complète. La sortie doit indiquer `Good signature from &quot;Jean DUPONT &lt;…&gt;&quot;` sans avertissement de confiance.

## Signer ses e-mails dans Thunderbird

Thunderbird embarque sa propre implémentation OpenPGP, avec un trousseau interne qui ne sait pas parler à une carte à puce. Pour signer avec la YubiKey, il faut le basculer sur le `gpg` du système.

- ```shell
  Paramètres → Général → Éditeur de configuration…
  
  mail.openpgp.allow_external_gnupg = true
  ```

  - Active la prise en charge de **GnuPG externe**. Sans ce réglage, l&apos;option permettant de saisir un identifiant de clé n&apos;apparaît pas dans les paramètres du compte.
  - Redémarrer Thunderbird.

- `Paramètres du compte → Chiffrement de bout en bout → Gestionnaire de clés OpenPGP → Fichier → Importer une ou des clés publiques depuis un fichier` *(ou `… depuis une URL`)*

  - Importer `cle-publique.asc`, ou `cles-publiques.asc` pour couvrir les deux YubiKeys d&apos;un coup. L&apos;import par URL prend directement le `https://github.com/jeandupont.gpg` renseigné dans le champ `url` de la carte.
  - Thunderbird n&apos;a besoin que de la clé **publique** : elle lui donne les UID et les empreintes des sous-clés. La clé privée reste sur la YubiKey, c&apos;est `gpg` qui l&apos;utilise.

- `Gestionnaire de clés OpenPGP → clic droit sur la clé → Propriétés de la clé`

  - Passer l&apos;acceptation au **niveau maximum** — *« Oui, j&apos;ai vérifié en personne que cette clé a la bonne empreinte »* — et cocher **chacune des adresses e-mail** de la clé. Une adresse non cochée fera passer les messages qui en partent pour non vérifiés.
  - Relever au passage l&apos;**identifiant de la clé** affiché ici : il sert à l&apos;étape suivante.

- `Paramètres du compte → Chiffrement de bout en bout → Ajouter une clé → Utiliser votre clé externe via GnuPG`

  - Coller l&apos;identifiant relevé dans le gestionnaire de clés, puis sélectionner cette clé comme **clé personnelle** du compte.
  - Cocher enfin **« Signer numériquement les messages non chiffrés »** pour signer par défaut. À la première signature, la YubiKey réclame le User PIN et un contact.
</content>
 </entry>
 
 
 
 <entry>
   <title>Flatcar Container Linux</title>
   <link href="https://blog.folf.fr/snippets/2026/07/05/flatcar/"/>
   <updated>2026-07-05T21:59:19+02:00</updated>
   <published>2026-07-05T16:15:56+02:00</published>
   <id>https://blog.folf.fr/snippets/2026/07/05/flatcar/</id>
   <content type="html">Flatcar propose les fichiers butane / ignition pour l&apos;auto-provisioning, les déploiements en masse, mais pour une simple petite machine virtuelle cela peut être considéré comme overkill. Voici donc quelques commandes que j&apos;exécute manuellement.

## `systemd-sysext`

### docker-compose

- ```shell
  mkdir -p /etc/sysupdate.docker-compose.d
  mkdir -p /opt/extensions/docker-compose
  
  cat &lt;&lt; &apos;EOF&apos; | tee /etc/sysupdate.docker-compose.d/docker-compose.conf
  [Transfer]
  Verify=false
  
  [Source]
  Type=url-file
  Path=https://extensions.flatcar.org/extensions
  MatchPattern=docker-compose-@v-%a.raw
  
  [Target]
  InstancesMax=3
  Type=regular-file
  Path=/opt/extensions/docker-compose
  CurrentSymlink=/etc/extensions/docker-compose.raw
  EOF
  
  /usr/lib/systemd/systemd-sysupdate -C docker-compose update
  reboot
  
  systemctl enable --now docker
  ```
  
  - Permet d&apos;installer docker-compose via systemd-sysupdate, et donc de bénéficier des mises à jour automatique, au même rythme que les releases flatcar.

## tailscale

- ```shell
  mkdir -p /etc/sysupdate.tailscale.d
  mkdir -p /opt/extensions/tailscale
  
  cat &lt;&lt; &apos;EOF&apos; | tee /etc/sysupdate.tailscale.d/tailscale.conf
  [Transfer]
  Verify=false
  
  [Source]
  Type=url-file
  Path=https://extensions.flatcar.org/extensions
  MatchPattern=tailscale-@v-%a.raw
  
  [Target]
  InstancesMax=3
  Type=regular-file
  Path=/opt/extensions/tailscale
  CurrentSymlink=/etc/extensions/tailscale.raw
  EOF
  
  /usr/lib/systemd/systemd-sysupdate -C tailscale update
  reboot
  
  systemctl enable --now tailscaled
  tailscale up
  ```
  
  - Permet d&apos;installer tailscale via systemd-sysupdate.

### python

- ```bash
  echo &quot;python&quot; | tee -a /etc/flatcar/enabled-sysext.conf
  ```

  - Permet d&apos;installer python3. Nécessite (à ce jour) la configuration réseau statique car pas de DHCP au démarrage.

## grub.cfg

- ```bash
  /oem/grub.cfg
  
  set linux_append=&quot;$linux_append ip=${STATIC_IP}::${STATIC_GW}:${STATIC_SUBNETMASK}::eth0:none:${STATIC_NAMESERVER}&quot;
  ```

  - Permet de remplacer la configuration réseau DHCP au démarrage par une configuration statique (par ex. si DHCP indisponible). Utile pour le téléchargement des extensions sinon plantage.</content>
 </entry>
 
 
 
 <entry>
   <title>Naim Uniti — API HTTP</title>
   <link href="https://blog.folf.fr/snippets/2026/03/16/naim-uniti/"/>
   <updated>2026-04-27T10:19:13+02:00</updated>
   <published>2026-04-22T16:16:25+02:00</published>
   <id>https://blog.folf.fr/snippets/2026/03/16/naim-uniti/</id>
   <content type="html">&gt; [!NOTE]
&gt; Il n&apos;existe **aucune documentation officielle** de Naim Audio pour cette API. Tout a été reverse-engineered depuis le trafic réseau de l&apos;app Naim et confirmé par des intégrations open-source (Home Assistant, Homebridge, Unfolded Circle, etc.).

## Connexion

- **Port HTTP** : `15081`
- **Port TCP streaming** : `4545` (push JSON temps réel, pas HTTP)
- **Pas d&apos;authentification** (réseau local uniquement)
- **URL de base** : `http://&lt;IP&gt;:15081`
- **Hostname mDNS** : `&lt;modèle&gt;-&lt;serial&gt;.local`

```
http://192.168.1.42:15081
```

## Informations système

- ```shell
  curl http://&lt;IP&gt;:15081/
  ```

  - Retourne la version de l&apos;API, le changestamp, la classe (`object.api.support`), la charge CPU.

- ```shell
  curl http://&lt;IP&gt;:15081/system
  ```

  - Infos complètes : `model`, `hostname`, `hardwareSerial`, `hardwareType`, `appVer` (firmware), `ipAddress`, `kernel`, `machine`, `udid`, `displayType`, `firstTimeSetupComplete`.

## Alimentation

- ```shell
  curl http://&lt;IP&gt;:15081/power
  ```

  - État actuel : `system` (`on` / `lona`), `state` (`on` / `standby`), `powerMode`, `serverMode`, `standbyTimeout`.

- ```shell
  curl -X PUT &quot;http://&lt;IP&gt;:15081/power?system=on&quot;
  ```

  - Allumer l&apos;appareil.

- ```shell
  curl -X PUT &quot;http://&lt;IP&gt;:15081/power?system=lona&quot;
  ```

  - Mettre en veille. La valeur est bien `lona`, pas `off` ni `standby`.

## Volume / Audio

- ```shell
  curl http://&lt;IP&gt;:15081/levels/room
  ```

  - Retourne `volume` (0-100), `mute` (0/1), `balance` (-50 à 50), `headphoneDetect` (0/1).

- ```shell
  curl http://&lt;IP&gt;:15081/levels
  ```

  - Vue d&apos;ensemble des niveaux audio (idem + `mode`).

- ```shell
  curl -X PUT &quot;http://&lt;IP&gt;:15081/levels/room?volume=45&quot;
  ```

  - Régler le volume (entier, 0 à 100).

- ```shell
  curl -X PUT &quot;http://&lt;IP&gt;:15081/levels/room?mute=1&quot;
  ```

  - Couper le son.

- ```shell
  curl -X PUT &quot;http://&lt;IP&gt;:15081/levels/room?mute=0&quot;
  ```

  - Rétablir le son.

- ```shell
  curl -X PUT &quot;http://&lt;IP&gt;:15081/levels/room?balance=-20&quot;
  ```

  - Régler la balance (-50 = tout à gauche, 50 = tout à droite).

## Lecture / Transport

- ```shell
  curl http://&lt;IP&gt;:15081/nowplaying
  ```

  - Métadonnées complètes : `transportState`, `title`, `artist`, `album`, `artwork`, `codec`, `bitDepth`, `bitRate`, `sampleRate`, `channels`, `duration`, `transportPosition`, `repeat`, `shuffle`, `source`, `station`, `genre`, `live`.

  - **transportState** : `0` = arrêté, `1` = en pause, `2` = en lecture, `3` = buffering.

- ```shell
  curl &quot;http://&lt;IP&gt;:15081/nowplaying?cmd=play&quot;
  ```

  - Lecture.

- ```shell
  curl &quot;http://&lt;IP&gt;:15081/nowplaying?cmd=pause&quot;
  ```

  - Pause.

- ```shell
  curl &quot;http://&lt;IP&gt;:15081/nowplaying?cmd=playpause&quot;
  ```

  - Basculer lecture/pause.

- ```shell
  curl &quot;http://&lt;IP&gt;:15081/nowplaying?cmd=stop&quot;
  ```

  - Arrêt.

- ```shell
  curl &quot;http://&lt;IP&gt;:15081/nowplaying?cmd=next&quot;
  ```

  - Piste suivante.

- ```shell
  curl &quot;http://&lt;IP&gt;:15081/nowplaying?cmd=prev&quot;
  ```

  - Piste précédente.

- ```shell
  curl &quot;http://&lt;IP&gt;:15081/nowplaying?cmd=seek&amp;position=120000&quot;
  ```

  - Seek à la position donnée (en millisecondes, ici 2 min).

- ```shell
  curl -X PUT &quot;http://&lt;IP&gt;:15081/nowplaying?repeat=0&quot;
  ```

  - Répétition désactivée.

- ```shell
  curl -X PUT &quot;http://&lt;IP&gt;:15081/nowplaying?repeat=1&quot;
  ```

  - Répéter la piste en cours.

- ```shell
  curl -X PUT &quot;http://&lt;IP&gt;:15081/nowplaying?repeat=2&quot;
  ```

  - Répéter toute la file.

- ```shell
  curl -X PUT &quot;http://&lt;IP&gt;:15081/nowplaying?shuffle=1&quot;
  ```

  - Activer la lecture aléatoire.

- ```shell
  curl -X PUT &quot;http://&lt;IP&gt;:15081/nowplaying?shuffle=0&quot;
  ```

  - Désactiver la lecture aléatoire.

## Sources / Entrées

- ```shell
  curl http://&lt;IP&gt;:15081/inputs
  ```

  - Liste toutes les sources disponibles. Chaque entrée contient `name`, `ussi`, `class`, `alias`, `disabled`, `selectable`, `multiroomMaster`.

- ```shell
  curl http://&lt;IP&gt;:15081/inputs/spotify
  ```

  - Infos sur une source spécifique.

- ```shell
  curl &quot;http://&lt;IP&gt;:15081/inputs/spotify?cmd=select&quot;
  ```

  - Basculer sur cette source.

- ```shell
  curl &quot;http://&lt;IP&gt;:15081/inputs/radio?cmd=play&quot;
  ```

  - Sélectionner et lancer la lecture d&apos;une source streaming.

### Identifiants de source connus

| ID | Nom | Classe |
|---|---|---|
| `ana1` – `ana4` | Analogique 1–4 | `object.input.analogue` |
| `dig1` – `dig5` | Numérique 1–5 | `object.input.digital` |
| `hdmi` | HDMI (ARC) | `object.input.hdmi` |
| `bluetooth` | Bluetooth | `object.input.bluetooth` |
| `radio` | Radio Internet | `object.input.radio.internet` |
| `spotify` | Spotify Connect | `object.input.spotify` |
| `tidal` | TIDAL | `object.input.tidal` |
| `qobuz` | Qobuz | `object.input.qobuz` |
| `usb` | USB | `object.input.usb` |
| `airplay` | AirPlay | `object.input.airplay` |
| `gcast` | Chromecast built-in | `object.input.googlecast` |
| `upnp` | Serveurs UPnP | `object.input.upnp` |
| `playqueue` | File de lecture | `object.input.playqueue` |
| `files` | Fichiers locaux / Démo | `object.input.files` |
| `roon` | Roon Ready | — |
| `multiroom` | Multiroom | — |

&gt; [!TIP]
&gt; Toutes les sources ne sont pas présentes sur tous les modèles. L&apos;endpoint `/inputs` retourne uniquement celles disponibles sur votre appareil.

## Favoris / Presets

- ```shell
  curl http://&lt;IP&gt;:15081/favourites
  ```

  - Liste tous les favoris. Chaque favori contient `name`, `ussi`, `available`.

- ```shell
  curl &quot;http://&lt;IP&gt;:15081/favourites/3754de0b49624983ab2179ed8524e9a2?cmd=play&quot;
  ```

  - Lancer un favori par son UUID (portion après `favourites/` dans le champ `ussi`).

## File de lecture

- ```shell
  curl http://&lt;IP&gt;:15081/inputs/playqueue
  ```

  - Infos sur la file de lecture. Le champ `current` contient le chemin vers la piste en cours (utilisable comme endpoint GET pour les métadonnées détaillées).

## Réseau

- ```shell
  curl http://&lt;IP&gt;:15081/network
  ```

  - Configuration réseau : `connectionState`, `current` (`network/wired` ou `network/wifi`), `dhcp`, `dns1`, `dns2`, `dnsName`, `gateway`, `hostname`, `ipAddress`, `macAddress`, `netmask`, `workgroup`.

## Découverte SSDP/UPnP

Les appareils Naim s&apos;annoncent comme **MediaRenderer UPnP**. Pour les trouver sur le réseau :

- ```shell
  gssdp-discover -t urn:schemas-upnp-org:device:MediaRenderer:2 --timeout=5
  ```

  - Recherche SSDP des MediaRenderers. L&apos;en-tête `LOCATION` pointe vers le XML de description contenant `manufacturer`, `friendlyName`, `modelName`, `serialNumber`, `UDN`.

## Socket TCP temps réel (port 4545)

&gt; [!IMPORTANT]
&gt; Ce n&apos;est **pas du HTTP**. C&apos;est une connexion TCP brute qui pousse du JSON en continu.

- ```shell
  nc &lt;IP&gt; 4545
  ```

  - Se connecter et recevoir les mises à jour en temps réel (état de lecture, métadonnées, position).

  - Valeurs de `state` : `playing`, `paused`, `stopped`.

Structure JSON poussée :

```json
{
  &quot;data&quot;: {
    &quot;state&quot;: &quot;playing&quot;,
    &quot;trackRoles&quot;: {
      &quot;title&quot;: &quot;Nom de la piste&quot;,
      &quot;icon&quot;: &quot;https://url-artwork...&quot;,
      &quot;mediaData&quot;: {
        &quot;metaData&quot;: {
          &quot;artist&quot;: &quot;Artiste&quot;,
          &quot;album&quot;: &quot;Album&quot;
        },
        &quot;activeResource&quot;: {
          &quot;bitsPerSample&quot;: &quot;24&quot;,
          &quot;sampleFrequency&quot;: &quot;96000&quot;
        }
      }
    },
    &quot;mediaRoles&quot;: {
      &quot;title&quot;: &quot;Nom de la source&quot;,
      &quot;mediaData&quot;: {
        &quot;metaData&quot;: {
          &quot;serviceID&quot;: &quot;spotify&quot;
        }
      }
    },
    &quot;status&quot;: {
      &quot;duration&quot;: 355000
    }
  },
  &quot;playTime&quot;: {
    &quot;i64_&quot;: 45000
  }
}
```
</content>
 </entry>
 
 
 
 <entry>
   <title>PipeWire Tips &amp; Tricks</title>
   <link href="https://blog.folf.fr/snippets/2026/03/01/pipewire/"/>
   <updated>2026-04-27T10:19:13+02:00</updated>
   <published>2026-04-22T16:16:25+02:00</published>
   <id>https://blog.folf.fr/snippets/2026/03/01/pipewire/</id>
   <content type="html">## Identifier les propriétés de son DAC

- ```shell
  wpctl status
  ```

  - Identifier le périphérique de sortie actuellement utilisé par PipeWire

- ```shell
  cat /proc/asound/cards
  ```

  - Identifier le numéro de la carte ALSA correspondant au DAC

- ```shell
  cat /proc/asound/cardX/stream0
  ```

  - Identifier les capacités de décodage du DAC

## Créer la configuration adéquate

- ```shell
  mkdir -p ~/.config/pipewire/pipewire.conf.d/
  ```

  - Créer un dossier de configuration PipeWire propre à notre compte utilisateur

- ```shell
  ~/.config/pipewire/pipewire.conf.d/10-hires-dac.conf
  
  context.properties = {
  	# Fréquence par défaut
  	default.clock.rate          = 384000
  	
  	# Autoriser PipeWire à basculer automatiquement sur ces fréquences sans rééchantillonner
  	default.clock.allowed-rates = [ 44100 48000 88200 96000 176400 192000 352800 384000 ]
  	
  	# Profondeur de bits maximale pour exploiter le DAC
  	default.audio.format        = &quot;S32_LE&quot;
  	
  	# Amélioration de la latence et réduction des craquements
  	default.clock.quantum       = 1024
  	default.clock.min-quantum   = 32
  	default.clock.max-quantum   = 8192
  }
  ```

  - Adapter la configuration avec les paramètres spécifique au modèle du DAC, récupérés avec les commandes précédentes.

- ```shell
  systemctl --user restart pipewire pipewire-pulse wireplumber
  ```

  - Redémarrer PipeWire pour appliquer la nouvelle configuration.

## Tester et Vérifier la qualité de lecture

- ```shell
  grep -i &quot;Momentary freq&quot; /proc/asound/card*/stream0
  ```

  - Après avoir lancé la lecture d&apos;un fichier Hi-Res, permet de voir en quelle qualité est lue le fichier en temps réel.

- ```shell
  pw-top
  ```

  - Petit outil GUI/CLI permettant de voir quel processus décode actuellement du contenu audio et à quelle qualité.

Il est important de ne pas modifier le volume et de laisser tout à 100% pour ne pas forcer Linux à re-encoder le flux audio !</content>
 </entry>
 
 
 
 <entry>
   <title>Linux Tips &amp; Tricks</title>
   <link href="https://blog.folf.fr/linux/"/>
   <updated>2026-07-27T10:57:30+02:00</updated>
   <published>2026-07-27T10:36:05+02:00</published>
   <id>https://blog.folf.fr/linux/</id>
   <content type="html">## Apt

- ```shell
  apt-cache policy (package)
  ```

  - Permet de déterminer à partir de quel dépôt est installé / téléchargé un paquet. Ainsi, si jamais plusieurs dépôts proposent le même paquet, il est possible de savoir celui qui aura la plus haute priorité.

- ```shell
  apt-cache show (package)
  ```

  - Permet d&apos;obtenir les métadonnées d&apos;un paquet.

## Grub

- ```shell
  /etc/default/grub
  
  GRUB_CMDLINE_LINUX=&quot;net.ifnames=0 biosdevname=0&quot;
  ```

  - Permet de forcer le nommage des interfaces réseau en ethX plûtot que enpXsX. Les interfaces en enpXsX sont succeptibles de changer aléatoirement en cas de modification matérielle ou montée de version logicielle.
  
- ```shell
  /etc/default/grub
  
  GRUB_CMDLINE_LINUX=&quot;console=tty0 console=ttyS0,115200n8&quot;
  GRUB_TERMINAL=&quot;console serial&quot;
  GRUB_SERIAL_COMMAND=&quot;serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1&quot;
  
  update-grub
  update-initramfs -u -k all
  
  sudo systemctl enable serial-getty@ttyS0.service
  ```

  - Permet d&apos;activer GRUB / un shell sur le port console.


## LVM

- ```shell
  lvmconfig --validate
  ```

  - Permet de vérifier la syntaxe de configuration du fichier `/etc/lvm/lvm.conf`

## Bash

- ```shell
  ~/.bashrc
  
  export HISTSIZE=-1
  export HISTFILESIZE=-1
  export HISTTIMEFORMAT=&quot;%F %T &quot;
  export PROMPT_COMMAND=&quot;history -a; $PROMPT_COMMAND&quot;
  shopt -s histappend
  ```

  - Permet de sauvegarder de manière illimitée l&apos;historique bash.
  
- ```shell
  grep -rnw &apos;/path/où/chercher/&apos; -e &quot;string&quot;
  ```
  
  - Permet de chercher une string de manière récursive dans un dossier voulu.
  

## Proxmox

- ```shell
  bash -c &quot;$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/kernel-clean.sh)&quot;
  ```

  - Permet de désinstaller les anciennes versions du noyau Linux qui subsistent en cas de mise à jour Proxmox.

## Git

- ```shell
  git rebase -i sha256commit(^)
  EDITOR=nano git rebase -i --root
  ```
  
  - Permet de lancer un rebase interactif à partir du commit indiqué (^ indique son parent) ou bien à partir de la racine (--root)
  
- ```shell
  git fetch --all
  git reset --hard origin/main
  ```

  - Permet de forcer git à revenir à la version sur le serveur (pour annuler les modifications locales)

## Qemu

- ```shell
  qemu-img convert -f vmdk -O qcow2 source.vmdk destination.qcow2
  ```

  - Permet de convertir une image de disque virtuelle VMDK en QCOW2.
  
- ```bash
  virt-sparsify --tmp /tmp_folder_with_disk_size_free_space/ --compress entree.qcow2 sortie.qcow2
  ```

  - Permet de nettoyer une image qcow2, avec zerofree puis compression et export.

## VirtualBox

- ```shell
  VBoxManage modifymedium disk &quot;/chemin/vers/image/disque&quot; --compact
  ```

  - Permet de compacter un fichier d&apos;image disque

- ```shell
  zerofree -v /dev/sdaX
  ```

  - Permet de remplir de zéros les emplacements vides d&apos;un disque dur Linux formaté en ext4.

## systemd

- ```shell
  systemctl list-jobs
  systemctl list-units --type=service --state=activating
  ```

  - Identifier les services qui sont en cours de démarrage, pouvant bloquer le démarrage complet de l&apos;hôte.

    ```shell
    systemctl status mon-service.service
    journalctl -u mon-service.service -f
    ```

    - Permet d&apos;approfondir pourquoi un service ne démarre pas en regardant l&apos;intégralité de ses logs.
  
- ```shell
  systemctl --failed
  ```
  
  - Identifier les services qui sont en cours d&apos;échec (état général visible via`systemctl status`).
  
    ```shell
    sudo systemctl reset-failed
    ```
  
    - Permet de réinitialiser l&apos;état d&apos;une unit considérée comme failed.
  
- ```bash
  systemd-analyze
  systemd-analyze blame # Liste les services les plus longs à démarrer
  systemd-analyze critical-chain # Affiche la chaîne critique avec le service précis qui bloque la séquence
  systemd-analyze plot &gt; boot.svg # exporte au format svg la chaîne de démarrage
  systemctl list-dependencies nom-du-service # Affiche l&apos;arbre de dépendances d&apos;un service lent
  ```
  
  - Permet d&apos;analyser le temps de démarrage d&apos;un système Linux.
  
- ```bash
  journalctl # permet de voir du début à la fin
  journalctl -b # permet de voir les logs du boot
  journalctl -b -1 # permet de voir les logs du boot précédent
  journalctl -r # permet de voir de la fin au début
  journalctl -f # permet de voir en temps réel
  journalctl -u nomduservice # permet de scoper un service spécifique
  journalctl -n 50 # permet d&apos;afficher les 50 premières lignes
  journalctl --since &quot;1 hour ago&quot; --until &quot;2 days ago&quot; # permet de ciber une période de temps
  journalctl -p err # permet de voir uniquement les erreurs (emerg, alert, crit, err, warning, notice, info, debug)
  journalctl -k # permet de voir les logs noyau (dmesg)
  journalctl --no-pager # désactive la pagination
  journalctl -o json-pretty # active une sortie json pretty
  ```
  - Permet de voir et naviguer dans les logs d&apos;un système Linux.


## openssh

- ```shell
  ~/.ssh/config
  
  Host monserveur
          HostName mon.serveur.fr
          User utilisateur
          ForwardAgent yes
  ```

  - Permet de pré-définir / enregistrer des paramètres de session SSH pour voir se connecter en une seule commande rapidement.
  - `ForwardAgent yes` permet de passer l&apos;agent SSH du client vers l&apos;hôte pour transférer les clés SSH entre les deux.

## Linux

- ```
  cat /proc/cmdline
  ```

  - Permet d&apos;afficher les arguments de lancement initramfs / dracut du noyau linux actuellement démarré.

- ```
  /etc/default/keyboard
  ```

  - Permet de modifier le keyboard layout d&apos;une console.
    - Il faut ensuite exécuter la commande `setupcon` pour appliquer.
  
- ```sh
  apt install parted -y
  partprobe /dev/sda
  ```
  
  - Permet d&apos;actualiser la table de partitions sans avoir à redémarrer le linux.

# NetworkManager

- ```bash
  systemctl mask NetworkManager-wait-online.service
  ```

  - Permet de désactiver l&apos;attente que NetworkManager ai un réseau de monté pour continuer le boot du linux. Utile sur un serveur mais inutile sur un desktop.

# Distrobox

Plein de commandes sans explication pour distrobox. Superbe outil permettant de faire tourner des distributions Linux dans des conteneurs avec le forward X11 etc nativement gérés.

- ```bash
  distrobox create --name mabox --image ubuntu:26.04
  distrobox create --name mabox2 --clone box
  distrobox create --home /chemin/home/personnalisé
  distrobox create --root --name test
  distrobox create --name unshared --unshare-netns --unshare-dev --unshare-ipc --unshare-process --unshare-all
  distrobox create --name nopassword --absolutely-disable-root-password-i-am-really-positively-sure
  distrobox create --name avecsystemd --image ubuntu:26.04 --init --aditionnal-packages &quot;systemd libpam-systemd dbus dbus-user-session polkitd libpulse0 pipewire-alsa&quot;
  
  distrobox enter mabox
  distrobox enter mabox -- htop
  
  distrobox ls
  distrobox stop mabox
  distrobox rm mabox
  distrobox upgrade --all
  
  distrobox-export --app vlc
  distrobox-export --bin /usr/bin/htop --export-path ~/.local/bin
  distrobox-export --service syncthing
  distrobox-export --app vlc --delete
  
  distrobox-host-exec flatpak run org.mozilla.firefox
  sudo ln -s /usr/bin/distrobox-host-exec /usr/local/bin/docker
  
  distrobox-ephemeral -i alpine:latest
  ```

</content>
 </entry>
 
 
 
 <entry>
   <title>Dell Tips &amp; Tricks</title>
   <link href="https://blog.folf.fr/snippets/2026/03/01/dell/"/>
   <updated>2026-04-27T10:19:13+02:00</updated>
   <published>2026-04-22T16:16:25+02:00</published>
   <id>https://blog.folf.fr/snippets/2026/03/01/dell/</id>
   <content type="html">## M1000e

- ```shell
  racadm getfaninfo -d -p glacier -w 50
  ```

  - Écrase la vitesse des ventilateurs par 50%.

- ```
  racadm getfanreqinfo
  ```

  - Récupère la vitesse des ventilateurs effective et demandée par les blades.</content>
 </entry>
 
 
 
 <entry>
   <title>Avoir des adresses IPv4/IPv6 chez soi avec un tunnel WireGuard</title>
   <link href="https://blog.folf.fr/wireguard/"/>
   <updated>2026-10-06T14:58:09+02:00</updated>
   <published>2026-04-22T16:16:25+02:00</published>
   <id>https://blog.folf.fr/wireguard/</id>
   <content type="html"># Introduction

J&apos;ai chez moi un serveur sous [**Proxmox**](https://www.proxmox.com/), un hyperviseur qui me permet de créer des machines virtuelles et des [conteneurs Linux](https://linuxcontainers.org/) pour héberger les services de mes diverses activités d&apos;auto-hébergement.

Avec un seul abonnement internet de particulier, je n&apos;ai qu&apos;une seule adresse IP résidentielle. Je suis donc vite limité par le nombre de ports disponibles, et je ne peux pas faire tourner tous les services que je souhaite dans de bonnes conditions.

Ouvrir des ports reste possible, mais certains opérateurs retirent cette option au fil du temps, et les box 4G/5G ne la proposent pas du tout à cause du [CG-NAT](https://fr.wikipedia.org/wiki/Carrier-grade_NAT).

Ces limitations m&apos;ont poussé à trouver un moyen d&apos;avoir **plusieurs adresses IP publiques chez moi**, dédiées, protégées par un anti-DDoS et peu chères. Faute de solution existante, j&apos;ai bricolé la mienne, puis je l&apos;ai transformée en script pour qu&apos;elle soit reproductible en quelques minutes.

# Principe de fonctionnement

![Comment fonctionne l&apos;internet](/assets/contenu/img/posts/tunnel-wireguard-ip-homelab/doc-schema-vps.avif)

Un VPS chez un hébergeur reçoit une ou plusieurs adresses IP publiques supplémentaires. Un tunnel WireGuard relie ce VPS à une machine chez moi, et le VPS lui **route** ces adresses telles quelles, sans NAT. Vu de l&apos;extérieur, ma machine est directement sur internet avec sa propre IP, tout en restant derrière ma box.

# Le choix de l&apos;hébergeur

Cette méthode fonctionne chez **n&apos;importe quel hébergeur** qui vend des adresses IP supplémentaires (aussi appelées IP failover) et les route vers votre VPS, comme [HostMyServers](https://www.hostmyservers.fr/), [OVHcloud](https://www.ovhcloud.com/fr/vps/) ou [RoyaleHosting](https://royalehosting.net/store/vps). Le script et la configuration restent les mêmes partout ; seule la commande des adresses dans l&apos;espace client change.

Quelques critères pour choisir :

- le **prix des adresses IP supplémentaires**, qui varie beaucoup d&apos;un hébergeur à l&apos;autre (frais uniques ou abonnement mensuel) ;
- une **protection anti-DDoS** incluse, puisque le VPS devient la porte d&apos;entrée de vos services ;
- le **débit réseau** du VPS : WireGuard consomme très peu de ressources et une offre d&apos;entrée de gamme suffit, mais tout votre trafic passera par lui ;
- un datacenter **proche de chez vous**, pour limiter la latence ajoutée par le tunnel.

# Achat d&apos;adresses IP supplémentaires

**Ce tutoriel nécessite au moins une adresse IPv4 supplémentaire sur le VPS.**

Commandez-la depuis l&apos;espace client de votre hébergeur, en général dans la configuration réseau du VPS. Prenez les adresses IPv4 **à l&apos;unité** : le script attribue une adresse à chaque profil. Le routage d&apos;un bloc entier vers un routeur est abordé dans « Pour aller plus loin ».

Une fois l&apos;adresse livrée, notez bien, pour la suite :

- l&apos;**adresse IP principale** du VPS : celle qu&apos;il avait à sa livraison, qui a généralement déjà un reverse DNS fourni par l&apos;hébergeur ;
- l&apos;**adresse IP supplémentaire** : celle que vous venez de commander.

Gardez ces deux adresses sous les yeux pour ne pas les confondre plus loin.

&gt; [!WARNING]
&gt; Commandez raisonnablement : il ne reste plus beaucoup d&apos;adresses IPv4, et il est inutile d&apos;en réserver si elles restent inutilisées.

# Prérequis sur le VPS

Une fois votre VPS livré, choisissez sa distribution dans l&apos;espace client. J&apos;installe **Debian 12** ou plus récent, mais n&apos;importe quelle distribution avec systemd convient : Debian, Ubuntu, Fedora, Rocky ou Alma, Arch, openSUSE, ou encore Flatcar.

Les identifiants de connexion arrivent généralement par e-mail une fois l&apos;installation terminée.

Connectez-vous en SSH avec un client comme [PuTTY](https://www.putty.org/) ou [Termius](https://termius.com/).

&gt; [!IMPORTANT]
&gt; L&apos;adresse IP supplémentaire **ne doit pas être montée sur le VPS** : ni sur son interface réseau, ni dans sa configuration (netplan, cloud-init, `/etc/network/interfaces`…). L&apos;hébergeur la route vers le VPS, mais c&apos;est la machine au bout du tunnel qui doit la porter. Si le VPS la monte aussi, il répond lui-même au trafic destiné à cette adresse et rien n&apos;arrive jusqu&apos;au client.

Vérifiez-le avec :

```bash
ip -4 addr
```

Si l&apos;adresse supplémentaire apparaît sur `eth0` (ou `ens3`, selon la distribution), retirez-la de la configuration réseau avant de continuer. Certains hébergeurs la configurent automatiquement, d&apos;autres non : dans ce second cas, il n&apos;y a rien à faire.

# Installation de WireGuard

Commençons par mettre à jour le VPS :

```bash
apt update
apt full-upgrade -y
reboot
```

Une fois le VPS redémarré, déployons le serveur WireGuard avec mon script. C&apos;est un fork de [angristan/wireguard-install](https://github.com/angristan/wireguard-install) devenu un installeur tout-en-un : VPN classique, routage d&apos;adresses IP publiques, choix de l&apos;endpoint pour chaque profil, intégration du pare-feu et vue d&apos;état.

```bash
curl -O https://raw.githubusercontent.com/tbringuier/wireguard-install/master/wireguard-install.sh
chmod +x wireguard-install.sh
./wireguard-install.sh
```

&gt; [!NOTE]
&gt; Le script est open source et documenté sur [github.com/tbringuier/wireguard-install](https://github.com/tbringuier/wireguard-install). Une grande partie a été écrite avec Claude, l&apos;IA d&apos;Anthropic, puis relue et testée par mes soins sur Debian, Ubuntu, Fedora, Rocky, Arch et openSUSE.

## Les questions du script

Le script pose une série de questions. Les valeurs proposées par défaut conviennent presque partout ; la seule à changer est la question sur le routage public, à laquelle il faut répondre `yes`.

```plaintext
Welcome to the WireGuard installer!
The git repository is available at: https://github.com/tbringuier/wireguard-install

I need to ask you a few questions before starting the setup.
You can keep the default options and just press enter if you are ok with them.

Public addresses of this server (leave empty the ones it does not have):
Public IPv4: 203.0.113.10
Public IPv6: 2001:db8:42::1
Hostname pointing to this server:
Public interface: eth0
WireGuard interface name: wg0
Tunnel MTU [1280-1500]: 1420
Server WireGuard IPv4: 10.66.66.1
Server WireGuard IPv6: fd42:42:42::1
Server WireGuard port [1-65535]: 62052
First DNS resolver to use for the clients: 1.1.1.1
Second DNS resolver to use for the clients (optional): 1.0.0.1
Enable public IP routing support [yes/no]: yes
Public clients can get an IPv6 from the block your provider routes to this server.
Public IPv6 prefix of this server (empty if none): 2001:db8:42::/64

WireGuard uses a parameter called AllowedIPs to determine what is routed over the VPN.
Allowed IPs list for generated clients (leave default to route everything): 0.0.0.0/0,::/0

Okay, that was all I needed. We are ready to setup your WireGuard server now.
You will be able to generate a client at the end of the installation.
Press any key to continue...
```

Quelques précisions :

- L&apos;adresse IPv4 publique proposée par défaut est celle de l&apos;interface du VPS, donc l&apos;**adresse principale** (celle qui a un reverse DNS), pas l&apos;adresse supplémentaire.
- L&apos;IPv6 et le nom d&apos;hôte sont facultatifs. Ils servent à proposer plusieurs endpoints aux clients, on y revient plus bas.
- Le préfixe IPv6 est détecté sur l&apos;interface du VPS. Il permet au script de proposer lui-même une IPv6 publique à chaque client.
- Si **ufw** ou **firewalld** est actif sur le VPS, le script le détecte et propose d&apos;ajouter lui-même les règles nécessaires (port UDP entrant, forwarding sur `wg0`). Répondez `y`.

## Le premier profil

Le script installe les paquets, active le routage, génère les clés, démarre le tunnel, puis demande le nom du premier profil. J&apos;appelle le mien MaVM :

```plaintext
The client name must consist of alphanumeric character(s). It may also include underscores or dashes and can&apos;t exceed 15 chars.
Client name: MaVM
```

Vient ensuite la question importante : le **mode d&apos;adressage** du client.

- `private` : le client reçoit une adresse privée du tunnel (10.66.66.x) et sort derrière l&apos;IP du VPS, comme avec un VPN classique.
- `public` : le client reçoit une de vos adresses supplémentaires, routée directement et sans NAT. C&apos;est ce que nous voulons.
- `mixed` : le client reçoit les deux.

```plaintext
A private client gets an address from the tunnel subnet and is NATed behind the server.
A public client gets one of the server&apos;s additional public addresses, routed without NAT.
A mixed client gets both.
Client address mode [private/public/mixed]: public

Public addresses must be routed to this server by your provider (additional/failover IP).
Public client IPv4 (leave empty if unused): 198.51.100.25
Public client IPv6 (leave empty if unused):
```

C&apos;est ici que l&apos;on saisit l&apos;**adresse IP supplémentaire** achetée chez votre hébergeur. Pour l&apos;IPv6, le script propose la prochaine adresse libre du bloc du VPS ; laissez vide si vous n&apos;en voulez pas.

&gt; [!NOTE]
&gt; Le script refuse une adresse déjà attribuée à un autre profil, ou configurée sur une interface du VPS (voir les prérequis).

Dernière question : l&apos;**endpoint** auquel le client se connectera. Si le VPS a une IPv6, elle est recommandée : ce sont surtout les IPv4 qui sont attaquées, et un tunnel monté en IPv6 ne souffre pas d&apos;une attaque visant l&apos;IPv4 du VPS. Il faut évidemment que le client dispose d&apos;une connectivité IPv6 ; sinon, choisissez l&apos;IPv4 ou un nom de domaine.

```plaintext
Endpoint this client connects to (it must be able to reach it):
   1) IPv6 [2001:db8:42::1]:62052 (recommended: IPv4 addresses attract most attacks)
   2) IPv4 203.0.113.10:62052
   3) Another hostname or address
Select an option [1-3]: 2
```

Le profil est créé : un QR code s&apos;affiche et le fichier `/root/wg0-client-MaVM.conf` est écrit.

## Ce que le script a mis en place

Tout le reste est automatique. Pour la curiosité, voici ce qui tourne désormais sur le VPS :

- une table **nftables** `inet wireguard`, qui fait le NAT des clients privés et clampe le MSS TCP dans les deux sens à la MTU du tunnel. C&apos;est ce qui règle les fameux soucis de MTU (merci Diggyworld et Hecate), y compris pour les clients Windows ou mobiles qui ne peuvent pas exécuter de hooks ;
- une entrée **proxy ARP** (`ip neigh proxy`) par adresse publique, pour que le VPS réponde aux requêtes ARP de l&apos;hébergeur, et son équivalent proxy NDP pour l&apos;IPv6 ;
- un service systemd `wg-public-ipv4-announce@wg0`, qui annonce chaque adresse publique en gratuitous ARP toutes les secondes. C&apos;est l&apos;équivalent de la boucle `arping` historique du tutoriel (merci titin et TheOrion-OVH) ;
- la régénération de `/etc/wireguard/wg0.conf` à chaque ajout ou suppression de profil, appliquée à chaud sans couper les autres tunnels, avec une sauvegarde de la version précédente dans `wg0.conf.bak`.

Et voici le profil client généré :

```ini
[Interface]
PrivateKey = MM2OFVfYrJFtdAgebfPJL2hDtjaslufqoJ1yzvdN+X8=
Address = 198.51.100.25/32
DNS = 1.1.1.1,1.0.0.1
MTU = 1420

[Peer]
PublicKey = udEYVLpHnWb4o7kgjZ4pCnfUaVjqd9inXAUmak9mXxM=
PresharedKey = t+rgwqN3j8LccHtgi7GULlwBrf8ghY8HAbZN6cagP8s=
Endpoint = 203.0.113.10:62052
AllowedIPs = 0.0.0.0/0,::/0
PersistentKeepalive = 15
```

Vous pouvez vérifier l&apos;état du serveur à tout moment :

```bash
systemctl status wg-quick@wg0
systemctl status wg-public-ipv4-announce@wg0
wg show
```

Pour ajouter d&apos;autres profils (une autre adresse supplémentaire, ou un simple client VPN en mode `private`), relancez le script. Son menu permet d&apos;ajouter ou de révoquer des profils, de désinstaller WireGuard, et d&apos;afficher une **vue d&apos;état** : pour chaque client, ses adresses, son endpoint, l&apos;adresse depuis laquelle il est connecté, la date du dernier handshake et le trafic échangé.

Le profil est prêt à être déployé.

# Déploiement du profil WireGuard

Le profil s&apos;installe sur n&apos;importe quelle plateforme : Windows, Linux, Android, macOS, iOS et bien d&apos;autres.

Voici les commandes pour un Linux à base de Debian :

```bash
# Installer WireGuard et resolvconf
apt install wireguard-tools resolvconf

# Installer le profil : collez le contenu de wg0-client-MaVM.conf dans ce fichier
nano /etc/wireguard/wg0.conf

# Activer le tunnel maintenant et à chaque démarrage
systemctl enable wg-quick@wg0 --now

# Vérifier que l&apos;adresse est bien montée
ip a
curl ifconfig.me
```

# Diagnostics

Si le profil ne fonctionne pas, vérifiez d&apos;abord que vous n&apos;avez pas confondu les adresses IP, puis relisez la section des prérequis. Ensuite, sur le VPS :

```bash
# Le tunnel est-il monté, et le client a-t-il fait un handshake récent ?
wg show

# Le service d&apos;annonce ARP tourne-t-il, et pour quelles adresses ?
systemctl status wg-public-ipv4-announce@wg0
journalctl -u wg-public-ipv4-announce@wg0 -f

# Le VPS répond-il en ARP pour l&apos;adresse supplémentaire ?
ip neigh show proxy

# La table nftables du tunnel (NAT et clamp MSS) est-elle chargée ?
nft list table inet wireguard

# Les annonces partent-elles réellement sur le réseau ?
tcpdump -ni eth0 arp
```

&gt; [!WARNING]
&gt; Remplacez `eth0` par le vrai nom de votre interface : il change selon la distribution et l&apos;hébergeur.

Les cas classiques :

- **Pas de handshake** dans `wg show` : le port UDP ou l&apos;endpoint est faux côté client, ou le client n&apos;a pas de connectivité (IPv6 par exemple) vers cet endpoint.
- **Handshake présent mais pas de trafic** : l&apos;adresse supplémentaire est configurée sur l&apos;interface du VPS (voir les prérequis), ou l&apos;hébergeur ne la route pas vers ce VPS.
- **Certains sites ne chargent pas** : problème de MTU. Le script fixe la MTU du tunnel des deux côtés et clampe le MSS sur le serveur pour tous les clients ; vérifiez que votre application WireGuard a conservé la ligne `MTU = 1420` du profil.
- **Ça marche une fois sur deux** après un redémarrage du VPS : regardez `journalctl -u wg-public-ipv4-announce@wg0`, c&apos;est le service d&apos;annonce ARP qui corrige ce cas.

# Conclusion et remerciements

Et voilà : vous avez des adresses IP failover chez vous, protégées par un anti-DDoS, utilisables sur n&apos;importe quel appareil.

Cette astuce m&apos;a fait franchir un grand pas dans l&apos;auto-hébergement, pour mes services comme pour ceux que j&apos;héberge pour d&apos;autres : elle offre la puissance d&apos;un VPS avec des IP dédiées, à prix réduit et avec une qualité de service comparable.

Ce tutoriel existe depuis juillet 2020. Il a été remasterisé en septembre 2024, puis simplifié en octobre 2026 : toutes les manipulations manuelles d&apos;alors (règles de pare-feu, sysctl, boucle arping, édition des profils) sont désormais faites par le script.

## Merci à

- [@Aven678](https://github.com/Aven678/), pour avoir énormément simplifié la gestion des IP et la création de profils ;
- [@DrKnaw](https://github.com/DrKnaw), pour avoir corrigé des bugs de mon système qui n&apos;était pas tout à fait fini à l&apos;époque ;
- [@MaelMagnien](https://web.archive.org/web/20240108141946/https://github.com/maelmagnien), pour avoir testé le tutoriel de bout en bout ;
- [@Gogow_](https://github.com/Gogowwww), pour m&apos;avoir fait déboguer ma doc plusieurs fois ;
- [@Diggyworld](https://github.com/Diggyworld), pour avoir passé toute une soirée à trouver une solution à ces fichus soucis de MTU ;
- [@titin](https://git.feelb.io/Titin), pour avoir trouvé la commande arping qui règle certains soucis de routage ;
- [@Hecate](https://github.com/TheHecateII), pour la règle iptables qui corrigeait les soucis de MTU ;
- [@TheOrion-OVH](https://github.com/TheOrion-OVH), pour le correctif ARP qui ne fonctionnait qu&apos;une fois sur deux et quelques fautes de frappe dans la documentation.

Et merci à toutes les personnes qui m&apos;ont écrit sur Discord pour améliorer cette documentation ou simplement dire merci.

## Pour aller plus loin

- **Routage d&apos;un bloc entier** (/29, /64…) vers un routeur chez soi : ce n&apos;est pas géré par le script, mais un ami a rédigé une très bonne documentation pour monter les adresses sur un routeur VyOS, ce qui évite de déployer un profil WireGuard par machine : [Router un subnet IPv4 chez soi avec WireGuard + VyOS](https://blog.azernet.xyz/router-un-subnet-ipv4-chez-soi-avec-wireguard-vyos-2/).

Si vous souhaitez faire figurer votre « fork » de cette documentation ici, écrivez-moi sur Discord ou Telegram.

## [Avoir des adresses IPv4/IPv6 chez soi avec un tunnel WireGuard]({{ site.url }}/wireguard) by [{{ site.author.name }}]({{ site.url }}) is licensed under [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/)
</content>
 </entry>
 

</feed>
