Phase 16: fine-grained RBAC (infra-operators) + DB rotation prep
All checks were successful
AI Review / AI Code Review (pull_request) Successful in 1s
PR Checks / Validate & Security Scan (pull_request) Successful in 8s

- Add infra-operators group to Keycloak realm
- Add K8s RBAC: operators get full CRUD in dev/staging, readonly in prod,
  cluster-level readonly for nodes/namespaces/storage, no infra ns access
- Update ArgoCD RBAC: operators → role:readonly
- Update oauth2-proxy: allow infra-operators group
- Add PostgreSQL NodePort (35432) for OpenBao Database engine access
- Update NetworkPolicy: allow NodePort traffic from node CIDR
- Extend keycloak-secrets-manager Role: statefulset get/patch for rotation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Claude 2026-02-19 15:33:23 +01:00
parent 414ed94cac
commit f71c583d69
7 changed files with 114 additions and 3 deletions

View File

@ -9,6 +9,7 @@ metadata:
data:
policy.csv: |
g, /infra-admins, role:admin
g, /infra-operators, role:readonly
g, /infra-bots, role:readonly
policy.default: "role:readonly"
scopes: "[groups]"

View File

@ -72,7 +72,7 @@ spec:
- port: 5432
protocol: TCP
---
# PostgreSQL: accept from Keycloak
# PostgreSQL: accept from Keycloak + NodePort (OpenBao rotation via SSH tunnel)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
@ -92,6 +92,12 @@ spec:
ports:
- port: 5432
protocol: TCP
- from:
- ipBlock:
cidr: 10.10.10.0/24
ports:
- port: 5432
protocol: TCP
---
# oauth2-proxy: allow ingress from nginx-ingress (auth subrequests)
apiVersion: networking.k8s.io/v1

View File

@ -0,0 +1,16 @@
apiVersion: v1
kind: Service
metadata:
name: keycloak-postgresql-nodeport
namespace: keycloak
spec:
type: NodePort
selector:
app.kubernetes.io/component: primary
app.kubernetes.io/instance: keycloak
app.kubernetes.io/name: postgresql
ports:
- port: 5432
targetPort: tcp-postgresql
nodePort: 35432
protocol: TCP

View File

@ -66,6 +66,7 @@ data:
],
"groups": [
{"name": "infra-admins"},
{"name": "infra-operators"},
{"name": "infra-bots"}
],
"clients": [

View File

@ -36,7 +36,7 @@ subjects:
name: keycloak-secrets-manager
namespace: kube-system
---
# Secrets management in keycloak namespace
# Secrets + statefulset management in keycloak namespace
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
@ -46,6 +46,9 @@ rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "create", "update", "patch"]
- apiGroups: ["apps"]
resources: ["statefulsets"]
verbs: ["get", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding

View File

@ -45,3 +45,87 @@ subjects:
- kind: Group
name: "oidc:infra-bots"
apiGroup: rbac.authorization.k8s.io
---
# infra-operators: cluster-level readonly (namespaces, nodes, storage)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: oidc-operators-cluster-readonly
rules:
- apiGroups: [""]
resources: ["namespaces", "nodes", "persistentvolumes"]
verbs: ["get", "list", "watch"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: oidc-infra-operators-cluster
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: oidc-operators-cluster-readonly
subjects:
- kind: Group
name: "oidc:infra-operators"
apiGroup: rbac.authorization.k8s.io
---
# infra-operators: full CRUD on workloads (bound per-namespace)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: oidc-operators-workload
rules:
- apiGroups: ["", "apps", "batch", "networking.k8s.io"]
resources: ["*"]
verbs: ["*"]
- apiGroups: ["autoscaling"]
resources: ["horizontalpodautoscalers"]
verbs: ["*"]
---
# operators: full CRUD in dev
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: oidc-operators-workload
namespace: dev
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: oidc-operators-workload
subjects:
- kind: Group
name: "oidc:infra-operators"
apiGroup: rbac.authorization.k8s.io
---
# operators: full CRUD in staging
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: oidc-operators-workload
namespace: staging
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: oidc-operators-workload
subjects:
- kind: Group
name: "oidc:infra-operators"
apiGroup: rbac.authorization.k8s.io
---
# operators: readonly in prod (reuses existing oidc-readonly ClusterRole)
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: oidc-operators-prod-readonly
namespace: prod
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: oidc-readonly
subjects:
- kind: Group
name: "oidc:infra-operators"
apiGroup: rbac.authorization.k8s.io

View File

@ -36,7 +36,7 @@ spec:
skip_provider_button = true
upstreams = ["static://202"]
cookie_samesite = "lax"
allowed_groups = ["/infra-admins", "infra-admins"]
allowed_groups = ["/infra-admins", "infra-admins", "/infra-operators", "infra-operators"]
resources:
requests:
cpu: 25m