<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Terraform on Mook’s Lab Chronicles</title><link>https://blog.homeblack.fr/tags/terraform/</link><description>Recent content in Terraform on Mook’s Lab Chronicles</description><generator>Hugo -- gohugo.io</generator><language>fr-FR</language><lastBuildDate>Sat, 19 Sep 2026 16:06:02 +0200</lastBuildDate><atom:link href="https://blog.homeblack.fr/tags/terraform/index.xml" rel="self" type="application/rss+xml"/><item><title>Mon bastion Warpgate : des clés SSH au simple alias</title><link>https://blog.homeblack.fr/p/bastion-warpgate-cles-ssh/</link><pubDate>Sat, 19 Sep 2026 16:06:02 +0200</pubDate><guid>https://blog.homeblack.fr/p/bastion-warpgate-cles-ssh/</guid><description>&lt;img src="https://blog.homeblack.fr/p/bastion-warpgate-cles-ssh/cover.png" alt="Featured image of post Mon bastion Warpgate : des clés SSH au simple alias" /&gt;&lt;p&gt;Dans mon terminal, accéder à un serveur tient en une seule ligne :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh dmz-nextcloud-01
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Pas d’adresse IP à chercher, pas de serveur de rebond à saisir, aucune clé personnelle à copier sur la cible. Pourtant, sous la surface, une chaîne complète s&amp;rsquo;active : OpenSSH, Traefik, mon bastion Warpgate, Authentik et un fichier de configuration alimenté par WarpgateSH.&lt;/p&gt;
&lt;p&gt;Dans &lt;a class="link" href="https://blog.homeblack.fr/p/warpgatesh-synchroniser-acces-ssh-warpgate/" &gt;le billet consacré à WarpgateSH&lt;/a&gt;, j’expliquais comment un script lourd à maintenir avait donné naissance à un outil dédié. Il me restait à présenter l&amp;rsquo;autre pan du système : le bastion lui-même, la gestion des clés SSH et son intégration dans mon système d’information — à l’échelle d’un homelab.&lt;/p&gt;
&lt;p&gt;Je vais remonter cette commande étape par étape, depuis mon terminal jusqu’au serveur. Pour en reproduire le principe, je partirais simplement d’une VM et d’une machine de test.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Les exemples s’appuient sur mon infrastructure et le code de WarpgateSH au 19 septembre 2026 ; la version de Warpgate vérifiée le 10 septembre était la 0.27.5. Les valeurs &lt;code&gt;example.net&lt;/code&gt;, &lt;code&gt;alice&lt;/code&gt; et &lt;code&gt;srv-demo&lt;/code&gt; sont fictives et réservées aux démonstrations.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="un-bastion-pour-quoi-faire-"&gt;Un bastion, pour quoi faire ?
&lt;/h2&gt;&lt;p&gt;Un bastion sert de point de passage contrôlé vers les ressources à administrer. Au lieu d’autoriser chaque poste à joindre directement chaque serveur, je centralise les flux sur un service unique. Il contrôle les identités, applique les droits et conserve des journaux d’accès.&lt;/p&gt;
&lt;p&gt;Warpgate remplit cette fonction chez moi. Je lui déclare les &lt;strong&gt;cibles&lt;/strong&gt; (les serveurs autorisés) et les &lt;strong&gt;rôles&lt;/strong&gt; (qui a le droit d&amp;rsquo;y accéder), tout en conservant mon client SSH habituel.&lt;/p&gt;
&lt;p&gt;À la différence d&amp;rsquo;un rebond classique via &lt;code&gt;ProxyJump&lt;/code&gt;, la connexion SSH directe depuis mon poste &lt;strong&gt;s’arrête au bastion&lt;/strong&gt;. Warpgate ouvre ensuite sa propre session vers la machine cible avec ses propres identifiants. Je n&amp;rsquo;ai donc jamais besoin d’y transférer mon agent SSH local.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.homeblack.fr/p/bastion-warpgate-cles-ssh/parcours-ssh.png"
width="1600"
height="850"
srcset="https://blog.homeblack.fr/p/bastion-warpgate-cles-ssh/parcours-ssh_hu_9e0bd21345f903d8.png 480w, https://blog.homeblack.fr/p/bastion-warpgate-cles-ssh/parcours-ssh_hu_e7022d0448aa93b1.png 1024w"
loading="lazy"
alt="Deux connexions distinctes : le poste s’authentifie auprès de Warpgate, puis Warpgate utilise sa clé pour joindre le serveur. Authentik valide l’identité humaine."
class="gallery-image"
data-flex-grow="188"
data-flex-basis="451px"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Vue logique : le réseau et le proxy de transport sont volontairement omis pour faire ressortir les deux authentifications.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Cette étanchéité constitue le point clé du montage : &lt;strong&gt;mon identité sur le bastion et le compte Linux de la cible sont deux entités totalement distinctes.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="clé-privée-clé-publique--qui-garde-quoi-"&gt;Clé privée, clé publique : qui garde quoi ?
&lt;/h2&gt;&lt;p&gt;Une paire de clés SSH repose sur deux éléments liés mathématiquement :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;la &lt;strong&gt;clé privée&lt;/strong&gt;, gardée secrète par le client qui prouve son identité ;&lt;/li&gt;
&lt;li&gt;la &lt;strong&gt;clé publique&lt;/strong&gt;, déposée sur le serveur qui vérifie cette identité.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Lors de la connexion, le client signe le défi d&amp;rsquo;authentification avec sa clé privée. Le serveur valide cette signature via la clé publique correspondante, sans que la clé privée ne transite sur le réseau.&lt;/p&gt;
&lt;p&gt;Sur un serveur OpenSSH standard, les clés autorisées sont listées dans &lt;code&gt;~/.ssh/authorized_keys&lt;/code&gt; au niveau du compte utilisateur Linux (&lt;code&gt;alice&lt;/code&gt;, &lt;code&gt;root&lt;/code&gt;, etc.).&lt;/p&gt;
&lt;p&gt;Pour illustrer la génération manuelle, je prends cette commande :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_bastion -C &lt;span class="s2"&gt;&amp;#34;poste-perso&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Cette commande produit deux fichiers (décrits dans le &lt;a class="link" href="https://man.openbsd.org/ssh-keygen" target="_blank" rel="noopener"
&gt;manuel &lt;code&gt;ssh-keygen&lt;/code&gt;&lt;/a&gt;) :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;~/.ssh/id_ed25519_bastion ← Fichier privé local
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;~/.ssh/id_ed25519_bastion.pub ← Clé publique à déployer côté serveur
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="chez-moi-les-clés-passent-par-lagent-bitwarden"&gt;Chez moi, les clés passent par l’agent Bitwarden
&lt;/h3&gt;&lt;p&gt;Je ne définis pas de mot de passe directement sur le fichier de clé : je délègue sa protection au coffre fort de Bitwarden et son utilisation à son &lt;strong&gt;agent SSH&lt;/strong&gt;. OpenSSH sollicite l’agent pour signer l’authentification sans lire de fichier privé sur mon disque. Le &lt;a class="link" href="https://bitwarden.com/help/ssh-agent/" target="_blank" rel="noopener"
&gt;guide SSH Bitwarden&lt;/a&gt; détaille sa mise en œuvre.&lt;/p&gt;
&lt;p&gt;Dans mon inventaire Ansible, la connexion pointe vers le socket de l&amp;rsquo;agent :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-o IdentityAgent=~/.bitwarden-ssh-agent.sock
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Une précision importante : stocker une clé non chiffrée dans Bitwarden protège sa copie dans le coffre, mais ne sécurise pas le fichier initial resté sur le disque s&amp;rsquo;il n&amp;rsquo;a pas été supprimé.&lt;/p&gt;
&lt;p&gt;Pour accéder au bastion via SSH, j’enregistre &lt;strong&gt;ma clé publique dans mon profil Warpgate&lt;/strong&gt;. Mon agent Bitwarden produit la signature ; le bastion la vérifie avec ma clé publique. Une fois authentifié, Warpgate initie la connexion vers la cible finale en utilisant &lt;strong&gt;sa propre clé privée&lt;/strong&gt;. J’autorise donc la clé publique du bastion dans le fichier &lt;code&gt;authorized_keys&lt;/code&gt; du serveur, et non ma clé personnelle.&lt;/p&gt;
&lt;p&gt;Ce flux par clé sert principalement aux comptes de service. Pour mes sessions d&amp;rsquo;administration courantes, je valide mon identité dans le navigateur via Authentik.&lt;/p&gt;
&lt;h3 id="ed25519-rsa-ecdsa--trois-algorithmes-distincts"&gt;Ed25519, RSA, ECDSA : trois algorithmes distincts
&lt;/h3&gt;&lt;p&gt;Ces appellations désignent des algorithmes utilisés pour les signatures d’authentification SSH :&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Ce que j’en retiens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ed25519&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;C’est mon choix pour les systèmes récents et le type de clé déployé par mon socle Ansible.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RSA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Je le réserverais aux besoins de rétrocompatibilité, avec des signatures SHA-2 comme l’expliquent les &lt;a class="link" href="https://www.openssh.com/txt/release-8.8" target="_blank" rel="noopener"
&gt;notes OpenSSH 8.8&lt;/a&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ECDSA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Variante basée sur des courbes elliptiques standardisées, disponible nativement dans OpenSSH.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;ed25519-sk&lt;/code&gt; / &lt;code&gt;ecdsa-sk&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Déclinaisons exploitant une clé de sécurité matérielle (FIDO2/YubiKey).&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Pour une nouvelle installation, j’écarte les algorithmes obsolètes comme DSA.&lt;/p&gt;
&lt;h3 id="et-la-clé-du-serveur-"&gt;Et la clé du serveur ?
&lt;/h3&gt;&lt;p&gt;SSH contrôle également l&amp;rsquo;authenticité de l&amp;rsquo;hôte distant pour éviter les interceptions.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;authorized_keys&lt;/code&gt; contrôle &lt;strong&gt;qui peut entrer&lt;/strong&gt; ;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;known_hosts&lt;/code&gt; enregistre &lt;strong&gt;les serveurs de confiance&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le poste client valide l&amp;rsquo;empreinte du bastion, puis le bastion valide celle du serveur cible. À la première connexion, je recommande de comparer l’empreinte proposée avec celle relevée depuis la console de la VM. Pour afficher cette dernière sur la cible, j’utiliserais cette commande :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;em&gt;Note d&amp;rsquo;usage : ne pas comparer cette empreinte revient à accepter aveuglément l&amp;rsquo;identité du serveur. C&amp;rsquo;est un point d&amp;rsquo;amélioration que je prévois d&amp;rsquo;automatiser entièrement dans mon lab.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="déploiement-dans-le-homelab"&gt;Déploiement dans le homelab
&lt;/h2&gt;&lt;p&gt;Mon bastion tourne sur une VM Proxmox dédiée (&lt;code&gt;dmz-bastion-01&lt;/code&gt;), provisionnée via Terraform (2 vCPU, 2 Gio de RAM, 30 Gio de stockage).&lt;/p&gt;
&lt;p&gt;Ansible déploie Docker et configure l&amp;rsquo;environnement. Voici un extrait de mon fichier Docker Compose :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;span class="lnt"&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;warpgate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;container_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;warpgate&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;restart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;unless-stopped&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s2"&gt;&amp;#34;2222:2222&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s2"&gt;&amp;#34;8888:8888&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;./data:/data&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Le port &lt;code&gt;2222&lt;/code&gt; reçoit les connexions SSH et le port &lt;code&gt;8888&lt;/code&gt; héberge l&amp;rsquo;interface d&amp;rsquo;administration HTTPS. Le dossier &lt;code&gt;./data&lt;/code&gt; conserve l&amp;rsquo;état persistant de l&amp;rsquo;application.&lt;/p&gt;
&lt;h2 id="traefik-en-frontal--un-nom-deux-parcours"&gt;Traefik en frontal : un nom, deux parcours
&lt;/h2&gt;&lt;p&gt;Traefik écoute sur &lt;code&gt;bastion.int.homeblack.fr&lt;/code&gt; et redirige les requêtes vers le bastion :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Portail HTTPS (SSO)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Navigateur → bastion.int.homeblack.fr:443
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → Traefik → Warpgate:8888 (HTTPS)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Session SSH
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;OpenSSH → bastion.int.homeblack.fr:2222
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → Traefik (Relais TCP) → Warpgate:2222
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → Serveur cible:22
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Traefik achemine le trafic vers Warpgate ; Warpgate gère les accès aux cibles.&lt;/strong&gt; Traefik ignore tout des machines finales et se contente de relayer les flux.&lt;/p&gt;
&lt;h3 id="reverse-proxy-https-pour-le-portail"&gt;Reverse proxy HTTPS pour le portail
&lt;/h3&gt;&lt;p&gt;Le portail web passe par un reverse proxy standard. L&amp;rsquo;inventaire Ansible définit le routage :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;bastion&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;bastion.int.homeblack.fr&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;upstream_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;https://10.60.0.17:8888&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Les liens d’approbation et le retour Authentik passent par le navigateur, via l’accès HTTPS du portail.&lt;/p&gt;
&lt;h3 id="relais-tcp-dédié-pour-le-flux-ssh"&gt;Relais TCP dédié pour le flux SSH
&lt;/h3&gt;&lt;p&gt;SSH ne traitant pas le protocole HTTP, Traefik utilise un &lt;strong&gt;routeur TCP&lt;/strong&gt;. La configuration statique déclare d&amp;rsquo;abord le point d&amp;rsquo;entrée :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;entryPoints&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;warpgate-ssh&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;:2222&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;La configuration dynamique (chargée via le provider &lt;code&gt;file&lt;/code&gt;) définit ensuite le routage :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;tcp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;routers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;warpgate-ssh&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;entryPoints&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;warpgate-ssh&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;HostSNI(`*`)&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;warpgate-ssh&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;warpgate-ssh&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;loadBalancer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;proxyProtocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;servers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;10.60.0.17:2222&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;La règle &lt;code&gt;HostSNI(`*`)&lt;/code&gt; accepte tous les flux TCP bruts sans interception TLS (détails dans la &lt;a class="link" href="https://doc.traefik.io/traefik/reference/routing-configuration/tcp/routing/rules-and-priority/" target="_blank" rel="noopener"
&gt;doc Traefik TCP&lt;/a&gt;). Le chiffrement SSH reste géré de bout en bout entre le client et Warpgate.&lt;/p&gt;
&lt;h3 id="conservation-des-ip-sources-avec-proxy-protocol"&gt;Conservation des IP sources avec PROXY Protocol
&lt;/h3&gt;&lt;p&gt;Lorsqu&amp;rsquo;un proxy transfère une connexion, le serveur final ne voit que l&amp;rsquo;IP du proxy.&lt;/p&gt;
&lt;p&gt;L’activation du &lt;strong&gt;PROXY protocol v2&lt;/strong&gt; permet à Traefik de transmettre l’IP d’origine du client dans un en-tête ajouté au début du flux TCP. Côté Warpgate (&lt;code&gt;ansible/inventory/group_vars/bastion.yml&lt;/code&gt;) :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;warpgate_ssh_external_host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;bastion.int.homeblack.fr&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;warpgate_ssh_external_port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2222&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;warpgate_ssh_proxy_protocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;warpgate_ssh_allowed_sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s2"&gt;&amp;#34;10.40.0.28/32&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# IP de Traefik autorisée&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="segmentation-réseau"&gt;Segmentation réseau
&lt;/h3&gt;&lt;p&gt;Dans mon architecture, Traefik réside sur le réseau &lt;code&gt;TRUST&lt;/code&gt; et Warpgate sur le réseau &lt;code&gt;DMZ&lt;/code&gt;. Le flux traverse le pare-feu inter-vlan. Pour une nouvelle installation, je privilégierais un reverse proxy dans le même segment réseau que le service, afin d’éviter ce passage entre zones.&lt;/p&gt;
&lt;p&gt;J’intègre aussi le filtrage des accès directs depuis les postes à la mise en place du bastion, avec l’objectif de faire passer les sessions d’administration par Warpgate.&lt;/p&gt;
&lt;h2 id="comment-les-serveurs-valident-le-bastion"&gt;Comment les serveurs valident le bastion
&lt;/h2&gt;&lt;p&gt;Une fois authentifié sur Warpgate, ce dernier initie la connexion SSH vers le serveur cible. La cible doit donc connaître la clé publique du bastion.&lt;/p&gt;
&lt;p&gt;Mon rôle Ansible &lt;code&gt;ssh-keys&lt;/code&gt; déploie automatiquement la clé publique du bastion (&lt;code&gt;warpgate-target&lt;/code&gt;) dans le fichier &lt;code&gt;authorized_keys&lt;/code&gt; du compte système distant :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Deploy authorized_keys&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ansible.posix.authorized_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{{ ssh_user }}&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;present&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{{ item }}&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{{ ssh_public_keys }}&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Côté Warpgate, les paramètres de la cible (IP, port, utilisateur Linux) sont injectés via Terraform.&lt;/p&gt;
&lt;p&gt;Pour me connecter à Nextcloud, la commande sous-jacente générée équivaut à :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh -p &lt;span class="m"&gt;2222&lt;/span&gt; -l &lt;span class="s1"&gt;&amp;#39;gregory.narcin:dmz-nextcloud-01&amp;#39;&lt;/span&gt; bastion.int.homeblack.fr
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Le paramètre &lt;code&gt;-l&lt;/code&gt; transmet l&amp;rsquo;instruction : « Connecte l&amp;rsquo;utilisateur Warpgate &lt;code&gt;gregory.narcin&lt;/code&gt; à la cible &lt;code&gt;dmz-nextcloud-01&lt;/code&gt; ». Après validation des droits, Warpgate ouvre la session SSH vers la VM avec son propre compte d&amp;rsquo;administration.&lt;/p&gt;
&lt;h2 id="sso-avec-authentik-et-accès-automatisés"&gt;SSO avec Authentik et accès automatisés
&lt;/h2&gt;&lt;p&gt;J’utilise &lt;a class="link" href="https://blog.homeblack.fr/p/authentik-sso-homelab/" &gt;Authentik&lt;/a&gt; comme fournisseur d’identité via &lt;strong&gt;OIDC&lt;/strong&gt;, OpenID Connect, pour l’authentification unique (&lt;strong&gt;SSO&lt;/strong&gt;).&lt;/p&gt;
&lt;p&gt;Pour une connexion humaine, l’option &lt;strong&gt;In-browser auth&lt;/strong&gt; affiche une URL et un code de validation dans le terminal. J’ouvre le lien, compare le code affiché et approuve la demande après authentification auprès d’Authentik. OpenSSH attend cette validation via la méthode &lt;code&gt;keyboard-interactive&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Pour raccorder Warpgate à Authentik, je configure une application cliente et son URL de redirection, par exemple &lt;code&gt;https://bastion.example.net/@warpgate/api/sso/return&lt;/code&gt;. Je sélectionne ensuite &lt;em&gt;In-browser auth&lt;/em&gt; dans la politique d’authentification SSH de mon compte, comme l’explique le &lt;a class="link" href="https://warpgate.null.page/sso/" target="_blank" rel="noopener"
&gt;guide SSO Warpgate&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Je gère la correspondance des rôles avec Ansible :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;warpgate_oidc_roles_claim&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;groups&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;warpgate_oidc_role_mappings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;warpgate-operators&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ops&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Les appartenances aux groupes Authentik sont ainsi converties en rôles d’accès dans Warpgate.&lt;/p&gt;
&lt;p&gt;Pour mes automatisations Ansible et mes pipelines CI/CD, je garde des comptes de service avec des clés SSH dédiées, comme &lt;code&gt;svc-ansible&lt;/code&gt; : je ne veux pas qu’un traitement attende une validation dans mon navigateur.&lt;/p&gt;
&lt;h2 id="déclaration-des-cibles-avec-terraform"&gt;Déclaration des cibles avec Terraform
&lt;/h2&gt;&lt;p&gt;Je déclare mes cibles Warpgate avec Terraform pour éviter de les ajouter manuellement dans l’interface :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-hcl" data-lang="hcl"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;warpgate_target&amp;#34; &amp;#34;ssh&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; for_each&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;ssh_targets&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;key&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;ssh_options&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;ip_address&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;22&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;ssh_username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;root&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; allow_insecure_algos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;public_key_auth&lt;/span&gt; {}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Mon parcours de création suit cet ordre : &lt;strong&gt;VM Terraform → Cible Warpgate Terraform → Socle Ansible → Synchronisation du client&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;J’exclus le bastion lui-même de cette liste et conserve un accès d’urgence indépendant pour pouvoir intervenir si Warpgate tombe en panne.&lt;/p&gt;
&lt;h2 id="automatisation-des-alias-avec-warpgatesh"&gt;Automatisation des alias avec WarpgateSH
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://github.com/M0okz/warpgatesh" target="_blank" rel="noopener"
&gt;WarpgateSH&lt;/a&gt; interroge l’API de Warpgate pour générer automatiquement la configuration SSH locale et les alias associés.&lt;/p&gt;
&lt;p&gt;Après avoir installé la CLI (&lt;a class="link" href="https://github.com/M0okz/warpgatesh/blob/main/docs/getting-started.md" target="_blank" rel="noopener"
&gt;guide de démarrage&lt;/a&gt;), je configure un profil ; voici les commandes avec les valeurs de démonstration :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;warpgatesh profile add lab https://bastion.example.net
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;warpgatesh profile default lab
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;warpgatesh profile ssh-auth lab in-browser
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;warpgatesh agent install
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;warpgatesh sync
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;warpgatesh ls
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;L&amp;rsquo;outil ajoute un appel d&amp;rsquo;inclusion dans &lt;code&gt;~/.ssh/config&lt;/code&gt; :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Include ~/.ssh/warpgatesh/config
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Le fichier généré (&lt;code&gt;~/.ssh/warpgatesh/config&lt;/code&gt;) contient les entrées de ce type :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# Profile lab
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Host srv-demo srv-demo.lab
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; HostName &amp;#34;bastion.example.net&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Port 2222
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; User &amp;#34;alice:srv-demo&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; UserKnownHostsFile ~/.ssh/warpgatesh/known_hosts/lab
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; StrictHostKeyChecking yes
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; PreferredAuthentications keyboard-interactive
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; KbdInteractiveAuthentication yes
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; PasswordAuthentication no
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; PubkeyAuthentication no
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Explication des directives :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Host&lt;/code&gt; : définit l&amp;rsquo;alias court et l&amp;rsquo;alias qualifié.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HostName&lt;/code&gt; : pointe vers le bastion.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;User&lt;/code&gt; : passe le sélecteur &lt;code&gt;utilisateur:cible&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PreferredAuthentications&lt;/code&gt; : impose l&amp;rsquo;authentification interactive pour déclencher la validation SSO par navigateur.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Je peux ensuite utiliser la commande raccourcie :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh srv-demo
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="diagnostic-et-retours-dexpérience"&gt;Diagnostic et retours d&amp;rsquo;expérience
&lt;/h2&gt;&lt;p&gt;J’ai rencontré des incohérences entre mes alias SSH : certains forçaient le mode &lt;code&gt;keyboard-interactive&lt;/code&gt;, tandis que d’autres autorisaient aussi les clés publiques.&lt;/p&gt;
&lt;p&gt;Pour comprendre ce qu’OpenSSH utilise réellement, je consulte sa configuration effective avec &lt;code&gt;ssh -G&lt;/code&gt;, sans ouvrir de connexion :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh -G srv-demo &lt;span class="p"&gt;|&lt;/span&gt; awk &lt;span class="s1"&gt;&amp;#39;/^(hostname|port|user|preferredauthentications|pubkeyauthentication) /&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Je peux ainsi repérer les conflits avec d’éventuelles directives &lt;code&gt;Host *&lt;/code&gt; globales.&lt;/p&gt;
&lt;p&gt;Pour valider un montage similaire, je vérifierais quatre points :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;L’accès aux cibles autorisées.&lt;/li&gt;
&lt;li&gt;Le refus d’accès à une cible non attribuée.&lt;/li&gt;
&lt;li&gt;Le compte Linux obtenu à l’arrivée, avec &lt;code&gt;whoami&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;La présence de la session dans les traces d’audit de Warpgate.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="bilan"&gt;Bilan
&lt;/h2&gt;&lt;p&gt;Avec ce montage, je centralise mes accès SSH sans intervenir sur chaque serveur à l’arrivée ou au départ d’un utilisateur. Je garde mes accès automatisés séparés grâce aux comptes de service dédiés.&lt;/p&gt;
&lt;p&gt;En contrepartie, mon bastion devient un composant critique de l’infrastructure. Je dois sauvegarder régulièrement sa clé privée d’administration et ses données d’audit, et en restreindre strictement les accès. C’est bien ce qui se passe en coulisses, et même un peu plus… mais si je raconte tout ici, ce billet va finir avec un sommaire en trois volumes.&lt;/p&gt;
&lt;p&gt;Selon moi, le plus simple est d’avancer par étapes : je commencerais par valider le flux SSH sur une seule cible, puis j’intégrerais le SSO OIDC avant d’automatiser la gestion des cibles et des alias sur mon poste.&lt;/p&gt;</description></item></channel></rss>