diff --git a/admin/k8s/public-domain.md b/admin/k8s/public-domain.md new file mode 100644 index 0000000..42a0abe --- /dev/null +++ b/admin/k8s/public-domain.md @@ -0,0 +1,106 @@ +# 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 conservés** : le hub reste aussi joignable en `kaya.freeboxos.fr:9081/9080` + (bascule non destructive). + +## 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/tcp` → `funk-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) : + +```bash +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) + +```bash +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) + +```bash +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 : + +```yaml +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). + +## Intégration auth par app + +| App | Mode | Note | +|---|---|---| +| Grafana, Open WebUI, ArgoCD | **OIDC natif** | redirect_uri sur l'URL publique `*.funklab.online` | +| Prometheus, Alertmanager, n8n, SearXNG | **Proxy Provider Authentik** | middleware forward-auth Traefik (app sans login propre) | + +Détail de l'ajout d'une app au hub : `admin/k8s/auth-portal-admin.md`. + +## 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://.funklab.online/...` (en plus des URLs internes si conservées). diff --git a/ansible/group_vars/all/vars.yml b/ansible/group_vars/all/vars.yml index ebb489d..4695642 100644 --- a/ansible/group_vars/all/vars.yml +++ b/ansible/group_vars/all/vars.yml @@ -1,6 +1,7 @@ --- admin_user: alkatrazz dns_domain: lab.local +public_domain: funklab.online # domaine public (OVH) — apps exposées en HTTPS via Traefik ntp_servers: - 0.fr.pool.ntp.org - 1.fr.pool.ntp.org diff --git a/ansible/roles/dnsmasq/templates/dnsmasq.conf.j2 b/ansible/roles/dnsmasq/templates/dnsmasq.conf.j2 index 4d0fe00..bc0d27f 100644 --- a/ansible/roles/dnsmasq/templates/dnsmasq.conf.j2 +++ b/ansible/roles/dnsmasq/templates/dnsmasq.conf.j2 @@ -28,6 +28,11 @@ address=/{{ host.name }}/{{ host.ip }} # Wildcard *.lab.local → IP MetalLB Traefik (ingress) address=/.{{ dns_domain }}/{{ traefik_ip }} +# Split-horizon : *.funklab.online résolu en interne vers Traefik (le LAN utilise +# les mêmes URLs publiques sans sortir sur Internet ; l'extérieur passe par le DNS +# OVH → IP publique → Freebox → s01). Ajouter ici chaque nouveau domaine public. +address=/{{ public_domain }}/{{ traefik_ip }} + # DHCP — plage dynamique pour le LAN cluster dhcp-range={{ dhcp_range_start }},{{ dhcp_range_end }},{{ dhcp_lease_time }} dhcp-option=option:dns-server,{{ lan_ip }} diff --git a/ansible/roles/gateway/templates/nftables.conf.j2 b/ansible/roles/gateway/templates/nftables.conf.j2 index 5ac64d2..457fa0c 100644 --- a/ansible/roles/gateway/templates/nftables.conf.j2 +++ b/ansible/roles/gateway/templates/nftables.conf.j2 @@ -86,6 +86,11 @@ table inet filter { ip daddr 192.168.10.202 tcp dport 8080 ct state new limit rate 30/minute burst 15 packets accept ip daddr { 192.168.10.201, 192.168.10.202 } ct state established,related accept + # Domaine public funklab.online — Internet DNATé (80/443) → Traefik (.200). + # Routage par sous-domaine + TLS Let's Encrypt côté Traefik. Rate-limit idem. + ip daddr 192.168.10.200 tcp dport { 80, 443 } ct state new limit rate 60/minute burst 30 packets accept + ip daddr 192.168.10.200 tcp dport { 80, 443 } ct state established,related accept + # WireGuard → lab : filtrage par pair (host_vars wireguard_peers) {% for peer in wireguard_peers | selectattr('access', 'equalto', 'full') | list %} # {{ peer.name }} — accès complet @@ -117,6 +122,9 @@ table ip nat { # kaya.freeboxos.fr:9081 → Authentik (VIP .201) ; :9080 → Guacamole (VIP .202) iif {{ wan_interface }} tcp dport 9081 dnat to 192.168.10.201:9000 iif {{ wan_interface }} tcp dport 9080 dnat to 192.168.10.202:8080 + + # funklab.online — Freebox 80/443 → Traefik (routage par sous-domaine + TLS) + iif {{ wan_interface }} tcp dport { 80, 443 } dnat to 192.168.10.200 } chain postrouting { diff --git a/k8s/infra/traefik/redirect.yaml b/k8s/infra/traefik/redirect.yaml new file mode 100644 index 0000000..7f7281c --- /dev/null +++ b/k8s/infra/traefik/redirect.yaml @@ -0,0 +1,29 @@ +# Redirection HTTP→HTTPS scopée à funklab.online (ne touche pas *.lab.local). +# Appliqué normalement (kubectl apply / ArgoCD infra) — CRD Traefik. +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: redirect-https + namespace: infra +spec: + redirectScheme: + scheme: https + permanent: true +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: funklab-http-redirect + namespace: infra +spec: + entryPoints: + - web + routes: + - match: HostRegexp(`^.+\.funklab\.online$`) + kind: Rule + middlewares: + - name: redirect-https + services: + # Service factice requis par le schéma ; la middleware redirige avant d'y aller + - name: noop@internal + kind: TraefikService diff --git a/k8s/infra/traefik/values.yaml b/k8s/infra/traefik/values.yaml new file mode 100644 index 0000000..64cd3b4 --- /dev/null +++ b/k8s/infra/traefik/values.yaml @@ -0,0 +1,53 @@ +# Traefik (ingress cluster) — valeurs Helm +# ⚠️ Traefik est installé/mis à jour HORS ArgoCD (helm au bootstrap, cf. +# admin/k8s/talos.md). Ce fichier versionne la config ; l'appliquer avec : +# helm upgrade traefik traefik/traefik -n infra -f k8s/infra/traefik/values.yaml +# Chart traefik-40.1.0 (Traefik v3.7.1). + +# --- Réplique de la config existante (helm get values) --- +service: + type: LoadBalancer + loadBalancerIP: 192.168.10.200 # VIP MetalLB — NE PAS changer +ingressRoute: + dashboard: + enabled: true +logs: + general: + level: INFO +ports: + web: + port: 80 + websecure: + port: 443 + +# --- Let's Encrypt (HTTP-01, un certificat par sous-domaine) --- +# Pas de redirection HTTP→HTTPS globale : les services internes en *.lab.local +# resteraient sinon cassés (Let's Encrypt ne certifie pas .lab.local). La redirection +# est scopée à funklab.online via une IngressRoute dédiée (k8s/infra/traefik/redirect.yaml). +certificatesResolvers: + letsencrypt: + acme: + email: aliyesilkaya93@gmail.com + storage: /data/acme.json + httpChallenge: + entryPoint: web + +# Stockage persistant de acme.json (sinon ré-émission à chaque redémarrage → +# risque de rate-limit Let's Encrypt) +persistence: + enabled: true + name: data + accessMode: ReadWriteOnce + size: 128Mi + storageClass: nfs + path: /data + +# acme.json doit être en 0600, sinon Traefik refuse de l'utiliser +deployment: + initContainers: + - name: volume-permissions + image: busybox:1.36 + command: ["sh", "-c", "touch /data/acme.json && chmod 600 /data/acme.json"] + volumeMounts: + - name: data + mountPath: /data