From 0fa43a507dc197566824622734d888cf5aaa3216 Mon Sep 17 00:00:00 2001 From: kfox1111 Date: Tue, 10 Oct 2023 01:09:11 -0700 Subject: [PATCH] Add plugin support to the spire agent (#22) * Exit code from diff indicating changes should not block commit. Signed-off-by: Kevin Fox * Push the changes that update-tags creates Signed-off-by: Kevin Fox * Add plugin support to the spire agent This adapts the existing spire server plugin support to be usable by the agent as well. Signed-off-by: Kevin Fox * Fix notes Signed-off-by: Kevin Fox * Add plugin support to the spire agent This adapts the existing spire server plugin support to be usable by the agent as well. Signed-off-by: Kevin Fox * Fix notes Signed-off-by: Kevin Fox * Update documentation Signed-off-by: Kevin Fox * Update example Signed-off-by: Kevin Fox --------- Signed-off-by: Kevin Fox Signed-off-by: kfox1111 --- charts/spire/charts/spire-agent/README.md | 5 ++ .../spire-agent/templates/configmap.yaml | 50 +++++++++++----- charts/spire/charts/spire-agent/values.yaml | 22 +++++++ charts/spire/charts/spire-server/README.md | 4 ++ .../spire-server/templates/_helpers.tpl | 49 ---------------- .../spire-server/templates/configmap.yaml | 2 +- charts/spire/charts/spire-server/values.yaml | 5 +- charts/spire/templates/NOTES.txt | 15 ++++- charts/spire/templates/_spire-lib.tpl | 57 +++++++++++++++++++ .../spire-plugins/node-agent-tpm/values.yaml | 16 +++++- tests/unit/spire_test.go | 30 ++++++++++ 11 files changed, 185 insertions(+), 70 deletions(-) diff --git a/charts/spire/charts/spire-agent/README.md b/charts/spire/charts/spire-agent/README.md index eb346c2..4881d04 100644 --- a/charts/spire/charts/spire-agent/README.md +++ b/charts/spire/charts/spire-agent/README.md @@ -77,6 +77,7 @@ A Helm chart to install the SPIRE agent. | `fsGroupFix.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:5c943903c3f9159c58c4fdf9809f404ab9cfc39b7bfed12bac44563ce0fe7875` | | `fsGroupFix.resources` | Specify resource needs as per https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ | `{}` | | `workloadAttestors.unix.enabled` | Enables the Unix workload attestor | `false` | +| `workloadAttestors.k8s.enabled` | Enables the Kubernetes workload attestor | `true` | | `workloadAttestors.k8s.skipKubeletVerification` | If true, kubelet certificate verification is skipped | `true` | | `workloadAttestors.k8s.disableContainerSelectors` | Set to true if using holdApplicationUntilProxyStarts in Istio | `false` | | `sds.enabled` | Enables Envoy SDS configuration | `false` | @@ -96,6 +97,10 @@ A Helm chart to install the SPIRE agent. | `extraContainers` | Additional containers to create with Spire Agent pods | `[]` | | `initContainers` | Additional init containers to create with Spire Agent pods | `[]` | | `hostAliases` | Customize /etc/hosts file as described here https://kubernetes.io/docs/tasks/network/customize-hosts-file-for-pods/ | `[]` | +| `customPlugins.keyManager` | Custom plugins of type KeyManager are configured here | `{}` | +| `customPlugins.nodeAttestor` | Custom plugins of type NodeAttestor are configured here | `{}` | +| `customPlugins.svidStore` | Custom plugins of type SVIDStore are configured here | `{}` | +| `customPlugins.workloadAttestor` | Custom plugins of type WorkloadAttestor are configured here | `{}` | | `experimental.enabled` | Allow configuration of experimental features | `false` | | `experimental.syncInterval` | Sync interval with SPIRE server with exponential backoff | `5s` | | `experimental.featureFlags` | List of developer feature flags | `[]` | diff --git a/charts/spire/charts/spire-agent/templates/configmap.yaml b/charts/spire/charts/spire-agent/templates/configmap.yaml index e19a577..0bfd9d9 100644 --- a/charts/spire/charts/spire-agent/templates/configmap.yaml +++ b/charts/spire/charts/spire-agent/templates/configmap.yaml @@ -1,5 +1,23 @@ {{- include "spire-lib.check-strict-mode" (list . "clusterName must be set" (eq (include "spire-lib.cluster-name" .) "example-cluster"))}} {{- include "spire-lib.check-strict-mode" (list . "trustDomain must be set" (eq (include "spire-lib.trust-domain" .) "example.org"))}} +{{- range $type, $tvals := .Values.customPlugins }} +{{- if not (has $type (list "keyManager" "nodeAttestor" "svidStore" "workloadAttestor")) }} +{{- fail (printf "Unknown plugin type specified: %s" $type) }} +{{- end }} +{{- range $name, $nval := $tvals }} +{{- if not (hasKey $nval "plugin_cmd") }} +{{- fail (printf "plugin_cmd is a required field. %s" $name) }} +{{- end }} +{{- if not (hasKey $nval "plugin_checksum") }} +{{- fail (printf "plugin_checksum is a required field.") }} +{{- end }} +{{- range $sname, $svals := $nval }} +{{- if not (has $sname (list "plugin_cmd" "plugin_checksum" "plugin_data")) }} +{{- fail (printf "Unknown plugin setting specified: %s" $sname) }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} {{- define "spire-agent.yaml-config" -}} agent: data_dir: "/run/spire" @@ -37,26 +55,28 @@ agent: plugins: NodeAttestor: - - k8s_psat: - plugin_data: - cluster: {{ include "spire-lib.cluster-name" . | quote }} + k8s_psat: + plugin_data: + cluster: {{ include "spire-lib.cluster-name" . | quote }} KeyManager: - - memory: - plugin_data: + memory: + plugin_data: WorkloadAttestor: - - k8s: - plugin_data: - # Defaults to the secure kubelet port by default. - # Minikube does not have a cert in the cluster CA bundle that - # can authenticate the kubelet cert, so skip validation. - skip_kubelet_verification: {{ .Values.workloadAttestors.k8s.skipKubeletVerification }} - disable_container_selectors: {{ .Values.workloadAttestors.k8s.disableContainerSelectors }} + {{- if .Values.workloadAttestors.k8s.enabled }} + k8s: + plugin_data: + # Defaults to the secure kubelet port by default. + # Minikube does not have a cert in the cluster CA bundle that + # can authenticate the kubelet cert, so skip validation. + skip_kubelet_verification: {{ .Values.workloadAttestors.k8s.skipKubeletVerification }} + disable_container_selectors: {{ .Values.workloadAttestors.k8s.disableContainerSelectors }} + {{- end }} {{- if .Values.workloadAttestors.unix.enabled }} - - unix: - plugin_data: + unix: + plugin_data: {{- end }} health_checks: @@ -84,4 +104,4 @@ metadata: {{- end }} data: agent.conf: | - {{- include "spire-agent.yaml-config" . | fromYaml | toPrettyJson | nindent 4 }} + {{- include "spire-lib.reformat-and-yaml2json" (dict "config" (include "spire-agent.yaml-config" .) "root" .) | nindent 4 }} diff --git a/charts/spire/charts/spire-agent/values.yaml b/charts/spire/charts/spire-agent/values.yaml index 54d11d6..81d110b 100644 --- a/charts/spire/charts/spire-agent/values.yaml +++ b/charts/spire/charts/spire-agent/values.yaml @@ -165,6 +165,8 @@ workloadAttestors: ## @param workloadAttestors.unix.enabled Enables the Unix workload attestor enabled: false k8s: + ## @param workloadAttestors.k8s.enabled Enables the Kubernetes workload attestor + enabled: true ## @param workloadAttestors.k8s.skipKubeletVerification If true, kubelet certificate verification is skipped skipKubeletVerification: true ## @param workloadAttestors.k8s.disableContainerSelectors Set to true if using holdApplicationUntilProxyStarts in Istio @@ -216,6 +218,26 @@ initContainers: [] ## @param hostAliases [array] Customize /etc/hosts file as described here https://kubernetes.io/docs/tasks/network/customize-hosts-file-for-pods/ hostAliases: [] +# NOTE: This is unsupported and only to configure currently supported spire built in plugins but plugins unsupported by the chart. +# Upgrades wont be tested for anything under this config. If you need this, please let the chart developers know your needs so we +# can prioritize proper support. +## @skip unsupportedBuiltInPlugins +unsupportedBuiltInPlugins: + keyManager: {} + nodeAttestor: {} + svidStore: {} + workloadAttestor: {} + +## @param customPlugins.keyManager Custom plugins of type KeyManager are configured here +## @param customPlugins.nodeAttestor Custom plugins of type NodeAttestor are configured here +## @param customPlugins.svidStore Custom plugins of type SVIDStore are configured here +## @param customPlugins.workloadAttestor Custom plugins of type WorkloadAttestor are configured here +customPlugins: + keyManager: {} + nodeAttestor: {} + svidStore: {} + workloadAttestor: {} + experimental: ## @param experimental.enabled Allow configuration of experimental features enabled: false diff --git a/charts/spire/charts/spire-server/README.md b/charts/spire/charts/spire-server/README.md index 7272fdf..bf4336c 100644 --- a/charts/spire/charts/spire-server/README.md +++ b/charts/spire/charts/spire-server/README.md @@ -285,6 +285,10 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr | `tornjak.config.clientCA.type` | Type of delivery for the user CA for TLS client verification. Options are `Secret` or `ConfigMap` (required for `mtls` connectionType) | `Secret` | | `tornjak.config.clientCA.name` | Name of the resource secret or configMap with user CA for TLS | `tornjak-client-ca` | | `tornjak.resources` | Resource requests and limits | `{}` | +| `customPlugins.keyManager` | Custom plugins of type KeyManager are configured here | `{}` | +| `customPlugins.nodeAttestor` | Custom plugins of type NodeAttestor are configured here | `{}` | +| `customPlugins.upstreamAuthority` | Custom plugins of type upstreamAuthority are configured here | `{}` | +| `customPlugins.notifier` | Custom plugins of type notifier are configured here | `{}` | | `experimental.enabled` | Allow configuration of experimental features | `false` | | `experimental.cacheReloadInterval` | The amount of time between two reloads of the in-memory entry cache. | `5s` | | `experimental.featureFlags` | List of developer feature flags | `[]` | diff --git a/charts/spire/charts/spire-server/templates/_helpers.tpl b/charts/spire/charts/spire-server/templates/_helpers.tpl index f1a9263..d4d503e 100644 --- a/charts/spire/charts/spire-server/templates/_helpers.tpl +++ b/charts/spire/charts/spire-server/templates/_helpers.tpl @@ -217,52 +217,3 @@ The code below determines what connection type should be used. {{- end }} {{ $args | toYaml }} {{- end -}} - -{{/* -Take a copy of the config and merge in .Values.customPlugins and .Values.unsupportedBuiltInPlugins passed through as root. -*/}} -{{- define "spire-server.config_merge" }} -{{- $pluginsToMerge := dict "plugins" dict }} -{{- range $type, $val := .root.Values.customPlugins }} -{{- if . }} -{{- $nt := printf "%s%s" (substr 0 1 $type | upper) (substr 1 -1 $type) }} -{{- $_ := set $pluginsToMerge.plugins $nt (deepCopy $val) }} -{{- end }} -{{- end }} -{{- range $type, $val := .root.Values.unsupportedBuiltInPlugins }} -{{- if . }} -{{- $nt := printf "%s%s" (substr 0 1 $type | upper) (substr 1 -1 $type) }} -{{- $_ := set $pluginsToMerge.plugins $nt (deepCopy $val) }} -{{- end }} -{{- end }} -{{- $newConfig := .config | fromYaml | mustMerge $pluginsToMerge }} -{{- $newConfig | 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 -*/}} -{{- define "spire-server.plugins_reformat" }} -{{- range $type, $v := . }} -{{ $type }}: -{{- range $name, $v2 := $v }} - - {{ $name }}: {{ $v2 | toYaml | nindent 8 }} -{{- end }} -{{- end }} -{{- end }} - -{{/* -Take a copy of the config as a yaml config and root var. -Merge in .root.Values.customPlugins and .Values.unsupportedBuiltInPlugins into config, -Reformat the plugin section from a dict of dicts to a dict of lists of dicts, -and export it back as as json string. -This makes it much easier for users to merge in plugin configs, as dicts are easier -to merge in values, but spire needs arrays. -*/}} -{{- define "spire-server.reformat-and-yaml2json" -}} -{{- $config := include "spire-server.config_merge" . | fromYaml }} -{{- $plugins := include "spire-server.plugins_reformat" $config.plugins | fromYaml }} -{{- $_ := set $config "plugins" $plugins }} -{{- $config | toPrettyJson }} -{{- end }} diff --git a/charts/spire/charts/spire-server/templates/configmap.yaml b/charts/spire/charts/spire-server/templates/configmap.yaml index 94e8d1e..e366716 100644 --- a/charts/spire/charts/spire-server/templates/configmap.yaml +++ b/charts/spire/charts/spire-server/templates/configmap.yaml @@ -260,4 +260,4 @@ metadata: {{- end }} data: server.conf: | - {{- include "spire-server.reformat-and-yaml2json" (dict "config" (include "spire-server.yaml-config" .) "root" .) | nindent 4 }} + {{- include "spire-lib.reformat-and-yaml2json" (dict "config" (include "spire-server.yaml-config" .) "root" .) | nindent 4 }} diff --git a/charts/spire/charts/spire-server/values.yaml b/charts/spire/charts/spire-server/values.yaml index d374a9b..8d4a9de 100644 --- a/charts/spire/charts/spire-server/values.yaml +++ b/charts/spire/charts/spire-server/values.yaml @@ -642,7 +642,10 @@ unsupportedBuiltInPlugins: upstreamAuthority: {} notifier: {} -## @skip customPlugins +## @param customPlugins.keyManager Custom plugins of type KeyManager are configured here +## @param customPlugins.nodeAttestor Custom plugins of type NodeAttestor are configured here +## @param customPlugins.upstreamAuthority Custom plugins of type upstreamAuthority are configured here +## @param customPlugins.notifier Custom plugins of type notifier are configured here customPlugins: keyManager: {} nodeAttestor: {} diff --git a/charts/spire/templates/NOTES.txt b/charts/spire/templates/NOTES.txt index 096b2ba..5da926b 100644 --- a/charts/spire/templates/NOTES.txt +++ b/charts/spire/templates/NOTES.txt @@ -1,6 +1,17 @@ Installed {{ .Chart.Name }}… -{{- $up := (index .Values "spire-server").unsupportedBuiltInPlugins }} -{{- $s := add (len $up.keyManager) (len $up.nodeAttestor) (len $up.upstreamAuthority) (len $up.notifier) }} +{{- $s := 0 }} +{{- if (index .Values "spire-server").enabled }} +{{- $up := (index .Values "spire-server").unsupportedBuiltInPlugins }} +{{- $s = add (len $up.keyManager) (len $up.nodeAttestor) (len $up.upstreamAuthority) (len $up.notifier) $s }} +{{- end }} +{{- if (index .Values "spire-agent").enabled }} +{{- $up := (index .Values "spire-agent").unsupportedBuiltInPlugins }} +{{- $s = add (len $up.keyManager) (len $up.nodeAttestor) (len $up.svidStore) (len $up.workloadAttestor) $s }} +{{- end }} +{{- if (index .Values "upstream-spire-agent").enabled }} +{{- $up := (index .Values "upstream-spire-agent").unsupportedBuiltInPlugins }} +{{- $s = add (len $up.keyManager) (len $up.nodeAttestor) (len $up.svidStore) (len $up.workloadAttestor) $s }} +{{- end }} {{- if gt $s 0 }} Warning: You're using an unsupported plugin. Functionality of this release and future upgrades aren't guaranteed to work smoothly. diff --git a/charts/spire/templates/_spire-lib.tpl b/charts/spire/templates/_spire-lib.tpl index 64152ac..12e854d 100644 --- a/charts/spire/templates/_spire-lib.tpl +++ b/charts/spire/templates/_spire-lib.tpl @@ -122,3 +122,60 @@ if strictMode is enabled and the boolean is true {{- end }} {{- end }} {{- end }} + +{{/* +Take a copy of the config and merge in .Values.customPlugins and .Values.unsupportedBuiltInPlugins passed through as root. +*/}} +{{- 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) }} +{{- end }} +{{- end }} +{{- end }} +{{- range $type, $val := .root.Values.unsupportedBuiltInPlugins }} +{{- 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) }} +{{- end }} +{{- end }} +{{- end }} +{{- $newConfig := .config | fromYaml | mustMerge $pluginsToMerge }} +{{- $newConfig | 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 +*/}} +{{- define "spire-lib.plugins_reformat" }} +{{- range $type, $v := . }} +{{ $type }}: +{{- range $name, $v2 := $v }} + - {{ $name }}: {{ $v2 | toYaml | nindent 8 }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Take a copy of the config as a yaml config and root var. +Merge in .root.Values.customPlugins and .Values.unsupportedBuiltInPlugins into config, +Reformat the plugin section from a dict of dicts to a dict of lists of dicts, +and export it back as as json string. +This makes it much easier for users to merge in plugin configs, as dicts are easier +to merge in values, but spire needs arrays. +*/}} +{{- define "spire-lib.reformat-and-yaml2json" -}} +{{- $config := include "spire-lib.config_merge" . | fromYaml }} +{{- $plugins := include "spire-lib.plugins_reformat" $config.plugins | fromYaml }} +{{- $_ := set $config "plugins" $plugins }} +{{- $config | toPrettyJson }} +{{- end }} diff --git a/examples/spire-plugins/node-agent-tpm/values.yaml b/examples/spire-plugins/node-agent-tpm/values.yaml index a2011b7..7024773 100644 --- a/examples/spire-plugins/node-agent-tpm/values.yaml +++ b/examples/spire-plugins/node-agent-tpm/values.yaml @@ -13,8 +13,8 @@ spire-server: name: spire-data image: registry: docker.io - repository: kfox1111/misc - version: tpm-server-test + repository: + tag: customPlugins: nodeAttestor: tpm: @@ -23,3 +23,15 @@ spire-server: plugin_data: - ca_path: /run/spire/data/certs hash_path: /run/spire/data/hashes + +spire-agent: + image: + registry: docker.io + repository: + tag: + customPlugins: + nodeAttestor: + tpm: + plugin_cmd: /bin/tpm_attestor_agent + plugin_checksum: bb7be714c27452231a6c7764b65912ce0cdeb66ff2a2c688d3e88bd0bd17d138 + plugin_data: {} diff --git a/tests/unit/spire_test.go b/tests/unit/spire_test.go index 4db554c..891771b 100644 --- a/tests/unit/spire_test.go +++ b/tests/unit/spire_test.go @@ -103,4 +103,34 @@ spire-server: Expect(notes).Should(ContainSubstring("\"aws_pca\": {")) }) }) + Describe("spire-agent.customPlugin.tpm", func() { + It("plugin set ok", func() { + objs, err := ValueStringRender(chart, ` +spire-agent: + customPlugins: + nodeAttestor: + tpm: + plugin_cmd: /bin/tpm_attestor_agent + plugin_checksum: bb7be714c27452231a6c7764b65912ce0cdeb66ff2a2c688d3e88bd0bd17d138 + plugin_data: {} +`) + Expect(err).Should(Succeed()) + notes := objs["spire/charts/spire-agent/templates/configmap.yaml"] + Expect(notes).Should(ContainSubstring("tpm")) + }) + }) + Describe("spire-server.unsupportedBuiltInPlugins", func() { + It("plugin set ok", func() { + objs, err := ValueStringRender(chart, ` +spire-agent: + unsupportedBuiltInPlugins: + nodeAttestor: + join_token: + plugin_data: {} +`) + Expect(err).Should(Succeed()) + notes := objs["spire/charts/spire-agent/templates/configmap.yaml"] + Expect(notes).Should(ContainSubstring("join_token")) + }) + }) })