Demande : « ne pas avoir le basic auth Traefik quand on est sur le Wi-Fi partagé avec le homelab ». La réponse intuitive — une règle `ClientIP(192.168.1.0/24)` — ne peut pas marcher, et la variante « je regarde CF-Connecting-IP » est un contournement d'authentification. Ce commit écrit la mesure pour qu'on ne re-découvre ni l'une ni l'autre. Mesuré (2026-07-28, homelab) : - `kadans.arcodange.fr` résout vers Cloudflare MÊME depuis le LAN, et redescend par le tunnel cloudflared. L'adresse de socket vue par Traefik est donc toujours celle d'un pod, en 10.42.x.x. - Traefik SAIT qui est le vrai client (journal d'accès : l'IPv6 de la maison, `2a01:cb04:dff:cf00::/64` — pas l'IPv4 qu'`ipify` injecte dans `localIp`), car l'entrypoint `web` fait confiance aux en-têtes venant de 10.42.0.0/16. - Mais le matcher de routeur `ClientIP()`, lui, juge la SOCKET. Prouvé par trois routeurs temporaires vers un Service sans endpoint (503 = règle matchée, 401 = repli sur le routeur normal) : témoin 503, `ClientIP(<IPv6 maison>)` 401, `ClientIP(10.42.0.0/16)` 503. Sondes retirées après mesure. D'où deux pièges, écrits là où on les rencontrerait : - `localIp@file` sur un routeur `.fr` laisserait entrer Internet entier, parce que `10.42.0.0/16` est dans son `sourceRange` et qu'`ipAllowList` juge lui aussi la socket. Vérifié : aucun routeur `.fr` ne le porte aujourd'hui — le piège est latent, et les cinq endroits d'où on pourrait le recopier (2 gabarits `.fr`, 3 values `.lab`) portent désormais l'avertissement. - Un en-tête posé par le client ne peut pas piloter une exemption d'auth : Cloudflare ne retire pas les en-têtes inconnus, et Traefik reste joignable en direct sur 192.168.1.201. Voie retenue, et pourquoi celle-là : la décision « suis-je à la maison ? » doit être prise là où la vraie IP est native et non falsifiable, donc au bord, chez Cloudflare. Une règle de transformation y pré-remplit l'en-tête `Authorization` pour les IP du foyer. Traefik ne bouge pas, aucun certificat public à produire, aucun changement DNS. Surface d'attaque ajoutée : AUCUNE — `kadans:kkadans` est déjà en clair dans le chart de kadans, Cloudflare ne fait que le taper à notre place. Et quand l'IP du foyer dérive, la règle cesse de matcher : le navigateur redemande le mot de passe. Dégradation douce, pas de panne. Le doc écrit l'action Cloudflare mot pour mot (expression, en-tête, valeur, et l'équivalent Terraform pour `cms/cloudflare`) plutôt que de la supposer faite, avec les deux commandes qui la vérifient — et nomme le repli si Cloudflare refuse de modifier `Authorization` : Cloudflare Access avec une politique de bypass, PAS un en-tête secret. Enfin, un contre-exemple utile : le bouncer CrowdSec, lui, juge la VRAIE IP (journal à l'appui). Son `clientTrustedIPs` contient `10.42.0.0/16` sans que ce soit un trou — noté pour que personne ne « corrige » cette ligne en croyant y reproduire le piège. ⚠ Aucun des réglages en cause ne vit dans ce dépôt : les valeurs Helm de Traefik, `dynamic.yaml`/`localIp` et le Middleware crowdsec sont dans `factory` (ansible), le tunnel et le DNS dans `cms`. Ce dépôt reçoit la mesure et les garde-fous parce que c'est lui qui héberge les gabarits `.fr` et les consommateurs de `localIp@file`. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01GdUCA5Uz8QyMwa2P4Pg2hK
182 lines
6.1 KiB
YAML
182 lines
6.1 KiB
YAML
image:
|
|
# -- The registry to pull the image from.
|
|
registry: ghcr.io
|
|
# -- The repository to pull the image from.
|
|
repository: plausible/community-edition
|
|
# -- The docker tag, if left empty chart's appVersion will be used.
|
|
# @default -- `.Chart.AppVersion`
|
|
tag: ''
|
|
# -- The pull policy for the controller.
|
|
pullPolicy: IfNotPresent
|
|
|
|
nameOverride: ''
|
|
fullnameOverride: ''
|
|
|
|
controller:
|
|
# -- Create a workload for this chart.
|
|
enabled: true
|
|
# -- Type of the workload object.
|
|
kind: Deployment
|
|
# -- The number of replicas.
|
|
replicas: 1
|
|
# -- Additional annotations for the controller object.
|
|
annotations: {}
|
|
# -- Additional labels for the controller object.
|
|
labels: {}
|
|
|
|
service:
|
|
# -- Create a service for exposing this chart.
|
|
enabled: true
|
|
# -- The service type used.
|
|
type: ClusterIP
|
|
# -- ClusterIP used if service type is `ClusterIP`.
|
|
clusterIP: ''
|
|
# -- LoadBalancerIP if service type is `LoadBalancer`.
|
|
loadBalancerIP: ''
|
|
# -- Allowed addresses when service type is `LoadBalancer`.
|
|
loadBalancerSourceRanges: []
|
|
# -- Additional annotations for the service object.
|
|
annotations: {}
|
|
# -- Additional labels for the service object.
|
|
labels: {}
|
|
|
|
serviceMonitor:
|
|
# -- Create a service monitor for prometheus operator.
|
|
enabled: false
|
|
# -- How frequently the exporter should be scraped.
|
|
interval: 30s
|
|
# -- Timeout value for individual scrapes.
|
|
timeout: 10s
|
|
# -- Additional annotations for the service monitor object.
|
|
annotations: {}
|
|
# -- Additional labels for the service monitor object.
|
|
labels: {}
|
|
|
|
ingressRoute:
|
|
# -- Create an IngressRoute object for exposing this chart.
|
|
create: true
|
|
# -- List of [entry points](https://doc.traefik.io/traefik/routing/routers/#entrypoints) on which the ingress route will be available.
|
|
entryPoints: [websecure]
|
|
# -- [Matching rule](https://doc.traefik.io/traefik/routing/routers/#rule) for the underlying router.
|
|
rule: Host(`analytics.arcodange.lab`)
|
|
# -- List of [middleware objects](https://doc.traefik.io/traefik/routing/providers/kubernetes-crd/#kind-middleware) for the ingress route.
|
|
middlewares:
|
|
# ⚠ Valable parce que la règle ci-dessus est en `.lab` (le client arrive en direct
|
|
# sur 192.168.1.201). À NE PAS recopier sur un hôte `.fr` : cf. doc/ce-que-traefik-voit.md §3.
|
|
- name: localIp@file
|
|
# -- Use an existing secret containing the TLS certificate.
|
|
tlsSecretName: ''
|
|
# -- Additional annotations for the ingress route object.
|
|
annotations: {}
|
|
# -- Additional labels for the ingress route object.
|
|
labels: {}
|
|
|
|
certificate:
|
|
# -- Create an Certificate object for the exposed chart.
|
|
create: false
|
|
# -- List of subject alternative names for the certificate.
|
|
dnsNames: []
|
|
# -- Name of the secret in which the certificate will be stored. Defaults to the first item in dnsNames.
|
|
secretName: ''
|
|
issuerRef:
|
|
# -- Type of the referenced certificate issuer. Can be "Issuer" or "ClusterIssuer".
|
|
kind: ClusterIssuer
|
|
# -- Name of the referenced certificate issuer.
|
|
name: ''
|
|
# -- Additional annotations for the certificate object.
|
|
annotations: {}
|
|
# -- Additional labels for the certificate object.
|
|
labels: {}
|
|
|
|
env:
|
|
# -- Timezone for the container.
|
|
- name: TZ
|
|
value: Europe/Paris
|
|
|
|
ports:
|
|
http:
|
|
# -- Enable the port inside the `Deployment` and `Service` objects.
|
|
enabled: true
|
|
# -- The port used as internal port and cluster-wide port if `.service.type` == `ClusterIP`.
|
|
port: 8000
|
|
# -- The external port used if `.service.type` == `NodePort`.
|
|
nodePort: null
|
|
# -- The protocol used for the service.
|
|
protocol: TCP
|
|
|
|
secret:
|
|
# -- Create a new secret object.
|
|
create: false
|
|
# -- Use an existing secret object.
|
|
existingSecret: 'plausible-config'
|
|
# -- Secret values used when not using an existing secret. Helm templates are supported for values.
|
|
values:
|
|
# -- Secret key for session tokens.
|
|
SECRET_KEY_BASE: '{{ randAlphaNum 42 | b64enc }}'
|
|
# -- Encryption token for TOTP secrets.
|
|
TOTP_VAULT_KEY: '{{ randAlphaNum 32 | b64enc }}'
|
|
|
|
# -- Additional annotations for the secret object.
|
|
annotations: {}
|
|
# -- Additional labels for the secret object.
|
|
labels: {}
|
|
|
|
geoip:
|
|
# -- Enable support for MaxMinds GeoLite2 database.
|
|
enabled: true
|
|
image:
|
|
# -- The repository for the geoip image.
|
|
repository: ghcr.io/maxmind/geoipupdate
|
|
# -- The docker tag for the geoip image.
|
|
tag: v7.1.1
|
|
# -- Required. MaxMind account ID.
|
|
accountId: '1266329'
|
|
# -- Required. Case-sensitive MaxMind license key.
|
|
# licenseKey: 'kvv2/data/plausible/geoip LICENSE_KEY'
|
|
# -- Optional. Database update frequency. Defaults to "168" which equals 7 days.
|
|
frequency: 168
|
|
# -- Optional. Specify the database mount path inside the containers.
|
|
mountPath: /geoip
|
|
|
|
serviceAccount:
|
|
# -- Create a `ServiceAccount` object.
|
|
create: true
|
|
# -- Specify the service account used for the controller.
|
|
name: ''
|
|
# -- Additional annotations for the service account object.
|
|
annotations: {}
|
|
# -- Additional labels for the service account object.
|
|
labels: {}
|
|
|
|
# -- Pod-level security attributes. More info [here](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#security-context).
|
|
securityContext: {}
|
|
# fsGroup: 1000
|
|
# runAsNonRoot: true
|
|
# runAsGroup: 1000
|
|
# runAsUser: 1000
|
|
|
|
# -- Compute resources used by the container. More info [here](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/).
|
|
resources: {}
|
|
# limits:
|
|
# cpu: 100m
|
|
# memory: 128Mi
|
|
# requests:
|
|
# cpu: 100m
|
|
# memory: 128Mi
|
|
|
|
# -- Pod-level affinity. More info [here](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling).
|
|
affinity: {}
|
|
# nodeAffinity:
|
|
# requiredDuringSchedulingIgnoredDuringExecution:
|
|
# nodeSelectorTerms:
|
|
# - matchExpressions:
|
|
# - key: kubernetes.io/hostname
|
|
# operator: In
|
|
# values:
|
|
# - my-node-xyz
|
|
|
|
# -- Pod-level tolerations. More info [here](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling).
|
|
tolerations: []
|
|
# - key: node-role.kubernetes.io/control-plane
|
|
# operator: Exists
|
|
# effect: NoSchedule |