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:
co-authored by
Claude Opus 4.8
parent
ba99ab3eb3
commit
203183f73c
@@ -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
|
||||
|
||||
@@ -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"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user