feat: add homelab-wide Authentik RBAC model and k8s OIDC auth wiring

Adds permissions claim + per-service admin groups in Authentik, scoped
Role/RoleBinding per service, public PKCE kubernetes OAuth2 client, and
kube-apiserver OIDC extraArgs. Also fixes paperless OIDC signup permissions
via adapter override and adds CoreDNS rewrite for authentik.riotpiao.com.
This commit is contained in:
Story Crater Bot
2026-08-25 15:03:44 -07:00
parent 8b49b347b5
commit 89f01b6f2e
17 changed files with 608 additions and 11 deletions
+32
View File
@@ -0,0 +1,32 @@
# Scoped operator access for forgejo-admins: the gitea Deployment + admin
# creds only. forgejo-db-*, forgejo-oidc, forgejo-tls, and the helm-managed
# forgejo-gitea-inline-config stay excluded. Inert until kube-apiserver's
# OIDC wiring lands.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: forgejo-operator
namespace: cicd
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
resourceNames: ["forgejo-gitea"]
verbs: ["get", "list", "watch", "update", "patch"]
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["forgejo-admin"]
verbs: ["get", "list", "watch", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: forgejo-admins-binding
namespace: cicd
subjects:
- kind: Group
name: "oidc:forgejo-admins"
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: forgejo-operator
apiGroup: rbac.authorization.k8s.io
+35
View File
@@ -0,0 +1,35 @@
# Scoped operator access for grafana-admins: dashboards + admin creds only.
# grafana-oidc (client secret) stays excluded - editing it is a security
# change, not app config. Inert until kube-apiserver's OIDC wiring lands.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: grafana-operator
namespace: logging
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
resourceNames: ["grafana"]
verbs: ["get", "list", "watch", "update", "patch"]
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["grafana-dashboards-default", "grafana-config-dashboards"]
verbs: ["get", "list", "watch", "update", "patch"]
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["grafana-admin"]
verbs: ["get", "list", "watch", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: grafana-admins-binding
namespace: logging
subjects:
- kind: Group
name: "oidc:grafana-admins"
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: grafana-operator
apiGroup: rbac.authorization.k8s.io
+32
View File
@@ -0,0 +1,32 @@
# Scoped operator access for homarr-admins: the homarr Deployment + app
# secrets only. homarr-oidc, auth-oidc-secret, db-encryption stay excluded
# (security-managed, not app config). Inert until kube-apiserver's OIDC
# wiring lands.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: homarr-operator
namespace: dashboard
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
resourceNames: ["homarr"]
verbs: ["get", "list", "watch", "update", "patch"]
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["homarr-secrets"]
verbs: ["get", "list", "watch", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: homarr-admins-binding
namespace: dashboard
subjects:
- kind: Group
name: "oidc:homarr-admins"
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: homarr-operator
apiGroup: rbac.authorization.k8s.io
+33
View File
@@ -0,0 +1,33 @@
# Scoped operator access for kmsvc-admins: management-service + its own
# config only. kmsvc-* CA/cluster/pool secrets and the strimzi-cluster-
# operator configmap are Strimzi-managed - hand-editing them gets reverted
# by the operator's reconcile loop or breaks the Kafka cluster. Inert until
# kube-apiserver's OIDC wiring lands.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: kmsvc-operator
namespace: sqs
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
resourceNames: ["management-service"]
verbs: ["get", "list", "watch", "update", "patch"]
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["management-service-config"]
verbs: ["get", "list", "watch", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: kmsvc-admins-binding
namespace: sqs
subjects:
- kind: Group
name: "oidc:kmsvc-admins"
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: kmsvc-operator
apiGroup: rbac.authorization.k8s.io
+20
View File
@@ -0,0 +1,20 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# NO top-level `namespace:` transformer - each Role/RoleBinding here targets
# a different service namespace (logging/storage/cicd/dashboard/sqs/temporal/
# llm-serving); a transformer would collapse them all into one, same bug
# already fixed once in k8s/infra/minio and k8s/infra/iam. Every resource
# here sets its own explicit metadata.namespace.
resources:
- grafana-operator-role.yaml
- minio-operator-role.yaml
- forgejo-operator-role.yaml
- homarr-operator-role.yaml
- portainer-operator-role.yaml
- kmsvc-operator-role.yaml
- temporal-operator-role.yaml
- llm-serving-operator-role.yaml
# paperless's Role/RoleBinding lives in k8s/apps/paperless/rbac.yaml instead -
# that app already has its own kustomization + namespace, no need to
# duplicate it here. All of these stay inert (grant nothing) until
# kube-apiserver has --oidc-groups-claim=groups wired up.
@@ -0,0 +1,31 @@
# Scoped operator access for llm-admins: the 4 predictor Deployments only -
# no configmap/secret exists in this namespace today. Inert until
# kube-apiserver's OIDC wiring lands.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: llm-serving-operator
namespace: llm-serving
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
resourceNames:
- reasoning-predictor
- ornith-predictor
- embeddings-predictor
- reranker-predictor
verbs: ["get", "list", "watch", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: llm-admins-binding
namespace: llm-serving
subjects:
- kind: Group
name: "oidc:llm-admins"
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: llm-serving-operator
apiGroup: rbac.authorization.k8s.io
+33
View File
@@ -0,0 +1,33 @@
# Scoped operator access for minio-admins: the tenant StatefulSet + root
# creds only - NOT the minio-operator Deployment (shared cluster-wide infra;
# editing it risks breaking MinIO for every tenant, not just this one).
# minio-oidc/sts-tls stay excluded (security-managed, not app config). Inert
# until kube-apiserver's OIDC wiring lands.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: minio-operator-admin
namespace: storage
rules:
- apiGroups: ["apps"]
resources: ["statefulsets"]
resourceNames: ["minio-cluster-az-a"]
verbs: ["get", "list", "watch", "update", "patch"]
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["minio-creds"]
verbs: ["get", "list", "watch", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: minio-admins-binding
namespace: storage
subjects:
- kind: Group
name: "oidc:minio-admins"
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: minio-operator-admin
apiGroup: rbac.authorization.k8s.io
@@ -0,0 +1,27 @@
# Scoped operator access for portainer-admins: just the Deployment - no
# configmap/secret exists for portainer today. Inert until kube-apiserver's
# OIDC wiring lands.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: portainer-operator
namespace: dashboard
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
resourceNames: ["portainer"]
verbs: ["get", "list", "watch", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: portainer-admins-binding
namespace: dashboard
subjects:
- kind: Group
name: "oidc:portainer-admins"
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: portainer-operator
apiGroup: rbac.authorization.k8s.io
@@ -0,0 +1,31 @@
# Scoped operator access for temporal-admins: temporal-worker + its dynamic
# config only. temporal-db-* (CNPG-managed) and webhook-server-cert stay
# excluded. Inert until kube-apiserver's OIDC wiring lands.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: temporal-operator
namespace: temporal
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
resourceNames: ["temporal-worker"]
verbs: ["get", "list", "watch", "update", "patch"]
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["temporal-dynamic-config"]
verbs: ["get", "list", "watch", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: temporal-admins-binding
namespace: temporal
subjects:
- kind: Group
name: "oidc:temporal-admins"
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: temporal-operator
apiGroup: rbac.authorization.k8s.io