Funk-lab/admin/k8s/public-domain.md
ALI YESILKAYA fe0aa89ffa
feat(security): durcissement edge funklab.online (vraie IP client + rate-limit + TLS/headers) (#95)
- Traefik externalTrafficPolicy: Local → IP client réelle (fin du SNAT kube-proxy)
- middleware ratelimit par IP (100/min, burst 200) sur les 6 routes publiques
- middleware security-headers (HSTS, nosniff, X-Frame SAMEORIGIN, Referrer-Policy)
- TLSOption globale : plancher TLS 1.2 + ciphers modernes
- Guacamole REMOTE_IP_VALVE → ban anti-bruteforce par IP (fin du piège n°5)
- nftables : fermeture des ports Internet legacy 9080/9081 (HTTP clair redondant)
- allowCrossNamespace pour mutualiser les middlewares depuis infra
- docs : section Durcissement (public-domain.md) + CAA OVH + pièges à jour

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 22:58:24 +02:00

8.6 KiB

Domaine public + HTTPS — funklab.online

Exposition des applications du cluster sur Internet via un domaine public (funklab.online, OVH) avec routage par sous-domaine et TLS Let's Encrypt. Une seule redirection Freebox (80+443) pour toutes les apps.

Architecture

Internet ─▶ grafana.funklab.online ─┐  DNS OVH : *.funklab.online → 82.67.223.17
           chat.funklab.online      │
           auth.funklab.online      │
                                    ▼
                          [ Freebox ] 80 + 443 → funk-s01
                                    ▼
                    s01 (DNAT nftables + rate-limit) → Traefik (VIP .200)
                                    ▼
                 Traefik : route par Host + cert Let's Encrypt (HTTP-01) → l'app
  • Split-horizon : dnsmasq résout *.funklab.online → Traefik (192.168.10.200) en interne → le LAN utilise les mêmes URLs sans sortir sur Internet.
  • Ports historiques 9080/9081 fermés côté Internet (durcissement 2026-07-07) : l'accès direct HTTP clair à Authentik/Guacamole n'existe plus depuis le WAN — tout passe par le domaine en HTTPS. Les VIPs .201/.202 restent joignables en LAN. (Penser à retirer aussi les redirections Freebox 9080/9081.)

Prérequis (une fois, côté fournisseurs)

  1. OVH — zone DNS funklab.online : enregistrement A, sous-domaine * (wildcard), cible 82.67.223.17. (Propagation quelques minutes à ~1 h.)
  2. Freebox : redirections 80/tcp et 443/tcpfunk-s01.

Vérifier la résolution : dig +short grafana.funklab.online → doit rendre 82.67.223.17.

Déploiement

1. Traefik — Let's Encrypt (hors ArgoCD, helm)

Config versionnée : k8s/infra/traefik/values.yaml. Toujours pinner la version du chart (sinon helm upgrade tire la dernière → risque de casse de l'ingress cluster) :

helm repo update traefik
helm upgrade traefik traefik/traefik -n infra --version 40.1.0 \
  -f k8s/infra/traefik/values.yaml
kubectl -n infra rollout status deploy/traefik

Rollback immédiat si l'ingress tombe : helm rollback traefik -n infra.

2. Redirection HTTP→HTTPS (scopée funklab.online)

kubectl apply -f k8s/infra/traefik/redirect.yaml

⚠️ Pas de redirection globale : les services internes *.lab.local resteraient cassés (Let's Encrypt ne certifie pas .lab.local).

3. Réseau (Ansible)

ansible-playbook -i inventory.yml playbooks/storage-01.yml --tags gateway,dnsmasq
  • gateway : DNAT 80/443 → Traefik + rate-limit
  • dnsmasq : *.funklab.online → Traefik (variable public_domain)

4. Exposer une app en HTTPS

Ajouter/adapter une IngressRoute sur l'entrypoint websecure avec le certResolver :

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata: { name: grafana-public, namespace: monitoring }
spec:
  entryPoints: [websecure]
  routes:
    - match: Host(`grafana.funklab.online`)
      kind: Rule
      services:
        - name: kube-prometheus-stack-grafana
          port: 80
  tls:
    certResolver: letsencrypt     # ← émission auto du cert à la 1re visite

Le certificat est émis automatiquement au premier accès (challenge HTTP-01 sur le port 80).

Apps exposées (état 2026-07-07)

Toutes en HTTPS (cert Let's Encrypt par sous-domaine), accessibles depuis le hub Authentik (https://auth.funklab.online) et directement par leur URL.

App URL publique Auth Redirect URI OIDC (Authentik)
Portail consoles (Guacamole) portail.funklab.online SSO OIDC https://portail.funklab.online/
Grafana grafana.funklab.online SSO OIDC (lab-admins→Admin, sinon Viewer) https://grafana.funklab.online/login/generic_oauth
Open WebUI openwebui.funklab.online SSO OIDC https://openwebui.funklab.online/oauth/oidc/callback
Argo CD argocd.funklab.online SSO OIDC (lab-admins→admin) https://argocd.funklab.online/auth/callback
n8n n8n.funklab.online login natif (SSO = Enterprise, indispo en communautaire)
  • Chaque app OIDC : provider Authentik Confidential, client secret archivé au vault (vault_<app>_oauth_client_secret / vault_argocd_oidc_client_secret) et dans un secret k8s. token_url/discovery joignent Authentik server-side via hairpin (vérifié : un pod atteint auth.funklab.online).
  • Non exposés (volontaire) : Prometheus, Alertmanager, SearXNG — pas de login propre, nécessiteraient un Proxy Provider (forward-auth) avant toute exposition Internet.

Manifests : k8s/apps/*/ingress-public.yaml, k8s/infra/monitoring/ingressroute-public.yaml, k8s/argocd-bootstrap/ingressroute-public.yaml. Config OIDC : Grafana/ArgoCD dans leurs values.yaml helm, Guacamole/Open WebUI dans leur deployment.yaml.

Intégration auth — comment ajouter une app

Type d'app Mode Étapes
App avec OIDC natif (Grafana, Open WebUI, ArgoCD…) OIDC provider Confidential + app (slug) dans Authentik, config OIDC côté app avec l'URL publique, IngressRoute websecure
App sans login (Prometheus…) Proxy Provider outpost Authentik + middleware forward-auth Traefik (non déployé à ce jour)
App avec login propre non-OIDC (n8n…) route seule IngressRoute websecure + tuile-lien Authentik (comptes gérés dans l'app)

Détail de l'ajout d'une app au hub : admin/k8s/auth-portal-admin.md.

Durcissement (edge) — état 2026-07-07

Couche de sécurité appliquée à toutes les routes funklab.online. Manifests : k8s/infra/traefik/{values.yaml,middlewares.yaml,tls-options.yaml} (Traefik hors ArgoCDkubectl apply + helm upgrade).

Mesure Effet
IP client réelle service.spec.externalTrafficPolicy: Local (values.yaml) Traefik voit l'IP source réelle (sinon SNAT kube-proxy → IP d'un nœud). Prérequis du rate-limit par IP et du ban Guacamole par IP.
Rate-limit par IP Middleware ratelimit (infra) sur chaque route ~100 req/min soutenu, burst 200, par IP client. Anti-scan/bruteforce volumétrique.
En-têtes sécurité Middleware security-headers (infra) HSTS 1 an+subdomains, nosniff, X-Frame-Options: SAMEORIGIN, Referrer-Policy.
TLS ≥ 1.2 TLSOption globale default/default Plancher TLS 1.2 + ciphers modernes (TLS 1.3 auto).
Ban Guacamole par IP REMOTE_IP_VALVE_ENABLED (deployment guacamole) Le ban 5-échecs vise l'IP réelle, plus tout le monde (fin du piège n°5).
Ports 9080/9081 fermés (WAN) rôle gateway (nftables) Supprime le chemin HTTP clair direct vers Authentik/Guacamole.

Les middlewares du namespace infra sont référencés cross-namespace → providers.kubernetesCRD.allowCrossNamespace: true requis (values.yaml).

Cross-check DNS (à faire côté OVH) : ajouter un enregistrement CAA pour restreindre l'émission de certificats à Let's Encrypt uniquement :

funklab.online.  CAA  0 issue "letsencrypt.org"
funklab.online.  CAA  0 iodef "mailto:aliyesilkaya93@gmail.com"

Application (Traefik est hors ArgoCD) :

helm upgrade traefik traefik/traefik -n infra --version 40.1.0 \
  -f k8s/infra/traefik/values.yaml           # externalTrafficPolicy + allowCrossNamespace
kubectl apply -f k8s/infra/traefik/middlewares.yaml
kubectl apply -f k8s/infra/traefik/tls-options.yaml
kubectl apply -f k8s/argocd-bootstrap/ingressroute-public.yaml   # ArgoCD non auto-sync
ansible-playbook -i inventory.yml playbooks/storage-01.yml --tags gateway   # ports legacy

Les IngressRoutes des apps ai/auth/monitoring sont synchronisées par ArgoCD (merge main).

Vérifier :

curl -sI https://auth.funklab.online | grep -i 'strict-transport\|x-frame'
nmap --script ssl-enum-ciphers -p 443 auth.funklab.online | grep -i tlsv1

Pistes suivantes (non faites) : MFA/TOTP Authentik (protège tout le hub), CrowdSec bouncer Traefik (réputation IP), restreindre ArgoCD au LAN+VPN (IPAllowList).

Pièges

  1. Cert bloqué en « TRAEFIK DEFAULT CERT » : DNS pas encore propagé ou Freebox 80 non redirigé → le challenge HTTP-01 échoue. Vérifier dig + la redirection. Logs : kubectl -n infra logs deploy/traefik | grep -i acme.
  2. Rate-limit Let's Encrypt (5 certs identiques/semaine) : ne pas boucler les redéploiements ; acme.json est persistant (PVC nfs) pour éviter la ré-émission.
  3. helm upgrade sans --version → tire la dernière chart : toujours pinner.
  4. redirect_uri OIDC : chaque provider Authentik doit lister l'URL publique https://<app>.funklab.online/... (en plus des URLs internes si conservées).