From 51cba5b5300df49ed90c0a3c83173869dacbc851 Mon Sep 17 00:00:00 2001 From: kfox1111 Date: Thu, 24 Aug 2023 14:13:28 -0700 Subject: [PATCH] Add customPlugins and unsupportedBuiltInPlugins sections to spire-server (#198) This patch enables end users to configure external plugins in the spire-server config. Unsupported internal plugins are not able to be set. --------- Signed-off-by: Kevin Fox Signed-off-by: kfox1111 Co-authored-by: Edwin Buck Co-authored-by: Faisal Memon --- charts/spire/README.md | 4 + charts/spire/charts/spire-server/README.md | 4 + .../spire-server/templates/_helpers.tpl | 49 ++++++++++ .../spire-server/templates/configmap.yaml | 96 +++++++++++-------- charts/spire/charts/spire-server/values.yaml | 16 ++++ charts/spire/templates/NOTES.txt | 7 ++ .../node-agent-tpm/Dockerfile.agent | 9 ++ .../node-agent-tpm/Dockerfile.server | 9 ++ .../spire-plugins/node-agent-tpm/values.yaml | 25 +++++ tests/unit/spire_test.go | 30 ++++++ 10 files changed, 210 insertions(+), 39 deletions(-) create mode 100644 examples/spire-plugins/node-agent-tpm/Dockerfile.agent create mode 100644 examples/spire-plugins/node-agent-tpm/Dockerfile.server create mode 100644 examples/spire-plugins/node-agent-tpm/values.yaml diff --git a/charts/spire/README.md b/charts/spire/README.md index 74dca6a..90570be 100644 --- a/charts/spire/README.md +++ b/charts/spire/README.md @@ -320,6 +320,10 @@ Now you can interact with the Spire agent socket from your own application. The | spire-server.controllerManager.service.port | int | `443` | | | spire-server.controllerManager.service.type | string | `"ClusterIP"` | | | spire-server.controllerManager.validatingWebhookConfiguration.failurePolicy | string | `"Fail"` | | +| spire-server.customPlugins.keyManager | object | `{}` | | +| spire-server.customPlugins.nodeAttestor | object | `{}` | | +| spire-server.customPlugins.notifier | object | `{}` | | +| spire-server.customPlugins.upstreamAuthority | object | `{}` | | | spire-server.dataStore.sql.databaseName | string | `"spire"` | Only used by "postgres" or "mysql" | | spire-server.dataStore.sql.databaseType | string | `"sqlite3"` | Other supported databases are "postgres" and "mysql" | | spire-server.dataStore.sql.externalSecret | object | `{"enabled":false,"key":"","name":""}` | When an external source creates the secret. The secret should reside in the same namespace as the spire server | diff --git a/charts/spire/charts/spire-server/README.md b/charts/spire/charts/spire-server/README.md index cfda011..24adf79 100644 --- a/charts/spire/charts/spire-server/README.md +++ b/charts/spire/charts/spire-server/README.md @@ -123,6 +123,10 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr | controllerManager.service.port | int | `443` | | | controllerManager.service.type | string | `"ClusterIP"` | | | controllerManager.validatingWebhookConfiguration.failurePolicy | string | `"Fail"` | | +| customPlugins.keyManager | object | `{}` | | +| customPlugins.nodeAttestor | object | `{}` | | +| customPlugins.notifier | object | `{}` | | +| customPlugins.upstreamAuthority | object | `{}` | | | dataStore.sql.databaseName | string | `"spire"` | Only used by "postgres" or "mysql" | | dataStore.sql.databaseType | string | `"sqlite3"` | Other supported databases are "postgres" and "mysql" | | dataStore.sql.externalSecret | object | `{"enabled":false,"key":"","name":""}` | When an external source creates the secret. The secret should reside in the same namespace as the spire server | diff --git a/charts/spire/charts/spire-server/templates/_helpers.tpl b/charts/spire/charts/spire-server/templates/_helpers.tpl index e0076aa..78a6edf 100644 --- a/charts/spire/charts/spire-server/templates/_helpers.tpl +++ b/charts/spire/charts/spire-server/templates/_helpers.tpl @@ -202,3 +202,52 @@ The code below determines what connection type should be used. {{- define "spire-tornjak.servicename" -}} {{- include "spire-tornjak.backend" . -}} {{- 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 d72eae9..83cd591 100644 --- a/charts/spire/charts/spire-server/templates/configmap.yaml +++ b/charts/spire/charts/spire-server/templates/configmap.yaml @@ -1,3 +1,21 @@ +{{- range $type, $tvals := .Values.customPlugins }} +{{- if not (has $type (list "keyManager" "nodeAttestor" "upstreamAuthority" "notifier")) }} +{{- 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-server.yaml-config" -}} {{- $upstreamAuthorityUsed := 0 }} {{- $keyManagerUsed := 0 }} @@ -33,18 +51,18 @@ server: plugins: DataStore: - - sql: - plugin_data: - {{ include "spire-server.datastore-config" . | nindent 10 }} + sql: + plugin_data: + {{ include "spire-server.datastore-config" . | nindent 10 }} {{- with .Values.nodeAttestor.k8sPsat }} {{- if eq (.enabled | toString) "true" }} NodeAttestor: - - k8s_psat: - plugin_data: - clusters: - {{ include "spire-lib.cluster-name" $root }}: - service_account_allow_list: {{ include "spire-server.serviceAccountAllowedList" $root | trim }} + k8s_psat: + plugin_data: + clusters: + {{ include "spire-lib.cluster-name" $root }}: + service_account_allow_list: {{ include "spire-server.serviceAccountAllowedList" $root | trim }} {{- end }} {{- end }} @@ -52,9 +70,9 @@ plugins: {{- if eq (.enabled | toString) "true" }} {{- $keyManagerUsed = add1 $keyManagerUsed }} KeyManager: - - disk: - plugin_data: - keys_path: "/run/spire/data/keys.json" + disk: + plugin_data: + keys_path: "/run/spire/data/keys.json" {{- end }} {{- end }} @@ -62,8 +80,8 @@ plugins: {{- if eq (.enabled | toString) "true" }} {{- $keyManagerUsed = add1 $keyManagerUsed }} KeyManager: - - memory: - plugin_data: + memory: + plugin_data: {{- end }} {{- end }} @@ -92,22 +110,22 @@ plugins: {{- end }} Notifier: - - k8sbundle: - plugin_data: - namespace: {{ .Values.notifier.k8sbundle.namespace | default (include "spire-server.namespace" .) | quote }} - config_map: {{ include "spire-lib.bundle-configmap" . | quote }} + k8sbundle: + plugin_data: + namespace: {{ .Values.notifier.k8sbundle.namespace | default (include "spire-server.namespace" .) | quote }} + config_map: {{ include "spire-lib.bundle-configmap" . | quote }} {{- with .Values.upstreamAuthority.disk }} {{- if eq (.enabled | toString) "true" }} {{- $upstreamAuthorityUsed = add1 $upstreamAuthorityUsed }} UpstreamAuthority: - - disk: - plugin_data: - cert_file_path: "/run/spire/upstream_ca/tls.crt" - key_file_path: "/run/spire/upstream_ca/tls.key" - {{- if ne .secret.data.bundle "" }} - bundle_file_path: "/run/spire/upstream_ca/bundle.crt" - {{- end }} + disk: + plugin_data: + cert_file_path: "/run/spire/upstream_ca/tls.crt" + key_file_path: "/run/spire/upstream_ca/tls.key" + {{- if ne .secret.data.bundle "" }} + bundle_file_path: "/run/spire/upstream_ca/bundle.crt" + {{- end }} {{- end }} {{- end }} @@ -115,15 +133,15 @@ plugins: {{- if eq (.enabled | toString) "true" }} {{- $upstreamAuthorityUsed = add1 $upstreamAuthorityUsed }} UpstreamAuthority: - - cert-manager: - plugin_data: - issuer_name: {{ default (include "spire-server.fullname" $root) .issuer_name }}-ca - issuer_kind: {{ .issuer_kind | quote }} - issuer_group: {{ .issuer_group | quote }} - namespace: {{ default $root.Release.Namespace .namespace | quote }} - {{- if ne .kube_config_file "" }} - kube_config_file: {{ .kube_config_file | quote }} - {{- end }} + cert-manager: + plugin_data: + issuer_name: {{ default (include "spire-server.fullname" $root) .issuer_name }}-ca + issuer_kind: {{ .issuer_kind | quote }} + issuer_group: {{ .issuer_group | quote }} + namespace: {{ default $root.Release.Namespace .namespace | quote }} + {{- if ne .kube_config_file "" }} + kube_config_file: {{ .kube_config_file | quote }} + {{- end }} {{- end }} {{- end }} @@ -131,11 +149,11 @@ plugins: {{- if eq (.enabled | toString) "true" }} {{- $upstreamAuthorityUsed = add1 $upstreamAuthorityUsed }} UpstreamAuthority: - - spire: - plugin_data: - server_address: {{ include "spire-server.upstream-spire-address" $root | quote }} - server_port: {{ .server.port }} - workload_api_socket: "/run/spire/upstream_agent/spire-agent.sock" + spire: + plugin_data: + server_address: {{ include "spire-server.upstream-spire-address" $root | quote }} + server_port: {{ .server.port }} + workload_api_socket: "/run/spire/upstream_agent/spire-agent.sock" {{- end }} {{- end }} @@ -191,4 +209,4 @@ metadata: {{- end }} data: server.conf: | - {{- include "spire-server.yaml-config" . | fromYaml | toPrettyJson | nindent 4 }} + {{- include "spire-server.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 0ecc5ba..5f869fb 100644 --- a/charts/spire/charts/spire-server/values.yaml +++ b/charts/spire/charts/spire-server/values.yaml @@ -468,6 +468,22 @@ tornjak: # cpu: 100m # memory: 128Mi +# 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. +# @ignored +unsupportedBuiltInPlugins: + keyManager: {} + nodeAttestor: {} + upstreamAuthority: {} + notifier: {} + +customPlugins: + keyManager: {} + nodeAttestor: {} + upstreamAuthority: {} + notifier: {} + # @ignored tests: bash: diff --git a/charts/spire/templates/NOTES.txt b/charts/spire/templates/NOTES.txt index dfe3e24..519b6c1 100644 --- a/charts/spire/templates/NOTES.txt +++ b/charts/spire/templates/NOTES.txt @@ -1 +1,8 @@ Installed {{ .Chart.Name }}… +{{- $up := (index .Values "spire-server").unsupportedBuiltInPlugins }} +{{- $s := add (len $up.keyManager) (len $up.nodeAttestor) (len $up.upstreamAuthority) (len $up.notifier) }} +{{- if gt $s 0 }} + +Warning: + You are using an unsupported plugin. Functionality of this release and future upgrades are not guaranteed to work smoothly. +{{- end }} diff --git a/examples/spire-plugins/node-agent-tpm/Dockerfile.agent b/examples/spire-plugins/node-agent-tpm/Dockerfile.agent new file mode 100644 index 0000000..4351d9e --- /dev/null +++ b/examples/spire-plugins/node-agent-tpm/Dockerfile.agent @@ -0,0 +1,9 @@ +FROM alpine +RUN \ + cd /tmp && \ + wget -O plugin.tar.gz https://github.com/boxboat/spire-tpm-plugin/releases/download/v1.3.3/spire_tpm_plugin_tpm_attestor_agent_linux_amd64_v1.3.3.tar.gz && \ + tar -zxvf plugin.tar.gz && \ + mv tpm_attestor_agent / + +FROM ghcr.io/spiffe/spire-server:1.5.4 +COPY --from=0 /tpm_attestor_agent /bin/tpm_attestor_agent diff --git a/examples/spire-plugins/node-agent-tpm/Dockerfile.server b/examples/spire-plugins/node-agent-tpm/Dockerfile.server new file mode 100644 index 0000000..afe018a --- /dev/null +++ b/examples/spire-plugins/node-agent-tpm/Dockerfile.server @@ -0,0 +1,9 @@ +FROM alpine +RUN \ + cd /tmp && \ + wget -O plugin.tar.gz https://github.com/boxboat/spire-tpm-plugin/releases/download/v1.3.3/spire_tpm_plugin_tpm_attestor_server_linux_amd64_v1.3.3.tar.gz && \ + tar -zxvf plugin.tar.gz && \ + mv tpm_attestor_server / + +FROM ghcr.io/spiffe/spire-server:1.5.4 +COPY --from=0 /tpm_attestor_server /bin/tpm_attestor_server diff --git a/examples/spire-plugins/node-agent-tpm/values.yaml b/examples/spire-plugins/node-agent-tpm/values.yaml new file mode 100644 index 0000000..a2011b7 --- /dev/null +++ b/examples/spire-plugins/node-agent-tpm/values.yaml @@ -0,0 +1,25 @@ +spire-server: + initContainers: + - name: tpm-init + image: busybox + command: + - sh + - -c + - | + mkdir -p /run/spire/data/certs + mkdir -p /run/spire/data/hashes + volumeMounts: + - mountPath: /run/spire/data + name: spire-data + image: + registry: docker.io + repository: kfox1111/misc + version: tpm-server-test + customPlugins: + nodeAttestor: + tpm: + plugin_cmd: /bin/tpm_attestor_server + plugin_checksum: 97442358ae946e3fb8f2464432b8c23efdc0b5d44ec1eea27babe59ef646cc2f + plugin_data: + - ca_path: /run/spire/data/certs + hash_path: /run/spire/data/hashes diff --git a/tests/unit/spire_test.go b/tests/unit/spire_test.go index 417b7c2..d22c0b8 100644 --- a/tests/unit/spire_test.go +++ b/tests/unit/spire_test.go @@ -41,4 +41,34 @@ spire-server: Expect(notes).Should(ContainSubstring("abc123")) }) }) + Describe("spire-server.customPlugin.tpm", func() { + It("plugin set ok", func() { + objs, err := ValueStringRender(chart, ` +spire-server: + customPlugins: + nodeAttestor: + tpm: + plugin_cmd: /bin/tpm_attestor_server + plugin_checksum: 97442358ae946e3fb8f2464432b8c23efdc0b5d44ec1eea27babe59ef646cc2f + plugin_data: {} +`) + Expect(err).Should(Succeed()) + notes := objs["spire/charts/spire-server/templates/configmap.yaml"] + Expect(notes).Should(ContainSubstring("tpm")) + }) + }) + Describe("spire-server.unsupportedBuiltInPlugins", func() { + It("plugin set ok", func() { + objs, err := ValueStringRender(chart, ` +spire-server: + unsupportedBuiltInPlugins: + nodeAttestor: + join_token: + plugin_data: {} +`) + Expect(err).Should(Succeed()) + notes := objs["spire/charts/spire-server/templates/configmap.yaml"] + Expect(notes).Should(ContainSubstring("join_token")) + }) + }) })