Add externalSecret support to spire-server kubeConfigs (#889)

Allow each kubeConfigs entry to reference an externally-managed Secret
(externalSecret{name,key}) instead of embedding the kubeconfig in values.
Entries may reference different Secrets and mix inline with external ones.
The kubeconfigs volume becomes a projected volume; consumer mount paths are
unchanged. Each entry must set exactly one of kubeConfig, kubeConfigBase64,
or externalSecret.

Signed-off-by: sabsari <[email protected]>
Co-authored-by: Claude Opus 4.8 <[email protected]>
This commit is contained in:
sabsari
2026-07-29 08:07:39 -07:00
committed by GitHub
co-authored by Claude Opus 4.8
parent ba99ab3eb3
commit 203183f73c
5 changed files with 98 additions and 11 deletions
@@ -179,6 +179,13 @@ Create the name of the service account to use
{{ include "spire-server.fullname" . | trimSuffix "-server" }}-controller-manager
{{- end }}
{{/*
Name of the chart-generated Secret holding the inline kubeConfigs entries.
*/}}
{{- define "spire-server.kubeconfigs-secret-name" -}}
{{ include "spire-server.fullname" . }}-kubeconfigs
{{- end }}
{{- define "spire-server.serviceAccountAllowedList" }}
{{- $releaseNamespace := include "spire-server.agent-namespace" . }}
{{- if ne (len .Values.nodeAttestor.k8sPSAT.serviceAccountAllowList) 0 }}
@@ -1,19 +1,37 @@
{{- $root := . }}
{{- with .Values.kubeConfigs }}
{{- $secretName := include "spire-server.kubeconfigs-secret-name" $root }}
{{- $inline := dict }}
{{- range $name, $value := .Values.kubeConfigs }}
{{- $present := list }}
{{- if hasKey $value "kubeConfig" }}{{ $present = append $present "kubeConfig" }}{{- end }}
{{- if hasKey $value "kubeConfigBase64" }}{{ $present = append $present "kubeConfigBase64" }}{{- end }}
{{- if hasKey $value "externalSecret" }}{{ $present = append $present "externalSecret" }}{{- end }}
{{- if ne (len $present) 1 }}
{{- fail (printf "kubeConfigs entry %q must set exactly one of kubeConfig, kubeConfigBase64, or externalSecret (got: %v)" $name $present) }}
{{- end }}
{{- if hasKey $value "externalSecret" }}
{{- if not $value.externalSecret.name }}
{{- fail (printf "kubeConfigs entry %q externalSecret.name cannot be empty" $name) }}
{{- end }}
{{- if eq $value.externalSecret.name $secretName }}
{{- fail (printf "kubeConfigs entry %q externalSecret.name %q is reserved for the chart-generated Secret; use a different name" $name $secretName) }}
{{- end }}
{{- else }}
{{- $_ := set $inline $name $value }}
{{- end }}
{{- end }}
{{- if gt (len $inline) 0 }}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "spire-server.fullname" $root }}-kubeconfigs
name: {{ include "spire-server.kubeconfigs-secret-name" $root }}
namespace: {{ include "spire-server.namespace" $root }}
data:
{{- range $name, $value := . }}
{{- if and (hasKey . "kubeConfig") (hasKey . "kubeConfigBase64") }}
{{- fail "You can not use both kubeConfig and kubeConfigBase64" }}
{{- end }}
{{- if (hasKey . "kubeConfig") }}
{{ $name }}: {{ .kubeConfig | b64enc }}
{{- range $name, $value := $inline }}
{{- if hasKey $value "kubeConfig" }}
{{ $name }}: {{ $value.kubeConfig | b64enc }}
{{- else }}
{{ $name }}: {{ .kubeConfigBase64 | nospace }}
{{ $name }}: {{ $value.kubeConfigBase64 | nospace }}
{{- end }}
{{- end }}
{{- end }}
@@ -601,8 +601,32 @@ spec:
emptyDir: {}
{{- if gt (len .Values.kubeConfigs) 0 }}
- name: kubeconfigs
secret:
secretName: {{ include "spire-server.fullname" . }}-kubeconfigs
projected:
sources:
{{- $inlineNames := list }}
{{- range $name, $value := .Values.kubeConfigs }}
{{- if not (hasKey $value "externalSecret") }}
{{- $inlineNames = append $inlineNames $name }}
{{- end }}
{{- end }}
{{- if gt (len $inlineNames) 0 }}
- secret:
name: {{ include "spire-server.kubeconfigs-secret-name" . }}
items:
{{- range $name := ($inlineNames | sortAlpha) }}
- key: {{ $name }}
path: {{ $name }}
{{- end }}
{{- end }}
{{- range $name, $value := .Values.kubeConfigs }}
{{- if hasKey $value "externalSecret" }}
- secret:
name: {{ $value.externalSecret.name }}
items:
- key: {{ $value.externalSecret.key | default $name }}
path: {{ $name }}
{{- end }}
{{- end }}
{{- end }}
{{- if $pluginLoaderNeeded }}
- name: plugins
@@ -1544,6 +1544,9 @@ tests:
tag: latest@sha256:90041f375e30f41aa7e0390075d8a69dc61900771d52fa98d63ee5d03d866a58
## @param kubeConfigs [object] Manage additional kubeconfig files to talk to external Kubernetes clusters
## Each entry sets exactly one of kubeConfig, kubeConfigBase64, or externalSecret. Use externalSecret to
## reference a kubeconfig from an externally-managed Secret instead of embedding it in values;
## entries may reference different Secrets and mix with inline ones.
kubeConfigs: {}
# clustera:
# kubeConfig: |
@@ -1551,6 +1554,10 @@ kubeConfigs: {}
# xxxxx
# clusterb:
# kubeConfigBase64: eXl5Cnl5eQo=
# clusterc:
# externalSecret:
# name: my-kubeconfigs-secret # name of the externally-managed Secret to read from
# key: clusterc # optional, defaults to the entry name
spireIdentityExchange:
## @param spireIdentityExchange.enabled Enable the server side of the SPIRE Identity Exchange system
+31
View File
@@ -257,4 +257,35 @@ spiffe-oidc-discovery-provider:
Expect(serverCM).Should(ContainSubstring(`"jwt_issuer": "https://oidc-discovery.example.org"`))
})
})
Describe("spire-server.kubeConfigs", func() {
secretTmpl := "spire/charts/spire-server/templates/kubeconfig-secret.yaml"
serverTmpl := "spire/charts/spire-server/templates/server-resource.yaml"
It("inline entry generates a Secret and a projected volume source referencing it", func() {
objs, err := ValueStringRender(chart, `
spire-server:
kubeConfigs:
clustera:
kubeConfig: |
apiVersion: v1
kind: Config
`)
Expect(err).Should(Succeed())
Expect(objs[secretTmpl]).Should(ContainSubstring("kind: Secret"))
Expect(objs[serverTmpl]).Should(ContainSubstring("projected:"))
Expect(objs[serverTmpl]).Should(ContainSubstring("path: clustera"))
})
It("externalSecret entry wires a projected source and skips the generated Secret", func() {
objs, err := ValueStringRender(chart, `
spire-server:
kubeConfigs:
clusterb:
externalSecret:
name: my-ext-secret
`)
Expect(err).Should(Succeed())
Expect(objs[secretTmpl]).ShouldNot(ContainSubstring("kind: Secret"))
Expect(objs[serverTmpl]).Should(ContainSubstring("name: my-ext-secret"))
Expect(objs[serverTmpl]).Should(ContainSubstring("path: clusterb"))
})
})
})