diff --git a/charts/spire/charts/spire-agent/README.md b/charts/spire/charts/spire-agent/README.md index 204a4cc..f89c02b 100644 --- a/charts/spire/charts/spire-agent/README.md +++ b/charts/spire/charts/spire-agent/README.md @@ -75,6 +75,18 @@ A Helm chart to install the SPIRE agent. | `fsGroupFix.resources` | Specify resource needs as per https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ | `{}` | | `keyManager.memory.enabled` | Enable the memory based Key Manager | `true` | | `nodeAttestor.k8sPsat.enabled` | Enable Psat k8s Node Attestor | `true` | +| `nodeAttestor.tpmDirect.enabled` | Enable the direct TPM node attestor, a 3rd party plugin by Boxboat. This plugin is experimental. | `false` | +| `nodeAttestor.tpmDirect.plugin.image.registry` | The OCI registry to pull the image from | `docker.io` | +| `nodeAttestor.tpmDirect.plugin.image.repository` | The repository within the registry | `boxboat/spire-tpm-plugin-tpm-attestor-agent` | +| `nodeAttestor.tpmDirect.plugin.image.pullPolicy` | The image pull policy | `IfNotPresent` | +| `nodeAttestor.tpmDirect.plugin.image.tag` | Overrides the image tag | `v1.8.7` | +| `nodeAttestor.tpmDirect.plugin.checksum` | The sha256 checksum of the plugin binary | `1d7c73ccac948ee86cbd78ddde2d30128a1838b403f7bb2100d38d916a252244` | +| `nodeAttestor.tpmDirect.plugin.path` | The filename in the container of the plugin | `/app/tpm_attestor_agent` | +| `nodeAttestor.tpmDirect.pubHash.enabled` | Enable Psat k8s nodeattestor | `true` | +| `nodeAttestor.tpmDirect.pubHash.image.registry` | The OCI registry to pull the image from | `docker.io` | +| `nodeAttestor.tpmDirect.pubHash.image.repository` | The repository within the registry | `boxboat/spire-tpm-plugin-get-tpm-pubhash` | +| `nodeAttestor.tpmDirect.pubHash.image.pullPolicy` | The image pull policy | `IfNotPresent` | +| `nodeAttestor.tpmDirect.pubHash.image.tag` | Overrides the image tag | `v1.8.7` | | `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` | diff --git a/charts/spire/charts/spire-agent/templates/configmap.yaml b/charts/spire/charts/spire-agent/templates/configmap.yaml index 42dd8e3..1fb312b 100644 --- a/charts/spire/charts/spire-agent/templates/configmap.yaml +++ b/charts/spire/charts/spire-agent/templates/configmap.yaml @@ -79,6 +79,15 @@ plugins: cluster: {{ include "spire-lib.cluster-name" . | quote }} {{- $nodeAttestorUsed = add1 $nodeAttestorUsed }} {{- end }} + {{- with .Values.nodeAttestor.tpmDirect }} + {{- if eq (.enabled | toString) "true" }} + tpm: + plugin_cmd: "/tpm/tpm_attestor_agent" + plugin_checksum: {{ .plugin.checksum }} + plugin_data: {} + {{- $nodeAttestorUsed = add1 $nodeAttestorUsed }} + {{- end }} + {{- end }} {{- if ne $nodeAttestorUsed 1 }} {{- fail (printf "You have to enable exactly one Node Attestor. There are %d enabled." $nodeAttestorUsed) }} {{- end }} diff --git a/charts/spire/charts/spire-agent/templates/daemonset.yaml b/charts/spire/charts/spire-agent/templates/daemonset.yaml index 2b8d900..54b8641 100644 --- a/charts/spire/charts/spire-agent/templates/daemonset.yaml +++ b/charts/spire/charts/spire-agent/templates/daemonset.yaml @@ -1,5 +1,10 @@ {{- $configSum := (include (print $.Template.BasePath "/configmap.yaml") . | sha256sum) }} {{- $podSecurityContext := fromYaml (include "spire-lib.podsecuritycontext" .) }} +{{- $mainSecurityContext := deepCopy .Values.securityContext }} +{{- if .Values.nodeAttestor.tpmDirect.enabled }} +{{- $podSecurityContext = mergeOverwrite (dict "runAsUser" 0 "runAsGroup" 0) .Values.podSecurityContext }} +{{- $_ := set $mainSecurityContext "privileged" true }} +{{- end }} {{- $cbh := eq (include "spire-agent.connect-by-hostname" .) "true" }} {{- $socketAlternateNames := index (include "spire-agent.socket-alternate-names" . | fromYaml) "names" }} {{- $socketPath := include "spire-agent.socket-path" . }} @@ -41,6 +46,47 @@ spec: {{- toYaml .Values.hostAliases | nindent 8 }} {{- end }} initContainers: + {{- if .Values.nodeAttestor.tpmDirect.enabled }} + {{- if .Values.nodeAttestor.tpmDirect.pubHash.enabled }} + - name: fingerprint-tpm + securityContext: + {{- $mainSecurityContext | toYaml | nindent 12 }} + image: {{ template "spire-lib.image" (dict "appVersion" $.Chart.AppVersion "image" .Values.nodeAttestor.tpmDirect.pubHash.image "global" .Values.global) }} + command: + - sh + - -c + - | + # Fetch the TPM's fingerprint from the TPM and output it on the Computers Console. This enables secure bootstrapping of the Node with the Server. + TPM=`/app/get_tpm_pubhash 2> /dev/null` + if [ $? -eq 0 ]; then + echo "TPM Fingerprint: $TPM" + (echo -n '<1>'; echo "TPM Fingerprint: $TPM") | tee /dev/kmsg + else + /app/get_tpm_pubhash + fi + volumeMounts: + - name: tpm + mountPath: /dev/tpmrm0 + readOnly: true + - name: kmsg + mountPath: /dev/kmsg + imagePullPolicy: {{ .Values.nodeAttestor.tpmDirect.pubHash.image.pullPolicy }} + {{- end }} + - name: init-tpm-direct + securityContext: + {{- .Values.securityContext | toYaml | nindent 12 }} + image: {{ template "spire-lib.image" (dict "appVersion" $.Chart.AppVersion "image" .Values.nodeAttestor.tpmDirect.plugin.image "global" .Values.global) }} + command: + - sh + - -ec + - | + # SPIRE must be able to fork the plugin directly within its container. Copy the plugin into a volume that can be mounted where SPIRE can execute it. + cp -a {{ .Values.nodeAttestor.tpmDirect.plugin.path }} /tpm/tpm_attestor_agent + volumeMounts: + - name: tpm-direct + mountPath: /tpm + imagePullPolicy: {{ .Values.nodeAttestor.tpmDirect.plugin.image.pullPolicy }} + {{- end }} - name: init # This is a small image with wait-for-it, choose whatever image # you prefer that waits for a service to be up. This image is built @@ -48,6 +94,8 @@ spec: image: {{ template "spire-lib.image" (dict "image" .Values.waitForIt.image "global" .Values.global) }} imagePullPolicy: {{ .Values.waitForIt.image.pullPolicy }} args: ["-t", "30", "-h", "{{ include "spire-agent.server-address" . | trim }}", "-p", {{ .Values.server.port | quote }}] + securityContext: + {{- .Values.securityContext | toYaml | nindent 12 }} resources: {{- toYaml .Values.waitForIt.resources | nindent 12 }} securityContext: @@ -104,7 +152,7 @@ spec: imagePullPolicy: {{ .Values.image.pullPolicy }} args: ["-config", "/opt/spire/conf/agent/agent.conf"] securityContext: - {{ toYaml .Values.securityContext | nindent 12 }} + {{- $mainSecurityContext | toYaml | nindent 12 }} env: - name: PATH value: "/opt/spire/bin:/bin" @@ -138,6 +186,14 @@ spec: mountPath: /run/spire/bundle readOnly: true {{- end }} + {{- if .Values.nodeAttestor.tpmDirect.enabled }} + - name: tpm-direct + mountPath: /tpm + readOnly: true + - name: tpm + mountPath: /dev/tpmrm0 + readOnly: true + {{- end }} - name: spire-agent-socket-dir mountPath: /tmp/spire-agent/public readOnly: false @@ -191,6 +247,10 @@ spec: configMap: name: {{ include "spire-lib.bundle-configmap" . }} {{- end }} + {{- if .Values.nodeAttestor.tpmDirect.enabled }} + - name: tpm-direct + emptyDir: {} + {{- end }} - name: spire-token projected: sources: @@ -202,6 +262,16 @@ spec: hostPath: path: {{ $socketPath | dir }} type: DirectoryOrCreate + {{- if .Values.nodeAttestor.tpmDirect.enabled }} + - name: tpm + hostPath: + path: /dev/tpmrm0 + type: CharDevice + - name: kmsg + hostPath: + path: /dev/kmsg + type: CharDevice + {{- end }} {{- if gt (len .Values.extraVolumes) 0 }} {{- toYaml .Values.extraVolumes | nindent 8 }} {{- end }} diff --git a/charts/spire/charts/spire-agent/values.yaml b/charts/spire/charts/spire-agent/values.yaml index e6cd720..242ee65 100644 --- a/charts/spire/charts/spire-agent/values.yaml +++ b/charts/spire/charts/spire-agent/values.yaml @@ -171,6 +171,37 @@ nodeAttestor: k8sPsat: ## @param nodeAttestor.k8sPsat.enabled Enable Psat k8s Node Attestor enabled: true + tpmDirect: + ## @param nodeAttestor.tpmDirect.enabled Enable the direct TPM node attestor, a 3rd party plugin by Boxboat. This plugin is experimental. + enabled: false + plugin: + ## @param nodeAttestor.tpmDirect.plugin.image.registry The OCI registry to pull the image from + ## @param nodeAttestor.tpmDirect.plugin.image.repository The repository within the registry + ## @param nodeAttestor.tpmDirect.plugin.image.pullPolicy The image pull policy + ## @param nodeAttestor.tpmDirect.plugin.image.tag Overrides the image tag + ## + image: + registry: docker.io + repository: boxboat/spire-tpm-plugin-tpm-attestor-agent + pullPolicy: IfNotPresent + tag: "v1.8.7" + ## @param nodeAttestor.tpmDirect.plugin.checksum The sha256 checksum of the plugin binary + checksum: 1d7c73ccac948ee86cbd78ddde2d30128a1838b403f7bb2100d38d916a252244 + ## @param nodeAttestor.tpmDirect.plugin.path The filename in the container of the plugin + path: /app/tpm_attestor_agent + pubHash: + ## @param nodeAttestor.tpmDirect.pubHash.enabled Enable Psat k8s nodeattestor + enabled: true + ## @param nodeAttestor.tpmDirect.pubHash.image.registry The OCI registry to pull the image from + ## @param nodeAttestor.tpmDirect.pubHash.image.repository The repository within the registry + ## @param nodeAttestor.tpmDirect.pubHash.image.pullPolicy The image pull policy + ## @param nodeAttestor.tpmDirect.pubHash.image.tag Overrides the image tag + ## + image: + registry: docker.io + repository: boxboat/spire-tpm-plugin-get-tpm-pubhash + pullPolicy: IfNotPresent + tag: "v1.8.7" # workloadAttestors determine a workload's properties and then generate a set of selectors associated with it. workloadAttestors: diff --git a/examples/tpm-direct/values.yaml b/examples/tpm-direct/values.yaml new file mode 100644 index 0000000..ab53a82 --- /dev/null +++ b/examples/tpm-direct/values.yaml @@ -0,0 +1,15 @@ +spire-agent: + nodeAttestor: + k8sPsat: + enabled: false + tpmDirect: + enabled: true + +spire-server: + controllerManager: + # K8s labels have a 63 character limit. TPM hashes are 64 chars. So you need to label the node with two labels with half of the tpm's hash each. The 'node-restriction.kubernetes.io/' prefix is so that the + # nodes can't update the hash themselves, an important security constraint. + parentIDTemplate: 'spiffe://{{ .TrustDomain }}/spire/agent/tpm/{{ index .NodeMeta.Labels "node-restriction.kubernetes.io/tpm-pubhash" }}{{ index .NodeMeta.Labels "node-restriction.kubernetes.io/tpm-pubhash2" }}' + nodeAttestor: + tpmDirect: + enabled: true