# SSH - From your first key to remote development A series of 9 article(s), in reading order. # GitHub - Connectez votre compte en SSH et travaillez avec le protocole git@ > Sécurisez votre connexion GitHub ! Suivez ce guide pas à pas pour configurer vos clés SSH et utiliser le protocole git@ pour des opérations Git plus sûres et plus rapides. --- ![GitHub - Connectez votre compte en SSH et travaillez avec le protocole git@](/img/v2/github_tips.webp) **TL;DR** Cet article explique pourquoi se connecter à GitHub en SSH est plus sûr qu'en HTTPS, puis détaille la mise en place : générer une paire de clés ed25519 avec `ssh-keygen`, l'ajouter à l'agent SSH et coller la clé publique dans les paramètres SSH de GitHub. Il se termine par une commande `ssh -T git@github.com` pour vérifier que la connexion fonctionne, afin que vous puissiez utiliser le protocole `git@` avec `git clone`. Utiliser SSH plutôt que HTTPS pour se connecter à GitHub est plus sûr. En effet, SSH repose sur la cryptographie à clé publique. Cela rend un accès non autorisé beaucoup plus difficile qu'avec un mot de passe, qui peut être volé par phishing ou par force brute. De plus, HTTPS transmet votre nom d'utilisateur et votre mot de passe (chiffrés) sur le réseau, ce qui peut être intercepté lors d'une attaque de type Man-in-the-Middle (MITM). SSH, lui, ne transmet aucun mot de passe une fois la configuration initiale faite. Voyons comment ajouter une clé SSH et, dès maintenant, pouvoir travailler avec GitHub via le protocole `git@` avec `git clone`. Ajouter une clé SSH sur votre ordinateur et l'utiliser pour vous connecter à GitHub est assez simple. D'abord, lancez la commande ci-dessous sur votre machine. Remplacez `your_email@example.com` par l'adresse e-mail liée à votre compte GitHub existant. ```bash $ ssh-keygen -t ed25519 -C "your_email@example.com" ``` On vous demandera de saisir une *passphrase* ; ce n'est pas obligatoire, appuyez simplement sur `Enter`. Vous verrez alors quelque chose comme ceci dans votre console : ```bash Generating public/private ed25519 key pair. Enter file in which to save the key (~/.ssh/id_ed25519): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in ~/.ssh/id_ed25519 Your public key has been saved in ~/.ssh/id_ed25519.pub The key fingerprint is: SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx your_email@example.com The key's randomart image is: +--[ED25519 256]--+ | ( ...... ) | | ( ...... ) | | ( ...... ) | | ( ...... ) | +----[SHA256]-----+ ``` Ensuite, il faut ajouter la clé à votre agent SSH. Lancez simplement : ```bash $ eval "$(ssh-agent -s)" $ ssh-add ~/.ssh/id_ed25519 ``` Enfin, ajoutez la clé à GitHub en vous rendant sur . ![GitHub - Add SSH key](./images/ssh_add_key.webp) Donnez un titre clair, par exemple `Home computer`. Dans la zone de texte `Key`, vous devez coller votre clé **publique**. Regardez ce qui s'est affiché à l'écran juste avant, lors de l'exécution de la commande `ssh-keygen`. Le chemin de la clé publique y était indiqué ; par exemple, `Your public key has been saved in ~/.ssh/id_ed25519.pub`. Lancez donc `cat ~/.ssh/id_ed25519.pub` dans votre console Linux et vous obtiendrez la valeur de la clé. Copiez/collez cette ligne dans la page de paramètres GitHub puis cliquez sur le bouton `Add SSH key`. Enfin, si vous voulez tester que la connexion est bien établie, lancez `ssh -T git@github.com`. Vous devriez obtenir `Hi cavo789! You've successfully authenticated, but GitHub does not provide shell access.` (avec votre propre pseudo, bien sûr). Deux suites possibles : si vous avez déjà des repositories clonés en HTTPS, [Git - Some tips for your .gitconfig file](/blog/git-config) montre un réglage en une ligne pour forcer SSH globalement sans rien re-cloner ; et le même mécanisme de clé fonctionne pour votre serveur d'hébergement, voir [How to connect to your hosting server using SSH](/blog/connect-using-ssh-to-your-hosting-server). --- # Docker secrets : utiliser votre clé SSH pendant le build > Accédez en toute sécurité à des repositories Git privés pendant votre build Docker grâce aux clés SSH et aux secrets Docker. Avec des exemples complets de Dockerfile et de compose.yaml pour éviter que vos secrets ne finissent dans l'image finale. --- ![Docker secrets : utiliser votre clé SSH pendant le build](/img/v2/docker_secrets.webp) **TL;DR** Cet article montre comment faire un `git clone` d'un repository privé pendant un build Docker sans jamais inscrire la clé SSH dans l'image finale, en utilisant la fonctionnalité BuildKit `RUN --mount=type=secret` de Docker, déclarée dans `compose.yaml` et référencée dans le `Dockerfile`. Il couvre aussi le cas distinct du partage de votre clé SSH avec le *container* en cours d'exécution (et non l'image) via un volume bind-monté, pour travailler en continu avec `git pull`/`push` à l'intérieur. Il y a beaucoup d'articles sur Internet mais je n'ai pas trouvé celui qui m'aurait permis, sans une quantité impressionnante d'essais et d'erreurs, de trouver la solution. Voici donc un article de plus à ajouter à la longue liste : comment accéder à un projet privé stocké sur Github lors de la création d'une image Docker. Autrement dit, la clé SSH n'est pas stockée dans l'image. Docker utilisera juste votre clé lors de l'exécution du *layer* de récupération du projet (celui qui contient l'instruction `git clone`) et n'en gardera aucune trace ensuite. Mon cas d'usage : je veux construire une image Docker et pendant la phase de build, j'ai besoin de récupérer une copie d'un repository privé que j'ai mis sur github.com. *L'équivalent CI de ce problème — récupérer une **image Docker privée** plutôt qu'un repository privé — est traité dans [GitLab - Using Docker private images](/blog/gitlab-using-private-images).* Quand j'accède au container, le projet sera bien présent mais je n'ai, nulle part dans mon image, de copie de ma clé SSH. Je ne pourrai donc pas lancer un `git pull` par exemple, puisque plus aucune authentification n'est possible. ## Aperçu {#preview} ![Vous êtes authentifié](./images/authenticated.webp) C'est la preuve que la technique fonctionne : la clé SSH a servi à s'authentifier auprès de GitHub pendant le build — sans jamais être inscrite dans l'image finale. ## Pourquoi ça fonctionne {#why-it-works} * Le `RUN --mount=type=secret` de Docker monte le secret uniquement pendant la durée de ce layer de build — il n'est jamais écrit dans un layer qui se retrouve dans l'image finale. * Copier votre clé SSH dans l'image (ou coder en dur un token) est une grave erreur de conception : n'importe qui lisant le Dockerfile, ou démarrant une session bash interactive pour lancer `printenv` ou parcourir les dossiers `.ssh`/`.git`, la trouvera. * Même un secret supprimé dans un layer ultérieur peut survivre : des outils comme [SecretScanner](https://github.com/deepfence/SecretScanner) scannent en profondeur, layer par layer, et le retrouveront s'il a été écrit sur le disque dans un layer précédent. *Le même raisonnement s'applique à votre historique git : [Git - pre-commit-hooks](/blog/git-precommit) liste des hooks qui bloquent un commit dès qu'un identifiant est détecté.* ## Quelle clé utiliser {#which-key-to-use} > **Caution:** > > Cette partie est l'une des plus importantes. D'abord, bien sûr, vous devez déjà avoir créé une clé SSH (voyez mon article [Github - Connectez votre compte en SSH et commencez à travailler avec le protocole git@](/blog/github-connect-using-ssh) si besoin). > > Ensuite, vous devez savoir quel protocole vous avez utilisé et c'est vraiment important. Votre clé est-elle stockée dans un fichier appelé `id_ed25519`, `id_rsa` ou autre chose ? Vous seul le savez. > > Lancez simplement `ls -alh ~/.ssh` pour obtenir la liste de vos clés : > > ![Mes propres clés](./images/ssh_keys.webp) > > J'utilise deux clés différentes comme vous pouvez le voir : `id_ed25519` et `id_rsa`. Les fichiers avec l'extension `.pub` sont les clés publiques ; ceux sans extension sont les clés privées. Il est important de savoir quelle clé a été utilisée pour lier votre profil Github. Vous pouvez simplement afficher le fichier dans votre console, par exemple `cat ~/.ssh/id_ed25519.pub` ou `cat ~/.ssh/id_rsa.pub` (le fichier de clé publique) et regarder la fin : l'email mentionné est-il celui que vous utilisez sur Github.com ? Si oui, vous avez probablement identifié la bonne clé. Dans mon cas, la clé que j'utilise pour github.com est `id_ed25519`, je vais donc utiliser celle-là dans le chapitre suivant. ## Création des fichiers {#creating-files} Nous aurons besoin de deux fichiers : `compose.yaml` et `Dockerfile`. ### compose.yaml {#composeyaml} Voici le contenu de mon `compose.yaml` : **compose.yaml** ```yaml services: app: build: context: . dockerfile: Dockerfile // highlight-next-line secrets: // highlight-next-line - my_ssh_key args: // highlight-next-line - KEY_NAME=id_ed25519 // highlight-next-line secrets: // highlight-next-line my_ssh_key: // highlight-next-line file: ${HOME}/.ssh/id_ed25519 ``` Comme vous pouvez le voir, il y a cinq lignes spécifiques. Dans notre entrée `services -> app --> build`, nous devons ajouter `secrets` et le nom d'un (ou plusieurs) secrets. J'ai aussi ajouté un argument appelé `KEY_NAME` simplement parce que je dois indiquer à Docker quelle clé j'utilise (est-ce `id_ed25519`, `id_rsa` ou une autre). Les secrets doivent être définis au même niveau d'indentation que `services` et la notation est celle-ci : **compose.yaml** ```yaml secrets: a_secret_name: file: existing_file ``` Vous pouvez mettre ce que vous voulez pour `a_secret_name` ; par exemple, `my_ssh_key`. > **docker compose config:** > > Vous pouvez, si vous le souhaitez, lancer `docker compose config` pour vérifier que votre fichier est correct. Vous y verrez aussi le path complet de la clé utilisée. > > ![Docker compose config](./images/config.webp) ### Dockerfile {#dockerfile} Il est temps de créer notre deuxième fichier, `Dockerfile` : **Dockerfile** ```docker FROM alpine:3.19 ARG KEY_NAME="id_rsa" USER root RUN apk add --no-cache bash ca-certificates git openssh-client // highlight-next-line RUN mkdir -p -m 0700 /root/.ssh && ssh-keyscan "github.com" >> /root/.ssh/known_hosts // highlight-next-line RUN --mount=type=secret,id=my_ssh_key,dst=/root/.ssh/${KEY_NAME} \ // highlight-next-line mkdir -p /app && cd /app \ // highlight-next-line && ssh -T git@github.com || true \ // highlight-next-line && git clone git@github.com:cavo789/my_private_repo.git WORKDIR /app # Keep the container running ENTRYPOINT ["tail", "-f", "/dev/null"] ``` > **N'oubliez pas de remplacer cavo789/my\_private\_repo.git par un de vos propres repositories:** Comme vous pouvez le voir, nous définissons `KEY_NAME="id_rsa"` comme valeur par défaut (mais elle sera écrasée par notre déclaration dans le fichier yaml) puis, plus loin, nous créons le dossier `/root/.ssh` et nous ajoutons `github.com` à la liste des hosts connus. Le plus important vient ensuite. Nous devons utiliser la syntaxe `RUN --mount=type=secret` pour indiquer à Docker que ce layer va utiliser un secret Docker. Nous devons fournir le nom de notre secret (défini dans notre fichier yaml, ici `my_ssh_key`) puis définir où ce secret doit être stocké **pendant ce layer**. Notre variable `KEY_NAME` trouve son utilité ici : notre clé SSH était `id_ed25519` et c'est la valeur de la variable `KEY_NAME`. Donc, en bref, `--mount=type=secret,id=my_ssh_key,dst=/root/.ssh/${KEY_NAME}` sera traduit en `--mount=type=secret,id=my_ssh_key,dst=/root/.ssh/id_ed25519`. Pendant ce layer donc, notre clé SSH locale sera enregistrée dans `/root/.ssh/id_ed25519`. Les autres lignes de ce layer `RUN` vont d'abord créer un dossier `/app`, s'y placer, puis lancer `ssh -T git@github.com` juste pour le débogage (nous nous attendons à voir à l'écran `Hi your_name! You've successfully authenticated`) et enfin nous clonons notre repository privé en SSH. ## Créer l'image et entrer dans le container {#create-the-image-and-jump-in-the-container} Bon, vous avez donc créé les deux fichiers sur votre disque dur. Lancez `docker compose --progress plain build --no-cache` dans votre console pour construire l'image en activant le mode verbeux. Comme vous pouvez le voir, nous pouvons confirmer que notre clé SSH a bien été partagée pendant le build — c'est la sortie « You're authenticated » montrée en haut de cet article. Vous allez maintenant créer le container, en lançant `docker compose up --detach`. ![Le container est en cours de création](./images/container_created.webp) Maintenant, juste pour vérifier, nous pouvons entrer dans le container en lançant `docker compose exec app /bin/bash`. ![Démarrage d'une console interactive](./images/bash_session.webp) Nous pouvons vérifier que notre clé n'a pas été stockée dans l'image en lançant `ls -alh /root/.ssh/` ![Aucune clé dans le dossier .ssh](./images/root_ssh_folder.webp) On peut aussi vérifier autrement : aller dans le dossier de notre projet et lancer `git pull` échouera avec l'erreur ci-dessous : ```bash git@github.com: Permission denied (publickey). fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists. ``` Et c'est exactement ce qu'on attend : notre clé ne fait pas partie de l'image, donc on ne peut plus se connecter à Github. ## Partager aussi vos clés avec le container {#sharing-your-keys-also-with-the-container} Bon, nous avons vu comment partager la clé SSH avec Docker mais uniquement pendant la phase de build. Comme vu plus haut, entrer dans le container et lancer `git pull` a échoué ; exactement ce que nous voulions dans le chapitre précédent. Mais maintenant, si nous avons besoin de partager notre clé aussi avec le container pour pouvoir travailler sur le projet, le mettre à jour et pousser des changements / récupérer des modifications, comment faire ? Éditez le fichier `compose.yaml` et ajoutez ces deux lignes : **compose.yaml** ```yaml services: app: build: context: . dockerfile: Dockerfile secrets: - my_ssh_key args: - KEY_NAME=id_ed25519 // highlight-next-line volumes: // highlight-next-line - ${HOME}/.ssh:/root/.ssh secrets: my_ssh_key: file: ${HOME}/.ssh/id_ed25519 ``` Puis recréez le container en lançant `docker compose up --detach`. Entrez dans le container avec `docker compose exec app /bin/bash` et allez dans le dossier de votre projet. Maintenant, en lançant `git pull`, vous verrez que ça fonctionne. Pourquoi ? Parce que vos clés SSH font maintenant partie du container, comme on peut le voir en lançant `ls -alh /root/.ssh` : ![Les clés font maintenant partie du container](./images/container_ssh_keys.webp) > **Les clés ne sont pas stockées dans l'image, même quand vous les avez partagées avec le container:** > > Pour être clair : la notion de volume que nous venons de mettre en place concerne le container et non l'image. Autrement dit, les clés SSH ne sont pas stockées dans l'image Docker. Vous pourriez la donner à quelqu'un d'autre (par exemple en sauvegardant l'image sur Docker Hub) ; vos clés n'y seront pas et resteront sur votre ordinateur. Votre image et vos secrets sont en sécurité. > > Ensuite, quand l'utilisateur lance `docker compose up --detach` avec le fichier `compose.yaml` contenant les deux lignes que nous venons d'ajouter (celles pour ajouter le volume) ; ce sont ses clés, sur son ordinateur, pas les vôtres. > **Faites attention à l'utilisateur qui tourne dans votre container:** > > Dans notre exemple ici, l'utilisateur par défaut est `root`, comme on peut le voir en entrant dans le container et en lançant `whoami`. > > ![Whoami](./images/whoami.webp) > > Donc, quand nous avons lancé `git pull`, c'était sous `root`. C'est pour cela que nous avons monté notre volume comme ceci : > > **compose.yaml** > > ```yaml > services: > app: > [...] > // highlight-next-line > volumes: > // highlight-next-line > - ${HOME}/.ssh:/root/.ssh > ``` > > Imaginez que l'utilisateur courant soit `christophe`. Dans ce cas, le montage devrait se faire comme ceci : > > **compose.yaml** > > ```yaml > services: > app: > [...] > // highlight-next-line > volumes: > // highlight-next-line > - ${HOME}/.ssh:/christophe/.ssh > ``` ## Conclusion {#conclusion} Deux besoins différents, deux mécanismes différents : un **secret de build** (`--mount=type=secret`) qui ne touche jamais l'image finale, pour des opérations ponctuelles comme cloner un repo privé pendant `docker build` ; et un **volume bind-monté**, pour quand le container en cours d'exécution a besoin de votre clé de façon continue pour `git pull`/`push`. Choisissez le premier par défaut, et n'ajoutez le second que quand vous devez réellement travailler depuis l'intérieur du container. --- # Utiliser sftp en ligne de commande, avec ou sans proxy > Connectez-vous à des serveurs SFTP depuis la ligne de commande Linux. Astuces pour automatiser avec sshpass, définir un port personnalisé et configurer l'accès via un serveur proxy. --- ![Utiliser sftp en ligne de commande, avec ou sans proxy](/img/v2/sftp.webp) **TL;DR** Cet article couvre la ligne de commande `sftp` : se connecter avec `sftp user@host`, automatiser l'authentification avec `sshpass`, faire passer la connexion par un proxy HTTP via `-o ProxyCommand='/usr/bin/nc --proxy-type http --proxy ...'`, utiliser un port personnalisé avec `-P`, et scripter des commandes distantes (comme télécharger tous les fichiers `.zip`) avec un here-document passé à `sftp`. Après mon article [SSH - Launch a terminal on your session without having to authenticate yourself](/blog/linux-ssh-scp), c'était évident : à côté des commandes `ssh` et `scp`, j'avais oublié `sftp`. Dans cet article, nous allons voir comment ouvrir une connexion SFTP vers un serveur distant, depuis la ligne de commande. Dans la deuxième partie, nous verrons aussi comment configurer la connexion sftp pour passer par un serveur proxy. *Pour les mêmes opérations depuis un client graphique sous Windows, voyez [WinSCP - Synchronize host and remote](/blog/winscp-synchronize-both) et [WinSCP - Download files with specific extension recursively](/blog/winscp-download-recursively-files).* ## Comment ouvrir une connexion sftp {#how-to-run-an-sftp-connection} Le plus simple est `sftp @` donc, si vous devez vous connecter au serveur ayant l'IP `1.2.3.4` avec l'utilisateur `christophe`, la commande à lancer est simplement `sftp christophe@1.2.3.4`. On vous demandera votre mot de passe avant de vous connecter au serveur. Si vous avez besoin d'automatiser, vous pouvez installer `sshpass` avec `sudo apt-get update && sudo apt-get install sshpass`. > **Note:** > > L'utilitaire Linux sshpass est utilisé par le script pour permettre une connexion SFTP sans devoir manipuler le mot de passe en clair. [Plus d'infos](https://www.redhat.com/sysadmin/ssh-automation-sshpass) Imaginons que votre utilisateur soit `christophe`, votre mot de passe `p@ssword` et l'IP du serveur `1.2.3.4`, vous pouvez alors vous connecter ainsi : `SSHPASS="p@ssword" sshpass -e sftp christophe@1.2.3.4`. Ci-dessus, nous créons une variable système temporaire appelée `SSHPASS` contenant notre mot de passe en clair, puis nous lançons `sshpass -e` suivi de notre commande sftp, donc `sftp @`. ## Utiliser un serveur proxy {#using-a-proxy-server} Passer par un proxy n'est pas si intuitif. Le flag à utiliser est `-o ProxyCommand=''` avec une commande spécifique. La commande est `/usr/bin/nc --proxy-type http --proxy PROXY:PORT %h %p` où : * `proxy-type` doit être initialisé à `http` ou `https` selon votre proxy. * `--proxy` doit être défini avec le nom de domaine du proxy (p.ex. `my.proxy.be`) et éventuellement suivi de `:8080`, c'est-à-dire le port à utiliser * ensuite `%h %p` doit faire partie de la commande * `%h` est un placeholder qui sera remplacé par le nom d'hôte du serveur SFTP auquel vous tentez de vous connecter. * `%p` est un autre placeholder qui sera remplacé par le numéro de port du serveur SFTP. La commande finale ressemblera à ceci : ```bash $ sftp -o ProxyCommand='/usr/bin/nc --proxy-type http --proxy my.proxy.be:8080 %h %p' christophe@1.2.3.4 ``` Et, si vous voulez utiliser `sshpass` : ```bash $ `SSHPASS="p@ssword" sshpass -e sftp -o ProxyCommand='/usr/bin/nc --proxy-type http --proxy my.proxy.be:8080 %h %p' christophe@1.2.3.4` ``` ## Définir le numéro de port du serveur SFTP {#setting-the-port-number-to-use-for-the-sftp-server} Si votre serveur SFTP n'écoute pas sur le port `22`, vous devrez préciser le numéro de port avec le flag `-P` : `sftp @ -P `. ## Lancer des commandes sur le serveur SFTP puis quitter {#running-commands-on-the-sftp-server-then-exit} Imaginez que vous souhaitiez récupérer des fichiers ZIP depuis votre serveur SFTP. Comme déjà vu, avec `sshpass` nous pouvons contourner l'authentification. Il suffit de s'assurer qu'une variable `SSHPASS` existe juste avant d'appeler la commande `sshpass`. Ensuite, pour pouvoir automatiser des commandes sur le serveur SFTP, nous pouvons utiliser la syntaxe *here-document*. L'allure générale sera celle-ci : ```bash ( SSHPASS="" sshpass -e sftp @ << ! ! ) ``` Donc, si votre utilisateur est `christophe`, votre mot de passe `p@ssword` et l'IP du serveur `1.2.3.4`, nous pouvons nous connecter au serveur et, par exemple, aller dans un dossier appelé `input_folder`, afficher la liste des fichiers / dossiers et, enfin, télécharger tous les fichiers zip présents dans le dossier : ```bash ( SSHPASS="p@ssword" sshpass -e sftp christophe@1.2.3.4 << ! cd input_folder ls -alh get *.zip ! ) ``` --- # SSH - Autosuggestions avec ZSH > Obtenez des autosuggestions SSH instantanées dans ZSH. Apprenez à installer le plugin zsh-ssh-config-suggestions et à afficher tous les alias de votre ~/.ssh/config avec un simple appui sur TAB. --- ![SSH - Autosuggestions avec ZSH](/img/v2/ssh.webp) **TL;DR** Arrêtez de mémoriser les IP de serveurs et les commandes SSH. Cet article vous montre comment fluidifier votre workflow SSH avec `zsh-ssh-config-suggestions`, un plugin ZSH qui fournit des autosuggestions pour vos connexions SSH. En s'appuyant sur votre fichier `~/.ssh/config`, ce plugin vous permet de simplement taper `ssh ` et d'appuyer sur Tab pour voir la liste de tous les alias de serveurs configurés. Découvrez comment installer et configurer ce plugin bien pratique pour vous connecter à vos serveurs plus vite et plus efficacement. Il y a quelques semaines, j'ai publié un article [SSH - Launch a terminal on your session without having to authenticate yourself](/blog/linux-ssh-scp#using-the-config-file) à propos de la commande `ssh` sous Linux. Je suis presque sûr que, comme moi, vous êtes fatigué de taper des lignes de commande comme `ssh christophe@1.2.3.4` pour démarrer une connexion SSH parce que... vous savez, vous n'avez pas besoin de vous connecter sur un serveur ; non, vous avez besoin de vous connecter au serveur où tourne l'application ; vous connaissez le nom de l'application *MyAmazingApp* mais certainement pas le nom du serveur ni son IP. C'est mon cas en tout cas. Du coup, je dois me connecter à mon coffre-fort, dans lequel je liste toutes les informations sur les applications, les noms de serveurs, les identifiants à utiliser, ... Ce serait plutôt cool de lancer `ssh MyAmazingApp` non ? *Deux articles voisins : [How to connect to your hosting server using SSH](/blog/connect-using-ssh-to-your-hosting-server) pour remplir ce `~/.ssh/config` au départ, et [Master your ssh command and select the host from a list](/blog/ssh-with-fuzzy-finder) quand l'autocomplétion ne suffit plus — passé la cinquantaine d'hosts, une liste filtrable vaut mieux que `TAB`.* Voici le résultat : tapez `ssh ` (avec l'espace à la fin) et appuyez sur `TAB` pour obtenir tous les alias de votre fichier `~/.ssh/config`, en autosuggestion. ![Utilisation de ssh-config-suggestions](./images/zsh-plugin-ssh-config-suggestions.gif) > **Vous devez ajouter un espace après `ssh`:** > > Pour que ça fonctionne, notez bien ceci : vous devez ajouter un espace après avoir tapé `ssh` et avant d'appuyer sur `tab`. ## Pourquoi ça fonctionne {#why-it-works} Le plugin lit les alias `Host` déjà déclarés dans votre fichier `~/.ssh/config` et les branche sur le système de complétion de ZSH — aucune liste séparée à maintenir, aucun coffre-fort à ouvrir, juste le fichier de configuration que vous avez déjà. Ce serait vraiment sympa de pouvoir lancer `ssh MyAmazingApp` et hop, je suis connecté sur le serveur. C'est là que le `~/.ssh/config` est si utile (référez-vous à cet [article](/blog/linux-ssh-scp#using-the-config-file)) mais on peut aller un cran plus loin : ce serait génial de taper `ssh` et, par magie, que Linux vous montre la liste des alias définis dans le fichier `~/.ssh/config`. Installons ça. ## Le voir en action avec Docker {#seeing-it-in-action-with-docker} Aucun serveur réel n'est jamais contacté par ce plugin — il lit uniquement les alias `Host` d'un fichier de configuration — ce qui en fait un test Docker en une commande parfaitement sûr. > **Pourquoi Docker d'abord ?:** > > Le Dockerfile ci-dessous installe Oh-My-Zsh et le plugin, l'active dans `~/.zshrc`, et écrit le > `~/.ssh/config` exact montré plus bas. Rien ne change sur votre propre machine, et rien n'essaie > de se connecter réellement où que ce soit. **Dockerfile** ```docker FROM ubuntu:24.04 ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update && \ apt-get install -y --no-install-recommends \ zsh \ git \ curl \ ca-certificates && \ rm -rf /var/lib/apt/lists/* # Install Oh-My-Zsh unattended (host framework required by the plugin) RUN sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" \ "" --unattended --keep-zshrc # Install zsh-ssh-config-suggestions and enable it in the default plugins=() list RUN git clone --depth=1 https://github.com/yngc0der/zsh-ssh-config-suggestions.git \ /root/.oh-my-zsh/custom/plugins/zsh-ssh-config-suggestions && \ sed -i 's/^plugins=(git)/plugins=(git zsh-ssh-config-suggestions)/' /root/.zshrc # The exact ~/.ssh/config from this article — no real hosts are ever # contacted, only the Host aliases are read for completion RUN mkdir -p /root/.ssh && \ printf '%s\n' \ 'Host MyAmazingApp_PROD' \ ' Hostname 1.2.3.4' \ ' User usr_app_prod' \ '' \ 'Host MyAmazingApp_TEST' \ ' Hostname 6.7.8.9' \ ' User usr_app_test' \ '' \ 'Host YourAmazingApp' \ ' Hostname 1.2.3.4' \ ' User you' \ '' \ 'Host LegacyApp' \ ' Hostname 1.2.3.4' \ ' User legacy' \ > /root/.ssh/config && \ chmod 600 /root/.ssh/config # Signal clearly that this shell runs inside a container RUN echo "PROMPT='%B%F{yellow}🐳 %F{cyan}%n%f%b %~ %# '" >> /root/.zshrc CMD ["/bin/zsh", "-i", "-l"] ``` Buildez-le et lancez-le : ```bash $ docker build -t ssh-suggest-demo . [+] Building 20.7s (8/8) FINISHED ✔ exporting to image $ docker run --rm -it ssh-suggest-demo 🐳 root ~ # ssh ``` Tapez `ssh ` (avec l'espace à la fin) et appuyez sur `TAB` — les mêmes quatre alias de la section « Utilisez-le » ci-dessous (`MyAmazingApp_PROD`, `MyAmazingApp_TEST`, `YourAmazingApp`, `LegacyApp`) apparaissent immédiatement, lus directement depuis le `~/.ssh/config` préparé dans l'image. ## Installation du plugin zsh-ssh-config-suggestions {#installation-of-the-zsh-ssh-config-suggestions-plugin} On suppose ici qu'Oh-My-Zsh est déjà installé (voir [How to install Oh-My-ZSH](/blog/zsh-install) si ce n'est pas le cas). Clonez simplement le repository officiel en lançant cette commande : `git clone https://github.com/yngc0der/zsh-ssh-config-suggestions.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-ssh-config-suggestions`. Vous obtiendrez une copie locale du plugin dans votre dossier de plugins `Oh-my-zsh`. Éditez ensuite le fichier `~/.zshrc`, cherchez `plugins=(` et ajoutez `zsh-ssh-config-suggestions` à la liste. Vous aurez par exemple quelque chose comme ceci : **\~/.zshrc** ```bash plugins=( git # ... your existing plugins zsh-ssh-config-suggestions ) ``` Fermez votre console, ouvrez-en une nouvelle (ou lancez `source ~/.zshrc` pour charger votre modification) et c'est terminé. ## Utilisez-le {#use-it} Imaginez que j'aie ce contenu dans mon `~/.ssh/config` : **\~/.ssh/config** ``` Host MyAmazingApp_PROD Hostname 1.2.3.4 User usr_app_prod Host MyAmazingApp_TEST Hostname 6.7.8.9 User usr_app_test Host YourAmazingApp Hostname 1.2.3.4 User you Host LegacyApp Hostname 1.2.3.4 User legacy ``` Maintenant, je n'ai vraiment plus besoin de me souvenir de quoi que ce soit et je n'ai même plus besoin de connaître le nom des alias ! Je dois juste taper (c'est important) : `ssh ` suivi de `TAB` et j'obtiens les mêmes suggestions que celles déjà montrées en haut de cet article. Comme vous pouvez le voir, le système affiche la liste des hosts définis dans mon fichier de configuration ! Je peux alors éditer le fichier, ajouter une nouvelle application et hop, la prochaine fois, j'aurai son nom dans la liste. Je n'ai plus besoin de me connecter à mon coffre-fort. Sympa non ? Ce n'est qu'un plugin parmi beaucoup d'autres ; si votre `~/.zshrc` commence à grossir avec ce genre de personnalisations, jetez un œil à [Beyond the Monolith - Organizing Your ZSH Workflow Like a Pro](/blog/modular-zsh-workflow) pour le garder maintenable. --- # SSH - Lancer un terminal sur votre session sans avoir à vous authentifier > Arrêtez de taper votre mot de passe SSH ! Apprenez à utiliser les clés SSH et `ssh-copy-id` pour lancer instantanément une session terminal Linux sans avoir à vous authentifier chaque fois. --- ![SSH - Lancer un terminal sur votre session sans avoir à vous authentifier](/img/v2/ssh.webp) **TL;DR** Ce guide explique comment mettre en place une connexion SSH sans mot de passe vers vos serveurs Linux. Vous allez apprendre à générer une paire de clés SSH, à utiliser `ssh-copy-id` pour transférer votre clé publique vers le serveur de façon sécurisée, et à vous connecter sans mot de passe. Le tutoriel aborde aussi `scp` pour les transferts de fichiers sécurisés et montre comment créer des raccourcis pratiques pour vos connexions grâce à un fichier `~/.ssh/config`, ce qui permet de se connecter avec un simple alias. Imaginez que vous devez régulièrement vous connecter à votre serveur Linux : vous devez lancer un outil comme Putty, saisir votre login, votre mot de passe, etc. et effectuer plusieurs opérations avant d'accéder au terminal. Si votre mot de passe n'est pas enregistré dans la configuration de Putty, vous devrez lancer un autre outil comme un coffre-fort de mots de passe ; bref, c'est pénible. Dans cet article, nous allons voir comment s'authentifier une fois pour toutes sur le serveur à l'aide d'une clé SSH. *Deux articles qui prolongent celui-ci : [How to connect to your hosting server using SSH](/blog/connect-using-ssh-to-your-hosting-server), appliqué à un vrai compte d'hébergement, et [Master your ssh command and select the host from a list](/blog/ssh-with-fuzzy-finder) quand votre `~/.ssh/config` contient une dizaine d'alias.* > **Information:** > > Pour illustrer cet article, partons du principe que `christophe` est le nom d'utilisateur distant (c'est le nom d'utilisateur sur le serveur, différent de votre nom d'utilisateur local) et que `my_blog.be` est l'adresse du serveur distant. > > Pensez à remplacer ces deux constantes par les vôtres ;-) ## TLDR {#tldr} Créez une clé SSH, copiez-la sur le serveur distant et lancez une connexion SSH. Les étapes 1 et 2 ne se font qu'une seule fois. **Les étapes à lancer dans votre console :** 1. `ssh-keygen -t ed25519 -C "christophe@my_blog.be" -f ~/.ssh/id_ed25519_my_blog` 2. `ssh-copy-id -i ~/.ssh/id_ed25519_my_blog christophe@my_blog.be` 3. `ssh -i ~/.ssh/id_ed25519_my_blog christophe@my_blog.be` ## En détail {#in-depth} ### 1. Créer la clé SSH {#1-creating-the-ssh-key} La première chose à faire, une seule fois, est de générer une clé SSH sur votre host pour le serveur distant. En lançant `ssh-keygen -t ed25519 -C "christophe@my_blog.be" -f ~/.ssh/id_ed25519_my_blog` (ou `ssh-keygen -t rsa -C "christophe@my_blog.be" -b 4096 -f ~/.ssh/id_rsa_my_blog` pour l'algorithme RSA) : 1. Vous allez créer une clé SSH pour l'utilisateur distant `christophe` sur le serveur `my_blog.be`. Vous obtiendrez une clé privée et une clé publique, 2. La clé sera enregistrée sur votre machine et le nom de fichier sera `~/.ssh/id_ed25519_my_blog` pour la clé privée (et `~/.ssh/id_ed25519_my_blog.pub` pour la publique), 3. La clé sera basée sur l'algorithme ed25519. ### 2. Copier la clé {#2-copying-the-key} Ceci fait, vous devez copier la clé SSH publique sur votre serveur distant. Pour cela, lancez `ssh-copy-id -i ~/.ssh/id_ed25519_my_blog christophe@my_blog.be`. Cela ne doit être fait qu'une seule fois. 1. Vous allez copier votre clé publique SSH locale `id_ed25519_my_blog` sur le serveur `my_blog.be`, 2. La clé sera ajoutée pour l'utilisateur distant `christophe`, 3. Vous devrez fournir le mot de passe de cet utilisateur lorsqu'il sera demandé. > **Information:** > > La commande `ssh-copy-id` ajoutera votre clé publique à la fin du fichier `~/.ssh/authorized_keys` sur le serveur. ### 3. Se connecter au serveur {#3-connect-to-the-server} Maintenant, tout est en place : vous pouvez lancer `ssh -i ~/.ssh/id_ed25519_my_blog christophe@my_blog.be` et cela devrait fonctionner puisque la clé publique `id_ed25519_my_blog` a déjà été copiée sur le serveur `my_blog.be` pour l'utilisateur `christophe`. > **Information:** > > Vous pouvez afficher ou éditer le contenu du fichier `~/.ssh/authorized_keys` en lançant `cat ~/.ssh/authorized_keys` (ou `vi ~/.ssh/authorized_keys`). Vous pourrez ainsi supprimer d'anciennes clés autorisées, par exemple. ## Copier des fichiers de votre host vers le serveur {#copying-files-from-your-host-to-the-server} Maintenant que nous pouvons nous connecter au serveur si facilement, nous pouvons par exemple copier des fichiers depuis notre host comme ceci : ```bash $ scp -i ~/.ssh/id_ed25519_my_blog local_file christophe@my_blog.be:/remote_file ``` Il faut d'abord indiquer notre fichier (ou dossier) local puis où le copier. ## Copier des fichiers du serveur vers votre host {#copy-files-from-the-server-to-your-host} Maintenant, dans l'autre sens, du serveur vers votre host : ```bash $ scp -i ~/.ssh/id_ed25519_my_blog christophe@my_blog.be:/remote_file local_file ``` ## Copier des dossiers {#copying-folders} Si vous devez copier un dossier, ajoutez simplement le flag récursif, c'est-à-dire `-r`. Par exemple, `scp -r christophe@my_blog.be:~/backup/ .` va copier tout le dossier `~/backup` du serveur dans mon dossier local courant. Vraiment pratique. ## Astuces {#tips} ### Lancer ssh depuis Microsoft Terminal {#running-ssh-from-microsoft-terminal} L'expérience SSH est bien, bien meilleure avec Microsoft Terminal qu'avec la vieille application Putty. Avec Microsoft Terminal, vous pouvez travailler en plein écran, avoir une jolie image de fond, utiliser une bien meilleure police (celle de Putty par défaut est affreuse), utiliser des onglets, ... Si vous avez déjà utilisé Putty, vous voyez ce que je veux dire. ### Utiliser le fichier config {#using-the-config-file} Comme nous l'avons vu, avec la commande `ssh` ou `scp`, nous devons préciser le nom d'utilisateur, le nom ou l'adresse IP du serveur et le nom du fichier contenant la clé privée. Ça fait beaucoup d'informations à fournir, non ? En plus, comme moi, je n'arrive pas à retenir toutes ces informations. Je veux juste me connecter à mon serveur d'applications, c'est tout ! Ce serait bien de pouvoir faire ssh my\_application et basta. Non ? C'est justement le but du fichier `~/.ssh/config` ! Pour me connecter à ce serveur, ma commande ssh ressemble à ceci : `ssh christophe@my_blog.be -i ~/.ssh/id_ed25519_my_blog`. Pas facile à retenir ! Créons un fichier `~/.ssh/config`. Si le fichier existe déjà, éditez-le : `code ~/.ssh/config`. Ajoutez ces lignes dans le fichier : **\~/.ssh/config** ``` Host my_app Hostname my_blog.be User christophe IdentityFile ~/.ssh/id_ed25519_my_blog ``` Enregistrez le fichier et retournez dans la console. Maintenant, il suffit de lancer `ssh my_app` pour établir la connexion. Plutôt simple, non ? Vous voulez en savoir plus ? Poursuivez votre lecture ici ## Pour aller plus loin {#going-further} Une fois que votre fichier `~/.ssh/config` commence à se remplir d'alias, deux articles complémentaires valent le coup d'œil : [SSH - Autosuggestions with ZSH](/blog/zsh-plugin-ssh-config-suggestions), pour ne plus jamais devoir retenir un alias, et [Utiliser sftp en ligne de commande](/blog/linux-sftp-cli) si vous avez besoin du pendant sftp de `scp`. --- # Comment se connecter à votre serveur d'hébergement en SSH > Apprenez à vous connecter à votre serveur d'hébergement en SSH avec une authentification par clé et un simple alias pour un accès facile. --- ![Comment se connecter à votre serveur d'hébergement en SSH](/img/v2/planethoster-using-ssh.webp) **TL;DR** Ce guide explique comment configurer SSH pour une authentification par clé, sans mot de passe, vers votre serveur d'hébergement. Il détaille la collecte des informations de connexion, la génération et la copie d'une clé publique SSH, et la création d'un alias dans `~/.ssh/config`. Résultat : un accès en une seule commande, comme `ssh planethoster`. Ce guide explique comment configurer SSH pour vous connecter à votre serveur d'hébergement via un alias court, sans devoir saisir un mot de passe à chaque connexion. Vous allez apprendre à rassembler les informations nécessaires, générer une paire de clés SSH, copier votre clé publique sur le serveur et configurer `~/.ssh/config` pour obtenir un raccourci pratique. Pour cet article, partons du principe que vous avez un compte d'hébergement PlanetHoster. À la fin de ce guide, vous pourrez vous connecter à votre serveur PlanetHoster avec une simple commande du type `ssh planethoster`. ## Le résultat {#the-result} Une fois la clé et l'alias `~/.ssh/config` en place (voir plus bas), démarrer une session tient en une commande — immédiat, sans demande de mot de passe : ![Succès](./images/success.webp) ## Pourquoi ça marche {#why-it-works} * Votre clé **publique** est copiée dans le fichier `~/.ssh/authorized_keys` du serveur — le serveur peut vérifier votre identité sans jamais voir de mot de passe. * Un court bloc dans `~/.ssh/config` regroupe l'host, le port, l'utilisateur et la clé sous un seul alias — `ssh planethoster` au lieu d'une commande à quatre options à retenir. * Une fois la clé en place, il n'y a plus rien à taper ni à retaper — le « mot de passe à chaque fois » évoqué en ouverture de cet article, c'est fini. ## Installation {#installation} **Vous devez obtenir les informations suivantes :** 1. Le nom d'utilisateur pour la connexion 2. Le nom du serveur (ou son adresse IP) 3. Le numéro de port à utiliser 4. Le mot de passe associé au compte utilisateur. Pour les trois premiers éléments, connectez-vous au [tableau de bord PlanetHoster](https://my.planethoster.com/v2/hosting-management/overview). Vous arrivez sur la page du tableau de bord. Dans la barre latérale gauche, allez dans **Hébergement Web --> Gestion du compte** et cliquez sur la flèche pour ouvrir la page : ![Ouvrir votre panneau de contrôle](./images/web_hosting.webp) En haut de [la page](https://mg.n0c.com/), vous trouverez les trois informations nécessaires : * Le *Current user* : le nom d'utilisateur à utiliser pour SSH. * Le nom de votre serveur (et son adresse IP). * Le port SSH à utiliser (probablement `5022`). ![La page de votre tableau de bord Planethoster](./images/planethoster_dashboard.webp) La quatrième information à rassembler est le mot de passe associé à votre compte utilisateur. *Si vous passez par un autre hébergeur, les étapes pour obtenir ces informations seront différentes. Reportez-vous à la documentation de votre fournisseur.* **Pour la suite de cet article, partons de ces valeurs :** 1. Utilisateur : `john_doe` 2. Nom du serveur : `node30-eu.n0c.com` 3. Numéro de port : `5022` 4. Mot de passe : `p@ssword` ### D'abord, essayez une connexion SSH manuelle {#first-just-try-with-a-manual-ssh-connection} Dans une console Linux (ou Windows Powershell), lancez la commande suivante : ```bash $ ssh -p 5022 john_doe@node30-eu.n0c.com ``` Un mot de passe vous sera demandé ; saisissez celui de votre compte. Si tout est correctement configuré, vous obtenez un shell. Vous serez dans le répertoire home de votre utilisateur (et non à la racine du système). Tapez `ls -al` pour lister les fichiers et `pwd` ou `whoami` pour confirmer que vous êtes bien dans votre répertoire home : ![Voir le répertoire racine via SSH](./images/ssh_logged_in.webp) > **Que faire en cas de timeout:** > > Si la commande `ssh` met longtemps puis se termine par un timeout, c'est probablement que vous utilisez des informations incorrectes, comme un mauvais nom d'utilisateur. Ne réessayez pas plusieurs fois d'affilée, sinon vous allez bloquer votre adresse IP : votre hébergeur peut détecter plusieurs tentatives de connexion échouées, les interpréter comme une menace de sécurité et bloquer votre IP. Si cela arrive, même avec la bonne commande, vous ne pourrez plus vous connecter à cause de l'IP blacklistée. > > Dans ce cas, il faudra attendre un certain temps (cela dépend de l'hébergeur). Le mieux, si vous êtes bloqué, est d'ouvrir un ticket de support et de demander le déblocage de votre IP. ### Créer une clé SSH pour se connecter à PlanetHoster {#create-an-ssh-key-to-connect-to-planethoster} À l'étape précédente, nous avons confirmé que la connexion au serveur fonctionne avec nos identifiants. Améliorons maintenant la connexion en mettant en place une authentification par clé SSH. Cela permettra de se connecter sans mot de passe par la suite. #### Créer une clé SSH {#create-an-ssh-key} Sur votre machine locale, lancez dans votre terminal une commande du type `ssh-keygen -t ed25519 -C "john_doe" -f ~/.ssh/id_ed25519_hosting`. *Le même mécanisme de clé est utilisé pour dialoguer avec GitHub, comme décrit dans [GitHub - Connect your account using SSH and start to work with git@ protocol](/blog/github-connect-using-ssh).* Cela crée une clé SSH privée nommée `~/.ssh/id_ed25519_hosting` (et la clé publique associée `~/.ssh/id_ed25519_hosting.pub`). Une passphrase vous sera demandée. Vous pouvez en définir une pour plus de sécurité, ou la laisser vide par confort (dans cet article, je n'utilise pas de passphrase). > **Information:** > > Curieux ? Lancez `cat ~/.ssh/id_ed25519_hosting.pub` pour voir le contenu de votre clé publique. La clé ressemblera à `ssh-ed25519 BASE64_STRING john_doe`. #### Copier votre clé sur le serveur {#copy-your-key-to-the-server} Pour pouvoir vous connecter sans mot de passe, vous devez copier votre clé publique sur le serveur. Lancez `ssh-copy-id -i ~/.ssh/id_ed25519_hosting -p 5022 john_doe@node30-eu.n0c.com` pour copier votre clé publique sur le serveur d'hébergement. Votre mot de passe vous sera demandé une dernière fois. ```bash $ ssh-copy-id -i ~/.ssh/id_ed25519_hosting -p 5022 john_doe@node30-eu.n0c.com /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/john_doe/.ssh/id_ed25519_hosting.pub" /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed /usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys john_doe@node30-eu.n0c.com's password: Number of key(s) added: 1 ``` Vérifiez que vous voyez bien un message indiquant que la ou les clés ont été ajoutées avec succès. Dès à présent, vous pourrez vous connecter sans fournir de mot de passe. Lancez la commande ci-dessous pour tester : ```bash $ ssh -i ~/.ssh/id_ed25519_hosting -p 5022 john_doe@node30-eu.n0c.com ``` Comme prévu, vous serez connecté immédiatement, sans demande de mot de passe, grâce à notre clé SSH. #### Simplifier la commande avec un fichier de configuration SSH {#simplify-the-command-with-an-ssh-config-file} La commande précédente est assez longue, non ? Peut-on la simplifier ? Oui ! Simplifions-la pour n'avoir qu'à taper `ssh planethoster`. Pour cela, lancez `vi ~/.ssh/config` (ou votre éditeur préféré) pour ouvrir votre fichier de configuration SSH. Si le fichier n'existe pas encore, créez-le. Ajoutez le bloc ci-dessous à la fin du fichier (pensez à adapter les valeurs à votre configuration) : **\~/.ssh/config** ``` Host planethoster HostName node30-eu.n0c.com User john_doe Port 5022 IdentityFile ~/.ssh/id_ed25519_hosting ``` Enregistrez et fermez le fichier (astuce : c'est `:w!` dans vi). Ensuite, définissez les bonnes permissions sur votre clé en lançant `chmod 0600 ~/.ssh/id_ed25519_hosting*`. Les fichiers de clés (privée et publique) ne seront ainsi accessibles que par vous. > **WARNING: UNPROTECTED PRIVATE KEY FILE!:** > > Si vous ne définissez pas les bonnes permissions avec `chmod`, vous obtiendrez une erreur comme celle-ci en tentant de vous connecter : > > ```text > @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ > @ WARNING: UNPROTECTED PRIVATE KEY FILE! @ > @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ > Permissions 0644 for '/home/john_doe/.ssh/id_ed25519_hosting' are too open. > ``` #### Établir une connexion SSH simplifiée {#establish-an-easy-ssh-connection} Désormais, pour démarrer une connexion SSH vers votre host, ouvrez un terminal et lancez `ssh planethoster` (ou l'alias que vous avez défini). Si tout est bien configuré, vous serez connecté **immédiatement** et **sans demande de mot de passe** — exactement le résultat montré en début d'article. ## Autres démos {#more-demos} ### Dépannage et astuces {#troubleshooting--tips} * Si une connexion échoue, lancez le client SSH en mode verbeux pour voir la sortie de debug : `ssh -vvv -p 5022 john_doe@node30-eu.n0c.com` (autrement dit, ajoutez `-vvv` comme option supplémentaire). * Si vous voyez une erreur `WARNING: UNPROTECTED PRIVATE KEY FILE!`, vérifiez les permissions locales de votre clé privée et utilisez `chmod 600` comme montré plus haut. * Si besoin, connectez-vous au serveur et vérifiez que votre clé est bien présente dans `~/.ssh/authorized_keys` et que `~/.ssh` et `authorized_keys` ont les bonnes permissions (lancez simplement `vi ~/.ssh/authorized_keys` pour ouvrir le fichier, l'éditer et supprimer les clés indésirables). ## Conclusion {#conclusion} Ce guide a montré comment mettre en place un accès SSH par clé vers un serveur d'hébergement et créer un raccourci `ssh planethoster`. Une fois que vous aurez accumulé une dizaine d'alias dans `~/.ssh/config`, les retenir deviendra le nouveau problème ; [Master your ssh command and select the host from a list](/blog/ssh-with-fuzzy-finder) les transforme en un sélecteur interactif. --- # Développement distant en SSH avec VSCode > Utilisez VS Code Remote - SSH : connectez-vous à vos serveurs de production, éditez et exécutez à distance en évitant les pièges classiques. --- ![Développement distant en SSH avec VSCode](/img/v2/vscode_ssh_dev.webp) **TL;DR** Cet article montre comment utiliser l'extension Remote - SSH de VS Code pour développer directement sur un serveur distant. Il détaille la mise en place des connexions, d'une simulation locale avec Docker jusqu'à un environnement de production, pour contourner les restrictions réseau et éviter les transferts de fichiers manuels. Résultat : édition et exécution de commandes directement sur l'host distant, avec toute la puissance de VS Code. Dans cet article, nous allons voir comment développer directement sur un serveur distant avec VS Code, en éditant les fichiers stockés sur le serveur sans garder de copie locale ni faire d'upload manuel. Mon cas d'usage était simple : je devais exécuter un script Python sur un serveur Linux qui pouvait accéder à [une base de données Oracle](/blog/docker-oracle-database-server). Mais mon environnement de développement local (mon ordinateur ou une VM Windows) ne pouvait pas joindre la base à cause de restrictions réseau. *Trois articles liés : [How to connect to your hosting server using SSH](/blog/connect-using-ssh-to-your-hosting-server) pour mettre en place la connexion par clé, sans mot de passe, sur laquelle repose cette extension ; [One Docker Image for Production and Devcontainers](/blog/docker-prod-devcontainer) pour l'approche inverse — amener l'environnement du serveur sur votre machine plutôt que le contraire ; et [Ouvrir un DevContainer sur un serveur distant avec VSCode Remote SSH](/blog/vscode-remote-ssh-proxyjump-devcontainer) pour l'étape suivante — ouvrir un devcontainer sur l'host distant afin d'atteindre des services inaccessibles depuis votre machine locale.* **Quick Jump:** [Le résultat en action](#the-result) | [Installation — simuler un serveur en local avec Docker](#installation--simulate-a-server-locally-with-docker) ## Le résultat {#the-result} Une fois l'extension Remote - SSH connectée (on voit ça plus bas), VS Code édite les fichiers et exécute les commandes directement sur l'host distant — pas d'upload, pas de copie locale : ![Travailler sur le serveur de production](./images/vscode_ssh_planethoster.webp) ## Pourquoi ça fonctionne {#why-it-works} * Aucun transfert de fichier manuel — l'éditeur lit et écrit directement sur le système de fichiers distant. * Le terminal intégré tourne sur l'host distant lui-même, exactement comme si vous étiez connecté en SSH. * Un seul outil pour éditer et exécuter — plus besoin d'une session SSH d'un côté et d'un client SFTP de l'autre. Entrons dans le vif du sujet : le développement distant avec VS Code et SSH. ## Installation — simuler un serveur en local avec Docker {#installation--simulate-a-server-locally-with-docker} On va d'abord simuler un serveur en local avec Docker, histoire d'apprendre le workflow sans risque. Ensuite, on refera les mêmes étapes sur un vrai serveur de production. Créons un container Docker qui jouera le rôle de serveur SSH Linux, pour s'entraîner à s'y connecter depuis VS Code. ### Créer le container Docker qui servira de serveur SSH {#create-the-docker-container-that-will-act-as-our-ssh-server} Créez un `Dockerfile` avec le contenu ci-dessous. **Project: /tmp/remote-ssh** > **Note:** > > Now, please run 'docker build -t ssh-server .' to build the Docker image then 'docker run -d -p 2222:22 --name remote-dev ssh-server' to create the container. **Dockerfile** ```docker FROM ubuntu:latest # Install the SSH server so we can use the `ssh` command to connect to the container # Also create the required `/var/run/sshd` directory RUN apt-get update \ && apt-get install -y --no-install-recommends openssh-server \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* \ && mkdir -p /var/run/sshd # Create our test user (christophe) and assign him a password (p@ssword) RUN useradd -rm -d /home/christophe -s /bin/bash christophe \ && echo 'christophe:p@ssword' | chpasswd # Do some changes to the ssh configuration like disabling authentication with the `root` user RUN sed -i 's/^#PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config \ && sed -i 's/^#PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config \ && sed -i 's/^#ChallengeResponseAuthentication.*/ChallengeResponseAuthentication no/' /etc/ssh/sshd_config \ && sed -i 's/^UsePAM.*/UsePAM no/' /etc/ssh/sshd_config \ && sed -i 's/^#ListenAddress.*/ListenAddress 0.0.0.0/' /etc/ssh/sshd_config \ && sed -i 's/^#Port.*/Port 22/' /etc/ssh/sshd_config EXPOSE 22 CMD ["/usr/sbin/sshd", "-D"] ``` Ensuite, il faut construire l'image Docker et créer le container. Pour le build, lancez `docker build -t ssh-server .`. Puis créez le container avec `docker run -d -p 2222 :22 --name remote-dev ssh-server`. ```bash $ docker build -t ssh-server . $ docker run -d -p 2222:22 --name remote-dev ssh-server ``` Le container joue le rôle de serveur SSH et (pour cette démo) crée un utilisateur `christophe` avec le mot de passe `p@ssword`. #### Tester notre container {#test-our-container} Testez le container en acceptant l'empreinte de l'host et en saisissant le mot de passe (`p@ssword`) quand il est demandé. ```bash $ ssh christophe@localhost -p 2222 The authenticity of host '[localhost]:2222 ([127.0.0.1]:2222)' can't be established. ECDSA key fingerprint is SHA256:/XRLXCojjc9ykoYiuM0aEhDKu5MyZuUU793NokTqxlI. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added '[localhost]:2222' (ECDSA) to the list of known hosts. christophe@localhost's password: ``` Une fois connecté, lancez des commandes comme `ls -alh`, `hostname` ou `whoami`. ![Jouer avec le terminal SSH](./images/ssh_terminal.webp) > **Tapez `exit` pour quitter le terminal et revenir sur votre host.:** Ce test rapide montre que le container fonctionne et qu'on peut s'y connecter en SSH. ## Autres démos {#more-demos} ### L'extension Remote - SSH {#the-remote---ssh-extension} Notre objectif : démarrer VS Code et éditer des fichiers directement sur le serveur. Pour ça, il faut installer l'extension [Remote - SSH](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-ssh) de Microsoft. ![Installer l'extension Remote - SSH](./images/installing_remote_ssh_extension.webp) Une fois installée, un nouveau bouton apparaît sur la gauche : **Remote Explorer**. Cliquez dessus et, dans le nouveau panneau, sélectionnez `Remotes (Tunnels/SSH)` dans la liste déroulante. (Si vous ne le voyez pas, vérifiez que l'extension est bien installée.) ![Remotes (Tunnels/SSH)](./images/remote_tunnels.webp) Cliquez sur le bouton `+` pour créer une nouvelle connexion : ![Ajouter une nouvelle connexion](./images/add_new_connection.webp) VS Code vous demande alors la chaîne de connexion SSH. Dans notre exemple, ce sera `ssh christophe@localhost -p 2222` parce que : * L'utilisateur défini dans notre `Dockerfile` est `christophe`, * Notre serveur est `localhost` (puisqu'il s'agit d'un container Docker qui tourne sur notre machine), et * Le port à utiliser est 2222 (celui qu'on a indiqué dans la commande `docker run`). VS Code vous demandera ensuite quel fichier de configuration SSH mettre à jour. > **Utilisateurs WSL2 & Windows:** > > Si vous utilisez WSL2, sachez que VS Code tournant sous Windows ne lit pas forcément votre fichier Linux `~/.ssh/config`. Recopiez les entrées nécessaires dans votre configuration SSH Windows (généralement `C:\Users\\.ssh\config`) et copiez aussi vos clés SSH (publique et privée) pour que VS Code puisse les utiliser. > > Vérifiez également que les fichiers de clés SSH sont accessibles au client que vous utilisez. VS Code va créer ou mettre à jour le fichier de configuration SSH choisi et afficher quelque chose comme ceci : ![Le fichier de configuration SSH](./images/ssh_config_file.webp) Maintenant, si vous cliquez sur la cible **SSH** dans le panneau de gauche, vous verrez le serveur remote-dev avec une icône de connexion à côté : ![Le serveur apparaît sous SSH](./images/ssh_unfolded.webp) Sélectionnez le type d'OS distant (Linux), authentifiez-vous, puis ouvrez le dossier home de l'utilisateur. Vous pouvez créer des fichiers (par exemple `hello.sh`), les rendre exécutables et les lancer depuis le terminal intégré. Un `hostname` affiche le nom d'host du container (pas celui de votre machine locale). > **Le hostname correspond à l'ID du container Docker:** Pour bien voir la différence, quittez VS Code, retournez dans votre terminal et lancez `ssh christophe@localhost -p 2222`. On retrouve bien notre fichier `hello.sh` : ![Vérification du script Bash hello.sh](./images/ssh_check.webp) > **Ce qu'on vient de démontrer:** > > Nous avons utilisé un container Docker local comme serveur SSH pour simuler du développement distant. L'éditeur modifie les fichiers sur l'host distant et le terminal intégré y exécute les commandes — un workflow fluide pour développer et débugger. ### On refait la même chose sur un vrai serveur de production {#repeat-on-a-real-production-server} D'abord, assurez-vous que votre configuration SSH et vos clés sont en place pour vous connecter au serveur de production. J'ai expliqué comment créer une connexion SSH vers un serveur d'hébergement (PlanetHoster, dans mon cas) dans cet article : [How to connect to your hosting server using SSH](/blog/connect-using-ssh-to-your-hosting-server). Si vous avez suivi ce guide, vous devriez pouvoir vous connecter à votre serveur de production avec des clés SSH et un alias configuré dans `~/.ssh/config` (par exemple `planethoster`). > **Parce que je travaille sous WSL2:** > > Un point important dans ma situation : je travaille sous WSL2, donc mon fichier Linux `~/.ssh/config` **n'est pas utilisé** par VS Code (qui tourne sous Windows). J'ai dû copier la configuration dans mon fichier Windows `C:\Users\Christophe\.ssh\config`. Dans mon cas, j'ai simplement ouvert Notepad et copié les lignes dont j'avais besoin. > > J'ai aussi dû copier mes clés (publique et privée) de Linux vers Windows. > > ![Fichier de configuration SSH Windows](./images/powershell.webp) > > Voici le contenu de ma configuration SSH sous Windows : > > **C:\Users\Christophe\\.ssh\config** > > ```ssh\config > Host planethoster > HostName node30-eu.n0c.com > User john_doe > Port 5022 > IdentityFile ~/.ssh/id_ed25519_planethoster > ``` Dans VS Code, ouvrez le panneau **Remote Explorer** et choisissez `Remotes (Tunnels/SSH)`. Sélectionnez l'host configuré (par exemple `planethoster`) et cliquez sur l'icône de connexion. Choisissez l'OS (Linux) et le dossier à ouvrir (généralement le home de l'utilisateur). Une fois connecté, vos modifications et vos commandes dans le terminal agissent directement sur l'host distant — aucun upload nécessaire. ![La liste déroulante du Remote Explorer](./images/remote_explorer_dropdown.webp) ![Connexion Planethoster](./images/vscode_remote_planethoster.webp) ![Connexion à Planethoster en cours](./images/vscode_connecting_planethoster.webp) Vous êtes maintenant connecté à votre serveur de production via VS Code et l'extension Remote - SSH — exactement le résultat montré en début d'article. Si vous modifiez des fichiers, vous modifiez directement ceux du serveur. Plus rien à uploader. > **Prudence en production:** > > Éditer des fichiers directement sur un serveur de production est risqué : une erreur peut provoquer une indisponibilité ou une perte de données. Privilégiez l'authentification par clé SSH, testez vos changements sur un environnement de staging, gardez des backups et réservez les opérations risquées aux fenêtres de maintenance. ## Sous le capot (à sauter si vous voulez juste l'utiliser) {#under-the-hood-skip-this-if-you-just-want-to-use-it} **Bonus - Un mot sur les paires de clés SSH** Voyez ça comme des mots de passe. Vous pourriez utiliser le même mot de passe pour plusieurs services, mais s'il est compromis, tous vos services sont en danger. Tout le monde sait ça, non ? C'est pareil pour les clés SSH. Si vous utilisez la même paire de clés pour plusieurs serveurs et qu'elle est compromise, tous ces serveurs sont en danger. Quand vous lancez une commande comme `ssh-keygen -t ed25519 -C "john_doe" -f ~/.ssh/id_ed25519`, `john_doe` n'est qu'un libellé pour vous aider à identifier la clé plus tard. Ça n'ajoute aucune sécurité. Ce n'est pas le nom d'utilisateur pour se connecter au serveur, c'est juste un commentaire. Vous pouvez tout à fait faire `ssh-keygen -t ed25519 -C "this is my super SSH key for all servers" -f ~/.ssh/id_ed25519` puis réutiliser cette clé sur plusieurs serveurs. Mais si elle est compromise, tous ces serveurs sont en danger. Pour cette raison, je conseille de créer une paire de clés SSH dédiée par serveur. Comme ça, si une clé est compromise, seul le serveur concerné est exposé. ## Conclusion {#conclusion} L'extension [Remote - SSH](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-ssh) de VS Code vous permet de développer directement sur des hosts distants avec toute la puissance de votre éditeur et de vos outils locaux. Combinée à un workflow sûr (tests en local, clés SSH, bonnes pratiques de sécurité évoquées plus haut), c'est une option très efficace quand des contraintes réseau empêchent le développement en local. Les commandes lancées dans le terminal intégré de VS Code s'exécutent sur l'host distant, comme si vous y étiez connecté en SSH. En début d'article, je disais ne pas pouvoir joindre une base de données depuis mon ordinateur à cause de restrictions réseau. Avec l'extension Remote - SSH, se connecter au serveur depuis l'éditeur devient possible, et plutôt simple. --- # Ouvrir un DevContainer sur un serveur distant avec VSCode Remote SSH et ProxyJump > Enchaînez VSCode Remote SSH, ProxyJump et Dev Containers pour ouvrir un devcontainer qui tourne directement sur un serveur Linux distant — vos scripts atteignent ainsi des services uniquement accessibles depuis le réseau de ce serveur. --- ![Ouvrir un DevContainer sur un serveur distant avec VSCode Remote SSH et ProxyJump](/img/v2/ssh-devcontainer.webp) **TL;DR** Les extensions Remote - SSH et Dev Containers de VSCode s'enchaînent : vous vous connectez à un serveur Linux distant via SSH, puis vous rouvrez le projet dans un devcontainer qui tourne *sur ce serveur*. Le container hérite des privilèges réseau du serveur : tout service uniquement joignable depuis ce serveur — un endpoint SFTP privé, une API sécurisée — devient joignable depuis votre terminal. Deux chemins pour y arriver : ProxyJump depuis votre portable (un clic, transparent), ou VSCode sur une VM Windows intermédiaire (solution de repli si le SSH direct n'est pas disponible). Je devais écrire un script Bash qui récupère des fichiers sur un serveur SFTP et envoie des requêtes POST à une API sécurisée. Assez simple — sauf que les deux se trouvent sur un segment réseau privé uniquement joignable depuis un serveur Linux précis. Et ce serveur Linux n'est lui-même joignable que depuis une VM Windows au bureau. Mon portable, au bout de la chaîne, ne voit aucun des deux services directement. Sans la technique ci-dessous, le workflow est lourd : ouvrir un terminal sur la VM, faire un SSH vers le serveur Linux, éditer le script avec `nano`, l'exécuter, copier-coller les erreurs vers mon éditeur. Pas génial. **Ou bien je peux me passer complètement de la VM et travailler directement depuis mon portable. Cet article présente les deux chemins — et la route directe depuis le portable est plus simple qu'elle n'y paraît.** Avec VSCode Remote SSH enchaîné à Dev Containers, le workflow devient : ouvrir VSCode, se connecter une fois, et travailler exactement comme en local — éditeur complet, devcontainer complet, et le serveur SFTP ainsi que l'API joignables depuis le terminal intégré. Il y a deux façons de mettre ça en place, et l'article couvre les deux. *Prérequis : vous devez déjà être à l'aise avec les bases de VSCode Remote SSH. Sinon, [SSH Remote development with VSCode](/blog/vscode-remote-ssh) couvre l'installation de l'extension et le fichier de configuration SSH depuis zéro.* **Quick Jump:** [Approche recommandée — ProxyJump depuis votre portable](#recommended-approach--proxyjump-from-your-laptop) | [Approche de repli — VSCode sur la VM Windows](#fallback-approach--vscode-on-the-windows-vm) | [Se connecter avec VSCode](#connect-with-vscode) | [Ouvrir le DevContainer](#open-the-devcontainer) ## Ce que vous obtenez au final {#what-you-get-at-the-end} Avec l'approche recommandée, l'architecture ressemble à ceci : ```mermaid graph TD A[💻 Your laptop] -->|"ProxyJump — transparent"| B[🖥️ Windows VM] B -->|SSH relay| C[🐧 Linux server] C -->|1 — fetch files| D[🗄️ SFTP server] D -->|files| C C -->|2 — POST requests| E[🔐 Secured API] style A fill:#4a90d9,color:#fff style B fill:#888,color:#fff style C fill:#27ae60,color:#fff style D fill:#8e44ad,color:#fff style E fill:#e67e22,color:#fff ``` VSCode tourne sur votre portable et se connecte au serveur Linux en un clic — la VM Windows n'est qu'un relais dans la configuration SSH, invisible à l'usage. Le devcontainer tourne sur le serveur Linux, donc il hérite du réseau du serveur : le serveur SFTP et l'API sécurisée sont joignables depuis l'intérieur du container, même s'ils sont invisibles depuis votre portable. ## Quelle approche vous convient ? {#which-approach-works-for-you} Deux situations, deux chemins : ```mermaid flowchart LR L["💻 Your laptop"] V["🖥️ Windows VM"] S["🐧 Linux server"] L -->|"Recommended\nProxyJump\nVSCode on laptop"| S L -->|"Fallback\nManual session\nVSCode on VM"| V V --> S style L fill:#4a90d9,color:#fff style V fill:#888,color:#fff style S fill:#27ae60,color:#fff ``` > **Serveur SSH sur la VM Windows:** > > L'approche recommandée nécessite un serveur SSH sur la VM Windows. Si vous êtes admin sur la VM, il suffit d'une commande pour installer le serveur SSH. **Pour vérifier si l'approche recommandée est disponible**, lancez ceci depuis le terminal de votre portable : ```bash ssh -J vm-user@windows-vm-ip dev-user@test.example.internal "whoami && hostname" ``` * Ça affiche dev-user et le nom d'hôte du serveur Linux → **utilisez l'approche recommandée**. * Ça part en timeout → votre portable ne peut pas joindre la VM du tout → **utilisez l'approche de repli**. * `Connection refused` sur le port 22 → la VM est joignable mais OpenSSH Server n'est pas installé → **voyez le correctif ci-dessous**. *La seconde ligne est la sortie de `hostname` sur la machine distante — elle reflète le nom défini dans `/etc/hostname`, qui peut différer de l'adresse utilisée pour vous connecter. C'est normal ; ce qui compte, c'est que la commande ait renvoyé quelque chose plutôt que de partir en timeout ou d'être refusée.* **Correctif : installer OpenSSH Server sur la VM Windows (droits admin requis)** `Connection refused` signifie que le chemin réseau fonctionne — seul le service SSH manque. Si vous avez les droits admin sur la VM, ouvrez un PowerShell **en tant qu'administrateur** et lancez : ```bash $ Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 $ Start-Service sshd $ Set-Service -Name sshd -StartupType Automatic ``` Une fois fait, relancez le test depuis votre portable — il devrait maintenant afficher dev-user et le nom d'hôte du serveur. Si c'est le cas, suivez l'**approche recommandée**. **L'autorisation de la clé est optionnelle.** La connexion fonctionne aussi avec l'authentification par mot de passe — on vous demandera simplement le mot de passe de la VM à chaque connexion de VSCode. Pour un usage occasionnel, c'est acceptable ; pour un usage quotidien, la clé rend la connexion totalement silencieuse. *Vous préférez une installation graphique ? Ouvrez **Paramètres → Système → Fonctionnalités optionnelles**, cherchez **OpenSSH Server** et installez-le depuis là — puis lancez les commandes `Start-Service` et `Set-Service` ci-dessus pour le démarrer et l'activer.* **Remarque** Si vous rencontrez des difficultés à l'installation d'OpenSSH Server (en CLI ou en interface graphique), vous pouvez en alternative télécharger le fichier `.msi` à la main depuis . Une fois téléchargé, double-cliquez sur le fichier `.msi` pour lancer l'installation. ## Pourquoi ça fonctionne {#why-it-works} * **Deux extensions, une seule chaîne.** Remote - SSH lance un processus VS Code Server sur l'host Linux. Dev Containers démarre ensuite un container sur *cet host* et y connecte VSCode — le container tourne sur le moteur Docker du serveur, pas sur celui de votre portable. * **Le privilège réseau est hérité de l'host.** La pile réseau du container vit sur le serveur Linux, donc chaque connexion sortante part de l'IP du serveur. Les services qui bloquent l'adresse de votre portable laissent passer le serveur. * **ProxyJump délègue le relais à SSH.** Avec `ProxyJump` dans `~/.ssh/config`, SSH ouvre une connexion vers la VM, puis y fait passer une seconde connexion SSH vers le serveur Linux. VSCode ne voit qu'un seul host et un seul clic. * **Aucun transfert de fichiers.** Les fichiers vivent sur le serveur. L'éditeur les lit et les écrit directement — pas de `scp`, pas de rsync, pas d'étape d'upload. ## Configurer SSH {#configure-ssh} ### Approche recommandée — ProxyJump depuis votre portable {#recommended-approach--proxyjump-from-your-laptop} #### Prérequis {#prerequisites} Votre clé privée SSH (`~/.ssh/`id\_ed25519) doit se trouver sur votre portable — si vous n'en avez pas encore, [cet article détaille la génération d'une paire de clés ed25519](/blog/github-connect-using-ssh). ProxyJump fait passer l'authentification dans le tunnel — la clé de votre portable authentifie les deux hosts, donc elle n'a jamais besoin de quitter votre machine. L'authentification par mot de passe fonctionne aussi si l'authentification par clé n'est pas en place sur la VM windows-vm-ip. #### Configurer SSH sur votre portable {#configure-ssh-on-your-laptop} Créez le fichier `C:\Users\your_laptop_user\.ssh\config` (Windows ; WSL inclus) ou `~/.ssh/config` (si vous êtes sous Linux/macOS) sur votre portable : ```txt Host linux-test Hostname test.example.internal User dev-user ProxyJump vm-user@windows-vm-ip IdentityFile ~/.ssh/id_ed25519 ``` La ligne `ProxyJump` indique à SSH d'atteindre linux-test en passant par la VM windows-vm-ip. À partir de là, `ssh `linux-test depuis votre portable se connecte directement au serveur Linux — plus d'étape manuelle sur la VM. #### Tester la chaîne complète {#test-the-full-chain} Lancez la commande avec l'alias nommé, maintenant que la configuration est en place : ```bash $ ssh linux-test "whoami && hostname" ``` La première exécution ressemblera à ceci : ```bash $ ssh -J vm-user@windows-vm-ip dev-user@test.example.internal "whoami && hostname" The authenticity of host 'windows-vm-ip (1.2.3.4)' can't be established. ED25519 key fingerprint is SHA256:AbCdEfGhIjKlMnOpQrStUvWxYz0123456789ABCD. This key is not known by any other names. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added 'windows-vm-ip' (ED25519) to the known hosts. vm-user@windows-vm-ip's password: The authenticity of host 'test.example.internal ()' can't be established. ED25519 key fingerprint is SHA256:AbCdEfGhIjKlMnOpQrStUvWxYz0123456789BCDE. This key is not known by any other names. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added 'test.example.internal' (ED25519) to the known hosts. dev-user@test.example.internal's password: dev-user test.example.internal ``` Deux choses se produisent ici, toutes deux normales et uniquement à la première fois : * **Les demandes de clé d'hôte** (`The authenticity of host ... can't be established`) : SSH n'a jamais vu ces hosts. Tapez `yes` pour chacun — l'empreinte est stockée dans `~/.ssh/known_hosts` et la question ne revient plus. * **Les demandes de mot de passe** : une pour la VM et une pour le serveur Linux — la connexion fonctionne dans tous les cas. Pour la rendre totalement silencieuse, autorisez votre clé publique sur chaque host (voir ci-dessous). **Optionnel : supprimer les demandes de mot de passe avec l'authentification par clé** Il y a deux sauts, donc deux endroits où autoriser votre clé. Faites-les dans l'ordre. ##### Étape 1 — autoriser votre clé sur la VM Windows {#step-1--authorize-your-key-on-the-windows-vm} Pour les comptes admin sous Windows, OpenSSH lit un fichier commun au système — et **pas** le `~\.ssh\authorized_keys` de l'utilisateur. Le dossier `C:\ProgramData\ssh\` a des ACL restrictives : même un membre du groupe Administrateurs a besoin d'une session PowerShell **réellement élevée** (clic droit → *Exécuter en tant qu'administrateur*). Sur la VM : **Étape 1a** — sur votre portable, affichez votre clé publique et copiez la ligne entière : ```bash $ cat ~/.ssh/id_ed25519.pub ``` **Étape 1b** — sur la VM (PowerShell en administrateur), ajoutez la clé copiée : ```bash $ Add-Content C:\ProgramData\ssh\administrators_authorized_keys "ssh-ed25519 AAAA...your-key-here" ``` Relancez le test — la demande de mot de passe de la VM devrait avoir disparu. Le serveur Linux demandera encore un mot de passe ; c'est l'étape 2. ##### Étape 2 — autoriser votre clé sur le serveur Linux {#step-2--authorize-your-key-on-the-linux-server} Avec ProxyJump déjà configuré dans `~/.ssh/config`, `ssh-copy-id` fonctionne de façon transparente — il passe par la VM et copie votre clé sur le serveur Linux en une seule commande : ```bash $ ssh-copy-id linux-test /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed /usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys dev-user@linux-test's password: Number of key(s) added: 1 Now try logging into the machine, with: "ssh 'linux-test'" and check to make sure that only the key(s) you wanted were added. ``` Il demande le mot de passe du serveur Linux une dernière fois, puis ajoute votre clé publique à `~/.ssh/authorized_keys` sur le serveur. Après ça, toute la chaîne est silencieuse — plus aucune question. **Quick Jump:** [SSH configuré — on continue avec VSCode](#connect-with-vscode) ### Approche de repli — VSCode sur la VM Windows {#fallback-approach--vscode-on-the-windows-vm} Suivez ce chemin si l'approche recommandée n'est pas disponible (serveur SSH non installé sur la VM windows-vm-ip, ou VM non joignable en SSH depuis votre portable). #### Créer le fichier de configuration SSH sur la VM {#create-the-ssh-config-file-on-the-vm} Ouvrez une session Powershell sur la VM windows-vm-ip. Si le sous-dossier `$HOME\.ssh` n'existe pas encore, créez-le : `New-Item -ItemType Directory -Path "$HOME\.ssh"`. Ouvrez (ou créez) `C:\Users\`vm-user`\.ssh\config` dans Notepad. Le fichier doit s'appeler `config`, **sans extension** — `config.txt` est silencieusement ignoré par tous les clients SSH. ```txt Host linux-test Hostname test.example.internal User dev-user IdentityFile ~/.ssh/id_ed25519 ``` #### Copier votre clé privée SSH sur la VM {#copy-your-ssh-private-key-to-the-vm} Le client SSH de la VM a besoin de votre clé privée. Parce que vous aurez probablement aussi besoin de votre clef publique dans le futur, copiez les deux manuellement via l'Explorateur (allez dans votre home Linux sous `\\wsl$\` et copiez `~/.ssh/`id\_ed25519 vers `C:\Users\`vm-user`\.ssh\`), ou lancez ce one-liner dans une session Powershell qui a accès à WSL : ```bash $ wsl sh -c 'cp "$HOME/.ssh/id_ed25519.pub" "/mnt/c/Users/$(cmd.exe /c echo %USERNAME% | tr -d "\r")/.ssh/id_ed25519.pub"' $ wsl sh -c 'cp "$HOME/.ssh/id_ed25519" "/mnt/c/Users/$(cmd.exe /c echo %USERNAME% | tr -d "\r")/.ssh/id_ed25519"' ``` #### Tester la connexion depuis la VM {#test-the-connection-from-the-vm} Depuis Powershell sur la VM, vérifiez que l'alias se résout et que l'authentification passe : ```bash PS C:\Users\vm-user\.ssh> ssh linux-test No updates available to install Last update check: 2026-09-04 00:00:01 Last login: Thu Sep 3 17:41:10 2026 from 10.x.x.x [dev-user@linux-server-01 ~]$ whoami dev-user [dev-user@linux-server-01 ~]$ pwd /home/dev-user [dev-user@linux-server-01 ~]$ exit logout Connection to test.example.internal closed. PS C:\Users\vm-user\.ssh> ``` ## Se connecter avec VSCode {#connect-with-vscode} SSH est maintenant configuré et la commande de test a renvoyé votre nom d'utilisateur — la connexion fonctionne de bout en bout. Que vous ayez suivi l'approche recommandée ou celle de repli, vous êtes au même point : prêt à ouvrir VSCode et à vous connecter au serveur Linux. > **Utilisateurs WSL — n'utilisez pas `code .` pour cette étape:** > > Si vous travaillez exclusivement dans WSL, votre réflexe est `code .` depuis le terminal. **Ne l'utilisez pas ici.** > > Quand VSCode s'ouvre via `code .` depuis WSL, il se connecte à WSL comme host distant. Dans ce mode, le Remote Explorer n'affiche que **WSL Targets** et **Dev Containers** — la catégorie **Remotes (Tunnels/SSH)** n'apparaît jamais, quelles que soient les extensions installées. > > Ouvrez plutôt VSCode à la manière Windows : depuis le **menu Démarrer**, la **barre des tâches**, ou un terminal PowerShell (juste `code`, sans argument). Vous obtenez une fenêtre locale, avec la liste complète du Remote Explorer — y compris **Remotes (Tunnels/SSH)**. Donc, **côté Windows, ouvrez une console Powershell** et installez l'extension **[Remote - SSH](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-ssh)** — c'est elle qui fournit la barre latérale Remote Explorer utilisée pour se connecter à l'host distant : Prerequisite: Remote - SSH ```bash $ code --install-extension ms-vscode-remote.remote-ssh Installing extension 'ms-vscode-remote.remote-ssh'... Extension 'ms-vscode-remote.remote-ssh' v0.120.0 was successfully installed. ``` Verify: ```bash $ code --list-extensions | grep remote-ssh ms-vscode-remote.remote-ssh ``` > **Sous Powershell, utilisez findstr au lieu de grep:** > > ```bash > $ code --list-extensions | grep remote-ssh > ``` Ouvrez VSCode — **sur votre portable** si vous avez suivi l'approche recommandée, ou **sur la VM Windows** si vous avez suivi l'approche de repli. Dans la barre latérale **Remote Explorer**, sélectionnez **Remotes (Tunnels/SSH)** dans la liste déroulante. > Utilisateurs WSL : encore une fois, parce que c'est important : passez par le menu Démarrer de Windows et lancez VSCode depuis là. Repérez linux-test dans la liste et cliquez sur l'icône flèche à côté pour l'ouvrir dans une nouvelle fenêtre. VSCode déploie un petit binaire serveur sur l'host Linux la première fois — surveillez la barre d'état en bas à gauche pour le message *Opening Remote...*. Dès que le nom de l'host y apparaît, vous êtes connecté. > **L'host n'apparaît pas dans la liste ?:** > > Deux choses à vérifier : > > 1. L'extension du fichier de configuration. Ouvrez l'Explorateur, allez dans `C:\Users\`vm-user`\.ssh\`, et vérifiez que le fichier s'appelle `config`, pas `config.txt`. L'Explorateur Windows masque les extensions connues par défaut — activez « Extensions de noms de fichiers » dans le menu Affichage pour en être sûr. > > 2. Une extension obsolète. Si l'host était visible avant et a disparu, ouvrez le panneau Extensions et mettez Remote - SSH à jour. C'est arrivé au moins une fois après une mise à jour de VS Code. > **Vous venez d'ouvrir un projet sur un serveur que votre portable ne peut pas joindre directement:** > > VSCode est connecté au serveur Linux. Vous pouvez déjà ouvrir des fichiers, lancer des terminaux, utiliser Git — tout fonctionne depuis votre portable, sur des fichiers qui vivent sur le serveur. L'endpoint SFTP et l'API sécurisée sont déjà joignables depuis le terminal intégré. Bravo ! > > **Le chapitre suivant est optionnel.** Il ajoute un DevContainer par-dessus cette connexion — utile si votre projet nécessite un runtime spécifique ou un environnement isolé. Si vous n'avez besoin que de l'éditeur distant, c'est terminé. ### Vérifier la connexion {#verify-the-connection} Ouvrez un terminal dans VSCode (**Terminal → New Terminal**) et lancez : ```bash $ hostname test.example.internal ``` Le nom d'hôte correspond au serveur Linux — le terminal tourne sur la machine distante, pas sur votre portable. C'est ce contexte réseau qui rend la suite possible. Les services invisibles depuis votre portable mais joignables depuis le serveur sont désormais accessibles depuis ce terminal. Si votre projet se connecte à un serveur SFTP, essayez tout de suite : ```bash $ sftp -i ~/.ssh/id_ed25519 -P 22 user@sftp.example.internal Connected to sftp.example.internal. sftp> ``` Vous êtes sur un prompt `sftp>` depuis un terminal à l'intérieur de VSCode sur votre portable — le réseau du serveur Linux fait le routage, de façon invisible. ## Ouvrir le DevContainer {#open-the-devcontainer} Une fois connecté à l'host distant, installez l'extension **Dev Containers** pour ouvrir le projet dans un container qui tourne sur cet host : Prerequisite: Dev Containers ```bash $ code --install-extension ms-vscode-remote.remote-containers Installing extension 'ms-vscode-remote.remote-containers'... Extension 'ms-vscode-remote.remote-containers' v0.395.0 was successfully installed. ``` Verify: ```bash $ code --list-extensions | grep remote-containers ms-vscode-remote.remote-containers ``` Ouvrez le dossier du projet sur le serveur Linux, puis appuyez sur `F1` et lancez **Dev Containers: Rebuild and Reopen in Container** la première fois (ou **Reopen in Container** lors des connexions suivantes). > **Docker doit être installé sur le serveur Linux:** > > Dev Containers nécessite Docker sur l'host distant, pas sur votre portable ni sur la VM. Si vous obtenez une erreur indiquant que Docker n'est pas trouvé, votre administrateur système doit l'installer, ou suivez le [guide d'installation officiel de Docker](https://docs.docker.com/engine/install/) pour la distribution de votre serveur. Une fois le container démarré, votre prompt change pour refléter l'environnement du container. À partir de là, le serveur SFTP et l'API sécurisée sont joignables — les deux services invisibles depuis votre portable sont maintenant sur le réseau local du container. ## Sous le capot (à sauter si vous voulez juste l'utiliser) {#under-the-hood-skip-this-if-you-just-want-to-use-it} **Remote - SSH** installe un processus `vscode-server` sur l'host Linux à la première connexion, réutilisé ensuite. Ce serveur gère toute la communication de l'éditeur — lecture de fichiers, I/O du terminal, language server protocol — à travers le tunnel SSH. **Dev Containers** dialogue ensuite avec la socket Docker de cet host *distant*. Il lit `devcontainer.json`, construit ou récupère l'image, et démarre le container. VSCode rebranche son protocole d'éditeur sur le runtime du container, en laissant le layer SSH intact en dessous. Résultat : deux connexions imbriquées : tunnel SSH → VS Code Server sur l'host → runtime du container. Avec l'approche recommandée, il y a un troisième niveau : le tunnel SSH lui-même passe par la VM via ProxyJump. Le VS Code Server n'en sait rien — il sait seulement qu'il tourne sur l'host Linux. Le ProxyJump se joue entièrement au niveau SSH. Les bind mounts du container référencent des chemins *sur le serveur Linux*, pas des chemins de votre portable ou de la VM — et c'est exactement ce qu'on veut. > **Cette commande de test est aussi une session SSH complète:** > > Vous vous souvenez de la commande utilisée plus haut pour vérifier si l'approche recommandée était disponible ? > > ```bash > ssh -J vm-user@windows-vm-ip dev-user@test.example.internal "whoami && hostname" > ``` > > Retirez la commande entre guillemets à la fin et vous obtenez un one-liner autonome qui ouvre une session SSH interactive sur le serveur Linux — directement depuis votre portable, sans aucun fichier de configuration SSH : > > ```bash > ssh -J vm-user@windows-vm-ip dev-user@test.example.internal > ``` > > Maintenant que la configuration SSH est en place, `ssh `linux-test fait la même chose en un mot. Mais la forme `-J` fonctionne sur n'importe quelle machine sans toucher à `~/.ssh/config` — pratique dès que vous avez besoin d'une session rapide depuis un poste inconnu. ## Bonus - one-liners ssh -J {#bonus---ssh--j-one-liners} Toutes les commandes ci-dessous fonctionnent depuis n'importe quelle machine sans toucher à `~/.ssh/config` — la chaîne complète est encodée dans le flag `-J`. **Vérification rapide — exécute une commande et quitte :** ```bash ssh -J vm-user@windows-vm-ip dev-user@test.example.internal "whoami && hostname" ``` **Shell interactif sur le serveur Linux :** ```bash ssh -J vm-user@windows-vm-ip dev-user@test.example.internal ``` Pour les sessions interactives qui lancent un programme précis, ajoutez `-t` afin d'allouer un pseudo-terminal. Sans lui, le programme distant démarre mais n'affiche aucun prompt — la session semble figée, alors qu'elle attend silencieusement une entrée. **Session SFTP sur un serveur uniquement joignable depuis l'host Linux :** ```bash ssh -t -J vm-user@windows-vm-ip dev-user@test.example.internal sftp -i ~/.ssh/id_ed25519 user@sftp.example.internal ``` **Session PostgreSQL sur une base uniquement joignable depuis l'host Linux :** ```bash ssh -t -J vm-user@windows-vm-ip dev-user@test.example.internal psql -h db.example.internal -U postgres ``` **Port forwarding — utilisez n'importe quel outil graphique depuis votre portable :** Pour les clients graphiques Windows (HeidiSQL, DBeaver, TablePlus…) qui ne peuvent pas passer directement par SSH, le port forwarding est la réponse. Il expose le port distant comme `localhost` sur votre machine — l'outil graphique ne sait pas qu'il traverse deux sauts SSH : ```bash ssh -fNL 5432:db.example.internal:5432 -J vm-user@windows-vm-ip dev-user@test.example.internal ``` Connectez ensuite HeidiSQL (ou n'importe quel client) à `Host: localhost`, `Port: 5432`. Le tunnel tourne silencieusement en arrière-plan (`-f`) sans commande distante (`-N`). Tuez le processus `ssh` quand vous avez fini. ## Conclusion {#conclusion} La combinaison de VSCode Remote SSH et Dev Containers résout un problème qui demande sinon un workflow manuel pénible en plusieurs étapes : développer du code qui doit tourner — ou atteindre des services qui n'existent que — sur un serveur que votre portable ne peut pas joindre directement. L'approche recommandée (ProxyJump) est la plus propre : une fois la configuration SSH en place, la connexion se fait en un clic et la VM Windows devient une infrastructure invisible. L'approche de repli est l'alternative fiable quand le serveur SSH de la VM n'est pas disponible. [SSH Remote development with VSCode](/blog/vscode-remote-ssh) est l'article compagnon si vous voulez partir d'un cas plus simple — éditer des fichiers sur un host distant sans devcontainer, avec un container Docker local comme terrain d'entraînement sûr avant de toucher à un vrai serveur. --- # Générateur de config SSH : stanzas wildcard, alias et connexions automatisées > Générez votre config SSH en quelques secondes — puis comprenez comment elle fonctionne : un wildcard élimine chaque ProxyJump et IdentityFile répété, les stanzas de domaine définissent les noms d'utilisateur pour les FQDN, les alias courts ont besoin de leur propre ligne User, et RemoteCommand vous connecte directement en tant qu'utilisateur applicatif. --- ![Générateur de config SSH : stanzas wildcard, alias et connexions automatisées](/img/v2/ssh.webp) **TL;DR** SSH lit chaque stanza correspondante de haut en bas, et la première valeur l'emporte pour chaque directive — c'est ce qui rend `Host * !bastion` si puissant : une seule stanza couvre tous les serveurs d'un coup, fini la répétition de `ProxyJump` et `IdentityFile`. Les stanzas de domaine ajoutent le bon `User` pour les connexions par FQDN ; les alias courts doivent le répéter explicitement, car SSH fait la correspondance sur ce que vous tapez, pas sur le hostname résolu. Trois directives — `ServerAliveInterval`, `ControlMaster`, `ConnectTimeout` — renforcent les valeurs globales sans en changer la logique. Pour une machine qui mélange SSH professionnel et personnel, `Include` sépare proprement les deux. `RemoteCommand` transforme une séquence `sudo su` + `cd` en un simple alias. Chaque serveur de mon environnement n'est accessible qu'à travers ma propre VM Windows qui joue le rôle de bastion. L'[article sur ProxyJump et les DevContainers](/blog/vscode-remote-ssh-proxyjump-devcontainer) montre une config avec un seul alias. Ajoutez un deuxième serveur et la config double. Ajoutez-en dix et ça devient un problème de maintenance : changez une fois le hostname du bastion et vous réécrivez dix stanzas, chacune portant les mêmes lignes `ProxyJump` et `IdentityFile`. La config ci-dessous couvre n'importe quel nombre de serveurs sans aucune répétition. ## La configuration {#the-config} Les deux chemins de connexion fonctionnent immédiatement — un alias court et un FQDN complet, sans aucun flag : ```bash $ ssh project_prod "whoami && hostname" user-b server-prod $ ssh server-test.cloud.project-a.internal "whoami && hostname" user-a server-test ``` Pas de flag `ProxyJump`, pas de clé explicite — la config gère les deux de façon transparente. Créez `C:\Users\your_laptop_user\.ssh\config` (Windows ; WSL inclus) ou `~/.ssh/config` (Linux/macOS) avec ceci : ```txt # 1. Bastion — no ProxyJump, no IdentityFile Host windows-vm-ip User vm-user # 2. Domain defaults — in effect when connecting via full FQDN only Host *.cloud.project-a.internal User user-a Host *.cloud.corp.example User user-b Host *.office.corp.example User user-b # 3. Short aliases — HostName + User # (domain defaults above do not apply to aliases — see article) Host project_test HostName server-test.cloud.project-a.internal User user-a Host project_prod HostName server-prod.office.corp.example User user-b # 4. Explicit exceptions — bypass the bastion for specific hosts # Must come BEFORE "Host *" — SSH uses first-value-wins per directive Host github.com ProxyJump none # 5. Global defaults — fallback for every host except the bastion Host * !windows-vm-ip ProxyJump vm-user@windows-vm-ip IdentityFile ~/.ssh/id_ed25519 ``` ## Comment SSH lit un fichier de configuration {#how-ssh-reads-a-config-file} Trois règles expliquent pourquoi les stanzas se composent sans conflit : * **Plusieurs stanzas peuvent correspondre.** Lancer `ssh project_prod` amène SSH à parcourir le fichier de haut en bas et à collecter les réglages de *chaque* stanza `Host` qui correspond à `project_prod`, pas seulement la première. * **La première valeur l'emporte pour chaque réglage — l'inverse de la plupart des outils.** En CSS, dans les fichiers `.env` ou dans la plupart des systèmes de configuration, la dernière déclaration écrase les précédentes. SSH fait l'inverse : la première valeur rencontrée est conservée, et les stanzas suivantes ne peuvent que compléter les réglages non encore définis. Mettez la stanza la plus spécifique en premier ; le fallback wildcard en dernier. * **Les motifs correspondent à l'argument de la ligne de commande, pas au hostname résolu.** `Host *.office.corp.example` correspond à `ssh server-prod.office.corp.example`. Il ne correspond **pas** à `ssh project_prod`, même si le `HostName` de `project_prod` se résout en `server-prod.office.corp.example`. ## Les cinq sections {#the-five-sections} Le snippet de config ci-dessus est découpé en cinq sections numérotées — voici ce que fait chacune et pourquoi elle est placée dans cet ordre. **Section 1 — Définition du bastion.** Déclare le jump host avec son nom d'utilisateur. Pas de `ProxyJump` ici (il boucherait sur lui-même) et pas d'`IdentityFile` (la VM utilise l'authentification que vous avez configurée séparément pour elle). **Section 2 — Valeurs par défaut par domaine.** Elles s'appliquent quand vous tapez un FQDN complet comme `ssh server-prod.office.corp.example`. SSH récupère le `User` ici, puisqu'aucune stanza précédente ne l'a défini. Plusieurs stanzas de domaine avec le même nom d'utilisateur, c'est normal — une par suffixe de domaine. Ces stanzas sont aussi utiles pour les scripts qui construisent des hostnames par programme ou pour les appels `ssh-copy-id`. **Section 3 — Alias courts.** Chaque alias déclare `HostName` (le vrai FQDN) et `User`. Ajouter un nouvel alias de serveur, c'est exactement deux lignes. **Section 4 — Exceptions explicites.** Tout host qui ne doit pas passer par le bastion — un service d'hébergement Git, un runner CI, une API SaaS — reçoit `ProxyJump none` ici. La position est structurante : cette stanza doit venir *avant* `Host *`. **Section 5 — Valeurs globales par défaut.** Le préfixe `!` dans `Host * !`windows-vm-ip signifie « tous les hosts *sauf* le bastion ». Placée en dernier, cette stanza fournit `ProxyJump` et `IdentityFile` comme fallbacks pour chaque stanza précédente qui ne les a pas définis — c'est-à-dire toutes. N'importe quelle stanza des sections 2, 3 ou 4 peut écraser ces valeurs simplement en déclarant la directive en premier, sans toucher à la section 5. ## Renforcer la section 5 (optionnel) {#hardening-section-5-optional} Quatre directives étendent les valeurs globales sans en changer la logique. Ajoutez celles qui correspondent à votre environnement — aucune n'est requise pour que la config fonctionne. ```txt # 4. Explicit exceptions — bypass the bastion for specific hosts # Must come BEFORE "Host *" — SSH uses first-value-wins per directive Host github.com ProxyJump none # 5. Global defaults — fallback for every host except the bastion Host * !windows-vm-ip ProxyJump vm-user@windows-vm-ip IdentityFile ~/.ssh/id_ed25519 # Optional — prevent silently-dropped idle connections ServerAliveInterval 30 ServerAliveCountMax 3 # Optional — fail fast instead of waiting the default 2 minutes ConnectTimeout 5 ``` * **`ServerAliveInterval 30` / `ServerAliveCountMax 3`** — SSH envoie un paquet keepalive toutes les 30 secondes. Après 3 paquets sans réponse (90 secondes de silence), la connexion est fermée proprement au lieu de rester bloquée sur un prompt gelé. Sans ça, un terminal inactif sur un serveur d'entreprise se fige souvent sans aucun message d'erreur. * **`ConnectTimeout 5`** — échoue après 5 secondes si le serveur est injoignable. Sur un réseau d'entreprise avec un bastion fiable, le timeout par défaut de 2 minutes n'est que du bruit. > **ControlMaster ne fonctionne pas avec OpenSSH sous Windows:** > > `ControlMaster auto` / `ControlPath` / `ControlPersist` activent le multiplexage SSH : la première connexion ouvre une socket de domaine Unix dans `~/.ssh/` ; les connexions suivantes réutilisent le tunnel au lieu d'en négocier un nouveau à travers le bastion. Sous Linux et macOS, ça marche bien. > > Sous **OpenSSH Windows avec ProxyJump**, ces trois directives cassent silencieusement toutes les connexions — `ssh` se bloque ou sort avec `Read from remote host: Unknown error` — parce que l'implémentation des sockets Unix sous Windows n'est pas compatible avec le forwarding stdio basé sur des pipes de ProxyJump. Le commentaire `# Optional` n'est pas une protection suffisante : les lignes sont copiées et cassent tout. > > Ne les mettez pas sous Windows. Si vous vous connectez depuis Linux ou macOS, ajoutez-les : > > ```text > ControlMaster auto > ControlPath ~/.ssh/cm_%C > ControlPersist 10m > ``` > > `%C` est un hash des paramètres de connexion — pas de deux-points, pas de caractères spéciaux. N'utilisez jamais `%r@%h:%p` : le `:` est illégal dans les noms de fichiers Windows et corrompt silencieusement le path de la socket, même quand ControlMaster est par ailleurs fonctionnel. ## Pourquoi chaque alias a besoin de sa propre ligne `User` (à passer si la config fonctionne déjà chez vous) {#why-each-alias-needs-its-own-user-line-skip-if-the-config-just-works-for-you} Les stanzas de domaine de la section 2 ne définissent `User` que quand SSH y trouve une correspondance — et comme noté plus haut, SSH fait correspondre les motifs à l'argument de la ligne de commande, pas au hostname résolu. En lançant `ssh project_prod` : 1. Ne correspond **pas** à `Host *.office.corp.example` (section 2) — `project_prod` n'est pas un FQDN. 2. Correspond à `Host project_prod` (section 3) → applique `HostName server-prod.office.corp.example` et `User `user-b. 3. Ne correspond **pas** à `Host github.com` (section 4) — `project_prod` n'est pas dans cette liste. 4. Correspond à `Host * !`windows-vm-ip (section 5) → applique `ProxyJump` et `IdentityFile`. Sans la ligne `User` dans la stanza de l'alias, SSH retombe sur votre nom d'utilisateur Windows local pour cette connexion — ce qui est presque certainement faux. En lançant directement `ssh server-prod.office.corp.example` : 1. Correspond à `Host *.office.corp.example` (section 2) → `User `user-b. 2. Ne correspond **pas** à `Host github.com` (section 4) — pas dans cette liste. 3. Correspond à `Host * !`windows-vm-ip (section 5) → `ProxyJump`, `IdentityFile`. Les deux chemins arrivent sur le bon utilisateur. Ils ont juste besoin de stanzas différentes pour y arriver. > **Utilisateurs ZSH : autocomplétez vos alias:** > > Le [plugin zsh-ssh-config-suggestions](/blog/zsh-plugin-ssh-config-suggestions) lit `~/.ssh/config` et propose des complétions sur `ssh `. Chaque alias de la section 3 devient une suggestion — plus votre liste d'alias est longue, plus c'est utile. ## Une réserve : le wildcard est vraiment universel (à passer si cette machine est purement professionnelle) {#one-caveat-the-wildcard-is-truly-universal-skip-if-this-machine-is-corporate-only} `Host * !`windows-vm-ip s'applique à **chaque** connexion SSH depuis cette machine — y compris `ssh github.com` ou `ssh localhost`. Les connexions vers des hosts externes tenteront un `ProxyJump` à travers la VM et échoueront si la VM n'est pas joignable. > **Devcontainers : déclarez les exceptions avant Host \*:** > > Quand la config SSH est bind-mountée dans un devcontainer, le hostname du bastion peut ne pas se résoudre dans le réseau du container. Toute connexion SSH depuis le container — `git push`, `scp`, `rsync` — tombe sur `Host *`, tente un `ProxyJump` à travers le bastion, n'arrive pas à le résoudre et sort avec : > > ```text > ssh: Could not resolve hostname your-bastion: Name or service not known > ``` > > Les hosts qui doivent contourner le bastion ont besoin d'une stanza `ProxyJump none` explicite **avant** `Host *`. SSH applique la règle « première valeur gagnante » par directive : un `ProxyJump none` placé après `Host *` est silencieusement ignoré. La section 4 de la config ci-dessus existe précisément pour ça — ajoutez-y tout host qui se connecte directement (hébergement Git, runners CI, API tierces). Trois options si ça vous pose problème : * **Remplacez `Host *` par une liste de domaines explicite :** `Host *.project-a.internal *.cloud.corp.example *.office.corp.example`. Les connexions vers tout ce qui est en dehors de cette liste ignorent complètement le bastion. * **Gardez `Host *`** si cette machine utilise SSH exclusivement pour des serveurs internes à votre réseau d'entreprise. Sur une machine de travail dédiée, le wildcard convient très bien. * **Écrasez des hosts spécifiques au-dessus de la section 4.** Comme les valeurs globales sont en dernier, toute stanza placée avant elles l'emporte. Ajoutez une exemption n'importe où au-dessus de la section 4 — la position à l'intérieur des sections 1 à 3 n'a pas d'importance : ```text # Anywhere above section 4 — fits naturally in section 3 alongside the other aliases Host github.com gitlab.com ProxyJump none IdentityFile ~/.ssh/id_ed25519_personal # Section 4 — global defaults (last, unchanged) Host * !windows-vm-ip ProxyJump ... ``` * **Découpez la config avec `Include`** — la solution la plus propre pour une machine utilisée à la fois pour du SSH professionnel et personnel : ```text # ~/.ssh/config Include ~/.ssh/config.d/work Include ~/.ssh/config.d/personal ``` `~/.ssh/config.d/work` contient la config d'entreprise complète (bastion, stanzas de domaine, alias, valeurs globales en dernier). `~/.ssh/config.d/personal` contient une config propre sans routage par bastion. Les directives `Include` sont traitées de haut en bas avant tout bloc `Host` du fichier principal — OpenSSH 7.3+ requis. ## Bonus : se connecter directement en tant qu'autre utilisateur {#bonus-log-in-directly-as-another-user} Certains serveurs exigent que vous vous connectiez avec votre propre compte puis basculiez vers un utilisateur applicatif — `sudo su app_user` suivi de `cd /opt/project`. La directive `RemoteCommand` automatise toute la séquence depuis l'alias. Le générateur ci-dessous produit **deux alias par serveur** : un alias simple pour VSCode Remote Explorer (sans `RemoteCommand`) et une variante `_app` pour les sessions de terminal qui vous connecte directement en tant qu'utilisateur applicatif. ## Générez votre configuration complète {#generate-your-complete-config} Remplissez vos valeurs une fois et copiez le résultat directement dans `~/.ssh/config`. Pour un second projet, copiez-collez uniquement les alias de la section 3 et changez le nom du projet — le bastion et les valeurs globales sont déjà en place. > **Testez d'abord la connexion au bastion:** > > Avant de tester un alias complet, vérifiez que le bastion lui-même est joignable et accepte votre clé : > > ```bash > $ ssh vm-user@windows-vm-ip "echo bastion-ok" > bastion-ok > ``` > > `echo bastion-ok` est une commande non interactive — elle se termine immédiatement après l'affichage, sans laisser de session ouverte. Si la sortie apparaît, la section 1 et la section 4 sont correctement câblées. Si ça bloque ou échoue, le problème se situe entre votre machine et le bastion, indépendamment de tout alias de serveur. > **VSCode Remote Explorer ne fonctionne pas avec RemoteCommand:** > > VSCode Remote SSH injecte son propre processus serveur pendant la connexion. `RemoteCommand` remplace le shell distant avant que cette injection puisse avoir lieu — la connexion se ferme avant que VSCode ait pu prendre pied. Utilisez les alias simples `project_test` / `project_prod` pour Remote Explorer ; naviguez vers le répertoire de l'application depuis VSCode une fois connecté. Le `RemoteCommand` de la variante `_app` enchaîne deux choses : un `cd` vers le répertoire de l'application et un `exec bash -l` pour démarrer un shell de login en tant qu'utilisateur applicatif. `RequestTTY yes` alloue un pseudo-terminal — sans ça, la session se ferme immédiatement après la commande au lieu de vous donner un shell interactif. ```bash $ ssh project_test_app mass_upload@server-test:/opt/folder_devcontainer$ ``` ```bash $ ssh project_prod_app app-user@server-prod:/opt/project$ ``` Les quatre alias récupèrent automatiquement le `ProxyJump` global de la section 4. > **sudo doit être sans mot de passe — ou utilisez su:** > > `sudo -u `app-user dans un `RemoteCommand` ne fonctionne silencieusement que si le fichier sudoers accorde `NOPASSWD` à votre compte pour cet utilisateur précis. Sinon, la connexion se bloquera en attendant un mot de passe que SSH ne peut pas fournir avant que le TTY soit complètement établi. > > Si sudo sans mot de passe n'est pas configuré, utilisez plutôt `sudo su - `app-user — avec `RequestTTY yes`, le TTY est alloué avant l'exécution de la commande, donc la demande de mot de passe s'affiche normalement : > > ```text > RemoteCommand sudo su - app-user -c "cd /opt/project && exec bash -l" > ``` > > Vous verrez la demande de mot de passe sudo une fois, puis vous arriverez dans le shell. ## Avertissement pour les environnements de production {#production-environment-warning} Une bannière `RemoteCommand` n'est visible que par celui qui a configuré cet alias SSH. Le bon endroit pour un avertissement de production, c'est le **serveur lui-même** — ainsi il atteint tous les utilisateurs, quel que soit leur client. Déposez un script dans `/etc/profile.d/` sur le serveur. Chaque shell interactif source ce répertoire à la connexion : ```bash title="/etc/profile.d/env-banner.sh (on the server)" #!/usr/bin/env bash case "$(hostname -s)" in *prod*|*prd*) printf '\033[38;5;88;48;5;224m ⚠ Production server — be careful! \033[0m\n\n' export PS1='\[\033[38;5;88;48;5;224m\] ⚠ PROD \[\033[0m\] \u@\h:\w\$ ' ;; esac ``` La paire de couleurs `38;5;88` (bordeaux foncé) sur `48;5;224` (rose pâle) est clairement visible sans être agressive — un rappel, pas un panneau d'alarme. ```bash $ ssh project_prod_app ⚠ Production server — be careful! app-user@server-prod:/opt/project$ ``` Comme `exec bash -l` dans le `RemoteCommand` démarre un shell de login, les scripts de profil s'exécutent et la bannière apparaît aussi sur chaque connexion `_app`. Aucun changement nécessaire dans la config SSH. > **Détection par hostname vs fichier explicite:** > > Faire correspondre `hostname -s` à `*prod*` est pratique mais fragile — un host nommé `deployments-node` ne correspondrait à rien. Un fichier marqueur explicite est plus fiable : > > ```bash > if [[ -f /etc/env-type ]] && grep -qx "production" /etc/env-type; then > ``` > > Créez `/etc/env-type` avec le contenu `production` lors du provisioning d'un serveur. Le test est sans ambiguïté, quel que soit le hostname. ## Conclusion {#conclusion} Une seule stanza wildcard élimine la répétition de `ProxyJump` et `IdentityFile` dans chaque alias. Les stanzas de domaine ajoutent les noms d'utilisateur pour l'accès direct par FQDN. Les alias courts répètent `User` explicitement — parce que SSH fait la correspondance sur ce que vous tapez, pas sur ce que l'alias résout. Ajouter un nouveau serveur, c'est deux lignes : `HostName` et `User`. Le routage est déjà géré. Quand un serveur exige un utilisateur et un répertoire spécifiques, `RemoteCommand` transforme cette séquence en un simple alias. Et quand un serveur de production a besoin d'un avertissement visuel clair pour tout le monde, `/etc/profile.d/` est le bon endroit — pas la config SSH. Si vous devez [générer ou gérer vos clés SSH](/blog/github-connect-using-ssh), cet article couvre la génération de clés `ed25519` et l'autorisation GitHub depuis zéro — la même clé que celle utilisée dans la ligne `IdentityFile` ci-dessus. ---