diff --git a/charts/spire-lib/templates/_helpers.tpl b/charts/spire-lib/templates/_helpers.tpl index e705980..179c5c5 100644 --- a/charts/spire-lib/templates/_helpers.tpl +++ b/charts/spire-lib/templates/_helpers.tpl @@ -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 diff --git a/charts/spire/charts/spire-agent/README.md b/charts/spire/charts/spire-agent/README.md index bac3e38..4764adb 100644 --- a/charts/spire/charts/spire-agent/README.md +++ b/charts/spire/charts/spire-agent/README.md @@ -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` | diff --git a/charts/spire/charts/spire-agent/templates/configmap.yaml b/charts/spire/charts/spire-agent/templates/configmap.yaml index 7ff8a6b..77252bb 100644 --- a/charts/spire/charts/spire-agent/templates/configmap.yaml +++ b/charts/spire/charts/spire-agent/templates/configmap.yaml @@ -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 }} diff --git a/charts/spire/charts/spire-agent/templates/daemonset.yaml b/charts/spire/charts/spire-agent/templates/daemonset.yaml index 477c05b..1668a3a 100644 --- a/charts/spire/charts/spire-agent/templates/daemonset.yaml +++ b/charts/spire/charts/spire-agent/templates/daemonset.yaml @@ -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: diff --git a/charts/spire/charts/spire-agent/values.yaml b/charts/spire/charts/spire-agent/values.yaml index 2f82e29..b6663a5 100644 --- a/charts/spire/charts/spire-agent/values.yaml +++ b/charts/spire/charts/spire-agent/values.yaml @@ -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 diff --git a/charts/spire/charts/spire-server/templates/configmap.yaml b/charts/spire/charts/spire-server/templates/configmap.yaml index c433543..c0e107f 100644 --- a/charts/spire/charts/spire-server/templates/configmap.yaml +++ b/charts/spire/charts/spire-server/templates/configmap.yaml @@ -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 }} diff --git a/charts/spire/charts/spire-server/templates/server-resource.yaml b/charts/spire/charts/spire-server/templates/server-resource.yaml index b849916..6fcb3ed 100644 --- a/charts/spire/charts/spire-server/templates/server-resource.yaml +++ b/charts/spire/charts/spire-server/templates/server-resource.yaml @@ -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 }} diff --git a/examples/spire-plugins/node-agent-tpm/values.yaml b/examples/spire-plugins/node-agent-tpm/values.yaml index 7024773..3d0d8a7 100644 --- a/examples/spire-plugins/node-agent-tpm/values.yaml +++ b/examples/spire-plugins/node-agent-tpm/values.yaml @@ -18,6 +18,11 @@ spire-server: customPlugins: nodeAttestor: tpm: + image: + registry: docker.io + repository: + tag: + 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: + tag: + pullPolicy: Always plugin_cmd: /bin/tpm_attestor_agent plugin_checksum: bb7be714c27452231a6c7764b65912ce0cdeb66ff2a2c688d3e88bd0bd17d138 plugin_data: {}