diff --git a/templates/attestation-policy.yaml b/templates/attestation-policy.yaml index 6219e6c..acd38b2 100644 --- a/templates/attestation-policy.yaml +++ b/templates/attestation-policy.yaml @@ -10,85 +10,309 @@ data: package policy import rego.v1 + + # This policy validates multiple TEE platforms + # The policy is meant to capture the TCB requirements + # for confidential containers. + + # This policy is used to generate an EAR Appraisal. + # Specifically it generates an AR4SI result. + # More information on AR4SI can be found at + # + + # For the `executables` trust claim, the value 33 stands for + # "Runtime memory includes executables, scripts, files, and/or + # objects which are not recognized." default executables := 33 + + # For the `hardware` trust claim, the value 97 stands for + # "A Verifier does not recognize an Attester's hardware or + # firmware, but it should be recognized." default hardware := 97 + + # For the `configuration` trust claim the value 36 stands for + # "Elements of the configuration relevant to security are + # unavailable to the Verifier." default configuration := 36 + # For the `filesystem` trust claim, the value 0 stands for + # "No assertion." + default file_system := 0 + + # For the `instance_identity` trust claim, the value 0 stands for + # "No assertion." + default instance_identity := 0 + + # For the `runtime_opaque` trust claim, the value 0 stands for + # "No assertion." + default runtime_opaque := 0 + + # For the `storage_opaque` trust claim, the value 0 stands for + # "No assertion." + default storage_opaque := 0 + + # For the `sourced_data` trust claim, the value 0 stands for + # "No assertion." + default sourced_data := 0 + trust_claims := { "executables": executables, "hardware": hardware, "configuration": configuration, + "file-system": file_system, + "instance-identity": instance_identity, + "runtime-opaque": runtime_opaque, + "storage-opaque": storage_opaque, + "sourced-data": sourced_data, } + extensions := [ + {"name": "ear.trustee.identifiers", + "key": -18, + "value": { + "validated": validated_identifiers + } + } + ] + + # Validated identifiers are information that describes a workload + # that are bound to the hardware evidence via attestation + # and bound to the workload by the guest runtime. + validated_identifiers := object.union_n([ + container_images_id, + container_uids_id, + ]) + + # Use list comprehension to parse all of the images specified in the policy. + container_images := [img | + container := input["init_data_claims"]["agent_policy_claims"]["containers"][_] + img := container["OCI"]["Annotations"]["io.kubernetes.cri.image-name"] + ] + + container_images_id := {"container_images": container_images} if { + count(container_images) > 0 + } else := {} + + # UIDs + container_uids := [uid | + container := input["init_data_claims"]["agent_policy_claims"]["containers"][_] + uid := container["OCI"]["Process"]["User"]["UID"] + ] + + container_uids_id := {"container_uids": container_uids} if { + count(container_uids) > 0 + } else := {} + ##### Azure vTPM SNP executables := 3 if { - input["az-snp-vtpm"].tpm.pcr03 in query_reference_value("snp_pcr03") - input["az-snp-vtpm"].tpm.pcr08 in query_reference_value("snp_pcr08") - input["az-snp-vtpm"].tpm.pcr09 in query_reference_value("snp_pcr09") - input["az-snp-vtpm"].tpm.pcr11 in query_reference_value("snp_pcr11") - input["az-snp-vtpm"].tpm.pcr12 in query_reference_value("snp_pcr12") + input["az_snp_vtpm"] + + input["az_snp_vtpm"].measurement in query_reference_value("measurement") + input["az_snp_vtpm"].tpm.pcr03 in query_reference_value("snp_pcr03") + input["az_snp_vtpm"].tpm.pcr08 in query_reference_value("snp_pcr08") + input["az_snp_vtpm"].tpm.pcr09 in query_reference_value("snp_pcr09") + input["az_snp_vtpm"].tpm.pcr11 in query_reference_value("snp_pcr11") + input["az_snp_vtpm"].tpm.pcr12 in query_reference_value("snp_pcr12") } hardware := 2 if { - input["az-snp-vtpm"] + input["az_snp_vtpm"] + + # Check the reported TCB to validate the ASP FW + count(query_reference_value("tcb_bootloader")) > 0 + input["az_snp_vtpm"].reported_tcb_bootloader in query_reference_value("tcb_bootloader") + input["az_snp_vtpm"].reported_tcb_microcode in query_reference_value("tcb_microcode") + input["az_snp_vtpm"].reported_tcb_snp in query_reference_value("tcb_snp") + input["az_snp_vtpm"].reported_tcb_tee in query_reference_value("tcb_tee") + } + + # Fallback: TCB reference values not collected for Azure yet + else := 3 if { + input["az_snp_vtpm"] } + # 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["az-snp-vtpm"] + input["az_snp_vtpm"] + + count(query_reference_value("smt_enabled")) > 0 + input["az_snp_vtpm"].platform_smt_enabled in query_reference_value("smt_enabled") + input["az_snp_vtpm"].platform_tsme_enabled in query_reference_value("tsme_enabled") + input["az_snp_vtpm"].policy_abi_major in query_reference_value("abi_major") + input["az_snp_vtpm"].policy_abi_minor in query_reference_value("abi_minor") + input["az_snp_vtpm"].policy_single_socket in query_reference_value("single_socket") + input["az_snp_vtpm"].policy_smt_allowed in query_reference_value("smt_allowed") + } + + # Fallback: configuration reference values not available + else := 3 if { + input["az_snp_vtpm"] } ##### Azure vTPM TDX executables := 3 if { - input["az-tdx-vtpm"].tpm.pcr03 in query_reference_value("tdx_pcr03") - input["az-tdx-vtpm"].tpm.pcr08 in query_reference_value("tdx_pcr08") - input["az-tdx-vtpm"].tpm.pcr09 in query_reference_value("tdx_pcr09") - input["az-tdx-vtpm"].tpm.pcr11 in query_reference_value("tdx_pcr11") - input["az-tdx-vtpm"].tpm.pcr12 in query_reference_value("tdx_pcr12") + input["az_tdx_vtpm"] + + input["az_tdx_vtpm"].tpm.pcr03 in query_reference_value("tdx_pcr03") + input["az_tdx_vtpm"].tpm.pcr08 in query_reference_value("tdx_pcr08") + input["az_tdx_vtpm"].tpm.pcr09 in query_reference_value("tdx_pcr09") + input["az_tdx_vtpm"].tpm.pcr11 in query_reference_value("tdx_pcr11") + input["az_tdx_vtpm"].tpm.pcr12 in query_reference_value("tdx_pcr12") } hardware := 2 if { - input["az-tdx-vtpm"].quote.header.tee_type == "81000000" - input["az-tdx-vtpm"].quote.header.vendor_id == "939a7233f79c4ca9940a0db3957f0607" + input["az_tdx_vtpm"] + + # Check the quote is a TDX quote signed by Intel SGX Quoting Enclave + input["az_tdx_vtpm"].quote.header.tee_type == "81000000" + input["az_tdx_vtpm"].quote.header.vendor_id == "939a7233f79c4ca9940a0db3957f0607" + + # Check OVMF code hash + count(query_reference_value("mr_td")) > 0 + input["az_tdx_vtpm"].quote.body.mr_td in query_reference_value("mr_td") + + # Check TCB status (covers quote.body.tcb_svn claim check) + input["az_tdx_vtpm"].tcb_status == "UpToDate" + } + + # Fallback: mr_td reference values not collected for Azure yet + else := 3 if { + input["az_tdx_vtpm"] + + input["az_tdx_vtpm"].quote.header.tee_type == "81000000" + input["az_tdx_vtpm"].quote.header.vendor_id == "939a7233f79c4ca9940a0db3957f0607" + input["az_tdx_vtpm"].tcb_status == "UpToDate" } configuration := 2 if { - input["az-tdx-vtpm"] + input["az_tdx_vtpm"] + + count(query_reference_value("xfam")) > 0 + input["az_tdx_vtpm"].quote.body.xfam in query_reference_value("xfam") + } + + # Fallback: xfam reference values not available + else := 3 if { + input["az_tdx_vtpm"] } - ##### Baremetal TDX + ##### Bare Metal TDX + # CoCo pattern extension: uses init_data for runtime config verification executables := 3 if { input["tdx"] + + # Check the kernel, initrd, and cmdline (including dmverity parameters) measurements + input["tdx"].quote.body.rtmr_1 in query_reference_value("rtmr_1") + input["tdx"].quote.body.rtmr_2 in query_reference_value("rtmr_2") + + # CoCo pattern: init_data check (runtime configuration hash) input.init_data in query_reference_value("init_data") } - hardware := 2 if { input["tdx"] } - configuration := 2 if { input["tdx"] } - ##### Baremetal SNP - executables := 3 if { - input["snp"] + # Support for deployments without rtmr_2 reference values + else := 4 if { + input["tdx"] + + # Check the kernel, initrd measurements + input["tdx"].quote.body.rtmr_1 in query_reference_value("rtmr_1") + + # CoCo pattern: init_data check (runtime configuration hash) input.init_data in query_reference_value("init_data") } - hardware := 2 if { input["snp"] } - configuration := 2 if { input["snp"] } - {{- if .Values.kbs.gpu.enabled }} - ##### GPU Attestation (NVIDIA H100/H200) — CPU-class evidence with GPU data hardware := 2 if { - input["snp"] - input["gpu"] + input["tdx"] + + # Check the quote is a TDX quote signed by Intel SGX Quoting Enclave + input["tdx"].quote.header.tee_type == "81000000" + input["tdx"].quote.header.vendor_id == "939a7233f79c4ca9940a0db3957f0607" + + # Check OVMF code hash + input["tdx"].quote.body.mr_td in query_reference_value("mr_td") + + # Check TCB status (covers quote.body.tcb_svn claim check) + input["tdx"].tcb_status == "UpToDate" + + # Check collateral expiration status + input["tdx"].collateral_expiration_status == "0" + } + + configuration := 2 if { + input["tdx"] + + # Check the TD has the expected attributes (e.g., debug not enabled) and features. + input["tdx"].quote.body.td_attributes.debug == false + input["tdx"].quote.body.xfam in query_reference_value("xfam") + } + + # Fallback: enforce debug disabled even without xfam reference values + else := 3 if { + input["tdx"] + + input["tdx"].quote.body.td_attributes.debug == false + + # CoCo pattern: init_data check (runtime configuration hash) + input.init_data in query_reference_value("init_data") } + ##### Bare Metal SNP + # CoCo pattern extension: uses init_data for runtime config verification executables := 3 if { input["snp"] - input["gpu"] + + # Check the launch measurement + input["snp"].measurement in query_reference_value("snp_launch_measurement") + + # CoCo pattern: init_data check (runtime configuration hash) input.init_data in query_reference_value("init_data") } + hardware := 2 if { + input["snp"] + + # Check the reported TCB to validate the ASP FW + input["snp"].reported_tcb_bootloader in query_reference_value("snp_bootloader") + input["snp"].reported_tcb_microcode in query_reference_value("snp_microcode") + input["snp"].reported_tcb_snp in query_reference_value("snp_snp_svn") + input["snp"].reported_tcb_tee in query_reference_value("snp_tee_svn") + } + + # 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["snp"] - input["gpu"] + + input["snp"].policy_debug_allowed == false + input["snp"].policy_migrate_ma == false + input["snp"].platform_smt_enabled == query_reference_value("snp_smt_enabled") + input["snp"].platform_tsme_enabled == query_reference_value("snp_tsme_enabled") + input["snp"].policy_abi_major == query_reference_value("snp_guest_abi_major") + input["snp"].policy_abi_minor == query_reference_value("snp_guest_abi_minor") + input["snp"].policy_single_socket == query_reference_value("snp_single_socket") + input["snp"].policy_smt_allowed == query_reference_value("snp_smt_allowed") + } + + # For the `configuration` trust claim 3 stands for + # "The configuration includes or exposes no known + # vulnerabilities." + # + # In this check, we do not specifically check every + # configuration value, but we make sure that some key + # configurations (like debug_allowed) are set correctly. + else := 3 if { + input["snp"] + + input["snp"].policy_debug_allowed == false + input["snp"].policy_migrate_ma == false + + # CoCo pattern: init_data check (runtime configuration hash) + input.init_data in query_reference_value("init_data") } - {{- end }} {{- if .Values.kbs.gpu.enabled }} default_gpu.rego: | package policy @@ -98,33 +322,102 @@ data: default hardware := 97 default executables := 33 default configuration := 36 + default file_system := 0 + default instance_identity := 0 + default runtime_opaque := 0 + default storage_opaque := 0 + default sourced_data := 0 trust_claims := { "executables": executables, "hardware": hardware, "configuration": configuration, + "file-system": file_system, + "instance-identity": instance_identity, + "runtime-opaque": runtime_opaque, + "storage-opaque": storage_opaque, + "sourced-data": sourced_data, } + extensions := [ + {"name": "ear.trustee.identifiers", + "key": -18, + "value": { + "validated": validated_identifiers + } + } + ] + + # Validated identifiers are information that describes a workload + # that are bound to the hardware evidence via attestation + # and bound to the workload by the guest runtime. + validated_identifiers := object.union_n([ + container_images_id, + container_uids_id, + ]) + + # Use list comprehension to parse all of the images specified in the policy. + container_images := [img | + container := input["init_data_claims"]["agent_policy_claims"]["containers"][_] + img := container["OCI"]["Annotations"]["io.kubernetes.cri.image-name"] + ] + + container_images_id := {"container_images": container_images} if { + count(container_images) > 0 + } else := {} + + # UIDs + container_uids := [uid | + container := input["init_data_claims"]["agent_policy_claims"]["containers"][_] + uid := container["OCI"]["Process"]["User"]["UID"] + ] + + container_uids_id := {"container_uids": container_uids} if { + count(container_uids) > 0 + } else := {} + + # GPUs verified by NRAS hardware := 2 if { - input.nvidia - input.nvidia["x-nvidia-gpu-attestation-report-cert-chain"]["x-nvidia-cert-status"] == "valid" - input.nvidia["x-nvidia-gpu-attestation-report-parsed"] - input.nvidia["x-nvidia-gpu-attestation-report-signature-verified"] - input.nvidia["x-nvidia-gpu-arch-check"] + input["nvidia"] + + input["nvidia"]["x-nvidia-gpu-attestation-report-cert-chain"]["x-nvidia-cert-ocsp-status"] == "good" + input["nvidia"]["x-nvidia-gpu-attestation-report-cert-chain"]["x-nvidia-cert-status"] == "valid" + + input["nvidia"]["x-nvidia-gpu-attestation-report-cert-chain-fwid-match"] + input["nvidia"]["x-nvidia-gpu-attestation-report-parsed"] + input["nvidia"]["x-nvidia-gpu-attestation-report-signature-verified"] + + input["nvidia"]["x-nvidia-gpu-arch-check"] } configuration := 2 if { - input.nvidia.secboot - input.nvidia.dbgstat == "disabled" + input["nvidia"].secboot + input["nvidia"].dbgstat == "disabled" + input["nvidia"]["x-nvidia-gpu-vbios-version"] in query_reference_value("allowed_vbios_versions") + input["nvidia"]["x-nvidia-gpu-driver-version"] in query_reference_value("allowed_driver_versions") + } + + else := 3 if { + input["nvidia"].secboot + input["nvidia"].dbgstat == "disabled" } executables := 3 if { - input.nvidia["x-nvidia-gpu-driver-rim-fetched"] - input.nvidia["x-nvidia-gpu-driver-rim-schema-validated"] - input.nvidia["x-nvidia-gpu-driver-rim-signature-verified"] - input.nvidia["x-nvidia-gpu-vbios-rim-fetched"] - input.nvidia["x-nvidia-gpu-vbios-rim-schema-validated"] - input.nvidia["x-nvidia-gpu-vbios-rim-signature-verified"] - input.nvidia.measres == "success" - } -{{- end }} \ No newline at end of file + input["nvidia"]["x-nvidia-gpu-vbios-rim-cert-chain"]["x-nvidia-cert-ocsp-status"] == "good" + input["nvidia"]["x-nvidia-gpu-vbios-rim-cert-chain"]["x-nvidia-cert-status"] == "valid" + + input["nvidia"]["x-nvidia-gpu-driver-rim-fetched"] + input["nvidia"]["x-nvidia-gpu-driver-rim-measurements-available"] + input["nvidia"]["x-nvidia-gpu-driver-rim-schema-validated"] + input["nvidia"]["x-nvidia-gpu-driver-rim-signature-verified"] + input["nvidia"]["x-nvidia-gpu-driver-rim-version-match"] + + input["nvidia"]["x-nvidia-gpu-vbios-rim-fetched"] + input["nvidia"]["x-nvidia-gpu-vbios-rim-measurements-available"] + input["nvidia"]["x-nvidia-gpu-vbios-rim-schema-validated"] + input["nvidia"]["x-nvidia-gpu-vbios-rim-signature-verified"] + input["nvidia"]["x-nvidia-gpu-vbios-rim-version-match"] + + input["nvidia"].measres == "success" + } +{{- end }} diff --git a/templates/resource-policy.yaml b/templates/resource-policy.yaml index 47c7f2a..3c2b583 100644 --- a/templates/resource-policy.yaml +++ b/templates/resource-policy.yaml @@ -11,14 +11,41 @@ data: import rego.v1 default allow = false + default hardware_failing = false allow if { - not any_not_affirming count(input.submods) > 0 + not executable_failing + not configuration_failing + not hardware_failing + } + executable_failing if { + some _, submod in input.submods + executables := submod["ear.trustworthiness-vector"]["executables"] + not in_affirming_range(executables) } - any_not_affirming if { + configuration_failing if { some _, submod in input.submods - submod["ear.status"] != "affirming" + configuration := submod["ear.trustworthiness-vector"]["configuration"] + not in_affirming_range(configuration) + } + + # Hardware trust claims are enforced by default. For TDX, no additional + # RVPS values are needed. For SNP, you must provide hardware-specific + # RVPS values (tcb_bootloader, tcb_microcode, tcb_snp, tcb_tee) from + # your environment. If these values are not available, you can disable + # hardware enforcement by setting kbs.resourcePolicy.enforceHardware: false +{{- if .Values.kbs.resourcePolicy.enforceHardware }} + hardware_failing if { + some _, submod in input.submods + hardware := submod["ear.trustworthiness-vector"]["hardware"] + not in_affirming_range(hardware) + } +{{- end }} + + in_affirming_range(val) if { + val >= 2 + val <= 31 } \ No newline at end of file diff --git a/values.yaml b/values.yaml index bb40e6c..e6d06be 100644 --- a/values.yaml +++ b/values.yaml @@ -77,6 +77,15 @@ kbs: # See docs/firmware-reference-values.md in coco-pattern for collection workflow enabled: false + # Resource policy configuration + resourcePolicy: + # Enforce hardware trust claims in resource policy + # When true: requires hardware claim >= 2 (affirming range) + # When false: allows resource access even if hardware claim fails + # Note: For SNP, hardware enforcement requires TCB reference values + # (tcb_bootloader, tcb_microcode, tcb_snp, tcb_tee) in RVPS + enforceHardware: true + # Attestation token certificate configuration # Used when secretStore.backend is "none" (cert-manager generates certs) attestation: