diff --git a/deploy/rbac/kustomization.yaml b/deploy/rbac/kustomization.yaml new file mode 100644 index 0000000..7a2e6d4 --- /dev/null +++ b/deploy/rbac/kustomization.yaml @@ -0,0 +1,7 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: nereus + +resources: + - nereus-deployer-sealedsecrets.yaml diff --git a/deploy/rbac/nereus-deployer-sealedsecrets.yaml b/deploy/rbac/nereus-deployer-sealedsecrets.yaml new file mode 100644 index 0000000..5acf4c1 --- /dev/null +++ b/deploy/rbac/nereus-deployer-sealedsecrets.yaml @@ -0,0 +1,22 @@ +# Bootstrap once with cluster-admin. The deployer cannot grant itself access. +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: nereus-deployer-sealedsecrets +rules: + - apiGroups: ["bitnami.com"] + resources: ["sealedsecrets"] + verbs: ["get", "list", "watch", "create", "update", "patch"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: nereus-deployer-sealedsecrets +subjects: + - kind: ServiceAccount + name: nereus-deployer + namespace: kube-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: nereus-deployer-sealedsecrets diff --git a/docs/ci-cd.md b/docs/ci-cd.md index bef1da7..7d17aab 100644 --- a/docs/ci-cd.md +++ b/docs/ci-cd.md @@ -28,6 +28,17 @@ After registering that deploy key, set the Forgejo Actions repository variable `MIRROR_ENABLED` to `true`. The mirror job stays skipped until both sides are configured, so an absent GitHub credential cannot break production deploys. +Before the first sealed deployment, a cluster administrator must bootstrap the +deployer's namespaced SealedSecret permission: + +```bash +kubectl apply -k deploy/rbac +``` + +The deployer cannot grant this Role to itself. The Role permits only management +and waiting on `bitnami.com/sealedsecrets` in `nereus`; the controller remains +the only identity allowed to create the decrypted Secret. + The workflow never prints these values. It writes the kubeconfig to the ephemeral workspace with mode `0600`, removes it in an `always()` step, and creates the Kubernetes registry pull secret through a pipe so its generated