diff --git a/Chart.lock b/Chart.lock new file mode 100644 index 0000000..853205a --- /dev/null +++ b/Chart.lock @@ -0,0 +1,15 @@ +dependencies: +- name: cert-manager + repository: https://charts.jetstack.io + version: v1.16.1 +- name: traefik + repository: https://traefik.github.io/charts + version: 33.2.1 +- name: longhorn + repository: https://charts.longhorn.io + version: 1.7.2 +- name: external-secrets + repository: https://charts.external-secrets.io + version: 0.10.7 +digest: sha256:7b35cac211af5d24103b3d1f94be2c1bbf9c6ce29574674d454d3060482e48ee +generated: "2026-05-07T20:39:30.022767+03:00" diff --git a/Chart.yaml b/Chart.yaml index 55f72ed..6681a78 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -23,8 +23,8 @@ description: | Git). type: application -version: 0.5.7 -appVersion: "0.5.7" +version: 0.6.0 +appVersion: "0.6.0" dependencies: - name: cert-manager @@ -44,3 +44,12 @@ dependencies: version: "1.7.2" repository: "https://charts.longhorn.io" condition: longhorn.enabled + # External Secrets Operator — declarative Secret delivery from + # OpenBao. Replaces Tower's imperative kubectl-stamp pattern for + # gitea-archive-pull (Phase 1 pilot, 2026-05-07). The other 4 + # Tower-stamped Secrets remain on the legacy path until a planned + # follow-up sprint (Item #9 in v3 open queue). + - name: external-secrets + version: "0.10.7" + repository: "https://charts.external-secrets.io" + condition: externalSecrets.enabled diff --git a/charts/external-secrets-0.10.7.tgz b/charts/external-secrets-0.10.7.tgz new file mode 100644 index 0000000..d76e28a Binary files /dev/null and b/charts/external-secrets-0.10.7.tgz differ diff --git a/templates/gitea-archive-pull-externalsecret.yaml b/templates/gitea-archive-pull-externalsecret.yaml new file mode 100644 index 0000000..5b262c3 --- /dev/null +++ b/templates/gitea-archive-pull-externalsecret.yaml @@ -0,0 +1,37 @@ +{{- if .Values.externalSecrets.enabled }} +{{- if .Values.externalSecrets.openbao.mountPath }} +# ExternalSecret — declarative replacement for Tower's imperative +# kubectl-stamp of `gitea-archive-pull` (server_adapters.go was the +# previous owner; removed in tower:0.76.20). The K8s Secret produced +# in odoosky-system has the same name + key shape (`token`) the +# addon-build init container expects, so consumer code is unchanged. +# +# refreshInterval=0 → ESO does NOT poll OpenBao on a schedule. Token +# rotation is operator-driven: `bao kv put v3/platform/gitea-archive-pull +# token=NEW_VALUE`, then bump an annotation on this CR to force a +# resync (`kubectl annotate externalsecret gitea-archive-pull -n +# odoosky-system rotate=$(date +%s) --overwrite`). Saves audit-log +# noise for a token that rotates quarterly at most. +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: gitea-archive-pull + namespace: odoosky-system + labels: + app.kubernetes.io/managed-by: cluster-platform-v3 +spec: + refreshInterval: "0" + secretStoreRef: + name: openbao-platform + kind: ClusterSecretStore + target: + name: gitea-archive-pull + creationPolicy: Owner + deletionPolicy: Retain + data: + - secretKey: token + remoteRef: + key: platform/gitea-archive-pull + property: token +{{- end }} +{{- end }} diff --git a/templates/openbao-secretstore.yaml b/templates/openbao-secretstore.yaml new file mode 100644 index 0000000..a1143c4 --- /dev/null +++ b/templates/openbao-secretstore.yaml @@ -0,0 +1,33 @@ +{{- if .Values.externalSecrets.enabled }} +{{- if .Values.externalSecrets.openbao.mountPath }} +# ClusterSecretStore — single store per cluster, namespace-spanning so a +# tenants-namespace ExternalSecret can also reference it (current Phase 1 +# scope only writes to odoosky-system, but downstream phases will fan to +# tenants ns for s3-backup-creds). +# +# auth.kubernetes.mountPath is per-cluster (Tower passes +# `kubernetes-` as a helm parameter). Each cluster +# authenticates against its own OpenBao auth mount, with the +# `eso-platform-read` policy bound to the role. ServiceAccount +# `external-secrets` is created by the ESO subchart in odoosky-system. +apiVersion: external-secrets.io/v1beta1 +kind: ClusterSecretStore +metadata: + name: openbao-platform + labels: + app.kubernetes.io/managed-by: cluster-platform-v3 +spec: + provider: + vault: + server: {{ .Values.externalSecrets.openbao.server | quote }} + path: "v3" + version: "v2" + auth: + kubernetes: + mountPath: {{ .Values.externalSecrets.openbao.mountPath | quote }} + role: {{ .Values.externalSecrets.openbao.role | default "eso" | quote }} + serviceAccountRef: + name: external-secrets + namespace: odoosky-system +{{- end }} +{{- end }} diff --git a/values.yaml b/values.yaml index f0dc2f5..cd34092 100644 --- a/values.yaml +++ b/values.yaml @@ -255,3 +255,27 @@ longhorn: persistence: defaultClass: false defaultClassReplicaCount: 1 + +# externalSecrets — pilot delivery path for platform-scope Secrets +# previously kubectl-stamped by Tower. 2026-05-07 Phase 1 scope: +# `gitea-archive-pull` only. The other 4 Tower-stamped Secrets stay +# on the legacy path until a dedicated sprint (Item #9, v3 open +# queue). +# +# .openbao.mountPath is per-cluster — Tower passes this as a helm +# parameter so each tenant cluster's ESO authenticates against its +# own OpenBao auth mount (`auth/kubernetes-`). Empty default +# means "off"; new clusters with no Tower wiring stay legacy. +externalSecrets: + enabled: true + openbao: + server: "https://vault.odoosky.org" + mountPath: "" # Tower fills this per-cluster, e.g. "kubernetes-customer1" + role: "eso" + +# external-secrets — values passed THROUGH to the upstream subchart +# (Chart.yaml dependency name = "external-secrets"). CRDs install on +# first apply. Resource limits conservative — ESO is event-driven +# and idle most of the time. +external-secrets: + installCRDs: true