diff --git a/templates/attestation-certificate.yaml b/templates/attestation-certificate.yaml new file mode 100644 index 0000000..1ae174d --- /dev/null +++ b/templates/attestation-certificate.yaml @@ -0,0 +1,23 @@ +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: attestation-token + namespace: trustee-operator-system + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/sync-wave: "3" +spec: + secretName: attestation-cert + duration: 8760h # 1 year + renewBefore: 720h # 30 days + commonName: {{ .Values.attestation.commonName | default "kbs-trustee-operator-system" }} + subject: + organizations: + - {{ .Values.attestation.organization | default "Red Hat" }} + privateKey: + algorithm: ECDSA + size: 256 + issuerRef: + name: attestation-self-signed-issuer + kind: Issuer \ No newline at end of file diff --git a/templates/attestation-issuer.yaml b/templates/attestation-issuer.yaml new file mode 100644 index 0000000..f469a63 --- /dev/null +++ b/templates/attestation-issuer.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: attestation-self-signed-issuer + namespace: trustee-operator-system + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/sync-wave: "2" +spec: + selfSigned: {} diff --git a/templates/attestation-policy.yaml b/templates/attestation-policy.yaml new file mode 100644 index 0000000..c208c07 --- /dev/null +++ b/templates/attestation-policy.yaml @@ -0,0 +1,79 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: attestation-policy + namespace: trustee-operator-system + annotations: + argocd.argoproj.io/sync-wave: "4" +data: + default_cpu.rego: | + package policy + + import rego.v1 + default executables := 33 + default hardware := 97 + default configuration := 36 + + ## miminimal but reliable attestation policy + ## hardware and firmware changes. This is not in our control. It's up to the user to update acceptable measurements + ## In conjuction with verification with the service provider. + ## currently setup for azure vTPM + + + ##### Azure vTPM SNP + executables := 3 if { + # input.azsnpvtpm.measurement in data.reference.measurement + input.azsnpvtpm.tpm.pcr03 in data.reference.snp_pcr03 + input.azsnpvtpm.tpm.pcr08 in data.reference.snp_pcr08 + input.azsnpvtpm.tpm.pcr09 in data.reference.snp_pcr09 + input.azsnpvtpm.tpm.pcr11 in data.reference.snp_pcr11 + input.azsnpvtpm.tpm.pcr12 in data.reference.snp_pcr12 + } + + hardware := 2 if { + # Check the reported TCB to validate the ASP FW + # input.azsnpvtpm.reported_tcb_bootloader in data.reference.tcb_bootloader + # input.azsnpvtpm.reported_tcb_microcode in data.reference.tcb_microcode + # input.azsnpvtpm.reported_tcb_snp in data.reference.tcb_snp + # input.azsnpvtpm.reported_tcb_tee in data.reference.tcb_tee + input.azsnpvtpm + } + + # For the 'configuration' trust claim 2 stands for + # "The configuration is a known and approved config." + # + # For this, we compare all the configuration fields. + configuration := 2 if { + # input.azsnpvtpm.platform_smt_enabled in data.reference.smt_enabled + # input.azsnpvtpm.platform_tsme_enabled in data.reference.tsme_enabled + # input.azsnpvtpm.policy_abi_major in data.reference.abi_major + # input.azsnpvtpm.policy_abi_minor in data.reference.abi_minor + # input.azsnpvtpm.policy_single_socket in data.reference.single_socket + # input.azsnpvtpm.policy_smt_allowed in data.reference.smt_allowed + input.azsnpvtpm + } + + ##### Azure vTPM TDX + executables := 3 if { + input.aztdxvtpm.tpm.pcr03 in data.reference.tdx_pcr03 + input.aztdxvtpm.tpm.pcr08 in data.reference.tdx_pcr08 + input.aztdxvtpm.tpm.pcr09 in data.reference.tdx_pcr09 + input.aztdxvtpm.tpm.pcr11 in data.reference.tdx_pcr11 + input.aztdxvtpm.tpm.pcr12 in data.reference.tdx_pcr12 + } + + hardware := 2 if { + # Check the quote is a TDX quote signed by Intel SGX Quoting Enclave + input.aztdxvtpm.quote.header.tee_type == "81000000" + input.aztdxvtpm.quote.header.vendor_id == "939a7233f79c4ca9940a0db3957f0607" + + # Check TDX Module version and its hash. Also check OVMF code hash. + # input.aztdxvtpm.quote.body.mr_seam in data.reference.mr_seam + # input.aztdxvtpm.quote.body.tcb_svn in data.reference.tcb_svn + # input.aztdxvtpm.quote.body.mr_td in data.reference.mr_td + } + + configuration := 2 if { + # input.aztdxvtpm.quote.body.xfam in data.reference.xfam + input.aztdxvtpm + } \ No newline at end of file diff --git a/templates/attestation-status-eso.yaml b/templates/attestation-status-eso.yaml new file mode 100644 index 0000000..111b9f1 --- /dev/null +++ b/templates/attestation-status-eso.yaml @@ -0,0 +1,23 @@ +{{- if ne .Values.global.secretStore.backend "none" }} +--- +apiVersion: "external-secrets.io/v1beta1" +kind: ExternalSecret +metadata: + annotations: + argocd.argoproj.io/sync-wave: "1" + name: attestation-status-eso + namespace: trustee-operator-system +spec: + refreshInterval: 15s + secretStoreRef: + name: {{ .Values.secretStore.name }} + kind: {{ .Values.secretStore.kind }} + data: + target: + name: attestation-status + template: + type: generic + dataFrom: + - extract: + key: {{ .Values.global.coco.attestationStatus }} +{{- end }} \ No newline at end of file diff --git a/templates/certificate.yaml b/templates/certificate.yaml index 1c8b2ec..4bc5444 100644 --- a/templates/certificate.yaml +++ b/templates/certificate.yaml @@ -5,6 +5,7 @@ metadata: namespace: imperative annotations: argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/sync-wave: "3" spec: secretName: kbs-tls-self-signed duration: 8760h # 1 year diff --git a/templates/cosign-eso.yaml b/templates/cosign-eso.yaml new file mode 100644 index 0000000..bca1478 --- /dev/null +++ b/templates/cosign-eso.yaml @@ -0,0 +1,23 @@ +{{- if and (ne .Values.global.secretStore.backend "none") (eq .Values.global.coco.securityPolicyFlavour "signed") }} +--- +apiVersion: "external-secrets.io/v1beta1" +kind: ExternalSecret +metadata: + annotations: + argocd.argoproj.io/sync-wave: "1" + name: cosigned-eso + namespace: trustee-operator-system +spec: + refreshInterval: 15s + secretStoreRef: + name: {{ .Values.secretStore.name }} + kind: {{ .Values.secretStore.kind }} + data: + target: + name: cosign-keys + template: + type: generic + dataFrom: + - extract: + key: {{ .Values.global.coco.cosignKeys }} +{{- end }} \ No newline at end of file diff --git a/templates/issuer.yaml b/templates/issuer.yaml index e64bd30..3180a37 100644 --- a/templates/issuer.yaml +++ b/templates/issuer.yaml @@ -5,6 +5,7 @@ metadata: namespace: imperative annotations: argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/sync-wave: "2" spec: selfSigned: {} diff --git a/templates/kbs-config-map.yaml b/templates/kbs-config-map.yaml index df4072a..e05ef3c 100644 --- a/templates/kbs-config-map.yaml +++ b/templates/kbs-config-map.yaml @@ -3,6 +3,8 @@ kind: ConfigMap metadata: name: kbs-config namespace: trustee-operator-system + annotations: + argocd.argoproj.io/sync-wave: "4" data: kbs-config.toml: | [http_server] @@ -11,31 +13,36 @@ data: private_key = "/etc/https-key/tls.key" certificate = "/etc/https-cert/tls.crt" [admin] - insecure_api = true + insecure_api = false auth_public_key = "/etc/auth-secret/publicKey" [attestation_token] - insecure_key = true + insecure_key = false attestation_token_type = "CoCo" + trusted_certs_paths = ["/etc/attestation-cert/tls.crt"] # Check for location in cert (based on key generated) [attestation_service] type = "coco_as_builtin" work_dir = "/opt/confidential-containers/attestation-service" policy_engine = "opa" - [attestation_service.attestation_token_broker] - type = "Ear" - policy_dir = "/opt/confidential-containers/attestation-service/policies" + [attestation_service.attestation_token_broker] + type = "Ear" + policy_dir = "/opt/confidential-containers/attestation-service/policies" - [attestation_service.attestation_token_config] - duration_min = 5 + [attestation_service.attestation_token_broker.signer] + key_path = "/etc/attestation-cert/tls.key" + cert_path = "/etc/attestation-cert/tls.crt" - [attestation_service.rvps_config] - type = "BuiltIn" + [attestation_service.attestation_token_config] + duration_min = 5 - [attestation_service.rvps_config.storage] - type = "LocalJson" - file_path = "/opt/confidential-containers/rvps/reference-values/reference-values.json" + [attestation_service.rvps_config] + type = "BuiltIn" + + [attestation_service.rvps_config.storage] + type = "LocalJson" + file_path = "/opt/confidential-containers/rvps/reference-values/reference-values.json" [[plugins]] name = "resource" diff --git a/templates/kbs-route.yaml b/templates/kbs-route.yaml index 8e6aa92..3b04a75 100644 --- a/templates/kbs-route.yaml +++ b/templates/kbs-route.yaml @@ -1,10 +1,12 @@ # Single cluster deploy don't use the route yet. ---- +--- apiVersion: route.openshift.io/v1 kind: Route metadata: name: kbs namespace: trustee-operator-system + annotations: + argocd.argoproj.io/sync-wave: "8" spec: subdomain: kbs port: diff --git a/templates/kbs.yaml b/templates/kbs.yaml index cce4648..95fb974 100644 --- a/templates/kbs.yaml +++ b/templates/kbs.yaml @@ -3,6 +3,8 @@ kind: KbsConfig metadata: name: kbsconfig namespace: trustee-operator-system + annotations: + argocd.argoproj.io/sync-wave: "7" spec: kbsConfigMapName: kbs-config kbsAuthSecretName: kbs-auth-public-key @@ -12,10 +14,18 @@ spec: {{- range .Values.kbs.secretResources }} - "{{ .name }}" {{- end }} + {{- if eq .Values.global.coco.securityPolicyFlavour "signed" }} + - "cosign-keys" + {{- end }} - "security-policy" + - "attestation-status" kbsHttpsKeySecretName: kbs-https-key kbsHttpsCertSecretName: kbs-https-certificate kbsResourcePolicyConfigMapName: resource-policy + kbsAttestationPolicyConfigMapName: attestation-policy + kbsAttestationCertSecretName: attestation-cert + kbsAttestationKeySecretName: attestation-cert + # TDX specific configuration (optional) {{- if .Values.kbs.tdx.enabled }} diff --git a/templates/pcrs-eso.yaml b/templates/pcrs-eso.yaml new file mode 100644 index 0000000..b3f1712 --- /dev/null +++ b/templates/pcrs-eso.yaml @@ -0,0 +1,20 @@ +--- +apiVersion: "external-secrets.io/v1beta1" +kind: ExternalSecret +metadata: + annotations: + argocd.argoproj.io/sync-wave: "1" + name: pcrs-eso + namespace: trustee-operator-system +spec: + refreshInterval: 15s + secretStoreRef: + name: {{ .Values.secretStore.name }} + kind: {{ .Values.secretStore.kind }} + target: + name: pcr-stash + template: + type: generic + dataFrom: + - extract: + key: 'secret/data/hub/pcrStash' diff --git a/templates/push-secret.yaml b/templates/push-secret.yaml index c970b27..aa36fcd 100644 --- a/templates/push-secret.yaml +++ b/templates/push-secret.yaml @@ -4,6 +4,8 @@ kind: PushSecret metadata: name: push-certs namespace: imperative + annotations: + argocd.argoproj.io/sync-wave: "5" spec: updatePolicy: Replace # Policy to overwrite existing secrets in the provider on sync deletionPolicy: Delete # the provider' secret will be deleted if the PushSecret is deleted diff --git a/templates/reference-values.yaml b/templates/reference-values.yaml index 7d62d19..6d8e6eb 100644 --- a/templates/reference-values.yaml +++ b/templates/reference-values.yaml @@ -1,25 +1,13 @@ +{{- if not (eq .Values.global.coco.secured true) }} apiVersion: v1 kind: ConfigMap metadata: - annotations: - argocd.argoproj.io/sync-wave: "1" name: rvps-reference-values namespace: trustee-operator-system + annotations: + argocd.argoproj.io/sync-wave: "4" data: reference-values.json: | - [ - ] - -# No reference values yet - # [ - # { - # "name": "sample.svn", - # "expired": "2025-01-01T00:00:00Z", - # "hash-value": [ - # { - # "alg": "sha256", - # "value": "1" - # } - # ] - # } - # ] + [ + ] +{{ end }} \ No newline at end of file diff --git a/templates/resource-policy.yaml b/templates/resource-policy.yaml index 963e166..008139d 100644 --- a/templates/resource-policy.yaml +++ b/templates/resource-policy.yaml @@ -3,7 +3,12 @@ kind: ConfigMap metadata: name: resource-policy namespace: trustee-operator-system + annotations: + argocd.argoproj.io/sync-wave: "4" data: policy.rego: | package policy - default allow = true \ No newline at end of file + default allow = false + allow { + input["submods"]["cpu0"]["ear.status"] == "affirming" + } \ No newline at end of file diff --git a/templates/rvps-values-policies.yaml b/templates/rvps-values-policies.yaml new file mode 100644 index 0000000..8c6fd6f --- /dev/null +++ b/templates/rvps-values-policies.yaml @@ -0,0 +1,69 @@ +{{ if eq .Values.global.coco.secured true }} +--- +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: rvps-policy + annotations: + argocd.argoproj.io/sync-wave: "6" +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: rvps-policy-cp + spec: + remediationAction: enforce + severity: medium + object-templates-raw: | + {{`{{- $pcr8Hash := fromConfigMap "imperative" "initdata" "PCR8_HASH" -}}`}} + {{`{{- $secretData := (lookup "v1" "Secret" "trustee-operator-system" "pcr-stash").data.json | base64dec | fromJson -}}`}} + {{`{{- $pcr03 := $secretData.measurements.sha256.pcr03 -}}`}} + {{`{{- $pcr09 := $secretData.measurements.sha256.pcr09 -}}`}} + {{`{{- $pcr11 := $secretData.measurements.sha256.pcr11 -}}`}} + {{`{{- $pcr12 := $secretData.measurements.sha256.pcr12 -}}`}} + {{`{{- $referenceValues := list (dict "name" "snp_pcr03" "expiration" "2027-12-12T00:00:00Z" "value" (list $pcr03)) (dict "name" "tdx_pcr03" "expiration" "2027-12-12T00:00:00Z" "value" (list $pcr03)) (dict "name" "snp_pcr08" "expiration" "2027-12-12T00:00:00Z" "value" (list $pcr8Hash)) (dict "name" "tdx_pcr08" "expiration" "2027-12-12T00:00:00Z" "value" (list $pcr8Hash)) (dict "name" "snp_pcr09" "expiration" "2027-12-12T00:00:00Z" "value" (list $pcr09)) (dict "name" "tdx_pcr09" "expiration" "2027-12-12T00:00:00Z" "value" (list $pcr09)) (dict "name" "snp_pcr11" "expiration" "2027-12-12T00:00:00Z" "value" (list $pcr11)) (dict "name" "tdx_pcr11" "expiration" "2027-12-12T00:00:00Z" "value" (list $pcr11)) (dict "name" "snp_pcr12" "expiration" "2027-12-12T00:00:00Z" "value" (list $pcr12)) (dict "name" "tdx_pcr12" "expiration" "2027-12-12T00:00:00Z" "value" (list $pcr12)) -}}`}} + - complianceType: mustonlyhave + objectDefinition: + apiVersion: v1 + kind: ConfigMap + metadata: + name: rvps-reference-values + namespace: trustee-operator-system + data: + reference-values.json: '{{`{{ toJson $referenceValues }}`}}' +--- +apiVersion: policy.open-cluster-management.io/v1 +kind: PlacementBinding +metadata: + name: rvps-placement-binding + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/sync-wave: "6" +placementRef: + name: rvps-placement-rule + kind: PlacementRule + apiGroup: apps.open-cluster-management.io +subjects: + - name: rvps-policy + kind: Policy + apiGroup: policy.open-cluster-management.io +--- +apiVersion: apps.open-cluster-management.io/v1 +kind: PlacementRule +metadata: + name: rvps-placement-rule + annotations: + argocd.argoproj.io/sync-wave: "6" +spec: + clusterConditions: + - status: 'True' + type: ManagedClusterConditionAvailable + clusterSelector: + matchLabels: + local-cluster: "true" +--- +{{ end }} \ No newline at end of file diff --git a/templates/securityPolicy-eso.yaml b/templates/securityPolicy-eso.yaml index cb5ea7c..0462150 100644 --- a/templates/securityPolicy-eso.yaml +++ b/templates/securityPolicy-eso.yaml @@ -19,5 +19,5 @@ spec: type: generic dataFrom: - extract: - key: {{ .Values.kbs.securityPolicy }} + key: {{ .Values.global.coco.securityPolicy }} {{- end }} \ No newline at end of file diff --git a/templates/tdx-config.yaml b/templates/tdx-config.yaml index d2ab516..d854da1 100644 --- a/templates/tdx-config.yaml +++ b/templates/tdx-config.yaml @@ -4,6 +4,8 @@ kind: ConfigMap metadata: name: tdx-config namespace: trustee-operator-system + annotations: + argocd.argoproj.io/sync-wave: "4" data: sgx_default_qcnl.conf: | { diff --git a/templates/tls-cert-eso.yaml b/templates/tls-cert-eso.yaml index 0e62533..9f88009 100644 --- a/templates/tls-cert-eso.yaml +++ b/templates/tls-cert-eso.yaml @@ -4,7 +4,7 @@ apiVersion: "external-secrets.io/v1beta1" kind: ExternalSecret metadata: annotations: - argocd.argoproj.io/sync-wave: "1" + argocd.argoproj.io/sync-wave: "6" name: tls-cert-eso namespace: trustee-operator-system spec: diff --git a/templates/tls-key-eso.yaml b/templates/tls-key-eso.yaml index 308f9b0..7199d18 100644 --- a/templates/tls-key-eso.yaml +++ b/templates/tls-key-eso.yaml @@ -4,7 +4,7 @@ apiVersion: "external-secrets.io/v1beta1" kind: ExternalSecret metadata: annotations: - argocd.argoproj.io/sync-wave: "1" + argocd.argoproj.io/sync-wave: "6" name: tls-key-eso namespace: trustee-operator-system spec: diff --git a/values.yaml b/values.yaml index 5965423..8f70490 100644 --- a/values.yaml +++ b/values.yaml @@ -1,19 +1,28 @@ --- -# Secret store configuration (overridden by values-global.yaml) +# Secret store configuration +# Default configuration is vault secretStore: - name: "" - kind: "" + name: "vault-backend" + kind: "ClusterSecretStore" global: secretStore: # Secret store backend, typically overridden by values-global.yaml backend: "" + # must be global as needs to be propagated to ansible playbooks. + # SHOULD be set in values-global.yaml + coco: + securityPolicy: secret/data/hub/securityPolicyConfig + securityPolicyFlavour: "insecure" # insecure, signed or reject is expected. + attestationStatus: secret/data/hub/attestationStatus + secured: false # true or false. If true, the cluster will be secured. If false, the cluster will be insecure. # KBS (Key Broker Service) configuration kbs: # Security policy is an expected secret and is required to be pushed into the KBS - securityPolicy: secret/data/hub/securityPolicyConfig + # presumes security policy flavour is signed + cosignKeys: secret/data/hub/coSignKeys # Public key for authentication by a user with the KBS management API publicKey: secret/data/hub/kbsPublicKey @@ -21,13 +30,12 @@ kbs: # Dynamic secret resources list - add new secrets here # Each entry generates an ESO and gets added to kbsSecretResources # requires a name of the secret and a key to retrieve using ESO, typically from vault. - secretResources: [] + secretResources: # Example: - # - name: "kbsres1" - # key: "secret/data/hub/kbsres1" - # - name: "passphrase" - # key: "secret/data/hub/passphrase" - + - name: "kbsres1" + key: "secret/data/hub/kbsres1" + - name: "passphrase" + key: "secret/data/hub/passphrase" # Intel TDX (Trust Domain Extensions) configuration tdx: # Enable TDX attestation support @@ -36,3 +44,9 @@ kbs: # For Azure: Use https://global.acccache.azure.net/sgx/certification/v4/ # For bare metal/Intel: Use https://api.trustedservices.intel.com/sgx/certification/v4/ collateralService: "https://api.trustedservices.intel.com/sgx/certification/v4/" +# Attestation token certificate configuration +# Used when secretStore.backend is "none" (cert-manager generates certs) +attestation: + commonName: "kbs-trustee-operator-system" + organization: "Red Hat" +