From d01b0008384e9d225329c223639b2d1db3c55389 Mon Sep 17 00:00:00 2001 From: Nikolai Emil Damm Date: Mon, 26 Oct 2026 18:37:51 +0100 Subject: [PATCH] fix(apps): remove the Headlamp demo app Headlamp is retired from the platform (devantler-tech/platform#2496) and the KSail web UI is taking over its role (devantler-tech/ksail#5827), so new platforms should not start with it. Remove the app with its Dex callback, network allowances, KEDA grant, security-exception namespaces, replica variable, local host name and docs. The Coroot observability components edit Dex callbacks and exception namespaces by list position, so their guarded indices move with the removed entries, and the OpenCost plugin patch that only targeted Headlamp goes. The observability check now requires a real apps render before it accepts the absence of OpenCost references. Co-Authored-By: Claude Opus 5.5 --- .github/retired-repo-links.json | 1 - AGENTS.md | 2 +- docs/ARCHITECTURE.md | 1 - docs/TEMPLATING.md | 7 +- docs/TENANTS.md | 2 +- hosts | 1 - k8s/bases/apps/headlamp/README.md | 6 - .../apps/headlamp/cilium-network-policy.yaml | 38 ----- k8s/bases/apps/headlamp/external-secret.yaml | 18 -- k8s/bases/apps/headlamp/helm-release.yaml | 156 ------------------ k8s/bases/apps/headlamp/helm-repository.yaml | 7 - k8s/bases/apps/headlamp/http-route.yaml | 31 ---- .../apps/headlamp/http-scaled-object.yaml | 22 --- k8s/bases/apps/headlamp/kustomization.yaml | 14 -- k8s/bases/apps/headlamp/namespace.yaml | 7 - .../headlamp/persistent-volume-claim.yaml | 11 -- k8s/bases/apps/kustomization.yaml | 1 - .../controller-rbac.yaml | 12 +- .../health-probes.yaml | 7 +- .../service-account-tokens.yaml | 12 +- .../dex/cilium-network-policy.yaml | 11 +- .../controllers/dex/helm-release.yaml | 1 - .../keda-http-add-on/reference-grant.yaml | 3 - .../keda/cilium-network-policy.yaml | 7 - .../opencost/cilium-network-policy.yaml | 11 +- .../controllers/opencost/helm-release.yaml | 4 +- .../external-secrets/grafana-oidc.yaml | 2 +- .../variables-cluster-config-map.yaml | 2 - .../remove-kps-ui/kustomization.yaml | 12 +- .../kustomization.yaml | 66 +------- .../headlamp/patches/trust-platform-ca.yaml | 18 -- k8s/providers/docker/apps/kustomization.yaml | 7 - .../cloudflare-origin-ca-trust-bundle.yaml | 2 +- scripts/validate-observability-option.sh | 24 +-- 34 files changed, 50 insertions(+), 476 deletions(-) delete mode 100644 k8s/bases/apps/headlamp/README.md delete mode 100644 k8s/bases/apps/headlamp/cilium-network-policy.yaml delete mode 100644 k8s/bases/apps/headlamp/external-secret.yaml delete mode 100644 k8s/bases/apps/headlamp/helm-release.yaml delete mode 100644 k8s/bases/apps/headlamp/helm-repository.yaml delete mode 100644 k8s/bases/apps/headlamp/http-route.yaml delete mode 100644 k8s/bases/apps/headlamp/http-scaled-object.yaml delete mode 100644 k8s/bases/apps/headlamp/kustomization.yaml delete mode 100644 k8s/bases/apps/headlamp/namespace.yaml delete mode 100644 k8s/bases/apps/headlamp/persistent-volume-claim.yaml delete mode 100644 k8s/providers/docker/apps/headlamp/patches/trust-platform-ca.yaml diff --git a/.github/retired-repo-links.json b/.github/retired-repo-links.json index e288c31..698bb58 100644 --- a/.github/retired-repo-links.json +++ b/.github/retired-repo-links.json @@ -5,7 +5,6 @@ "README.md", "CONTRIBUTING.md", "AGENTS.md", "CLAUDE.md", "SECURITY.md", "docs", ".claude/skills", "k8s/bases/README.md", "k8s/bases/apps/README.md", - "k8s/bases/apps/headlamp/README.md", "k8s/bases/infrastructure/README.md", "k8s/bases/infrastructure/audit-log-forwarder/README.md", "k8s/bases/infrastructure/controllers/cert-manager/README.md", diff --git a/AGENTS.md b/AGENTS.md index c14de99..15db750 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -56,7 +56,7 @@ k8s/ # All Kubernetes manifests bootstrap/ # Flux post-build substitution variables (base ConfigMap + SOPS secret) infrastructure/ # Organized by resource type: controllers/, certificates/, gateway/, # cluster-policies/, external-secrets/, alerts/, vault-*/ (OpenBao), etc. - apps/ # App deployments — whoami, homepage, headlamp + apps/ # App deployments — whoami, homepage components/ # Shared Kustomize components: HelmRelease defaults and drift detection # (listed by every HelmRelease-owning kustomization), plus the # opt-in profile transformations diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index da0ecc3..777782e 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -33,7 +33,6 @@ controllers to save resources (noted inline). ### Demo apps - **Homepage** — a dashboard landing page for the platform -- **Headlamp** — a Kubernetes web UI - **whoami** — a tiny debug echo service They are deliberately lightweight, so a new platform has something to look at. To run **your own** diff --git a/docs/TEMPLATING.md b/docs/TEMPLATING.md index 5b775e9..d58ea74 100644 --- a/docs/TEMPLATING.md +++ b/docs/TEMPLATING.md @@ -73,8 +73,8 @@ ksail --config ksail.prod.yaml cluster update ksail --config ksail.prod.yaml workload reconcile ``` -This profile is transitional. It enables Coroot, removes OpenCost and its Headlamp -plugin, retires the legacy Loki and Alloy log path, and keeps kube-prometheus-stack +This profile is transitional. It enables Coroot, removes OpenCost, +retires the legacy Loki and Alloy log path, and keeps kube-prometheus-stack until the metrics and alerting migration is proven separately. Cost allocation is therefore unavailable in this profile; it does not connect OpenCost to Coroot Prometheus. Coroot is the only observability UI in this profile: it removes the Grafana, Prometheus and Alertmanager @@ -250,8 +250,7 @@ See [`.github/workflows/`](../.github/workflows) for the exact names in use. Kyverno, alerting configs, OpenBao vault, External Secrets Operator, ClusterSecretStore, vault-config Job, vault-seed PushSecrets, vault-backup CronJob, and the rest of the controller set. -- [`k8s/bases/apps/`](../k8s/bases/apps) — the demo applications (homepage, whoami, - headlamp). +- [`k8s/bases/apps/`](../k8s/bases/apps) — the demo applications (homepage, whoami). - [`k8s/components/`](../k8s/components) — shared transformations: the HelmRelease retry defaults and drift detection every HelmRelease-owning kustomization lists, and opt-in profile transformations such as removing OpenCost across controller, diff --git a/docs/TENANTS.md b/docs/TENANTS.md index 827a90d..f834b5d 100644 --- a/docs/TENANTS.md +++ b/docs/TENANTS.md @@ -1,6 +1,6 @@ # Onboarding a GitOps tenant -The platform ships with a few lightweight **demo apps** (homepage, whoami, headlamp) +The platform ships with a few lightweight **demo apps** (homepage, whoami) in [`k8s/bases/apps/`](../k8s/bases/apps). To run **your own** application on the platform, add it as a **tenant**: an application that runs on the cluster but lives in its **own repository**. The tenant repo builds a container image and publishes its diff --git a/hosts b/hosts index c450b57..ac24b20 100644 --- a/hosts +++ b/hosts @@ -1,6 +1,5 @@ 127.0.0.1 dex.platform.lan 127.0.0.1 flux.platform.lan -127.0.0.1 headlamp.platform.lan 127.0.0.1 oauth2-proxy.platform.lan 127.0.0.1 platform.lan 127.0.0.1 whoami.platform.lan diff --git a/k8s/bases/apps/headlamp/README.md b/k8s/bases/apps/headlamp/README.md deleted file mode 100644 index f8387b1..0000000 --- a/k8s/bases/apps/headlamp/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# Headlamp - -Headlamp is a lightweight Kubernetes UI which supports multiple clusters and is extensible at its core. - -- [Documentation](https://headlamp.dev/docs/latest/) -- [Helm Chart](https://github.com/headlamp-k8s/headlamp/tree/main/charts/headlamp) diff --git a/k8s/bases/apps/headlamp/cilium-network-policy.yaml b/k8s/bases/apps/headlamp/cilium-network-policy.yaml deleted file mode 100644 index 6d827e7..0000000 --- a/k8s/bases/apps/headlamp/cilium-network-policy.yaml +++ /dev/null @@ -1,38 +0,0 @@ -apiVersion: cilium.io/v2 -kind: CiliumNetworkPolicy -metadata: - name: allow-headlamp - namespace: headlamp -spec: - endpointSelector: {} - ingress: - # KEDA interceptor proxy routes traffic from gateway - - fromEndpoints: - - matchLabels: - k8s:io.kubernetes.pod.namespace: keda - toPorts: - - ports: - - port: "4466" - protocol: TCP - egress: - # Kube API for dashboard - - toEntities: - - kube-apiserver - # Dex OIDC, external services (via Cloudflare proxy) - - toEntities: - - world - toPorts: - - ports: - - port: "443" - protocol: TCP - # DNS resolution - - toEndpoints: - - matchLabels: - k8s:io.kubernetes.pod.namespace: kube-system - k8s-app: kube-dns - toPorts: - - ports: - - port: "53" - protocol: UDP - - port: "53" - protocol: TCP diff --git a/k8s/bases/apps/headlamp/external-secret.yaml b/k8s/bases/apps/headlamp/external-secret.yaml deleted file mode 100644 index e1daa82..0000000 --- a/k8s/bases/apps/headlamp/external-secret.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: external-secrets.io/v1 -kind: ExternalSecret -metadata: - name: headlamp-oidc - namespace: headlamp -spec: - refreshInterval: 1h - secretStoreRef: - name: openbao - kind: ClusterSecretStore - target: - name: headlamp-oidc - creationPolicy: Owner - data: - - secretKey: client-secret - remoteRef: - key: infrastructure/oidc/dex - property: client-secret diff --git a/k8s/bases/apps/headlamp/helm-release.yaml b/k8s/bases/apps/headlamp/helm-release.yaml deleted file mode 100644 index 5db2184..0000000 --- a/k8s/bases/apps/headlamp/helm-release.yaml +++ /dev/null @@ -1,156 +0,0 @@ -apiVersion: helm.toolkit.fluxcd.io/v2 -kind: HelmRelease -metadata: - name: headlamp - namespace: headlamp - labels: - helm.toolkit.fluxcd.io/helm-test: enabled - helm.toolkit.fluxcd.io/remediation: enabled -spec: - interval: 2m - timeout: 10m - chart: - spec: - chart: headlamp - version: 0.42.0 - sourceRef: - kind: HelmRepository - name: headlamp - valuesFrom: - - kind: Secret - name: headlamp-oidc - valuesKey: client-secret - targetPath: config.oidc.clientSecret - optional: true - postRenderers: - - kustomize: - patches: - - target: - kind: Deployment - name: headlamp - patch: | - - op: add - path: /metadata/annotations/secret.reloader.stakater.com~1reload - value: headlamp-oidc - - op: add - path: /spec/strategy - value: - type: RollingUpdate - rollingUpdate: - maxSurge: 0 - maxUnavailable: 1 - - op: add - path: /spec/template/spec/volumes/- - value: - name: tmp-dir - emptyDir: {} - - op: add - path: /spec/template/spec/volumes/- - value: - name: headlamp-config - emptyDir: {} - - op: add - path: /spec/template/spec/containers/0/volumeMounts/- - value: - name: tmp-dir - mountPath: /tmp - - op: add - path: /spec/template/spec/containers/0/volumeMounts/- - value: - name: headlamp-config - mountPath: /home/headlamp/.config - - op: add - path: /spec/template/spec/containers/1/volumeMounts/- - value: - name: tmp-dir - mountPath: /tmp - # Chart hardcodes liveness/readiness with K8s defaults - # (timeoutSeconds: 1, failureThreshold: 3, periodSeconds: 10). - # Headlamp is KEDA-scaled to 0 in prod; every cold start logs - # 1-3 Unhealthy probe warnings while the Go binary initialises. - # Add a startupProbe so liveness/readiness are gated until the - # main container is actually serving. - # initialDelaySeconds skips past the cold-start window so the - # first probe lands on a serving container — zero failure - # events during the merge-queue's 90s steady-state Warning - # check. - - op: add - path: /spec/template/spec/containers/0/startupProbe - value: - httpGet: - path: / - port: http - initialDelaySeconds: 20 - periodSeconds: 5 - timeoutSeconds: 3 - failureThreshold: 12 # 60s grace beyond initial delay - - target: - kind: Deployment - name: headlamp - patch: | - apiVersion: apps/v1 - kind: Deployment - metadata: - name: headlamp - spec: - template: - spec: - volumes: - - name: plugins-dir - emptyDir: null - persistentVolumeClaim: - claimName: headlamp - # https://github.com/kubernetes-sigs/headlamp/blob/main/charts/headlamp/values.yaml - values: - replicaCount: ${headlamp_replicas:=1} - podDisruptionBudget: - enabled: true - minAvailable: 0 - maxUnavailable: null - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app.kubernetes.io/name: headlamp - app.kubernetes.io/instance: headlamp - config: - watchPlugins: true - oidc: - clientID: public-client - issuerURL: https://dex.${domain} - scopes: "profile,email,groups" - pluginsManager: - enabled: true - configContent: | - plugins: - - name: headlamp_flux - source: https://artifacthub.io/packages/headlamp/headlamp-plugins/headlamp_flux - version: 0.6.0 - - name: headlamp_cert-manager - source: https://artifacthub.io/packages/headlamp/headlamp-plugins/headlamp_cert-manager - version: 0.1.0 - - name: headlamp_keda - source: https://artifacthub.io/packages/headlamp/headlamp-plugins/headlamp_keda - version: 0.1.1-beta - - name: headlamp_opencost - source: https://artifacthub.io/packages/headlamp/headlamp-plugins/headlamp_opencost - version: 0.1.3 - - name: headlamp_kubescape - source: https://artifacthub.io/packages/headlamp/kubescape-headlamp-plugin/headlamp_kubescape - version: 0.11.2 - installOptions: - parallel: true - maxConcurrent: 3 - persistentVolumeClaim: - enabled: false - podSecurityContext: - fsGroup: 101 - runAsUser: 100 - runAsGroup: 101 - securityContext: - runAsUser: 100 - runAsGroup: 101 - ingress: - enabled: false diff --git a/k8s/bases/apps/headlamp/helm-repository.yaml b/k8s/bases/apps/headlamp/helm-repository.yaml deleted file mode 100644 index b643968..0000000 --- a/k8s/bases/apps/headlamp/helm-repository.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1 -kind: HelmRepository -metadata: - name: headlamp - namespace: headlamp -spec: - url: https://kubernetes-sigs.github.io/headlamp/ diff --git a/k8s/bases/apps/headlamp/http-route.yaml b/k8s/bases/apps/headlamp/http-route.yaml deleted file mode 100644 index cb9ea29..0000000 --- a/k8s/bases/apps/headlamp/http-route.yaml +++ /dev/null @@ -1,31 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: headlamp - namespace: headlamp - annotations: - gethomepage.dev/enabled: "true" - gethomepage.dev/name: Headlamp - gethomepage.dev/description: A web UI for managing Kubernetes clusters. - gethomepage.dev/group: Kubernetes - gethomepage.dev/icon: https://pbs.twimg.com/profile_images/1537480067227566080/waXG0X7n_400x400.jpg - gethomepage.dev/href: https://headlamp.${domain} - gethomepage.dev/pod-selector: app.kubernetes.io/name=headlamp -spec: - parentRefs: - - name: platform - namespace: kube-system - sectionName: https - hostnames: - - headlamp.${domain} - rules: - - backendRefs: - - name: keda-add-ons-http-interceptor-proxy - namespace: keda - port: 8080 - filters: - - type: ResponseHeaderModifier - responseHeaderModifier: - set: - - name: Strict-Transport-Security - value: max-age=63072000; includeSubDomains; preload diff --git a/k8s/bases/apps/headlamp/http-scaled-object.yaml b/k8s/bases/apps/headlamp/http-scaled-object.yaml deleted file mode 100644 index 0920854..0000000 --- a/k8s/bases/apps/headlamp/http-scaled-object.yaml +++ /dev/null @@ -1,22 +0,0 @@ ---- -apiVersion: http.keda.sh/v1alpha1 -kind: HTTPScaledObject -metadata: - name: headlamp - namespace: headlamp -spec: - hosts: - - headlamp.${domain} - scaleTargetRef: - name: headlamp - service: headlamp - port: 80 - replicas: - min: 0 - max: 1 # OIDC login stores state in-memory per pod; limit to 1 so the callback always lands on the same pod. - scaledownPeriod: 300 - scalingMetric: - requestRate: - granularity: 1s - targetValue: 100 - window: 1m diff --git a/k8s/bases/apps/headlamp/kustomization.yaml b/k8s/bases/apps/headlamp/kustomization.yaml deleted file mode 100644 index c168bc0..0000000 --- a/k8s/bases/apps/headlamp/kustomization.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -resources: - - namespace.yaml - - external-secret.yaml - - persistent-volume-claim.yaml - - helm-release.yaml - - helm-repository.yaml - - http-route.yaml - - http-scaled-object.yaml - - cilium-network-policy.yaml -components: - - ../../../components/helmrelease-flux-defaults/ - - ../../../components/helmrelease-drift-detection/ diff --git a/k8s/bases/apps/headlamp/namespace.yaml b/k8s/bases/apps/headlamp/namespace.yaml deleted file mode 100644 index e832f82..0000000 --- a/k8s/bases/apps/headlamp/namespace.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: headlamp - labels: - pod-security.kubernetes.io/enforce: restricted - pod-security.kubernetes.io/enforce-version: latest diff --git a/k8s/bases/apps/headlamp/persistent-volume-claim.yaml b/k8s/bases/apps/headlamp/persistent-volume-claim.yaml deleted file mode 100644 index b3e286b..0000000 --- a/k8s/bases/apps/headlamp/persistent-volume-claim.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: headlamp - namespace: headlamp -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 256Mi diff --git a/k8s/bases/apps/kustomization.yaml b/k8s/bases/apps/kustomization.yaml index 1480e33..84443e9 100644 --- a/k8s/bases/apps/kustomization.yaml +++ b/k8s/bases/apps/kustomization.yaml @@ -1,6 +1,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: -- headlamp/ - homepage/ - whoami/ diff --git a/k8s/bases/infrastructure/cluster-security-exceptions/controller-rbac.yaml b/k8s/bases/infrastructure/cluster-security-exceptions/controller-rbac.yaml index 30d5fcb..7111549 100644 --- a/k8s/bases/infrastructure/cluster-security-exceptions/controller-rbac.yaml +++ b/k8s/bases/infrastructure/cluster-security-exceptions/controller-rbac.yaml @@ -2,9 +2,9 @@ # Exempt controllers and middleware that legitimately need broad RBAC. # Infrastructure controllers (Flux, Kyverno, cert-manager, CNPG, Velero, # KEDA, monitoring, KubeVirt, CDI, Longhorn) and platform middleware -# (headlamp cluster dashboard, homepage service discovery, dex identity, -# oauth2-proxy auth, opencost cost monitoring, reloader config watcher) -# all require cluster-wide read or read/write access to function correctly. +# (homepage service discovery, dex identity, oauth2-proxy auth, opencost +# cost monitoring, reloader config watcher) all require cluster-wide read +# or read/write access to function correctly. apiVersion: kubescape.io/v1beta1 kind: ClusterSecurityException metadata: @@ -16,9 +16,8 @@ spec: (Flux), policy engines (Kyverno), certificate managers (cert-manager), database operators (CNPG), backup controllers (Velero), autoscalers (KEDA), monitoring (Prometheus/opencost), virtualisation (KubeVirt, - CDI), storage (Longhorn), cluster dashboards (headlamp), service - discovery (homepage), identity (dex), auth proxies (oauth2-proxy), - and config watchers (reloader). + CDI), storage (Longhorn), service discovery (homepage), identity + (dex), auth proxies (oauth2-proxy), and config watchers (reloader). posture: - controlID: C-0053 action: ignore @@ -60,7 +59,6 @@ spec: - longhorn-system - external-dns - oauth2-proxy - - headlamp - kubelet-serving-cert-approver - opencost - reloader diff --git a/k8s/bases/infrastructure/cluster-security-exceptions/health-probes.yaml b/k8s/bases/infrastructure/cluster-security-exceptions/health-probes.yaml index f1a2fbd..a83bd39 100644 --- a/k8s/bases/infrastructure/cluster-security-exceptions/health-probes.yaml +++ b/k8s/bases/infrastructure/cluster-security-exceptions/health-probes.yaml @@ -3,9 +3,9 @@ # Many controllers, sidecars, and init containers do not expose health # endpoints. Adding probes to upstream charts is impractical and they # typically have their own readiness mechanisms (leader election, watch -# loops). App namespaces (headlamp, homepage) are included because their -# charts deploy init containers and sidecars that lack probe endpoints, -# while main application containers have probes configured. +# loops). The homepage app namespace is included because its chart +# deploys init containers and sidecars that lack probe endpoints, while +# the main application container has probes configured. apiVersion: kubescape.io/v1beta1 kind: ClusterSecurityException metadata: @@ -42,7 +42,6 @@ spec: - external-dns - kubelet-serving-cert-approver - opencost - - headlamp - kubevirt - cdi - dex diff --git a/k8s/bases/infrastructure/cluster-security-exceptions/service-account-tokens.yaml b/k8s/bases/infrastructure/cluster-security-exceptions/service-account-tokens.yaml index f4788aa..90d4c41 100644 --- a/k8s/bases/infrastructure/cluster-security-exceptions/service-account-tokens.yaml +++ b/k8s/bases/infrastructure/cluster-security-exceptions/service-account-tokens.yaml @@ -2,8 +2,7 @@ # Exempt service account token auto-mount controls for infrastructure # and middleware namespaces. Controllers and platform middleware that # manage cluster resources (Flux, Kyverno, cert-manager, KEDA, VPA, -# headlamp dashboard, homepage service discovery, etc.) genuinely need -# API tokens to function. +# homepage service discovery, etc.) genuinely need API tokens to function. apiVersion: kubescape.io/v1beta1 kind: ClusterSecurityException metadata: @@ -12,10 +11,10 @@ spec: reason: >- Infrastructure controllers and platform middleware need Kubernetes API access via SA tokens. This includes GitOps controllers, policy - engines, certificate managers, autoscalers, monitoring, cluster - dashboards (headlamp), service discovery (homepage), identity - providers (dex), auth proxies (oauth2-proxy), and config watchers - (reloader). The platform uses RBAC to restrict what each SA can do. + engines, certificate managers, autoscalers, monitoring, service + discovery (homepage), identity providers (dex), auth proxies + (oauth2-proxy), and config watchers (reloader). The platform uses + RBAC to restrict what each SA can do. posture: - controlID: C-0261 action: ignore @@ -56,5 +55,4 @@ spec: - opencost - dex - homepage - - headlamp - oauth2-proxy diff --git a/k8s/bases/infrastructure/controllers/dex/cilium-network-policy.yaml b/k8s/bases/infrastructure/controllers/dex/cilium-network-policy.yaml index cc30cf7..25d4101 100644 --- a/k8s/bases/infrastructure/controllers/dex/cilium-network-policy.yaml +++ b/k8s/bases/infrastructure/controllers/dex/cilium-network-policy.yaml @@ -13,15 +13,6 @@ spec: - ports: - port: "5556" protocol: TCP - # OIDC token exchange from Headlamp backend (hairpin via gateway ClusterIP - # preserves the originating pod identity instead of using the 'ingress' entity) - - fromEndpoints: - - matchLabels: - k8s:io.kubernetes.pod.namespace: headlamp - toPorts: - - ports: - - port: "5556" - protocol: TCP # gRPC from oauth2-proxy - fromEndpoints: - matchLabels: @@ -33,7 +24,7 @@ spec: - port: "5558" protocol: TCP # OIDC token + userinfo exchange from Grafana (native auth.generic_oauth). - # Same in-cluster path as headlamp/oauth2-proxy: Grafana's server-side + # Same in-cluster path as oauth2-proxy: Grafana's server-side # calls hit the Dex Service directly and arrive with the monitoring pod # identity, so they must be allowed explicitly here. - fromEndpoints: diff --git a/k8s/bases/infrastructure/controllers/dex/helm-release.yaml b/k8s/bases/infrastructure/controllers/dex/helm-release.yaml index c6c10d2..c1fb424 100644 --- a/k8s/bases/infrastructure/controllers/dex/helm-release.yaml +++ b/k8s/bases/infrastructure/controllers/dex/helm-release.yaml @@ -66,7 +66,6 @@ spec: secret: ${dex_client_secret} redirectURIs: - "https://oauth2-proxy.${domain}/oauth2/callback" - - "https://headlamp.${domain}/oidc-callback" - "https://budget.${domain}/oidc/callback" - "https://vault.${domain}/ui/vault/auth/oidc/oidc/callback" - "https://grafana.${domain}/login/generic_oauth" diff --git a/k8s/bases/infrastructure/controllers/keda-http-add-on/reference-grant.yaml b/k8s/bases/infrastructure/controllers/keda-http-add-on/reference-grant.yaml index 684e018..9004c0e 100644 --- a/k8s/bases/infrastructure/controllers/keda-http-add-on/reference-grant.yaml +++ b/k8s/bases/infrastructure/controllers/keda-http-add-on/reference-grant.yaml @@ -6,9 +6,6 @@ metadata: namespace: keda spec: from: - - group: gateway.networking.k8s.io - kind: HTTPRoute - namespace: headlamp - group: gateway.networking.k8s.io kind: HTTPRoute namespace: whoami diff --git a/k8s/bases/infrastructure/controllers/keda/cilium-network-policy.yaml b/k8s/bases/infrastructure/controllers/keda/cilium-network-policy.yaml index 159b416..9c2be31 100644 --- a/k8s/bases/infrastructure/controllers/keda/cilium-network-policy.yaml +++ b/k8s/bases/infrastructure/controllers/keda/cilium-network-policy.yaml @@ -41,13 +41,6 @@ spec: - matchLabels: k8s:io.kubernetes.pod.namespace: keda # HTTP interceptor -> backend services in KEDA-managed namespaces - - toEndpoints: - - matchLabels: - k8s:io.kubernetes.pod.namespace: headlamp - toPorts: - - ports: - - port: "4466" - protocol: TCP - toEndpoints: - matchLabels: k8s:io.kubernetes.pod.namespace: whoami diff --git a/k8s/bases/infrastructure/controllers/opencost/cilium-network-policy.yaml b/k8s/bases/infrastructure/controllers/opencost/cilium-network-policy.yaml index ba31de0..c2c6e59 100644 --- a/k8s/bases/infrastructure/controllers/opencost/cilium-network-policy.yaml +++ b/k8s/bases/infrastructure/controllers/opencost/cilium-network-policy.yaml @@ -16,12 +16,11 @@ spec: protocol: TCP - port: "9090" protocol: TCP - # Headlamp's OpenCost plugin queries the cost API/UI through the - # kube-apiserver service proxy (/api/v1/namespaces/opencost/services/ - # opencost:http-ui/proxy/...), so the request reaches OpenCost from the - # API server. Its source address varies by CNI path (direct, SNAT'd via - # host, or coming from another node), so we allow the three - # Cilium-managed entities the API server may appear as. + # Cost API/UI requests made through the kube-apiserver service proxy + # (/api/v1/namespaces/opencost/services/opencost:http-ui/proxy/...) + # reach OpenCost from the API server. Its source address varies by CNI + # path (direct, SNAT'd via host, or coming from another node), so we + # allow the three Cilium-managed entities the API server may appear as. - fromEntities: - kube-apiserver - host diff --git a/k8s/bases/infrastructure/controllers/opencost/helm-release.yaml b/k8s/bases/infrastructure/controllers/opencost/helm-release.yaml index 9928105..47063b8 100644 --- a/k8s/bases/infrastructure/controllers/opencost/helm-release.yaml +++ b/k8s/bases/infrastructure/controllers/opencost/helm-release.yaml @@ -27,7 +27,7 @@ spec: # deploy gate (.github/actions/check-event-warnings) even though it is # harmless. Two mitigations: # 1. maxUnavailable: 0 — surge the new pod to Ready before the old is - # terminated (matches the homepage/headlamp convention). + # terminated (matches the homepage convention). # 2. preStop.sleep — keep the opencost container serving :9003 for 15s # after it is marked for deletion, so kubelet's probes during drain # land on a live endpoint instead of a torn-down route. Native sleep @@ -127,7 +127,7 @@ spec: # scheduler reserves real baseline and the pod isn't first to be # evicted under node memory pressure. memory: 128Mi - # 256Mi OOM-killed when Headlamp issued the 14d allocation queries — + # 256Mi OOM-killed under 14-day allocation queries — # the cost-model engine holds the full window in memory while # aggregating. 512Mi gives ~4× headroom over the request and is # still well under the chart's 1Gi default. diff --git a/k8s/bases/infrastructure/external-secrets/grafana-oidc.yaml b/k8s/bases/infrastructure/external-secrets/grafana-oidc.yaml index 7da0676..a9c28f9 100644 --- a/k8s/bases/infrastructure/external-secrets/grafana-oidc.yaml +++ b/k8s/bases/infrastructure/external-secrets/grafana-oidc.yaml @@ -15,7 +15,7 @@ spec: data: # Dex 'public-client' secret. Grafana does native OIDC against Dex # (auth.generic_oauth); this is the same shared confidential-client - # secret Headlamp/oauth2-proxy use. + # secret oauth2-proxy uses. - secretKey: client-secret remoteRef: key: infrastructure/oidc/dex diff --git a/k8s/clusters/prod/bootstrap/variables-cluster-config-map.yaml b/k8s/clusters/prod/bootstrap/variables-cluster-config-map.yaml index 8f5ae23..701ae62 100644 --- a/k8s/clusters/prod/bootstrap/variables-cluster-config-map.yaml +++ b/k8s/clusters/prod/bootstrap/variables-cluster-config-map.yaml @@ -38,9 +38,7 @@ data: # KEDA-managed apps: HelmRelease replicas are the initial value only; # KEDA HTTPScaledObject overrides at runtime. Setting the initial to 1 # saves memory until the first KEDA reconciliation. - # headlamp: min 0, max 1 (OIDC state in-memory — must stay single-pod) # whoami: min 0, max 2 - headlamp_replicas: "1" whoami_replicas: "1" hubble_ui_replicas: "1" # Auth chain: 2 replicas for HA during VPA evictions (prevents 502s). diff --git a/k8s/components/observability-coroot/remove-kps-ui/kustomization.yaml b/k8s/components/observability-coroot/remove-kps-ui/kustomization.yaml index 5dd1ec4..04bf9ff 100644 --- a/k8s/components/observability-coroot/remove-kps-ui/kustomization.yaml +++ b/k8s/components/observability-coroot/remove-kps-ui/kustomization.yaml @@ -83,20 +83,20 @@ patches: # first, after remove-opencost-references took OpenCost's), then Grafana's own. patch: |- - op: test - path: /spec/values/config/staticClients/0/redirectURIs/9 + path: /spec/values/config/staticClients/0/redirectURIs/8 value: "https://prometheus.${domain}/oauth2/callback" - op: remove - path: /spec/values/config/staticClients/0/redirectURIs/9 + path: /spec/values/config/staticClients/0/redirectURIs/8 - op: test - path: /spec/values/config/staticClients/0/redirectURIs/6 + path: /spec/values/config/staticClients/0/redirectURIs/5 value: "https://alertmanager.${domain}/oauth2/callback" - op: remove - path: /spec/values/config/staticClients/0/redirectURIs/6 + path: /spec/values/config/staticClients/0/redirectURIs/5 - op: test - path: /spec/values/config/staticClients/0/redirectURIs/4 + path: /spec/values/config/staticClients/0/redirectURIs/3 value: "https://grafana.${domain}/login/generic_oauth" - op: remove - path: /spec/values/config/staticClients/0/redirectURIs/4 + path: /spec/values/config/staticClients/0/redirectURIs/3 - target: version: v1 kind: ConfigMap diff --git a/k8s/components/observability-coroot/remove-opencost-references/kustomization.yaml b/k8s/components/observability-coroot/remove-opencost-references/kustomization.yaml index 1e95522..210e2d6 100644 --- a/k8s/components/observability-coroot/remove-opencost-references/kustomization.yaml +++ b/k8s/components/observability-coroot/remove-opencost-references/kustomization.yaml @@ -6,54 +6,6 @@ kind: Component # from a layer are ignored; the executable contract rejects surviving OpenCost # references or an incomplete oauth2-proxy boundary. patches: - - target: - group: helm.toolkit.fluxcd.io - version: v2 - kind: HelmRelease - name: headlamp - namespace: headlamp - patch: | - - op: test - path: /spec/values/pluginsManager/configContent - value: | - plugins: - - name: headlamp_flux - source: https://artifacthub.io/packages/headlamp/headlamp-plugins/headlamp_flux - version: 0.6.0 - - name: headlamp_cert-manager - source: https://artifacthub.io/packages/headlamp/headlamp-plugins/headlamp_cert-manager - version: 0.1.0 - - name: headlamp_keda - source: https://artifacthub.io/packages/headlamp/headlamp-plugins/headlamp_keda - version: 0.1.1-beta - - name: headlamp_opencost - source: https://artifacthub.io/packages/headlamp/headlamp-plugins/headlamp_opencost - version: 0.1.3 - - name: headlamp_kubescape - source: https://artifacthub.io/packages/headlamp/kubescape-headlamp-plugin/headlamp_kubescape - version: 0.11.2 - installOptions: - parallel: true - maxConcurrent: 3 - - op: replace - path: /spec/values/pluginsManager/configContent - value: | - plugins: - - name: headlamp_flux - source: https://artifacthub.io/packages/headlamp/headlamp-plugins/headlamp_flux - version: 0.6.0 - - name: headlamp_cert-manager - source: https://artifacthub.io/packages/headlamp/headlamp-plugins/headlamp_cert-manager - version: 0.1.0 - - name: headlamp_keda - source: https://artifacthub.io/packages/headlamp/headlamp-plugins/headlamp_keda - version: 0.1.1-beta - - name: headlamp_kubescape - source: https://artifacthub.io/packages/headlamp/kubescape-headlamp-plugin/headlamp_kubescape - version: 0.11.2 - installOptions: - parallel: true - maxConcurrent: 3 - target: version: v1 kind: ConfigMap @@ -284,10 +236,10 @@ patches: name: controller-rbac patch: | - op: test - path: /spec/match/namespaceSelector/matchExpressions/0/values/16 + path: /spec/match/namespaceSelector/matchExpressions/0/values/15 value: opencost - op: replace - path: /spec/match/namespaceSelector/matchExpressions/0/values/16 + path: /spec/match/namespaceSelector/matchExpressions/0/values/15 value: observability - op: test path: /spec/reason @@ -297,9 +249,8 @@ patches: (Flux), policy engines (Kyverno), certificate managers (cert-manager), database operators (CNPG), backup controllers (Velero), autoscalers (KEDA), monitoring (Prometheus/opencost), virtualisation (KubeVirt, - CDI), storage (Longhorn), cluster dashboards (headlamp), service - discovery (homepage), identity (dex), auth proxies (oauth2-proxy), - and config watchers (reloader). + CDI), storage (Longhorn), service discovery (homepage), identity + (dex), auth proxies (oauth2-proxy), and config watchers (reloader). - op: replace path: /spec/reason value: >- @@ -308,9 +259,8 @@ patches: (Flux), policy engines (Kyverno), certificate managers (cert-manager), database operators (CNPG), backup controllers (Velero), autoscalers (KEDA), monitoring (Prometheus/Coroot), virtualisation (KubeVirt, CDI), - storage (Longhorn), cluster dashboards (headlamp), service discovery - (homepage), identity (dex), auth proxies (oauth2-proxy), and config - watchers (reloader). + storage (Longhorn), service discovery (homepage), identity (dex), auth + proxies (oauth2-proxy), and config watchers (reloader). - target: group: kubescape.io version: v1beta1 @@ -344,7 +294,7 @@ patches: namespace: dex patch: |- - op: test - path: /spec/values/config/staticClients/0/redirectURIs/9 + path: /spec/values/config/staticClients/0/redirectURIs/8 value: "https://opencost.${domain}/oauth2/callback" - op: remove - path: /spec/values/config/staticClients/0/redirectURIs/9 + path: /spec/values/config/staticClients/0/redirectURIs/8 diff --git a/k8s/providers/docker/apps/headlamp/patches/trust-platform-ca.yaml b/k8s/providers/docker/apps/headlamp/patches/trust-platform-ca.yaml deleted file mode 100644 index ee84f05..0000000 --- a/k8s/providers/docker/apps/headlamp/patches/trust-platform-ca.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: helm.toolkit.fluxcd.io/v2 -kind: HelmRelease -metadata: - name: headlamp - namespace: headlamp -spec: - values: - config: - extraArgs: - - "-oidc-ca-file=/etc/ssl/certs/platform-ca/ca-certificates.crt" - volumes: - - name: trust-bundle - configMap: - name: platform-ca - volumeMounts: - - name: trust-bundle - mountPath: /etc/ssl/certs/platform-ca - readOnly: true diff --git a/k8s/providers/docker/apps/kustomization.yaml b/k8s/providers/docker/apps/kustomization.yaml index c873a44..0b44104 100644 --- a/k8s/providers/docker/apps/kustomization.yaml +++ b/k8s/providers/docker/apps/kustomization.yaml @@ -2,12 +2,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - - ../../../bases/apps/headlamp/ - ../../../bases/apps/homepage/ - ../../../bases/apps/whoami/ -patches: - - target: - kind: HelmRelease - name: headlamp - namespace: headlamp - path: headlamp/patches/trust-platform-ca.yaml diff --git a/k8s/providers/hetzner/infrastructure/cluster-issuers/cloudflare-origin-ca-trust-bundle.yaml b/k8s/providers/hetzner/infrastructure/cluster-issuers/cloudflare-origin-ca-trust-bundle.yaml index 3ab8335..92d273b 100644 --- a/k8s/providers/hetzner/infrastructure/cluster-issuers/cloudflare-origin-ca-trust-bundle.yaml +++ b/k8s/providers/hetzner/infrastructure/cluster-issuers/cloudflare-origin-ca-trust-bundle.yaml @@ -1,6 +1,6 @@ # Distributes the Cloudflare Origin ECC CA root + system CAs to all namespaces # as the ConfigMap "cloudflare-origin-ca". Required so that in-cluster clients -# (e.g. Headlamp) can verify TLS connections to the Cilium gateway when CoreDNS +# can verify TLS connections to the Cilium gateway when CoreDNS # resolves *.platform.example.com directly to the in-cluster service instead # of routing through Cloudflare's publicly-trusted edge. apiVersion: trust.cert-manager.io/v1alpha1 diff --git a/scripts/validate-observability-option.sh b/scripts/validate-observability-option.sh index 64be86b..324481b 100755 --- a/scripts/validate-observability-option.sh +++ b/scripts/validate-observability-option.sh @@ -720,19 +720,6 @@ assert_opencost_resources_absent() { fi } -# Verifies the exact number of rendered resources that reference OpenCost. -assert_opencost_reference_count() { - local rendered_path="$1" - local expected="$2" - local actual - - actual="$(opencost_reference_count "${rendered_path}")" - if [[ "${actual}" != "${expected}" ]]; then - echo "expected ${expected} OpenCost-referencing resource(s) in ${rendered_path}, found ${actual}" >&2 - return 1 - fi -} - # Verifies that both OpenCost resources and serialized references are absent. assert_opencost_absent() { local rendered_path="$1" @@ -1059,8 +1046,13 @@ for rendered_path in "${local_infrastructure_default}" "${prod_infrastructure_de assert_security_exception_namespace_count "${rendered_path}" service-account-tokens observability 0 assert_heartbeat_policy_exclusion_absent "${rendered_path}" done -assert_opencost_reference_count "${local_apps_default}" 1 -assert_opencost_reference_count "${prod_apps_default}" 1 +# No default app consumes OpenCost. The demo apps prove each absence check +# examined a real apps render rather than an empty one. +for rendered_path in "${local_apps_default}" "${prod_apps_default}"; do + assert_resource_count "${rendered_path}" HelmRelease homepage homepage 1 + assert_resource_count "${rendered_path}" HelmRelease whoami whoami 1 + assert_opencost_absent "${rendered_path}" +done render k8s/providers/docker/infrastructure-controllers-coroot/ "${docker_controllers}" render k8s/providers/docker/infrastructure-coroot/ "${docker_infrastructure}" @@ -1305,7 +1297,7 @@ for config_path in ksail.yaml ksail.prod.yaml; do done for documented_boundary in \ "This profile is transitional." \ - "removes OpenCost and its Headlamp" \ + "It enables Coroot, removes OpenCost," \ "retires the legacy Loki and Alloy log path" \ "stages one hardened \`cluster-heartbeat\` CronJob" \ "uses a separate optional \`CLUSTER_HEARTBEAT_URL\` secret" \