Aller au contenu

🧰 Installation de Teleport avec Traefik en front-end

Dans un précédent article, j'avais documenté l'installation bare-metal de Teleport. Voyons maintenant comment faire avec Docker qui va nous permettre de déployer un serveur Teleport en quelques minutes.

Je scinderai la documentation en deux parties : une avec Traefik seul et une autre avec HAProxy/Traefik. Pourquoi HAProxy ? Il permet de router le traffic vers différents reverse-proxy suivant les url saisies (sans casser le chiffrement), ce qui est très pratique quand on veut héberger plusieurs infrastructures (prod, preprod, dev, lab...) avec une seule et même IP publique.

👋 Teleport

L'application bastion Teleport est une solution open-source développée par Teleport Inc. pour la gestion des accès sécurisés à des infrastructures informatiques. Elle propose des fonctionnalités telles que l'accès sécurisé aux serveurs SSH, aux machines Windows, aux clusters Kubernetes, aux bases de données, et aux applications internes, tout en centralisant la gestion des identités et des autorisations.

Dans cet exemple, Teleport nous servira de porte d'entrée depuis l'extérieur pour prendre la main sur des serveurs non exposés sur Internet. En tête de l'infra, j'ai mis OPNSense.

Schéma de principe

📋 Prérequis

Avant de se lancer il faut préparer les DNS locaux et du registrar. Dans cet exemple, les ndd seront :

  • teleport.prod-1.domaine.fr
  • *.teleport.prod-1.domaine.fr

Cas du DNS sur le LAN

Dans mon cas, j'utilise Pi-Hole pour mon LAN. Il suffit de déclarer le RR teleport.prod-1.domaine.fr rattaché à l'IP (ici 10.100.1.253) du serveur faisant tourner Docker depuis l'interface web de PI-Home. De même j'ai créé un RR du type CNAME de *.teleport.prod-1.domaine.fr associé à teleport.prod-1.domaine.fr.

Il faut également déclaré le RR *.teleport.prod-1.domaine.fr pointant vers la même IP de cette manière dans PI-Hole :

Configuration de PI-Hole
vim /etc/dnsmasq.d/07-my-teleport-dns.conf
address=/teleport.prod-1.domaine.fr/10.100.1.253

# Relance du service
service pihole-FTL restart

# Vérification
dig +short teleport.prod-1.domaine.fr
10.100.1.253

dig +short nddtemp.teleport.prod-1.domaine.fr
10.100.1.253

🧰 Installation de Teleport avec Traefik en frontend

Traefik

Création arborescence et fichiers
mkdir -p traefik/{config/{dyn_traefik,secrets},logs}
touch config/acme.json
chmod 600 config/acme.json

traefik/
|-- config/
|   |-- acme.json
|   |-- dyn_traefik/
|   |-- secrets/
`-- logs/

Les fichiers contenant les secrets "ovh_*" seront contenus dans le répertoire traefik/config/secrets. Ils seront utilisés pour l'authentification sur l'API d'OVH dans le cadre de la génération du certificat TLS avec Let's Encrypt et le challenge DNS.

traefik/compose.yml
---
networks:
  traefik:
    external: true

secrets:
  ovh_endpoint:
    file: "./config/secrets/ovh_endpoint.secret"
  ovh_application_key:
    file: "./config/secrets/ovh_application_key.secret"
  ovh_application_secret:
    file: "./config/secrets/ovh_application_secret.secret"
  ovh_consumer_key:
    file: "./config/secrets/ovh_consumer_key.secret"

services:
  traefik:
    image: "traefik:v3"
    container_name: traefik
    networks:
      - traefik
    secrets:
      - "ovh_endpoint"
      - "ovh_application_key"
      - "ovh_application_secret"
      - "ovh_consumer_key"
    environment:
      - "TZ=Europe/Paris"
      - "OVH_ENDPOINT_FILE=/run/secrets/ovh_endpoint"
      - "OVH_APPLICATION_KEY_FILE=/run/secrets/ovh_application_key"
      - "OVH_APPLICATION_SECRET_FILE=/run/secrets/ovh_application_secret"
      - "OVH_CONSUMER_KEY_FILE=/run/secrets/ovh_consumer_key"
    volumes:
      # Mapping sur le socket interne de Docker
      - '/var/run/docker.sock:/var/run/docker.sock:ro'
      # Mapping du fichier de configuration statique
      - './config/traefik.yml:/traefik.yml'
      # Mapping du dossier contenant la configuration dynamique
      - './config/dyn_traefik/:/dyn_traefik/'
      # Mapping du fichier de stockage des certificats
      - './config/acme.json:/acme.json'
      - "./logs:/var/log"
    ports:
      - "80:80"
      - "443:443"
    restart: unless-stopped
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.traefik-dashboard.rule=Host(`traefik.prod-1.domaine.fr`)"
      - "traefik.http.routers.traefik-dashboard.service=api@internal"
      - "traefik.http.routers.traefik-dashboard.entrypoints=websecure"
      - "traefik.http.routers.traefik-dashboard.tls.certresolver=letsencrypt"
      - "traefik.http.routers.traefik-dashboard.middlewares=traefik-dashboard-ipallowlist"
      - "traefik.http.middlewares.traefik-dashboard-ipallowlist.ipallowlist.sourcerange=127.0.0.1/32, 192.168.1.0/24"
      - "traefik.http.services.traefik-dashboard-service.loadbalancer.server.port=8080"
config/traefik.yml
---
api:
  dashboard: true

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false
  file:
    directory: /dyn_traefik/
    watch: true

entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https

  websecure:
    address: ":443"

certificatesResolvers:
  letsencrypt:
    acme:
      caServer: "https://acme-v02.api.letsencrypt.org/directory"
      email: "VOTRE_EMAIL"
      storage: "/acme.json"
      keyType: EC384
      dnsChallenge:
        provider: ovh
        delayBeforeCheck: 10
        resolvers:
          - "1.1.1.1:53"
          - "8.8.8.8:53"

# La connexion à Teleport se fera à travers 
# une liaison https avec un certificat auto-signé
serversTransport:
  insecureSkipVerify: true

log:
  filePath: "/var/log/traefik.log"
  format: json
  level: INFO
  maxSize: 5
  maxBackups: 50
  maxAge: 10
  compress: true

accessLog:
  filePath: "/var/log/access.log"
  format: json
  fields:
    defaultMode: keep
    names:
      StartUTC: drop

Lancez la commande docker compose pull. Nous lancerons les conteneurs après avoir la rotation des logs.

Lancement du conteneur Traefik
docker network create traefik
docker compose pull
docker compose up -d

Teleport

Création arborescence et fichiers
mkdir -p teleport/{config/data}
teleport/compose.yml
---
networks:
  traefik:
    external: true
services:
  teleport:
    image: public.ecr.aws/gravitational/teleport-distroless:17.4.2
    container_name: teleport
    volumes:
      - ./config:/etc/teleport
      - ./data:/var/lib/teleport
    labels:
      - "traefik.enable=true"
      - "traefik.http.services.teleport-prod.loadbalancer.server.port=3080"
      - "traefik.http.services.teleport-prod.loadbalancer.server.scheme=https"
      - "traefik.http.routers.teleport-prod-https.entrypoints=websecure"
      - "traefik.http.routers.teleport-prod-https.rule=Host(`teleport.maison.raspot.in`) || HostRegexp(`^.+.teleport.prod-1.domaine.fr$`)"
      - "traefik.http.routers.teleport-prod-https.tls=true"
      - "traefik.http.routers.teleport-prod-https.tls.certresolver=letsencrypt"
      - "traefik.http.routers.teleport-prod-https.tls.domains[0].main=teleport.prod-1.domaine.fr"
      - "traefik.http.routers.teleport-prod-https.tls.domains[0].sans=*.teleport.prod-1.domaine.fr"
    networks:
      - traefik
    restart: unless-stopped

Configuration du service Teleport :

teleport/config/teleport.yml
version: v3
teleport:
  nodename: teleport.prod-1.domaine.fr
  data_dir: /var/lib/teleport
  log:
    output: stderr
    severity: INFO
    format:
      output: text
auth_service:
  enabled: yes
  listen_addr: 0.0.0.0:3025
  proxy_listener_mode: multiplex
  cluster_name: teleport.prod-1.domaine.fr
ssh_service:
  enabled: no
proxy_service:
  enabled: yes
  web_listen_addr: 0.0.0.0:3080
  public_addr: ['teleport.prod-1.domaine.fr:443']
  https_keypairs: []
  acme: {}
  trust_x_forwarded_for: true # Pour récupérer l'IP réelle du client et non celle du CT Traefik
Lancement du conteneur Teleport
docker network create traefik
docker compose pull
docker compose up -d

Et voilà ! Il ne vous reste plus qu'à vous connecter sur l'URL https://teleport.prod-1.domaine.fr ou bien en utilisant la commande curl.

Lancement du conteneur Teleport
curl https://teleport.prod-1.domaine.fr/ 
# Sortie
<a href="/web">Found</a>.

👤 Création du premier utilisateur

Création de l'utilisateur
cd /src/docker/teleport
docker compose exec teleport tctl users add bart.simpson --roles=editor,access --logins=bart.simpson
User "bart.simpson" has been created but requires a password. Share this URL with the user to complete user setup, link is valid for 1h:
https://teleport.prod-1.domaine.fr:443/web/invite/c98f4d8ecdda397e3fe4c72aaa2d5352

NOTE: Make sure teleport.prod-1.domaine.fr:443 points at a Teleport proxy which users can access.

Rendez-vous sur l'URL en question et renseignez le formulaire d'inscription.

alt text

Paramétrez ensuite le MFA (avec Vaultwarden par exemple) :

alt text

Et vous voilà connecté au dashboard de Teleport. Vous pouvez dès à présent ajouter des serveurs ou des applications.

🧰 Installation de Teleport avec HAProxy et Traefik

HAproxy

Extrait de /etc/haproxy/haproxy.cfg
(...)
frontend https
    bind *:443
    mode tcp

    option tcplog

    tcp-request inspect-delay 5s
    tcp-request content accept if { req_ssl_hello_type 1 }

    use_backend TRAEFIK_TELEPORT_HTTPS_BACKEND if { req_ssl_sni -i teleport.prod-1.domaine.fr }
    use_backend TRAEFIK_TELEPORT_HTTPS_BACKEND if { req_ssl_sni -m end .teleport.prod-1.domaine.fr }

backend TRAEFIK_TELEPORT_HTTPS_BACKEND
    mode tcp
    server TRAEFIK_TELEPORT_HTTPS_BACKEND01 10.1.1.253:443 send-proxy check
(...)

Important : vous devez mettre send-proxy et non send-proxy-v2 sinon vous ne pourrez pas connecter les nodes Teleport au serveur.

Traefik

Modifiez la partie entryPoints du fichier config/traefik.yml comme ceci :

Extrait de config/traefik.yml
---
(...)
entryPoints:
  web:
    address: ":80"
    forwardedHeaders:
      trustedIPs:
        - "127.0.0.1/32"
        - "10.100.1.254" # IP de HAProxy   
    proxyProtocol:
      trustedIPs:
        - "127.0.0.1/32"
        - "10.100.1.254" # IP de HAProxy
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https

  websecure:
    address: ":443"
    forwardedHeaders:
      trustedIPs:
        - "127.0.0.1/32"
        - "10.100.1.254" # IP de HAProxy   
    proxyProtocol:
      trustedIPs:
        - "127.0.0.1/32"
        - "10.100.1.254" # IP de HAProxy 
(...)

🏁 Conclusion

L'installation de Teleport est clairement plus simple avec Docker et Traefik que l'installation bare-metal. Maintenant que notre serveur est opérationnel, nous allons renforcer la sécurité et connecter des serveurs et des applications.