From 1fc551ac9db38ab8d9508f35af5cbedd65dfce67 Mon Sep 17 00:00:00 2001 From: 0xFEEDC0DE64 Date: Fri, 30 Oct 2026 21:18:05 +0100 Subject: [PATCH] Deploy isolated Old IPMI browser behind Authentik in oldimpi --- apps/authentik/values.yaml | 44 ++++++++++++++++++++++ apps/oldipmi/README.md | 38 +++++++++++++++++++ apps/oldipmi/resources/middleware.yaml | 8 ++++ apps/oldipmi/resources/network-policy.yaml | 25 ++++++++++++ apps/oldipmi/values.yaml | 18 +++++++++ bootstrap/kustomization.yaml | 2 + bootstrap/oldipmi-project.yaml | 28 ++++++++++++++ bootstrap/oldipmi.yaml | 36 ++++++++++++++++++ 8 files changed, 199 insertions(+) create mode 100644 apps/oldipmi/README.md create mode 100644 apps/oldipmi/resources/middleware.yaml create mode 100644 apps/oldipmi/resources/network-policy.yaml create mode 100644 apps/oldipmi/values.yaml create mode 100644 bootstrap/oldipmi-project.yaml create mode 100644 bootstrap/oldipmi.yaml diff --git a/apps/authentik/values.yaml b/apps/authentik/values.yaml index 84c09de..3b16dac 100644 --- a/apps/authentik/values.yaml +++ b/apps/authentik/values.yaml @@ -203,6 +203,50 @@ additionalObjects: order: 0 attrs: enabled: true + oldipmi.yaml: | + version: 1 + metadata: + name: Old IPMI forward authentication + labels: + blueprints.goauthentik.io/description: Admin-only access to Old IPMI + entries: + - model: authentik_providers_proxy.proxyprovider + id: oldipmi-provider + state: present + identifiers: + name: Old IPMI + attrs: + authorization_flow: !Find [authentik_flows.flow, [slug, default-provider-authorization-explicit-consent]] + invalidation_flow: !Find [authentik_flows.flow, [slug, default-provider-invalidation-flow]] + client_type: confidential + grant_types: + - authorization_code + - refresh_token + mode: forward_single + internal_host: "" + external_host: https://oldipmi.brunner.ninja + property_mappings: + - !Find [authentik_providers_oauth2.scopemapping, [managed, goauthentik.io/providers/proxy/scope-proxy]] + - !Find [authentik_providers_oauth2.scopemapping, [scope_name, openid]] + - !Find [authentik_providers_oauth2.scopemapping, [scope_name, profile]] + - !Find [authentik_providers_oauth2.scopemapping, [scope_name, email]] + - model: authentik_core.application + id: oldipmi-application + state: present + identifiers: + slug: oldipmi + attrs: + name: Old IPMI + provider: !KeyOf oldipmi-provider + launch_url: https://oldipmi.brunner.ninja + - model: authentik_policies.policybinding + state: present + identifiers: + target: !KeyOf oldipmi-application + group: !Find [authentik_core.group, [name, authentik Admins]] + order: 0 + attrs: + enabled: true - apiVersion: v1 kind: ConfigMap metadata: diff --git a/apps/oldipmi/README.md b/apps/oldipmi/README.md new file mode 100644 index 0000000..40819f2 --- /dev/null +++ b/apps/oldipmi/README.md @@ -0,0 +1,38 @@ +# Legacy IPMI browser + +Argo Application `oldipmi` deploys the independent published `oldipmi` chart into +namespace **oldimpi** (intentional spelling). Its chart targetRevision is the +single selected version. Chart source: https://code.brunner.ninja/feedc0de/oldipmi + +Open https://oldipmi.brunner.ninja. Traefik uses its existing wildcard TLS +certificate and the namespace's Authentik forward-auth middleware for every +browser asset and WebSocket handshake. The `Old IPMI` application is restricted +to `authentik Admins` by the blueprint in `apps/authentik/values.yaml`. +The existing shared `/outpost.goauthentik.io/` route handles login callbacks. +As with Kubernetes Dashboard, the proxy provider is attached additively to the +embedded Authentik outpost, preserving all its other providers. Recovery: apply +the blueprint, then add `Old IPMI` to that outpost through Authentik's admin UI. + +Cloudflare DNS: unproxied CNAME `oldipmi.brunner.ninja` → `brunner.ninja`, following +the existing admin service domains. DNS is account-managed outside Argo CD. + +The chart's namespace-wide NetworkPolicy admits only Traefik pods on the web +port. Cilium allows external destinations, including Internet, 192.168.0.0/16 +and Funkfeuer 10.12.0.0/16, but explicitly denies cluster/node/API identities, +ServiceCIDR, link-local metadata, MetalLB's pool, and the cluster's Funkfeuer +service address. Public resolvers and no Kubernetes search domains prevent +ordinary `.svc.cluster.local` discovery. This is not a domain-name firewall; +IP policy enforces the boundary even if a name resolves to a cluster IP. +Public ingress URLs remain reachable. Keep the explicit service/MetalLB +exclusions aligned with platform range changes; pod/node exclusions follow +Cilium identities automatically. Cilium policy must reconcile before startup. + +This is one shared disposable browser session. BMC passwords, downloads and +sessions are not persisted or backed up. Log out of the BMC after use. A pod +replacement resets the desktop. No existing PVCs or legacy cluster writer are +involved. The user's local Podman session is left intact for comparison. + +Verification: check Argo Synced/Healthy, non-root/zero-capability runtime, +HTTP and WebSocket authentication redirects, admin policy/outpost membership, +external connectivity and denied pod/node/Service/LoadBalancer destinations. +Actual hardware KVM verification requires identifying the BMC and logging in. diff --git a/apps/oldipmi/resources/middleware.yaml b/apps/oldipmi/resources/middleware.yaml new file mode 100644 index 0000000..382b39c --- /dev/null +++ b/apps/oldipmi/resources/middleware.yaml @@ -0,0 +1,8 @@ +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: oldipmi-authentik +spec: + forwardAuth: + address: http://authentik-server.authentik.svc.cluster.local/outpost.goauthentik.io/auth/traefik + trustForwardHeader: true diff --git a/apps/oldipmi/resources/network-policy.yaml b/apps/oldipmi/resources/network-policy.yaml new file mode 100644 index 0000000..106174a --- /dev/null +++ b/apps/oldipmi/resources/network-policy.yaml @@ -0,0 +1,25 @@ +apiVersion: cilium.io/v2 +kind: CiliumNetworkPolicy +metadata: + name: oldipmi-external-only + annotations: + argocd.argoproj.io/sync-wave: '-1' +spec: + endpointSelector: {} + egress: + # Includes Internet, 192.168.0.0/16 LAN and 10.12.0.0/16 Funkfeuer. + - toEntities: [world] + egressDeny: + # Identity-based protection follows pod and node address changes. + - toEntities: [cluster, host, remote-node, kube-apiserver] + - toCIDR: + - 10.96.0.0/12 # Kubernetes ServiceCIDR + - 169.254.0.0/16 # Link-local/metadata services + - 10.12.4.170/32 # Cluster services exposed through Funkfeuer + toCIDRSet: + # MetalLB's complete reserved pool: 192.168.0.10-192.168.0.49. + - cidr: 192.168.0.10/31 + - cidr: 192.168.0.12/30 + - cidr: 192.168.0.16/28 + - cidr: 192.168.0.32/28 + - cidr: 192.168.0.48/31 diff --git a/apps/oldipmi/values.yaml b/apps/oldipmi/values.yaml new file mode 100644 index 0000000..8e17f0b --- /dev/null +++ b/apps/oldipmi/values.yaml @@ -0,0 +1,18 @@ +ingress: + enabled: true + className: traefik + host: oldipmi.brunner.ninja + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.tls: 'true' + traefik.ingress.kubernetes.io/router.middlewares: oldimpi-oldipmi-authentik@kubernetescrd +networkPolicy: + ingressFrom: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: default + podSelector: + matchLabels: + app.kubernetes.io/name: traefik + app.kubernetes.io/instance: traefik-default + # Cilium policy supplies external egress with explicit cluster exclusions. diff --git a/bootstrap/kustomization.yaml b/bootstrap/kustomization.yaml index d999299..91843ee 100644 --- a/bootstrap/kustomization.yaml +++ b/bootstrap/kustomization.yaml @@ -149,3 +149,5 @@ resources: - ghostfolio-postgresql.yaml - ghostfolio-valkey.yaml - ghostfolio.yaml +- oldipmi-project.yaml +- oldipmi.yaml diff --git a/bootstrap/oldipmi-project.yaml b/bootstrap/oldipmi-project.yaml new file mode 100644 index 0000000..6463d4f --- /dev/null +++ b/bootstrap/oldipmi-project.yaml @@ -0,0 +1,28 @@ +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: oldipmi + namespace: argocd +spec: + description: Isolated legacy IPMI browser + sourceRepos: + - https://brunner.ninja/charts + - https://code.brunner.ninja/feedc0de/homelab-gitops.git + destinations: + - namespace: oldimpi + server: https://kubernetes.default.svc + namespaceResourceWhitelist: + - group: '' + kind: ConfigMap + - group: '' + kind: Service + - group: apps + kind: Deployment + - group: networking.k8s.io + kind: Ingress + - group: networking.k8s.io + kind: NetworkPolicy + - group: cilium.io + kind: CiliumNetworkPolicy + - group: traefik.io + kind: Middleware diff --git a/bootstrap/oldipmi.yaml b/bootstrap/oldipmi.yaml new file mode 100644 index 0000000..e40f72a --- /dev/null +++ b/bootstrap/oldipmi.yaml @@ -0,0 +1,36 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: oldipmi + namespace: argocd +spec: + project: oldipmi + sources: + - repoURL: https://brunner.ninja/charts + chart: oldipmi + targetRevision: 0.0.0 + helm: + releaseName: oldipmi + valueFiles: + - $values/apps/oldipmi/values.yaml + - repoURL: https://code.brunner.ninja/feedc0de/homelab-gitops.git + targetRevision: main + ref: values + - repoURL: https://code.brunner.ninja/feedc0de/homelab-gitops.git + targetRevision: main + path: apps/oldipmi/resources + destination: + server: https://kubernetes.default.svc + namespace: oldimpi + syncPolicy: + automated: + enabled: true + prune: false + selfHeal: true + managedNamespaceMetadata: + labels: + pod-security.kubernetes.io/enforce: baseline + pod-security.kubernetes.io/audit: restricted + pod-security.kubernetes.io/warn: restricted + syncOptions: + - CreateNamespace=true