Skip to content

Commit 95ad702

Browse files
butler54claude
andcommitted
feat!: add Kyverno-based cc_init_data injection for OSC 1.12 / Trustee 1.1
Replace broken io.katacontainers.config.agent.policy annotation with Kyverno MutatingPolicy that injects cc_init_data from ConfigMaps into pods with kata runtime classes. - Add coco-kyverno-policies chart with MutatingPolicy, ValidatingPolicy, and ClusterPolicy for ConfigMap namespace propagation - Update imperative job to generate both default and debug initdata ConfigMaps with Kyverno validation fields - Update workload pod templates to use coco.io/initdata-configmap annotation instead of inline policy - Update values-simple.yaml: OSC 1.12, Trustee 1.1, Kyverno Helm app - Add conditional memory annotation for non-Azure platforms - Delete insecure-policy.rego (policy now embedded in cc_init_data) BREAKING CHANGE: Requires Kyverno and OSC 1.12 / Trustee 1.1.0 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 6125928 commit 95ad702

12 files changed

Lines changed: 318 additions & 19 deletions

File tree

ansible/init-data-gzipper.yaml

Lines changed: 101 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
hub_domain: "{{ global.hubClusterDomain | default('none') | lower}}"
1010
security_policy_flavour: "{{ global.coco.securityPolicyFlavour | default('insecure') }}"
1111
template_src: "initdata-default.toml.tpl"
12+
debug_template_src: "initdata-debug.toml.tpl"
1213
tasks:
1314
- name: Create temporary working directory
1415
ansible.builtin.tempfile:
@@ -37,32 +38,82 @@
3738
- name: Define temp file paths
3839
ansible.builtin.set_fact:
3940
rendered_path: "{{ tmpdir.path }}/rendered.toml"
41+
debug_rendered_path: "{{ tmpdir.path }}/debug-rendered.toml"
4042

41-
- name: Render template to temp file
43+
- name: Render default template to temp file
4244
ansible.builtin.template:
4345
src: "{{ template_src }}"
4446
dest: "{{ rendered_path }}"
4547
mode: "0600"
4648

49+
- name: Render debug template to temp file
50+
ansible.builtin.template:
51+
src: "{{ debug_template_src }}"
52+
dest: "{{ debug_rendered_path }}"
53+
mode: "0600"
54+
55+
- name: Read raw aa.toml from rendered template
56+
ansible.builtin.shell: |
57+
set -o pipefail
58+
python3 -c "
59+
import tomllib
60+
with open('{{ rendered_path }}', 'rb') as f:
61+
data = tomllib.load(f)
62+
print(data['data']['aa.toml'], end='')
63+
"
64+
register: raw_aa_toml
65+
changed_when: false
66+
67+
- name: Read raw cdh.toml from rendered template
68+
ansible.builtin.shell: |
69+
set -o pipefail
70+
python3 -c "
71+
import tomllib
72+
with open('{{ rendered_path }}', 'rb') as f:
73+
data = tomllib.load(f)
74+
print(data['data']['cdh.toml'], end='')
75+
"
76+
register: raw_cdh_toml
77+
changed_when: false
4778

48-
- name: Gzip and base64 encode the rendered content
79+
- name: Read raw policy.rego from default template
80+
ansible.builtin.shell: |
81+
set -o pipefail
82+
python3 -c "
83+
import tomllib
84+
with open('{{ rendered_path }}', 'rb') as f:
85+
data = tomllib.load(f)
86+
print(data['data']['policy.rego'], end='')
87+
"
88+
register: raw_policy_rego
89+
changed_when: false
90+
91+
- name: Read raw policy.rego from debug template
92+
ansible.builtin.shell: |
93+
set -o pipefail
94+
python3 -c "
95+
import tomllib
96+
with open('{{ debug_rendered_path }}', 'rb') as f:
97+
data = tomllib.load(f)
98+
print(data['data']['policy.rego'], end='')
99+
"
100+
register: debug_raw_policy_rego
101+
changed_when: false
102+
103+
- name: Gzip and base64 encode the default rendered content
49104
ansible.builtin.shell: |
50105
set -o pipefail
51106
cat "{{ rendered_path }}" | gzip | base64 -w0
52107
register: initdata_encoded
53108
changed_when: false
54109

55-
# This block runs a shell script that calculates a hash value (PCR8_HASH) derived from the contents of 'initdata.toml'.
56-
# The script performs the following steps:
57-
# 1. hash=$(sha256sum initdata.toml | cut -d' ' -f1): Computes the sha256 hash of 'initdata.toml' and assigns it to $hash.
58-
# 2. initial_pcr=0000000000000000000000000000000000000000000000000000000000000000: Initializes a string of zeros as the initial PCR value.
59-
# 3. PCR8_HASH=$(echo -n "$initial_pcr$hash" | xxd -r -p | sha256sum | cut -d' ' -f1):
60-
# Concatenates initial_pcr and $hash, converts from hex to binary,
61-
# computes its sha256 hash, and stores the result as PCR8_HASH.
62-
# 4. echo $PCR8_HASH: Outputs the PCR hash value.
63-
# The important part: The 'register: pcr8_hash' registers the **stdout of the command**,
64-
# which is the value output by 'echo $PCR8_HASH', as 'pcr8_hash.stdout' in Ansible.
65-
# It does NOT register an environment variable, but rather the value actually printed by 'echo'.
110+
- name: Gzip and base64 encode the debug rendered content
111+
ansible.builtin.shell: |
112+
set -o pipefail
113+
cat "{{ debug_rendered_path }}" | gzip | base64 -w0
114+
register: debug_initdata_encoded
115+
changed_when: false
116+
66117
- name: Register init data pcr into a var
67118
ansible.builtin.shell: |
68119
set -o pipefail
@@ -72,8 +123,16 @@
72123
register: pcr8_hash
73124
changed_when: false
74125

126+
- name: Register debug init data pcr into a var
127+
ansible.builtin.shell: |
128+
set -o pipefail
129+
hash=$(sha256sum "{{ debug_rendered_path }}" | cut -d' ' -f1)
130+
initial_pcr=0000000000000000000000000000000000000000000000000000000000000000
131+
PCR8_HASH=$(echo -n "$initial_pcr$hash" | xxd -r -p | sha256sum | cut -d' ' -f1) && echo $PCR8_HASH
132+
register: debug_pcr8_hash
133+
changed_when: false
75134

76-
- name: Create/update ConfigMap with gzipped+base64 content
135+
- name: Create/update default initdata ConfigMap
77136
kubernetes.core.k8s:
78137
kubeconfig: "{{ kubeconfig | default(omit) }}"
79138
state: present
@@ -83,6 +142,34 @@
83142
metadata:
84143
name: "initdata"
85144
namespace: "imperative"
145+
labels:
146+
coco.io/type: initdata
86147
data:
87148
INITDATA: "{{ initdata_encoded.stdout }}"
88149
PCR8_HASH: "{{ pcr8_hash.stdout }}"
150+
version: "0.1.0"
151+
algorithm: "sha256"
152+
aa.toml: "{{ raw_aa_toml.stdout }}"
153+
cdh.toml: "{{ raw_cdh_toml.stdout }}"
154+
policy.rego: "{{ raw_policy_rego.stdout }}"
155+
156+
- name: Create/update debug initdata ConfigMap
157+
kubernetes.core.k8s:
158+
kubeconfig: "{{ kubeconfig | default(omit) }}"
159+
state: present
160+
definition:
161+
apiVersion: v1
162+
kind: ConfigMap
163+
metadata:
164+
name: "debug-initdata"
165+
namespace: "imperative"
166+
labels:
167+
coco.io/type: initdata
168+
data:
169+
INITDATA: "{{ debug_initdata_encoded.stdout }}"
170+
PCR8_HASH: "{{ debug_pcr8_hash.stdout }}"
171+
version: "0.1.0"
172+
algorithm: "sha256"
173+
aa.toml: "{{ raw_aa_toml.stdout }}"
174+
cdh.toml: "{{ raw_cdh_toml.stdout }}"
175+
policy.rego: "{{ debug_raw_policy_rego.stdout }}"

charts/coco-supported/hello-openshift/insecure-policy.rego renamed to ansible/initdata-debug.toml.tpl

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,32 @@
1+
algorithm = "sha256"
2+
version = "0.1.0"
3+
4+
[data]
5+
"aa.toml" = '''
6+
[token_configs]
7+
[token_configs.coco_as]
8+
url = "https://kbs.{{ hub_domain }}"
9+
10+
[token_configs.kbs]
11+
url = "https://kbs.{{ hub_domain }}"
12+
cert = """{{ trustee_cert }}"""
13+
'''
14+
15+
"cdh.toml" = '''
16+
socket = 'unix:///run/confidential-containers/cdh.sock'
17+
credentials = []
18+
19+
[kbc]
20+
name = "cc_kbc"
21+
url = "https://kbs.{{ hub_domain }}"
22+
kbs_cert = """{{ trustee_cert }}"""
23+
24+
25+
[image]
26+
image_security_policy_uri = 'kbs:///default/security-policy/{{ security_policy_flavour }}'
27+
'''
28+
29+
"policy.rego" = '''
130
package agent_policy
231

332
default AddARPNeighborsRequest := true
@@ -35,4 +64,5 @@ default UpdateEphemeralMountsRequest := true
3564
default UpdateInterfaceRequest := true
3665
default UpdateRoutesRequest := true
3766
default WaitProcessRequest := true
38-
default WriteStreamRequest := true
67+
default WriteStreamRequest := true
68+
'''
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
apiVersion: v2
2+
name: coco-kyverno-policies
3+
description: Kyverno policies for CoCo cc_init_data injection and validation
4+
type: application
5+
version: 0.1.0
6+
appVersion: "1.0.0"
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{{- range .Values.workloadNamespaces }}
2+
---
3+
apiVersion: kyverno.io/v1
4+
kind: ClusterPolicy
5+
metadata:
6+
name: propagate-initdata-to-{{ . }}
7+
annotations:
8+
argocd.argoproj.io/sync-wave: "1"
9+
spec:
10+
rules:
11+
- name: copy-initdata
12+
match:
13+
any:
14+
- resources:
15+
kinds:
16+
- ConfigMap
17+
namespaces:
18+
- {{ $.Values.initdataSourceNamespace }}
19+
selector:
20+
matchLabels:
21+
coco.io/type: initdata
22+
generate:
23+
synchronize: true
24+
apiVersion: v1
25+
kind: ConfigMap
26+
name: "{{ "{{" }}request.object.metadata.name{{ "}}" }}"
27+
namespace: {{ . }}
28+
clone:
29+
namespace: {{ $.Values.initdataSourceNamespace }}
30+
name: "{{ "{{" }}request.object.metadata.name{{ "}}" }}"
31+
{{- end }}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
apiVersion: policies.kyverno.io/v1
2+
kind: MutatingPolicy
3+
metadata:
4+
name: inject-coco-initdata
5+
annotations:
6+
policies.kyverno.io/title: Inject CoCo InitData
7+
policies.kyverno.io/category: Confidential Computing
8+
policies.kyverno.io/severity: medium
9+
policies.kyverno.io/subject: Pod
10+
policies.kyverno.io/description: >-
11+
Injects cc_init_data annotation into pods with a kata runtime class
12+
by reading from a ConfigMap specified via the coco.io/initdata-configmap
13+
annotation. Adapted from upstream kyverno inject-coco-initdata policy.
14+
argocd.argoproj.io/sync-wave: "1"
15+
spec:
16+
matchConstraints:
17+
resourceRules:
18+
- apiGroups: [""]
19+
apiVersions: ["v1"]
20+
operations: ["CREATE"]
21+
resources: ["pods"]
22+
matchConditions:
23+
- name: has-kata-runtime
24+
expression: >-
25+
has(object.spec.runtimeClassName) &&
26+
object.spec.runtimeClassName.startsWith("kata")
27+
- name: has-initdata-configmap-annotation
28+
expression: >-
29+
has(object.metadata.annotations) &&
30+
'coco.io/initdata-configmap' in object.metadata.annotations &&
31+
object.metadata.annotations['coco.io/initdata-configmap'] != ''
32+
- name: no-existing-cc-init-data
33+
expression: >-
34+
!has(object.metadata.annotations) ||
35+
!('io.katacontainers.config.hypervisor.cc_init_data' in object.metadata.annotations)
36+
variables:
37+
- name: configMapName
38+
expression: "object.metadata.annotations['coco.io/initdata-configmap']"
39+
- name: configMap
40+
expression: >-
41+
namespaceObject.get('configmaps', variables.configMapName)
42+
mutations:
43+
- patchType: JSONPatch
44+
jsonPatch:
45+
expression: >-
46+
[
47+
JSONPatch{
48+
op: "add",
49+
path: "/metadata/annotations/io.katacontainers.config.hypervisor.cc_init_data",
50+
value: variables.configMap.data['INITDATA']
51+
}
52+
]
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
apiVersion: policies.kyverno.io/v1
2+
kind: ValidatingPolicy
3+
metadata:
4+
name: validate-initdata-configmap
5+
annotations:
6+
policies.kyverno.io/title: Validate InitData ConfigMap Required Fields
7+
policies.kyverno.io/category: Confidential Computing
8+
policies.kyverno.io/severity: medium
9+
policies.kyverno.io/subject: ConfigMap
10+
policies.kyverno.io/description: >-
11+
Validates that ConfigMaps with label coco.io/type=initdata contain
12+
all required fields with proper values.
13+
argocd.argoproj.io/sync-wave: "1"
14+
spec:
15+
evaluation:
16+
background:
17+
enabled: true
18+
matchConstraints:
19+
resourceRules:
20+
- apiGroups: [""]
21+
apiVersions: ["v1"]
22+
operations: ["CREATE", "UPDATE"]
23+
resources: ["configmaps"]
24+
matchConditions:
25+
- name: has-initdata-label
26+
expression: >-
27+
has(object.metadata.labels) &&
28+
'coco.io/type' in object.metadata.labels &&
29+
object.metadata.labels['coco.io/type'] == 'initdata'
30+
variables:
31+
- name: configData
32+
expression: "object.data.orValue({})"
33+
- name: allowedAlgorithms
34+
expression: "['sha256', 'sha384', 'sha512']"
35+
validationActions: ["Audit"]
36+
validations:
37+
- expression: "'version' in variables.configData && variables.configData['version'] != ''"
38+
messageExpression: "'ConfigMap must contain a non-empty version field'"
39+
- expression: "'algorithm' in variables.configData && variables.configData['algorithm'] in variables.allowedAlgorithms"
40+
messageExpression: "'ConfigMap must contain an algorithm field with value sha256, sha384, or sha512, found: ' + ('algorithm' in variables.configData ? variables.configData['algorithm'] : 'missing')"
41+
- expression: "'policy.rego' in variables.configData && variables.configData['policy.rego'] != ''"
42+
messageExpression: "'ConfigMap must contain a non-empty policy.rego field'"
43+
- expression: "'aa.toml' in variables.configData && variables.configData['aa.toml'] != ''"
44+
messageExpression: "'ConfigMap must contain a non-empty aa.toml field'"
45+
- expression: "'cdh.toml' in variables.configData && variables.configData['cdh.toml'] != ''"
46+
messageExpression: "'ConfigMap must contain a non-empty cdh.toml field'"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
workloadNamespaces:
2+
- hello-openshift
3+
- kbs-access
4+
5+
initdataSourceNamespace: imperative

charts/coco-supported/hello-openshift/templates/insecure-policy-pod.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ metadata:
55
labels:
66
app: insecure-policy
77
annotations:
8-
io.katacontainers.config.agent.policy: '{{ tpl ( .Files.Get "insecure-policy.rego") . | b64enc }}'
8+
coco.io/initdata-configmap: initdata
99
spec:
1010
runtimeClassName: {{ include "hello-openshift.runtimeClassName" . }}
1111
containers:

charts/coco-supported/hello-openshift/templates/secure-pod.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ metadata:
66
app: secure
77
annotations:
88
peerpods: "true"
9+
coco.io/initdata-configmap: initdata
910
spec:
1011
runtimeClassName: {{ include "hello-openshift.runtimeClassName" . }}
1112
containers:

charts/coco-supported/kbs-access/templates/secure-pod.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ metadata:
66
app: secure
77
annotations:
88
peerpods: "true"
9+
coco.io/initdata-configmap: initdata
10+
{{- if .Values.defaultMemory }}
11+
io.katacontainers.config.hypervisor.default_memory: {{ .Values.defaultMemory | quote }}
12+
{{- end }}
913
spec:
1014
runtimeClassName: kata-remote
1115
containers:

0 commit comments

Comments
 (0)