Skip to content

Commit 2d16f28

Browse files
committed
coco: introducing the hello-coco app
Add hello-coco Helm chart demonstrating SPIRE agent deployment in confidential containers using x509pop node attestation. The chart deploys a test pod in a CoCo peer-pod (confidential VM with AMD SNP or Intel TDX) that fetches SPIRE agent certificates from KBS after TEE attestation, establishing hardware as the root of trust instead of Kubernetes. The pod contains three containers: init container fetches sealed secrets from KBS, SPIRE agent uses x509pop for node attestation, and test workload receives SPIFFE SVIDs via unix attestation. This validates the complete integration flow between ZTVP and CoCo components. Note: This could be dropped, if we stick with only the todoapp. Signed-off-by: Beraldo Leal <bleal@redhat.com>
1 parent 467c9f1 commit 2d16f28

6 files changed

Lines changed: 299 additions & 0 deletions

File tree

charts/hello-coco/Chart.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
apiVersion: v2
2+
name: hello-coco
3+
description: A Helm chart for SPIRE Agent CoCo test pod demonstrates x509pop attestation with KBS
4+
type: application
5+
version: 0.0.1
6+
maintainers:
7+
- name: Beraldo Leal
8+
email: bleal@redhat.com
9+
- name: Chris Butler
10+
email: chris.butler@redhat.com
11+
keywords:
12+
- spire
13+
- coco
14+
- confidentialcontainers
15+
- attestation
16+
- x509pop
17+
annotations:
18+
category: Test
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: spire-agent-coco
5+
namespace: zero-trust-workload-identity-manager
6+
data:
7+
agent.conf: |
8+
{
9+
"agent": {
10+
"data_dir": "/var/lib/spire",
11+
"log_level": "debug",
12+
"retry_bootstrap": true,
13+
"server_address": "spire-server.apps.{{ .Values.global.clusterDomain }}",
14+
"server_port": "443",
15+
"socket_path": "/tmp/spire-agent/public/spire-agent.sock",
16+
"trust_bundle_path": "/run/spire/bundle/bundle.crt",
17+
"trust_domain": "apps.{{ .Values.global.clusterDomain }}"
18+
},
19+
"health_checks": {
20+
"bind_address": "0.0.0.0",
21+
"bind_port": 9982,
22+
"listener_enabled": true,
23+
"live_path": "/live",
24+
"ready_path": "/ready"
25+
},
26+
"plugins": {
27+
"KeyManager": [
28+
{
29+
"disk": {
30+
"plugin_data": {
31+
"directory": "/var/lib/spire"
32+
}
33+
}
34+
}
35+
],
36+
"NodeAttestor": [
37+
{
38+
"x509pop": {
39+
"plugin_data": {
40+
"private_key_path": "/sealed/key.pem",
41+
"certificate_path": "/sealed/cert.pem"
42+
}
43+
}
44+
}
45+
],
46+
"WorkloadAttestor": [
47+
{
48+
"unix": {
49+
"plugin_data": {}
50+
}
51+
}
52+
]
53+
},
54+
"telemetry": {
55+
"Prometheus": {
56+
"host": "0.0.0.0",
57+
"port": "9402"
58+
}
59+
}
60+
}
61+
---
62+
apiVersion: v1
63+
kind: ConfigMap
64+
metadata:
65+
name: spiffe-helper-config
66+
namespace: zero-trust-workload-identity-manager
67+
data:
68+
helper.conf: |-
69+
agent_address = "/tmp/spire-agent/public/spire-agent.sock"
70+
cmd = ""
71+
cmd_args = ""
72+
cert_dir = "/svids"
73+
renew_signal = ""
74+
svid_file_name = "svid.pem"
75+
svid_key_file_name = "svid_key.pem"
76+
svid_bundle_file_name = "svid_bundle.pem"
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
# SPIRE Agent with x509pop attestation running in CoCo peer pod.
2+
# Uses CDH sealed secrets for agent credentials (cert/key fetched from KBS after TEE attestation).
3+
apiVersion: v1
4+
kind: Pod
5+
metadata:
6+
name: spire-agent-cc
7+
namespace: zero-trust-workload-identity-manager
8+
labels:
9+
app: spire-agent-cc
10+
spec:
11+
runtimeClassName: kata-remote
12+
# shareProcessNamespace allows SPIRE agent to inspect workload processes for unix attestation
13+
# This is secure because the real isolation boundary is the confidential VM (peer-pod with TEE),
14+
# not individual containers. All containers in this pod are part of the same trust boundary.
15+
shareProcessNamespace: true
16+
serviceAccountName: spire-agent
17+
nodeSelector:
18+
workload-type: coco
19+
imagePullSecrets:
20+
- name: global-pull-secret
21+
22+
containers:
23+
# SPIRE Agent Sidecar
24+
- name: spire-agent
25+
image: registry.redhat.io/zero-trust-workload-identity-manager/spiffe-spire-agent-rhel9@sha256:4073ef462525c2ea1326f3c44ec630e33cbab4b428e8314a85d38756c2460831
26+
command: ["/bin/sh", "-c"]
27+
args:
28+
- |
29+
/spire-agent run -config /opt/spire/conf/agent/agent.conf
30+
env:
31+
- name: PATH
32+
value: "/opt/spire/bin:/bin"
33+
- name: MY_NODE_NAME
34+
value: "coco-vm-node" # Virtual node name for CoCo
35+
ports:
36+
- containerPort: 9982
37+
name: healthz
38+
protocol: TCP
39+
livenessProbe:
40+
httpGet:
41+
path: /live
42+
port: healthz
43+
scheme: HTTP
44+
initialDelaySeconds: 15
45+
periodSeconds: 60
46+
readinessProbe:
47+
httpGet:
48+
path: /ready
49+
port: healthz
50+
scheme: HTTP
51+
initialDelaySeconds: 10
52+
periodSeconds: 30
53+
volumeMounts:
54+
- name: spire-config
55+
mountPath: /opt/spire/conf/agent
56+
readOnly: true
57+
- name: spire-bundle
58+
mountPath: /run/spire/bundle
59+
readOnly: true
60+
- name: spire-socket
61+
mountPath: /tmp/spire-agent/public
62+
- name: spire-persistence
63+
mountPath: /var/lib/spire
64+
- name: sealed-creds
65+
mountPath: /sealed
66+
readOnly: true
67+
securityContext:
68+
readOnlyRootFilesystem: true
69+
allowPrivilegeEscalation: false
70+
capabilities:
71+
drop:
72+
- ALL
73+
seccompProfile:
74+
type: RuntimeDefault
75+
76+
# SPIFFE Helper Sidecar
77+
- name: spiffe-helper
78+
image: ghcr.io/spiffe/spiffe-helper:0.10.1
79+
imagePullPolicy: IfNotPresent
80+
args:
81+
- "-config"
82+
- "/etc/helper.conf"
83+
volumeMounts:
84+
- name: spiffe-helper-config
85+
readOnly: true
86+
mountPath: /etc/helper.conf
87+
subPath: helper.conf
88+
- name: spire-socket
89+
readOnly: true
90+
mountPath: /tmp/spire-agent/public
91+
- name: svids
92+
mountPath: /svids
93+
securityContext:
94+
allowPrivilegeEscalation: false
95+
capabilities:
96+
drop:
97+
- ALL
98+
readOnlyRootFilesystem: false
99+
seccompProfile:
100+
type: RuntimeDefault
101+
102+
# Test Workload Container
103+
- name: test-workload
104+
image: registry.redhat.io/ubi9/ubi-minimal:latest
105+
command: ["/bin/sh", "-c"]
106+
args:
107+
- |
108+
echo "=== SPIRE Agent CoCo Test Started ==="
109+
echo "Waiting for SPIFFE certificates..."
110+
111+
# Wait for SPIFFE certificates
112+
while [ ! -f /svids/svid.pem ]; do
113+
echo "Waiting for SPIFFE certificates..."
114+
sleep 2
115+
done
116+
117+
echo "SPIFFE certificates found!"
118+
ls -la /svids/
119+
120+
echo "=== Testing SPIFFE X.509 certificates ==="
121+
echo "Certificate details:"
122+
openssl x509 -in /svids/svid.pem -text -noout | head -20
123+
124+
echo "=== Sleeping for manual inspection ==="
125+
sleep 3600
126+
volumeMounts:
127+
- name: svids
128+
mountPath: /svids
129+
readOnly: true
130+
securityContext:
131+
allowPrivilegeEscalation: false
132+
capabilities:
133+
drop:
134+
- ALL
135+
readOnlyRootFilesystem: false
136+
seccompProfile:
137+
type: RuntimeDefault
138+
139+
volumes:
140+
- name: spire-config
141+
configMap:
142+
name: spire-agent-coco
143+
- name: spiffe-helper-config
144+
configMap:
145+
name: spiffe-helper-config
146+
- name: spire-bundle
147+
configMap:
148+
name: spire-bundle
149+
- name: spire-socket
150+
emptyDir: {}
151+
- name: spire-persistence
152+
emptyDir: {}
153+
- name: sealed-creds
154+
secret:
155+
secretName: {{ .Values.sealedSecret.name }}
156+
- name: svids
157+
emptyDir: {}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Sealed Secret for SPIRE agent x509pop attestation
2+
#
3+
# This creates a K8s Secret with sealed secret references that CDH will unseal
4+
# inside the TEE after successful hardware attestation.
5+
#
6+
# Format: sealed.fakejwsheader.<base64-json>.fakesignature
7+
# The JSON payload contains the KBS resource path, and CDH fetches the real secret.
8+
#
9+
{{- define "hello-coco.sealedRef" -}}
10+
{{- $json := printf `{"version":"0.1.0","type":"vault","name":"kbs:///%s","provider":"kbs","provider_settings":{},"annotations":{}}` . -}}
11+
sealed.fakejwsheader.{{ $json | b64enc }}.fakesignature
12+
{{- end }}
13+
apiVersion: v1
14+
kind: Secret
15+
metadata:
16+
name: {{ .Values.sealedSecret.name }}
17+
namespace: zero-trust-workload-identity-manager
18+
type: Opaque
19+
stringData:
20+
cert.pem: {{ include "hello-coco.sealedRef" .Values.sealedSecret.certPath | quote }}
21+
key.pem: {{ include "hello-coco.sealedRef" .Values.sealedSecret.keyPath | quote }}

charts/hello-coco/values.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Default values for hello coco
2+
3+
# SPIRE trust domain
4+
# The SPIRE agent must be configured with the same trust domain as the SPIRE Server
5+
# This ensures the agent can successfully authenticate and workloads receive valid SPIFFE IDs
6+
# Typically set to apps.<your cluster domain>
7+
trustDomain: "apps.example.com"
8+
9+
# KBS URL for CDH (Confidential Data Hub) to fetch sealed secrets after TEE attestation
10+
# Dev (single cluster): http://kbs-service.trustee-operator-system.svc.cluster.local:8080
11+
# Prod (separate trusted cluster): https://kbs.trusted-cluster.example.com
12+
kbsUrl: "http://kbs-service.trustee-operator-system.svc.cluster.local:8080"
13+
14+
# Sealed secret configuration for SPIRE agent x509pop attestation
15+
# These are KBS resource paths where the agent cert/key are stored
16+
sealedSecret:
17+
# Name of the K8s Secret to create with sealed references
18+
name: "spire-agent-sealed-creds"
19+
# KBS resource path for the certificate (e.g., default/spire-cert-qtodo/cert)
20+
certPath: "default/spire-cert-qtodo/cert"
21+
# KBS resource path for the private key (e.g., default/spire-key-qtodo/key)
22+
keyPath: "default/spire-key-qtodo/key"

values-coco-dev.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,11 @@ clusterGroup:
325325
project: hub
326326
chart: sandboxed-policies
327327
chartVersion: 0.0.*
328+
hello-coco:
329+
name: hello-coco
330+
namespace: zero-trust-workload-identity-manager
331+
project: hub
332+
path: charts/hello-coco
328333
argoCD:
329334
resourceExclusions: |
330335
- apiGroups:

0 commit comments

Comments
 (0)