Skip to content

Comment j'expose un port local de ma machine dans un cluster distant ?

Context

Aujourd'hui au travail, nous nous sommes posé la question de "Comment je pourrais exposer un port local de ma machine à mes pods déployés dans un cluster GKE ?". Pour ne pas nous simplifier la vie, nous voulions répondre à cette question sans utiliser ngrok et sans exposer une gateway sur internet. Un sacré défi !

L'idée

OpenSSH est notre ami

Mon idée fut la suivante :

"Et si j'ouvrais un tunnel SSH entre mon pod et mon interface réseau local ?"

Avant d'ouvrir un tunnel SSH, il me fallait déjà avoir un serveur SSH déployé dans mon cluster. Pour cela, j'ai décidé d'utiliser openssh et de l'installer manuellement pour vous expliquer chacune des étapes.

Le déploiement

Pour commencer, j'ai déployé un pod et un service Kubernetes. La seule chose importante est la valeur de containerPort qui sera le port utilisée comme port de destination de mon tunnel ssh ( les explications arrivent ).

apiVersion: v1
kind: Pod
metadata:
  name: my-gateway
  labels:
    app.kubernetes.io/name: gateway
spec:
  containers:
  - name: debian
    image: debian:latest
    command: ["tail", "-f", "/dev/null"]
    ports:
      - containerPort: 8080
        name: http
        protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
  name: gateway-service
spec:
  selector:
    app.kubernetes.io/name: gateway
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: http

Commande de déploiement : kubectl apply -f ./my-gateway.yaml.

La configuration

Une fois le pod déployé, il nous faut configurer OpenSSH :

  1. Se connecter au pod :
    kubectl exec -it my-gateway -- bash
    
  2. Mettre à jour les paquets apt et installer OpenSSH :
    apt-get update && apt-get install -y openssh-server
    
  3. Modifier le port par défaut d'OpenSSH pour éviter tout conflit :
    sed -i 's/#Port 22/Port 2300/g' /etc/ssh/sshd_config
    
  4. Activer les connexions provenant de tout hôte distant :
    sed -i 's/#GatewayPorts no/GatewayPorts yes/g' /etc/ssh/sshd_config
    
  5. Créer un répertoire pour un utilisateur root :
    mkdir /root/.ssh
    
  6. Dans un nouveau terminal, copier la clé publique de son poste local dans le pod :
    kubectl cp ~/.ssh/id_rsa.pub my-gateway:/root/.ssh/id_rsa.pub
    
  7. Se reconnecter au pod (reprendre l'étape 1).
  8. Ajouter la clé SSH dans le dossier des clés autorisées :
    cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
    
  9. Démarrer OpenSSH :
    service ssh start
    

Note

Toute cette configuration pourrait évidemment se trouver dans un Dockerfile.

L'API

Dans le cadre du POC j'ai développé une petite API basée sur Flask qui expose une route sur le chemin / et qui retourne le message Hello on my local computer :).

Voici le code :

from flask import Flask

app = Flask(__name__)

@app.route("/")
def home():
    return "Hello on my local computer :)"

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=8080)

Pour exécuter l'API :

  1. Il suffit d'installer flask : pip install flask
  2. Executer le code python : python main.py

On assemble le tout !

Une fois le serveur OpenSSH configuré et l'API exécutée, il faut maintenant assembler le tout. L'assemblage se passe en deux étapes :

  1. Créer le pont local entre notre pod gateway et notre interface locale :
    kubectl port-forward my-gateway 2300:2300
    
    Cette commande permet de créer un tunnel sur le port 2300 entre notre pod gateway déployé sur notre cluster distant et notre interface locale.
  2. Créer un reverse tunnel SSH afin d'exposer sur l'interface de notre pod gateway notre port local :
    ssh -R 0.0.0.0:8080:0.0.0.0:8080 root@127.0.0.1 -p 2300
    
    La commande ssh -R 0.0.0.0:8080:0.0.0.0:8080 root@127.0.0.1 -p 2300 est une commande utilisée pour établir une connexion sécurisée à un serveur distant en utilisant le protocole SSH.

Plus spécifiquement, cette commande crée un tunnel SSH inversé qui permet de rediriger le trafic de port à travers la connexion SSH entre une machine locale et une machine distante. Les options utilisées dans la commande sont les suivantes :

  • ssh est le nom de la commande utilisée pour établir une connexion SSH.
  • -R spécifie que nous souhaitons établir un tunnel SSH inversé.
  • 0.0.0.0:8080:0.0.0.0:8080 est la redirection de port que nous souhaitons mettre en place. Dans cet exemple, nous redirigeons tout le trafic entrant sur le port 8080 de la machine distante vers le port 8080 de la machine locale.
  • root@127.0.0.1 est l'adresse IP de la machine distante à laquelle nous souhaitons nous connecter. Dans cet exemple, nous nous connectons à la machine locale avec l'adresse IP 127.0.0.1 (c'est-à-dire la machine sur laquelle nous exécutons la commande).
  • -p 2300 spécifie que nous souhaitons utiliser le port SSH 2300 pour établir la connexion.

En résumé, cette commande crée un tunnel SSH inversé qui redirige tout le trafic entrant sur le port 8080 de la machine distante vers le port 8080 de la machine locale. C'est exactement ce que l'on veut !

Note

Pour en savoir plus je vous conseille cet excellent article

On teste

Pour tester, j'ai déployé un autre pod avec la commande suivante :

kubectl run -i --tty --rm --image debian test-gateway bash

Une fois le pod déployé, j'ai installé curl ( apt-get update && apt-get install -y curl) et j'ai essayé de joindre mon service :

root@test-gateway:/# curl http://gateway-service
Hello on my local computer :)

Bingo ! J'arrive bien à joindre mon port local à partir d'un pod distant 😄

Warning

Ce code n'est pas production ready et est bien évidemment perfectible.