diff --git a/k8s/bases/apps/crossview/helm-release.yaml b/k8s/bases/apps/crossview/helm-release.yaml new file mode 100644 index 000000000..774b24684 --- /dev/null +++ b/k8s/bases/apps/crossview/helm-release.yaml @@ -0,0 +1,42 @@ +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: crossview + namespace: crossview + labels: + helm.toolkit.fluxcd.io/remediation: enabled +spec: + interval: 10m + timeout: 10m + chart: + spec: + chart: crossview + # 4.5.0-rc.1 is the latest published tag but a release candidate; pin the + # latest stable. + version: 4.4.0 + sourceRef: + kind: HelmRepository + name: crossview + # https://github.com/crossplane-contrib/crossview/blob/main/helm/crossview/values.yaml + values: + global: + namespace: crossview + # The namespace is managed declaratively (namespace.yaml) so it carries the + # Pod Security Standards labels; don't let the chart create it. + createNamespace: false + app: + replicas: ${crossview_replicas:=1} + # The chart bundles a Postgres for session/user state. Run it ephemeral (no + # PVC) instead of provisioning a Longhorn volume: Crossview is an + # SSO-fronted, read-only dashboard, so losing session/user rows on a pod + # restart is acceptable — and it avoids the autoscaler-node Longhorn + # data-loss footgun that bites stateful prod volumes. + database: + persistence: + enabled: false + config: + server: + cors: + # Post-SSO-login redirect target — must be the public URL, or users + # bounce to localhost after authenticating. + origin: https://crossview.${domain} diff --git a/k8s/bases/apps/crossview/helm-repository.yaml b/k8s/bases/apps/crossview/helm-repository.yaml new file mode 100644 index 000000000..fac3f1599 --- /dev/null +++ b/k8s/bases/apps/crossview/helm-repository.yaml @@ -0,0 +1,7 @@ +apiVersion: source.toolkit.fluxcd.io/v1 +kind: HelmRepository +metadata: + name: crossview + namespace: crossview +spec: + url: https://crossplane-contrib.github.io/crossview/ diff --git a/k8s/bases/apps/crossview/httproute.yaml b/k8s/bases/apps/crossview/httproute.yaml new file mode 100644 index 000000000..9949ca076 --- /dev/null +++ b/k8s/bases/apps/crossview/httproute.yaml @@ -0,0 +1,39 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: crossview + namespace: crossview + annotations: + gethomepage.dev/enabled: "true" + gethomepage.dev/name: Crossplane + gethomepage.dev/description: Crossplane resource dashboard (Crossview). + gethomepage.dev/group: Kubernetes + gethomepage.dev/icon: crossplane + gethomepage.dev/href: https://crossview.${domain} + gethomepage.dev/pod-selector: app.kubernetes.io/name=crossview +spec: + parentRefs: + - name: platform + namespace: kube-system + sectionName: https + hostnames: + - crossview.${domain} + rules: + # Front with oauth2-proxy (Dex SSO) like the other infra dashboards + # (homepage/coroot/opencost/longhorn) — Crossview exposes infrastructure + # state, so it must never be reachable unauthenticated on the public domain. + - filters: + - type: RequestHeaderModifier + requestHeaderModifier: + set: + - name: X-Auth-Request-Redirect + value: https://crossview.${domain}/ + - type: ResponseHeaderModifier + responseHeaderModifier: + set: + - name: Strict-Transport-Security + value: max-age=63072000; includeSubDomains; preload + backendRefs: + - name: oauth2-proxy + namespace: oauth2-proxy + port: 80 diff --git a/k8s/bases/apps/crossview/kustomization.yaml b/k8s/bases/apps/crossview/kustomization.yaml new file mode 100644 index 000000000..dbfb89a1e --- /dev/null +++ b/k8s/bases/apps/crossview/kustomization.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - namespace.yaml + - helm-repository.yaml + - helm-release.yaml + - httproute.yaml + - networkpolicy.yaml diff --git a/k8s/bases/apps/crossview/namespace.yaml b/k8s/bases/apps/crossview/namespace.yaml new file mode 100644 index 000000000..7efe6f992 --- /dev/null +++ b/k8s/bases/apps/crossview/namespace.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: crossview + labels: + pod-security.kubernetes.io/enforce: restricted + pod-security.kubernetes.io/enforce-version: latest diff --git a/k8s/bases/apps/crossview/networkpolicy.yaml b/k8s/bases/apps/crossview/networkpolicy.yaml new file mode 100644 index 000000000..6f3bede2b --- /dev/null +++ b/k8s/bases/apps/crossview/networkpolicy.yaml @@ -0,0 +1,44 @@ +apiVersion: cilium.io/v2 +kind: CiliumNetworkPolicy +metadata: + name: allow-crossview + namespace: crossview +spec: + endpointSelector: {} + ingress: + # Authenticated traffic from oauth2-proxy/auth-proxy + # (gateway -> oauth2-proxy -> auth-proxy -> crossview app on 3001). + - fromEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: oauth2-proxy + toPorts: + - ports: + - port: "3001" + protocol: TCP + # Intra-namespace: app <-> bundled Postgres (and any Helm-test pods). + - fromEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: crossview + egress: + # Kube API — Crossview reads Crossplane CRDs, claims, composites, managed + # resources, and providers. + - toEntities: + - kube-apiserver + # Intra-namespace: app -> bundled Postgres (5432). + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: crossview + # DNS resolution (engages Cilium's L7 DNS proxy). + - toEndpoints: + - matchLabels: + k8s:io.kubernetes.pod.namespace: kube-system + k8s-app: kube-dns + toPorts: + - ports: + - port: "53" + protocol: UDP + - port: "53" + protocol: TCP + rules: + dns: + - matchPattern: "*" diff --git a/k8s/bases/apps/headlamp/helm-release.yaml b/k8s/bases/apps/headlamp/helm-release.yaml index e43c844d3..db0a8e0fb 100644 --- a/k8s/bases/apps/headlamp/helm-release.yaml +++ b/k8s/bases/apps/headlamp/helm-release.yaml @@ -144,6 +144,13 @@ spec: - name: headlamp_kubescape source: https://artifacthub.io/packages/headlamp/kubescape-headlamp-plugin/headlamp_kubescape version: 0.11.2 + # Crossplane dashboard. Surfaces the Flux-managed Crossview app + # (apps/crossview) inside Headlamp via its "Open Crossview" action. Do + # NOT use the plugin's Install/Update/Uninstall buttons — those run a + # cluster-admin Helm Job; Crossview is managed declaratively by Flux. + - name: crossview-headlamp + source: https://artifacthub.io/packages/headlamp/crossview-headlamp/crossview-headlamp + version: 0.1.4 installOptions: parallel: true maxConcurrent: 3 diff --git a/k8s/bases/apps/kustomization.yaml b/k8s/bases/apps/kustomization.yaml index 275a82879..15a957781 100644 --- a/k8s/bases/apps/kustomization.yaml +++ b/k8s/bases/apps/kustomization.yaml @@ -4,6 +4,10 @@ kind: Kustomization resources: - actual-budget/ - ascoachingogvaner/ + # Crossplane resource dashboard (Crossview). Prod-only in practice: it reads + # Crossplane CRDs, and Crossplane runs only on the Hetzner provider. The + # docker provider deploys no apps, so it is naturally excluded from local. + - crossview/ # fleetdm disabled 2026-06-03 — not in use. The manifests under # bases/apps/fleetdm/ are retained; re-enable by uncommenting the line below. # The prod-only HelmRelease patch (providers/hetzner/apps/fleetdm/) was diff --git a/k8s/bases/infrastructure/controllers/auth-proxy/config-map.yaml b/k8s/bases/infrastructure/controllers/auth-proxy/config-map.yaml index 25ee7c99f..015730a74 100644 --- a/k8s/bases/infrastructure/controllers/auth-proxy/config-map.yaml +++ b/k8s/bases/infrastructure/controllers/auth-proxy/config-map.yaml @@ -42,6 +42,12 @@ data: rule: "Host(`longhorn.${domain}`)" entryPoints: ["web"] service: longhorn + # Hetzner-only (Crossplane dashboard): inert on local/CI where no + # HTTPRoute sends crossview.${domain} to oauth2-proxy. + crossview: + rule: "Host(`crossview.${domain}`)" + entryPoints: ["web"] + service: crossview services: homepage: loadBalancer: @@ -68,3 +74,7 @@ data: loadBalancer: servers: - url: "http://keda-add-ons-http-interceptor-proxy.keda.svc.cluster.local:8080" + crossview: + loadBalancer: + servers: + - url: "http://crossview-service.crossview.svc.cluster.local:80" diff --git a/k8s/bases/infrastructure/controllers/oauth2-proxy/reference-grant.yaml b/k8s/bases/infrastructure/controllers/oauth2-proxy/reference-grant.yaml index 10c890b2a..1a2f4274e 100644 --- a/k8s/bases/infrastructure/controllers/oauth2-proxy/reference-grant.yaml +++ b/k8s/bases/infrastructure/controllers/oauth2-proxy/reference-grant.yaml @@ -27,6 +27,12 @@ spec: - group: gateway.networking.k8s.io kind: HTTPRoute namespace: longhorn-system + # Hetzner-only: the Crossview dashboard HTTPRoute (crossview ns) backends to + # the oauth2-proxy Service for SSO. Inert on local/CI where crossview has no + # HTTPRoute. + - group: gateway.networking.k8s.io + kind: HTTPRoute + namespace: crossview to: - group: "" kind: Service