Enable easy plugin loading (#859)

* Enable easy plugin loading

customPluings have to be loaded into the main container somehow. Extend
the existing cel plugin loader to allow users to easily specify an image
to load it from.

Signed-off-by: Kevin Fox <[email protected]>

* Add some missing bits

Signed-off-by: Kevin Fox <[email protected]>

* Incorperate feedback

Signed-off-by: Kevin Fox <[email protected]>

* Fix formatting

Signed-off-by: Kevin Fox <[email protected]>

* Fix formatting

Signed-off-by: Kevin Fox <[email protected]>

---------

Signed-off-by: Kevin Fox <[email protected]>
This commit is contained in:
kfox1111
2026-06-25 22:49:42 -07:00
committed by GitHub
parent a8a94544e2
commit 7687339420
8 changed files with 172 additions and 27 deletions
+29 -7
View File
@@ -200,14 +200,20 @@ Take a copy of the config and merge in .Values.customPlugins and .Values.unsuppo
*/}}
{{- define "spire-lib.config_merge" }}
{{- $pluginsToMerge := dict "plugins" dict }}
{{- range $type, $val := .root.Values.customPlugins }}
{{- if . }}
{{- if eq $type "svidStore" }}
{{- $_ := set $pluginsToMerge.plugins "SVIDStore" (deepCopy $val) }}
{{- else }}
{{- $nt := printf "%s%s" (substr 0 1 $type | upper) (substr 1 -1 $type) }}
{{- $_ := set $pluginsToMerge.plugins $nt (deepCopy $val) }}
{{- range $type, $instances := .root.Values.customPlugins }}
{{- if $instances }}
{{- $nt := (eq $type "svidStore") | ternary "SVIDStore" (printf "%s%s" (substr 0 1 $type | upper) (substr 1 -1 $type)) }}
{{- $processedInstances := dict }}
{{- range $instanceName, $config := $instances }}
{{- $pluginData := deepCopy $config }}
{{- $hasImage := hasKey $pluginData "image" }}
{{- $_ := unset $pluginData "image" }}
{{- if and $hasImage $pluginData.plugin_cmd }}
{{- $_ := set $pluginData "plugin_cmd" (printf "/plugins/%s/%s" $type $instanceName) }}
{{- end }}
{{- $_ := set $processedInstances $instanceName $pluginData }}
{{- end }}
{{- $_ := set $pluginsToMerge.plugins $nt $processedInstances }}
{{- end }}
{{- end }}
{{- range $type, $val := .root.Values.unsupportedBuiltInPlugins }}
@@ -224,6 +230,22 @@ Take a copy of the config and merge in .Values.customPlugins and .Values.unsuppo
{{- $newConfig | toYaml }}
{{- end }}
{{/*
Find all customPlugins that specify an image, and build a list of binaries to copy into the plugin volume in the plugin loader.
*/}}
{{- define "spire-lib.extract_custom_plugin_images" }}
{{- $pluginList := list }}
{{- range $type, $instances := .Values.customPlugins }}
{{- range $instanceName, $config := $instances }}
{{- if $config.image }}
{{- $entry := dict "plugin_cmd" $config.plugin_cmd "image" $config.image "name" (printf "%s/%s" $type $instanceName) }}
{{- $pluginList = append $pluginList $entry }}
{{- end }}
{{- end }}
{{- end }}
{{- $pluginList | toYaml }}
{{- end }}
{{/*
Take a copy of the plugin section and return a yaml string based version
reformatted from a dict of dicts to a dict of lists of dicts
@@ -165,6 +165,10 @@ A Helm chart to install the SPIRE agent.
| `tools.kubectl.image.repository` | The repository within the registry | `kubectl` |
| `tools.kubectl.image.pullPolicy` | The image pull policy | `IfNotPresent` |
| `tools.kubectl.image.tag` | Overrides the image tag whose default is the chart appVersion | `""` |
| `tools.busybox.image.registry` | The OCI registry to pull the image from | `""` |
| `tools.busybox.image.repository` | The repository within the registry | `busybox` |
| `tools.busybox.image.pullPolicy` | The image pull policy | `IfNotPresent` |
| `tools.busybox.image.tag` | Overrides the image tag whose default is the chart appVersion | `1.37.0-uclibc` |
| `sockets.hostBasePath` | Path on which the agent socket is made available when admin.mountOnHost is true | `/run/spire/agent/sockets` |
| `sockets.admin.enabled` | Enable the admin socket. Useful for admin tasks or the Delegated Identity API. | `false` |
| `sockets.admin.mountOnHost` | Enable the admin socket to be visible on the host. | `false` |
@@ -14,7 +14,7 @@
{{- fail (printf "plugin_checksum is a required field.") }}
{{- end }}
{{- range $sname, $svals := $nval }}
{{- if not (has $sname (list "plugin_cmd" "plugin_checksum" "plugin_data")) }}
{{- if not (has $sname (list "plugin_cmd" "plugin_checksum" "plugin_data" "image")) }}
{{- fail (printf "Unknown plugin setting specified: %s" $sname) }}
{{- end }}
{{- end }}
@@ -40,6 +40,8 @@
{{- end }}
{{- $socketAlternateNames := index (include "spire-agent.socket-alternate-names" . | fromYaml) "names" }}
{{- $socketPath := include "spire-agent.socket-path" . }}
{{- $pluginsToLoad := include "spire-lib.extract_custom_plugin_images" . | fromYamlArray }}
{{- $pluginLoaderNeeded := gt (len $pluginsToLoad) 0 }}
---
apiVersion: apps/v1
kind: DaemonSet
@@ -100,6 +102,56 @@ spec:
{{- toYaml .Values.hostAliases | nindent 8 }}
{{- end }}
initContainers:
{{- if $pluginLoaderNeeded }}
- name: init-plugins
securityContext:
{{- include "spire-lib.securitycontext" . | nindent 12 }}
image: {{ template "spire-lib.image" (dict "appVersion" $.Chart.AppVersion "image" .Values.tools.busybox.image "global" .Values.global) }}
# SPIRE must be able to fork the plugin directly within its container. First copy a busybox so that the plugin can be copied into the right place.
command:
- busybox
- sh
- -ec
- |
cp -a /bin/busybox /plugins/busybox
volumeMounts:
- name: plugins
mountPath: /plugins
imagePullPolicy: {{ .Values.tools.busybox.image.pullPolicy }}
{{- end }}
{{- range $idx, $plugin := $pluginsToLoad }}
- name: {{ printf "init-plugin-%d" $idx }}
securityContext:
{{- include "spire-lib.securitycontext" $ | nindent 12 }}
image: {{ template "spire-lib.image" (dict "appVersion" $.Chart.AppVersion "image" $plugin.image "global" $.Values.global) }}
command:
- /plugins/busybox
- sh
- -ec
- |
/plugins/busybox mkdir -p /plugins/{{ dir $plugin.name }}
/plugins/busybox cp -a {{ $plugin.plugin_cmd }} /plugins/{{ $plugin.name }}
volumeMounts:
- name: plugins
mountPath: /plugins
imagePullPolicy: {{ $plugin.image.pullPolicy }}
{{- end }}
{{- if $pluginLoaderNeeded }}
- name: finalize-plugins
securityContext:
{{- include "spire-lib.securitycontext" . | nindent 12 }}
image: {{ template "spire-lib.image" (dict "appVersion" $.Chart.AppVersion "image" .Values.tools.busybox.image "global" .Values.global) }}
command:
- busybox
- sh
- -ec
- |
rm -f /plugins/busybox
volumeMounts:
- name: plugins
mountPath: /plugins
imagePullPolicy: {{ .Values.tools.busybox.image.pullPolicy }}
{{- end }}
{{- if or (eq .Values.workloadAttestors.k8s.verification.type "hostCert") (eq .Values.workloadAttestors.k8s.verification.type "auto") }}
- name: gather-host-cert
securityContext:
@@ -326,6 +378,11 @@ spec:
mountPath: /run/spire/bundle
{{- end }}
{{- end }}
{{- if $pluginLoaderNeeded }}
- name: plugins
mountPath: /plugins
readOnly: true
{{- end }}
{{- if .Values.nodeAttestor.tpmDirect.enabled }}
- name: tpm-direct
mountPath: /tpm
@@ -419,6 +476,11 @@ spec:
emptyDir: {}
{{- end }}
{{- end }}
{{- if $pluginLoaderNeeded }}
- name: plugins
mountPath: /plugins
readOnly: true
{{- end }}
{{- if .Values.sockets.admin.mountOnHost }}
- name: spire-agent-admin-socket-dir
hostPath:
@@ -447,6 +447,17 @@ tools:
repository: kubectl
pullPolicy: IfNotPresent
tag: ""
busybox:
## @param tools.busybox.image.registry The OCI registry to pull the image from
## @param tools.busybox.image.repository The repository within the registry
## @param tools.busybox.image.pullPolicy The image pull policy
## @param tools.busybox.image.tag Overrides the image tag whose default is the chart appVersion
##
image:
registry: ""
repository: busybox
pullPolicy: IfNotPresent
tag: 1.37.0-uclibc
sockets:
## @param sockets.hostBasePath Path on which the agent socket is made available when admin.mountOnHost is true
@@ -16,7 +16,7 @@
{{- fail (printf "plugin_checksum is a required field.") }}
{{- end }}
{{- range $sname, $svals := $nval }}
{{- if not (has $sname (list "plugin_cmd" "plugin_checksum" "plugin_data")) }}
{{- if not (has $sname (list "plugin_cmd" "plugin_checksum" "plugin_data" "image")) }}
{{- fail (printf "Unknown plugin setting specified: %s" $sname) }}
{{- end }}
{{- end }}
@@ -64,6 +64,8 @@
{{- fail "clientKeyPath can only be set with database type mysql or aws_mysql." }}
{{- end }}
{{- end }}
{{- $pluginsToLoad := include "spire-lib.extract_custom_plugin_images" . | fromYamlArray }}
{{- $pluginLoaderNeeded := or .Values.credentialComposer.cel.enabled (gt (len $pluginsToLoad) 0) }}
{{- if not .Values.externalServer }}
apiVersion: apps/v1
{{- if eq .Values.kind "statefulset" }}
@@ -121,10 +123,10 @@ spec:
securityContext:
{{- $podSecurityContext | toYaml | nindent 8 }}
{{- include "spire-lib.default_cluster_priority_class_name" . | nindent 6 }}
{{- if or (gt (len .Values.initContainers) 0) (and .Values.upstreamAuthority.certManager.enabled .Values.upstreamAuthority.certManager.ca.create) .Values.nodeAttestor.tpmDirect.enabled .Values.credentialComposer.cel.enabled $needsChown }}
{{- if or (gt (len .Values.initContainers) 0) (and .Values.upstreamAuthority.certManager.enabled .Values.upstreamAuthority.certManager.ca.create) .Values.nodeAttestor.tpmDirect.enabled $pluginLoaderNeeded $needsChown }}
initContainers:
{{- if .Values.credentialComposer.cel.enabled }}
- name: init-cel
{{- if $pluginLoaderNeeded }}
- name: init-plugins
securityContext:
{{- include "spire-lib.securitycontext" . | nindent 12 }}
image: {{ template "spire-lib.image" (dict "appVersion" $.Chart.AppVersion "image" .Values.tools.busybox.image "global" .Values.global) }}
@@ -134,28 +136,62 @@ spec:
- sh
- -ec
- |
cp -a /bin/busybox /cel/busybox
cp -a /bin/busybox /plugins/busybox
volumeMounts:
- name: cel
mountPath: /cel
imagePullPolicy: {{ .Values.credentialComposer.cel.image.pullPolicy }}
- name: init-cel2
- name: plugins
mountPath: /plugins
imagePullPolicy: {{ .Values.tools.busybox.image.pullPolicy }}
{{- end }}
{{- if .Values.credentialComposer.cel.enabled }}
- name: init-cel
securityContext:
{{- include "spire-lib.securitycontext" . | nindent 12 }}
image: {{ template "spire-lib.image" (dict "appVersion" $.Chart.AppVersion "image" .Values.credentialComposer.cel.image "global" .Values.global) }}
# Second, use the previously copied busybox to copy the plugin into a volume that can be mounted where SPIRE can execute it.
command:
- /cel/busybox
- /plugins/busybox
- sh
- -ec
- |
/cel/busybox cp -a {{ .Values.credentialComposer.cel.pluginPath }} /cel/credentialcomposer-cel
/cel/busybox rm -f /cel/busybox
/plugins/busybox cp -a {{ .Values.credentialComposer.cel.pluginPath }} /plugins/credentialcomposer-cel
volumeMounts:
- name: cel
mountPath: /cel
- name: plugins
mountPath: /plugins
imagePullPolicy: {{ .Values.credentialComposer.cel.image.pullPolicy }}
{{- end }}
{{- range $idx, $plugin := $pluginsToLoad }}
- name: {{ printf "init-plugin-%d" $idx }}
securityContext:
{{- include "spire-lib.securitycontext" $ | nindent 12 }}
image: {{ template "spire-lib.image" (dict "appVersion" $.Chart.AppVersion "image" $plugin.image "global" $.Values.global) }}
command:
- /plugins/busybox
- sh
- -ec
- |
/plugins/busybox mkdir -p /plugins/{{ dir $plugin.name }}
/plugins/busybox cp -a {{ $plugin.plugin_cmd }} /plugins/{{ $plugin.name }}
volumeMounts:
- name: plugins
mountPath: /plugins
imagePullPolicy: {{ $plugin.image.pullPolicy }}
{{- end }}
{{- if $pluginLoaderNeeded }}
- name: finalize-plugins
securityContext:
{{- include "spire-lib.securitycontext" . | nindent 12 }}
image: {{ template "spire-lib.image" (dict "appVersion" $.Chart.AppVersion "image" .Values.tools.busybox.image "global" .Values.global) }}
command:
- busybox
- sh
- -ec
- |
rm -f /plugins/busybox
volumeMounts:
- name: plugins
mountPath: /plugins
imagePullPolicy: {{ .Values.tools.busybox.image.pullPolicy }}
{{- end }}
{{- if .Values.nodeAttestor.tpmDirect.enabled }}
- name: init-tpm-direct
securityContext:
@@ -337,9 +373,9 @@ spec:
mountPath: /kubeconfigs
readOnly: true
{{- end }}
{{- if .Values.credentialComposer.cel.enabled }}
- name: cel
mountPath: /cel
{{- if $pluginLoaderNeeded }}
- name: plugins
mountPath: /plugins
readOnly: true
{{- end }}
{{- if .Values.nodeAttestor.tpmDirect.enabled }}
@@ -546,8 +582,8 @@ spec:
secret:
secretName: {{ include "spire-server.fullname" . }}-kubeconfigs
{{- end }}
{{- if .Values.credentialComposer.cel.enabled }}
- name: cel
{{- if $pluginLoaderNeeded }}
- name: plugins
emptyDir: {}
{{- end }}
{{- if .Values.nodeAttestor.tpmDirect.enabled }}
@@ -18,6 +18,11 @@ spire-server:
customPlugins:
nodeAttestor:
tpm:
image:
registry: docker.io
repository: <your repo here>
tag: <your tag here>
pullPolicy: Always
plugin_cmd: /bin/tpm_attestor_server
plugin_checksum: 97442358ae946e3fb8f2464432b8c23efdc0b5d44ec1eea27babe59ef646cc2f
plugin_data:
@@ -32,6 +37,11 @@ spire-agent:
customPlugins:
nodeAttestor:
tpm:
image:
registry: docker.io
repository: <your repo here>
tag: <your tag here>
pullPolicy: Always
plugin_cmd: /bin/tpm_attestor_agent
plugin_checksum: bb7be714c27452231a6c7764b65912ce0cdeb66ff2a2c688d3e88bd0bd17d138
plugin_data: {}