Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 101 additions & 14 deletions ansible/init-data-gzipper.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
hub_domain: "{{ global.hubClusterDomain | default('none') | lower}}"
security_policy_flavour: "{{ global.coco.securityPolicyFlavour | default('insecure') }}"
template_src: "initdata-default.toml.tpl"
debug_template_src: "initdata-debug.toml.tpl"
tasks:
- name: Create temporary working directory
ansible.builtin.tempfile:
Expand Down Expand Up @@ -37,32 +38,82 @@
- name: Define temp file paths
ansible.builtin.set_fact:
rendered_path: "{{ tmpdir.path }}/rendered.toml"
debug_rendered_path: "{{ tmpdir.path }}/debug-rendered.toml"

- name: Render template to temp file
- name: Render default template to temp file
ansible.builtin.template:
src: "{{ template_src }}"
dest: "{{ rendered_path }}"
mode: "0600"

- name: Render debug template to temp file
ansible.builtin.template:
src: "{{ debug_template_src }}"
dest: "{{ debug_rendered_path }}"
mode: "0600"

- name: Read raw aa.toml from rendered template
ansible.builtin.shell: |
set -o pipefail
python3 -c "
import tomllib
with open('{{ rendered_path }}', 'rb') as f:
data = tomllib.load(f)
print(data['data']['aa.toml'], end='')
"
register: raw_aa_toml
changed_when: false

- name: Read raw cdh.toml from rendered template
ansible.builtin.shell: |
set -o pipefail
python3 -c "
import tomllib
with open('{{ rendered_path }}', 'rb') as f:
data = tomllib.load(f)
print(data['data']['cdh.toml'], end='')
"
register: raw_cdh_toml
changed_when: false

- name: Gzip and base64 encode the rendered content
- name: Read raw policy.rego from default template
ansible.builtin.shell: |
set -o pipefail
python3 -c "
import tomllib
with open('{{ rendered_path }}', 'rb') as f:
data = tomllib.load(f)
print(data['data']['policy.rego'], end='')
"
register: raw_policy_rego
changed_when: false

- name: Read raw policy.rego from debug template
ansible.builtin.shell: |
set -o pipefail
python3 -c "
import tomllib
with open('{{ debug_rendered_path }}', 'rb') as f:
data = tomllib.load(f)
print(data['data']['policy.rego'], end='')
"
register: debug_raw_policy_rego
changed_when: false

- name: Gzip and base64 encode the default rendered content
ansible.builtin.shell: |
set -o pipefail
cat "{{ rendered_path }}" | gzip | base64 -w0
register: initdata_encoded
changed_when: false

# This block runs a shell script that calculates a hash value (PCR8_HASH) derived from the contents of 'initdata.toml'.
# The script performs the following steps:
# 1. hash=$(sha256sum initdata.toml | cut -d' ' -f1): Computes the sha256 hash of 'initdata.toml' and assigns it to $hash.
# 2. initial_pcr=0000000000000000000000000000000000000000000000000000000000000000: Initializes a string of zeros as the initial PCR value.
# 3. PCR8_HASH=$(echo -n "$initial_pcr$hash" | xxd -r -p | sha256sum | cut -d' ' -f1):
# Concatenates initial_pcr and $hash, converts from hex to binary,
# computes its sha256 hash, and stores the result as PCR8_HASH.
# 4. echo $PCR8_HASH: Outputs the PCR hash value.
# The important part: The 'register: pcr8_hash' registers the **stdout of the command**,
# which is the value output by 'echo $PCR8_HASH', as 'pcr8_hash.stdout' in Ansible.
# It does NOT register an environment variable, but rather the value actually printed by 'echo'.
- name: Gzip and base64 encode the debug rendered content
ansible.builtin.shell: |
set -o pipefail
cat "{{ debug_rendered_path }}" | gzip | base64 -w0
register: debug_initdata_encoded
changed_when: false

- name: Register init data pcr into a var
ansible.builtin.shell: |
set -o pipefail
Expand All @@ -72,8 +123,16 @@
register: pcr8_hash
changed_when: false

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

- name: Create/update ConfigMap with gzipped+base64 content
- name: Create/update default initdata ConfigMap
kubernetes.core.k8s:
kubeconfig: "{{ kubeconfig | default(omit) }}"
state: present
Expand All @@ -83,6 +142,34 @@
metadata:
name: "initdata"
namespace: "imperative"
labels:
coco.io/type: initdata
data:
INITDATA: "{{ initdata_encoded.stdout }}"
PCR8_HASH: "{{ pcr8_hash.stdout }}"
version: "0.1.0"
algorithm: "sha256"
aa.toml: "{{ raw_aa_toml.stdout }}"
cdh.toml: "{{ raw_cdh_toml.stdout }}"
policy.rego: "{{ raw_policy_rego.stdout }}"

- name: Create/update debug initdata ConfigMap
kubernetes.core.k8s:
kubeconfig: "{{ kubeconfig | default(omit) }}"
state: present
definition:
apiVersion: v1
kind: ConfigMap
metadata:
name: "debug-initdata"
namespace: "imperative"
labels:
coco.io/type: initdata
data:
INITDATA: "{{ debug_initdata_encoded.stdout }}"
PCR8_HASH: "{{ debug_pcr8_hash.stdout }}"
version: "0.1.0"
algorithm: "sha256"
aa.toml: "{{ raw_aa_toml.stdout }}"
cdh.toml: "{{ raw_cdh_toml.stdout }}"
policy.rego: "{{ debug_raw_policy_rego.stdout }}"
Original file line number Diff line number Diff line change
@@ -1,3 +1,32 @@
algorithm = "sha256"
version = "0.1.0"

[data]
"aa.toml" = '''
[token_configs]
[token_configs.coco_as]
url = "https://kbs.{{ hub_domain }}"

[token_configs.kbs]
url = "https://kbs.{{ hub_domain }}"
cert = """{{ trustee_cert }}"""
'''

"cdh.toml" = '''
socket = 'unix:///run/confidential-containers/cdh.sock'
credentials = []

[kbc]
name = "cc_kbc"
url = "https://kbs.{{ hub_domain }}"
kbs_cert = """{{ trustee_cert }}"""


[image]
image_security_policy_uri = 'kbs:///default/security-policy/{{ security_policy_flavour }}'
'''

"policy.rego" = '''
package agent_policy

default AddARPNeighborsRequest := true
Expand Down Expand Up @@ -35,4 +64,5 @@ default UpdateEphemeralMountsRequest := true
default UpdateInterfaceRequest := true
default UpdateRoutesRequest := true
default WaitProcessRequest := true
default WriteStreamRequest := true
default WriteStreamRequest := true
'''
6 changes: 6 additions & 0 deletions charts/all/coco-kyverno-policies/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
apiVersion: v2
name: coco-kyverno-policies
description: Kyverno policies for CoCo cc_init_data injection and validation
type: application
version: 0.1.0
appVersion: "1.0.0"
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{{- range .Values.workloadNamespaces }}
---
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: propagate-initdata-to-{{ . }}
annotations:
argocd.argoproj.io/sync-wave: "1"
spec:
rules:
- name: copy-initdata
match:
any:
- resources:
kinds:
- ConfigMap
namespaces:
- {{ $.Values.initdataSourceNamespace }}
selector:
matchLabels:
coco.io/type: initdata
generate:
generateExisting: true
synchronize: true
apiVersion: v1
kind: ConfigMap
name: "{{ "{{" }}request.object.metadata.name{{ "}}" }}"
namespace: {{ . }}
clone:
namespace: {{ $.Values.initdataSourceNamespace }}
name: "{{ "{{" }}request.object.metadata.name{{ "}}" }}"
{{- end }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: inject-coco-initdata
annotations:
policies.kyverno.io/title: Inject CoCo InitData
policies.kyverno.io/category: Confidential Computing
policies.kyverno.io/severity: medium
policies.kyverno.io/subject: Pod,Deployment
policies.kyverno.io/description: >-
Injects cc_init_data annotation into pods with a kata runtime class
by reading from a ConfigMap specified via the coco.io/initdata-configmap
annotation. Kyverno autogen extends this to Deployments, StatefulSets,
DaemonSets, and Jobs automatically.
argocd.argoproj.io/sync-wave: "1"
pod-policies.kyverno.io/autogen-controllers: Deployment,StatefulSet,DaemonSet,Job
spec:
rules:
- name: inject-initdata
match:
any:
- resources:
kinds:
- Pod
operations:
- CREATE
preconditions:
all:
- key: "{{ "{{" }}request.object.spec.runtimeClassName || '' {{ "}}" }}"
operator: AnyIn
value: ["kata", "kata-cc", "kata-remote"]
- key: "{{ "{{" }}request.object.metadata.annotations.\"coco.io/initdata-configmap\" || '' {{ "}}" }}"
operator: NotEquals
value: ""
- key: "{{ "{{" }}request.object.metadata.annotations.\"io.katacontainers.config.hypervisor.cc_init_data\" || '' {{ "}}" }}"
operator: Equals
value: ""
context:
- name: initdata
configMap:
name: "{{ "{{" }}request.object.metadata.annotations.\"coco.io/initdata-configmap\"{{ "}}" }}"
namespace: "{{ "{{" }}request.namespace{{ "}}" }}"
mutate:
patchStrategicMerge:
metadata:
annotations:
io.katacontainers.config.hypervisor.cc_init_data: "{{ "{{" }}initdata.data.INITDATA{{ "}}" }}"
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: validate-initdata-configmap
annotations:
policies.kyverno.io/title: Validate InitData ConfigMap Required Fields
policies.kyverno.io/category: Confidential Computing
policies.kyverno.io/severity: medium
policies.kyverno.io/subject: ConfigMap
policies.kyverno.io/description: >-
Validates that ConfigMaps with label coco.io/type=initdata contain
all required fields with proper values.
argocd.argoproj.io/sync-wave: "1"
spec:
evaluation:
background:
enabled: true
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["configmaps"]
matchConditions:
- name: has-initdata-label
expression: >-
has(object.metadata.labels) &&
'coco.io/type' in object.metadata.labels &&
object.metadata.labels['coco.io/type'] == 'initdata'
variables:
- name: configData
expression: "object.data.orValue({})"
- name: allowedAlgorithms
expression: "['sha256', 'sha384', 'sha512']"
validationActions: ["Audit"]
validations:
- expression: "'version' in variables.configData && variables.configData['version'] != ''"
messageExpression: "'ConfigMap must contain a non-empty version field'"
- expression: "'algorithm' in variables.configData && variables.configData['algorithm'] in variables.allowedAlgorithms"
messageExpression: "'ConfigMap must contain an algorithm field with value sha256, sha384, or sha512, found: ' + ('algorithm' in variables.configData ? variables.configData['algorithm'] : 'missing')"
- expression: "'policy.rego' in variables.configData && variables.configData['policy.rego'] != ''"
messageExpression: "'ConfigMap must contain a non-empty policy.rego field'"
- expression: "'aa.toml' in variables.configData && variables.configData['aa.toml'] != ''"
messageExpression: "'ConfigMap must contain a non-empty aa.toml field'"
- expression: "'cdh.toml' in variables.configData && variables.configData['cdh.toml'] != ''"
messageExpression: "'ConfigMap must contain a non-empty cdh.toml field'"
5 changes: 5 additions & 0 deletions charts/all/coco-kyverno-policies/values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
workloadNamespaces:
- hello-openshift
- kbs-access

initdataSourceNamespace: imperative
4 changes: 2 additions & 2 deletions charts/coco-supported/hello-openshift/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

{{/*
Determine runtime class name based on cluster platform
Returns "kata-remote" for Azure/AWS, "kata-cc" for other platforms
Determine runtime class name based on cluster platform.
Cloud (Azure/AWS) uses "kata-remote" for peer-pods; baremetal uses "kata-cc" for confidential containers.
*/}}
{{- define "hello-openshift.runtimeClassName" -}}
{{- if or (eq .Values.global.clusterPlatform "Azure") (eq .Values.global.clusterPlatform "AWS") -}}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: insecure-policy
labels:
app: insecure-policy
spec:
replicas: 1
selector:
matchLabels:
app: insecure-policy
template:
metadata:
labels:
app: insecure-policy
annotations:
coco.io/initdata-configmap: initdata
spec:
runtimeClassName: {{ include "hello-openshift.runtimeClassName" . }}
containers:
- name: hello-openshift
image: quay.io/openshift/origin-hello-openshift
ports:
- containerPort: 8888
securityContext:
privileged: false
allowPrivilegeEscalation: false
runAsNonRoot: true
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
Loading
Loading