Gate Prometheus behind Authentik #147

Merged
k.renner merged 1 commits from fix/prometheus-authentik-gate into master 2026-07-10 17:08:20 +00:00
8 changed files with 198 additions and 14 deletions
@@ -545,6 +545,88 @@ data:
kubernetes_ingress_secret_name: "authentik-outpost-tls"
kubernetes_json_patches: null
# ------------------------------------------------------------------
# prometheus gate
#
# Prometheus (prometheus.renner.uno, monitoring ns, :9090) had no auth
# of its own. Its admin API is disabled (read-only), but the full 90d
# metrics DB is an infra-recon oracle — hostnames, IPs, service
# inventory (2026-07-10 audit). Same proxy-outpost pattern as
# alertmanager-gate above; reuses the shared "Monitoring Access" group
# (created generically for exactly this). Grafana is unaffected — it
# queries the ClusterIP Service, not this ingress.
- id: prometheus-gate-provider
model: authentik_providers_proxy.proxyprovider
identifiers:
name: prometheus-gate
state: present
attrs:
authorization_flow: !Find [authentik_flows.flow, [slug, default-provider-authorization-implicit-consent]]
invalidation_flow: !Find [authentik_flows.flow, [slug, default-provider-invalidation-flow]]
mode: proxy
internal_host: http://kube-prometheus-stack-prometheus.monitoring.svc.cluster.local:9090
internal_host_ssl_validation: false
external_host: https://prometheus.renner.uno
cookie_domain: renner.uno
access_token_validity: hours=24
refresh_token_validity: days=30
intercept_header_auth: true
# No skip_path_regex: the Prometheus UI is a plain React SPA with no
# service worker and no manifest (verified 2026-07-10 — index.html
# has no serviceWorker/manifest/webmanifest refs), same as the
# Alertmanager UI, so no credentials:'omit' PWA fetches to carve out.
- model: authentik_core.application
identifiers:
slug: prometheus-gate
state: present
attrs:
name: Prometheus (Gate)
slug: prometheus-gate
provider: !KeyOf prometheus-gate-provider
meta_launch_url: https://prometheus.renner.uno
meta_description: "Authentik-gated Prometheus"
# Only the Monitoring Access group passes the gate (shared with
# alertmanager-gate).
- model: authentik_policies.policybinding
identifiers:
target: !Find [authentik_core.application, [slug, prometheus-gate]]
group: !KeyOf monitoring-access-group
order: 0
state: present
attrs:
enabled: true
negate: false
failure_result: false
- model: authentik_outposts.outpost
identifiers:
name: prometheus-gate
state: present
attrs:
type: proxy
service_connection: !KeyOf k8s-conn
providers:
- !KeyOf prometheus-gate-provider
config:
log_level: info
authentik_host: "http://authentik-server.authentik.svc.cluster.local"
authentik_host_insecure: false
authentik_host_browser: "https://auth.renner.uno/"
refresh_interval: "minutes=5"
container_image: null
object_naming_template: "ak-outpost-%(name)s"
kubernetes_namespace: authentik
kubernetes_replicas: 1
kubernetes_service_type: ClusterIP
kubernetes_disabled_components: ["ingress"]
kubernetes_image_pull_secrets: []
kubernetes_ingress_annotations: {}
kubernetes_ingress_class_name: null
kubernetes_ingress_secret_name: "authentik-outpost-tls"
kubernetes_json_patches: null
- model: authentik_outposts.outpost
identifiers:
name: authentik Embedded Outpost
@@ -10,6 +10,7 @@ resources:
- vscode-gate-egress.yaml
- shelly-manager-gate-egress.yaml
- alertmanager-gate-egress.yaml
- prometheus-gate-egress.yaml
- outpost-servicemonitors.yaml
- ldap-outpost-cert.yaml
- ldap-outpost-lb.yaml
@@ -0,0 +1,24 @@
---
# Allow the dedicated prometheus-gate outpost to egress into the monitoring
# namespace and reach Prometheus (:9090). Without this, the authentik
# baseline CNP has no egress rule to monitoring, so the gate outpost's
# forwarded requests are dropped. Scoped to the gate outpost's pod label.
# Mirrors alertmanager-gate-egress.yaml.
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: prometheus-gate-egress
namespace: authentik
spec:
endpointSelector:
matchLabels:
goauthentik.io/outpost-name: prometheus-gate
egress:
- toEndpoints:
- matchLabels:
"k8s:io.kubernetes.pod.namespace": monitoring
app.kubernetes.io/name: prometheus
toPorts:
- ports:
- port: "9090"
protocol: TCP
@@ -33,21 +33,24 @@ spec:
values:
# Prometheus
prometheus:
# LAN-only ingress at prometheus.renner.uno. No
# external-dns/cloudflare label, so only external-dns-unifi
# publishes the hostname — resolves on LAN, NXDOMAIN from the
# public internet.
# prometheus.renner.uno is served by a standalone, Authentik-gated
# Ingress (prometheus-gate-ingress.yaml), NOT this Helm-generated one.
# Prometheus had no auth of its own — its full 90d metrics DB is an
# infra-recon oracle (hostnames, IPs, service inventory). Gated behind
# the prometheus-gate proxy outpost, same as alertmanager-gate (#146).
# The chart Ingress is disabled here — two Ingresses for one host would
# conflict. tls (prometheus-tls) and externalUrl below are unchanged;
# the standalone Ingress reuses the same secret.
#
# Grafana is unaffected: its datasource targets the ClusterIP Service
# (kube-prometheus-stack-prometheus.monitoring:9090), not this ingress.
# Blackbox uptime probes that target https://prometheus.renner.uno
# follow the gate's 302 to the Authentik login (200), so they stay
# green — but now measure Authentik reachability, not Prometheus. Same
# already-true for alerts.renner.uno since #146. Repointing those probes
# to an internal endpoint is a separate follow-up.
ingress:
enabled: true
ingressClassName: traefik
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
hosts:
- prometheus.renner.uno
tls:
- secretName: prometheus-tls
hosts:
- prometheus.renner.uno
enabled: false
prometheusSpec:
# Rewrites the in-cluster URL embedded in alert generatorURLs and
# status pages so click-through from emails works.
@@ -14,6 +14,8 @@ resources:
- loki-ingress.yaml
- alertmanager-gate-service.yaml
- alertmanager-gate-ingress.yaml
- prometheus-gate-service.yaml
- prometheus-gate-ingress.yaml
- dashboards/
# prometheusrule-garage.yaml lives in ./monitoring-rules/ and is applied by a
# separate Flux Kustomization that dependsOn `monitoring` — otherwise the
@@ -0,0 +1,39 @@
---
# prometheus.renner.uno — Prometheus, gated behind Authentik.
#
# Prometheus has no auth of its own; its admin API is disabled (read-only),
# but the full 90d metrics DB exposes hostnames, IPs and service inventory
# to any unauthenticated LAN client. This Ingress backends the Authentik
# prometheus-gate proxy outpost (ExternalName → authentik ns); the
# monitoring CNP allows only that outpost to reach :9090. Replaces the
# Helm-chart-generated Prometheus Ingress (disabled via
# prometheus.ingress.enabled=false in helmrelease-prometheus.yaml) — two
# Ingresses for the same host would conflict. Mirrors
# alertmanager-gate-ingress.yaml.
#
# Grafana is unaffected: its datasource targets the ClusterIP Service
# (kube-prometheus-stack-prometheus:9090), not this hostname.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: prometheus-gate
namespace: monitoring
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
spec:
ingressClassName: traefik
rules:
- host: prometheus.renner.uno
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: ak-outpost-prometheus-gate
port:
number: 9000
tls:
- secretName: prometheus-tls
hosts:
- prometheus.renner.uno
@@ -0,0 +1,22 @@
---
# ExternalName mirror so the monitoring-namespace Prometheus gate Ingress
# can backend to the prometheus-gate outpost that Authentik deploys into the
# authentik namespace (cross-namespace Service refs aren't supported in a
# standard Ingress). Mirrors ak-outpost-alertmanager-gate.
#
# Traffic path:
# browser → traefik (prometheus.renner.uno)
# → ak-outpost-prometheus-gate (ExternalName → authentik ns)
# → outpost validates the Authentik session
# → http://kube-prometheus-stack-prometheus.monitoring.svc:9090
apiVersion: v1
kind: Service
metadata:
name: ak-outpost-prometheus-gate
namespace: monitoring
spec:
type: ExternalName
externalName: ak-outpost-prometheus-gate.authentik.svc.cluster.local
ports:
- port: 9000
targetPort: 9000
@@ -39,6 +39,17 @@ spec:
- ports:
- port: "9093"
protocol: TCP
# The Authentik prometheus-gate outpost forwards gated browser traffic
# to Prometheus :9090. Same scoping as alertmanager-gate above. Matching
# egress: authentik/prometheus-gate-egress.yaml.
- fromEndpoints:
- matchLabels:
"k8s:io.kubernetes.pod.namespace": authentik
goauthentik.io/outpost-name: prometheus-gate
toPorts:
- ports:
- port: "9090"
protocol: TCP
# Kubelet probes + control-plane (PrometheusRule/ServiceMonitor admission
# webhooks via kube-prometheus-stack-operator are called by kube-apiserver).
# Also covers external alloy-syslog traffic — ETP=Cluster on the LB SNATs