From 910b792452c5e24ef5c5627d776fe6d05fc7d824 Mon Sep 17 00:00:00 2001 From: Ruben Hensen Date: Fri, 23 Oct 2026 17:47:46 +0200 Subject: [PATCH] longhorn: log in through authentik on the Gateway Longhorn has no users of its own; nginx guarded it with basic auth using apr1 hashes, which Envoy Gateway does not support. An Envoy Gateway SecurityPolicy now sends visitors through an authentik OIDC application that only admins may use. The client secret lives in Vault at kv/longhorn-oidc and reaches both authentik and Envoy through External Secrets. --- apps/templates/authentik-helm.yaml | 17 ++++++ authentik/blueprint-longhorn-oidc.yaml | 53 ++++++++++++++++++ authentik/external-secret.yaml | 18 ++++++ gateway/gateway.yaml | 14 +++++ longhorn/httproute.yaml | 76 ++++++++++++++++++++++++++ longhorn/kustomization.yaml | 1 + 6 files changed, 179 insertions(+) create mode 100644 authentik/blueprint-longhorn-oidc.yaml create mode 100644 longhorn/httproute.yaml diff --git a/apps/templates/authentik-helm.yaml b/apps/templates/authentik-helm.yaml index 90b75ce..d252efa 100644 --- a/apps/templates/authentik-helm.yaml +++ b/apps/templates/authentik-helm.yaml @@ -86,6 +86,11 @@ spec: secretKeyRef: name: actualbudget-oidc-client-secret key: client_secret + - name: LONGHORN_OIDC_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: longhorn-oidc-client-secret + key: client_secret # Point OpenSSL and requests (which uses certifi otherwise) at # patched-ca-bundle, mounted below until the image drops the # compromised eMudhra intermediates. @@ -119,6 +124,9 @@ spec: - name: blueprint-session configMap: name: authentik-blueprint-session + - name: blueprint-longhorn + configMap: + name: authentik-blueprint-longhorn volumeMounts: - name: blueprint-vault mountPath: /blueprints/custom/vault-oidc.yaml @@ -141,6 +149,9 @@ spec: - name: blueprint-session mountPath: /blueprints/custom/session-duration.yaml subPath: session-duration.yaml + - name: blueprint-longhorn + mountPath: /blueprints/custom/longhorn-oidc.yaml + subPath: longhorn-oidc.yaml - name: patched-ca-bundle mountPath: /etc/ssl/certs readOnly: true @@ -181,6 +192,9 @@ spec: - name: blueprint-session configMap: name: authentik-blueprint-session + - name: blueprint-longhorn + configMap: + name: authentik-blueprint-longhorn volumeMounts: - name: blueprint-vault mountPath: /blueprints/custom/vault-oidc.yaml @@ -203,6 +217,9 @@ spec: - name: blueprint-session mountPath: /blueprints/custom/session-duration.yaml subPath: session-duration.yaml + - name: blueprint-longhorn + mountPath: /blueprints/custom/longhorn-oidc.yaml + subPath: longhorn-oidc.yaml - name: patched-ca-bundle mountPath: /etc/ssl/certs readOnly: true diff --git a/authentik/blueprint-longhorn-oidc.yaml b/authentik/blueprint-longhorn-oidc.yaml new file mode 100644 index 0000000..ffe15b8 --- /dev/null +++ b/authentik/blueprint-longhorn-oidc.yaml @@ -0,0 +1,53 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: authentik-blueprint-longhorn +data: + longhorn-oidc.yaml: | + version: 1 + metadata: + name: Longhorn OIDC + entries: + # Envoy Gateway does the login for Longhorn, which has no users of its + # own (see longhorn/httproute.yaml). + - model: authentik_providers_oauth2.oauth2provider + id: longhorn-provider + state: present + identifiers: + name: Longhorn + attrs: + name: Longhorn + authorization_flow: !Find [authentik_flows.flow, [slug, default-provider-authorization-implicit-consent]] + authentication_flow: !Find [authentik_flows.flow, [slug, default-authentication-flow]] + invalidation_flow: !Find [authentik_flows.flow, [slug, default-provider-invalidation-flow]] + client_type: confidential + client_id: longhorn + client_secret: !Env [LONGHORN_OIDC_CLIENT_SECRET, ""] + redirect_uris: + - matching_mode: strict + url: https://longhorn.rubenhensen.nl/oauth2/callback + # Envoy's test port until the switch from ingress-nginx + - matching_mode: strict + url: https://longhorn.rubenhensen.nl:10443/oauth2/callback + signing_key: !Find [authentik_crypto.certificatekeypair, [name, "authentik Self-signed Certificate"]] + property_mappings: + - !Find [authentik_providers_oauth2.scopemapping, [managed, goauthentik.io/providers/oauth2/scope-openid]] + - !Find [authentik_providers_oauth2.scopemapping, [managed, goauthentik.io/providers/oauth2/scope-email]] + - !Find [authentik_providers_oauth2.scopemapping, [managed, goauthentik.io/providers/oauth2/scope-profile]] + - model: authentik_core.application + id: longhorn-app + state: present + identifiers: + slug: longhorn + attrs: + name: Longhorn + provider: !KeyOf longhorn-provider + meta_launch_url: https://longhorn.rubenhensen.nl + # Longhorn can delete volumes, so only admins get in. + - model: authentik_policies.policybinding + state: present + identifiers: + order: 0 + target: !KeyOf longhorn-app + attrs: + group: !Find [authentik_core.group, [name, "authentik Admins"]] diff --git a/authentik/external-secret.yaml b/authentik/external-secret.yaml index 996edde..d4e10ec 100644 --- a/authentik/external-secret.yaml +++ b/authentik/external-secret.yaml @@ -135,3 +135,21 @@ spec: remoteRef: key: kv/actualbudget-oidc property: client_secret +--- +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: longhorn-oidc-client-secret +spec: + secretStoreRef: + name: vault-backend + kind: ClusterSecretStore + refreshInterval: 15m + target: + name: longhorn-oidc-client-secret + creationPolicy: Owner + data: + - secretKey: client_secret + remoteRef: + key: kv/longhorn-oidc + property: client_secret diff --git a/gateway/gateway.yaml b/gateway/gateway.yaml index 49085b7..cd3321c 100644 --- a/gateway/gateway.yaml +++ b/gateway/gateway.yaml @@ -212,3 +212,17 @@ spec: selector: matchLabels: kubernetes.io/metadata.name: argocd + - name: https-longhorn + protocol: HTTPS + port: 443 + hostname: longhorn.rubenhensen.nl + tls: + certificateRefs: + - name: letsencrypt-prod + namespace: longhorn-system + allowedRoutes: + namespaces: + from: Selector + selector: + matchLabels: + kubernetes.io/metadata.name: longhorn-system diff --git a/longhorn/httproute.yaml b/longhorn/httproute.yaml new file mode 100644 index 0000000..8779fd0 --- /dev/null +++ b/longhorn/httproute.yaml @@ -0,0 +1,76 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: longhorn +spec: + parentRefs: + - name: homelab + namespace: envoy-gateway-system + sectionName: https-longhorn + hostnames: + - longhorn.rubenhensen.nl + rules: + - backendRefs: + - name: longhorn-frontend + port: 80 + # No limit on the whole request, as with nginx. Envoy's default of 15s + # cuts off uploads and long-lived connections. + timeouts: + request: 0s +--- +# Longhorn has no login of its own. Envoy sends visitors through authentik +# first; the authentik application only lets admins in. +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: SecurityPolicy +metadata: + name: longhorn-oidc +spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: longhorn + oidc: + provider: + issuer: https://authentik.rubenhensen.nl/application/o/longhorn/ + clientID: longhorn + clientSecret: + name: longhorn-oidc-client-secret + scopes: + - openid + - email + - profile + logoutPath: /logout +--- +# Same client secret as authentik's, under the key Envoy Gateway expects. +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: longhorn-oidc-client-secret +spec: + secretStoreRef: + name: vault-backend + kind: ClusterSecretStore + refreshInterval: 15m + target: + name: longhorn-oidc-client-secret + creationPolicy: Owner + data: + - secretKey: client-secret + remoteRef: + key: kv/longhorn-oidc + property: client_secret +--- +# Lets the Gateway serve the certificate cert-manager keeps in this namespace. +apiVersion: gateway.networking.k8s.io/v1 +kind: ReferenceGrant +metadata: + name: gateway-tls +spec: + from: + - group: gateway.networking.k8s.io + kind: Gateway + namespace: envoy-gateway-system + to: + - group: "" + kind: Secret + name: letsencrypt-prod diff --git a/longhorn/kustomization.yaml b/longhorn/kustomization.yaml index 77548f5..b86c55b 100644 --- a/longhorn/kustomization.yaml +++ b/longhorn/kustomization.yaml @@ -5,5 +5,6 @@ namespace: longhorn-system resources: - ingress.yaml +- httproute.yaml - volume-snapshot.yaml - https://github.com/kubernetes-csi/external-snapshotter/client/config/crd