Chapter 7. Deploying Confidential Containers on Azure


You can deploy Confidential Containers on Microsoft Azure Cloud Computing Services after you deploy OpenShift sandboxed containers.

Important

Confidential Containers on Azure is a Technology Preview feature only. Technology Preview features are not supported with Red Hat production service level agreements (SLAs) and might not be functionally complete. Red Hat does not recommend using them in production. These features provide early access to upcoming product features, enabling customers to test functionality and provide feedback during the development process.

For more information about the support scope of Red Hat Technology Preview features, see Technology Preview Features Support Scope.

Cluster requirements

  • You have configured outbound connectivity for the pod VM subnet.
  • You have installed Red Hat OpenShift Container Platform 4.15 or later on the cluster where you are installing the Confidential compute attestation Operator.

You deploy Confidential Containers by performing the following steps:

  1. Install the Confidential compute attestation Operator.
  2. Create the route for Trustee.
  3. Enable the Confidential Containers feature gate.
  4. Create initdata.
  5. Update the peer pods config map.
  6. Optional: Customize the Kata agent policy.
  7. Delete the KataConfig custom resource (CR).
  8. Re-create the KataConfig CR.
  9. Create the Trustee authentication secret.
  10. Create the Trustee config map.
  11. Configure Trustee values, policies, and secrets.
  12. Create the KbsConfig CR.
  13. Verify the Trustee configuration.
  14. Verify the attestation process.

7.1. Installing the Confidential compute attestation Operator

You can install the Confidential compute attestation Operator on Azure by using the CLI.

Prerequisites

  • You have installed the OpenShift CLI (oc).
  • You have access to the cluster as a user with the cluster-admin role.

Procedure

  1. Create a trustee-namespace.yaml manifest file:

    apiVersion: v1
    kind: Namespace
    metadata:
      name: trustee-operator-system
    Copy to clipboard
  2. Create the trustee-operator-system namespace by running the following command:

    $ oc apply -f trustee-namespace.yaml
    Copy to clipboard
  3. Create a trustee-operatorgroup.yaml manifest file:

    apiVersion: operators.coreos.com/v1
    kind: OperatorGroup
    metadata:
      name: trustee-operator-group
      namespace: trustee-operator-system
    spec:
      targetNamespaces:
      - trustee-operator-system
    Copy to clipboard
  4. Create the operator group by running the following command:

    $ oc apply -f trustee-operatorgroup.yaml
    Copy to clipboard
  5. Create a trustee-subscription.yaml manifest file:

    apiVersion: operators.coreos.com/v1alpha1
    kind: Subscription
    metadata:
      name: trustee-operator-system
      namespace: trustee-operator-system
    spec:
      channel: stable
      installPlanApproval: Automatic
      name: trustee-operator
      source: redhat-operators
      sourceNamespace: openshift-marketplace
    Copy to clipboard
  6. Create the subscription by running the following command:

    $ oc apply -f trustee-subscription.yaml
    Copy to clipboard
  7. Verify that the Operator is correctly installed by running the following command:

    $ oc get csv -n trustee-operator-system
    Copy to clipboard

    This command can take several minutes to complete.

  8. Watch the process by running the following command:

    $ watch oc get csv -n trustee-operator-system
    Copy to clipboard

    Example output

    NAME                      DISPLAY                        PHASE
    trustee-operator.v0.3.0   Trustee Operator  0.3.0        Succeeded
    Copy to clipboard

7.2. Enabling the Confidential Containers feature gate

You must enable the Confidential Containers feature gate.

Prerequisites

  • You have subscribed to the OpenShift sandboxed containers Operator.

Procedure

  1. Create a cc-feature-gate.yaml manifest file:

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: osc-feature-gates
      namespace: openshift-sandboxed-containers-operator
    data:
      confidential: "true"
    Copy to clipboard
  2. Create the config map by running the following command:

    $ oc apply -f cc-feature-gate.yaml
    Copy to clipboard

7.3. Creating the route for Trustee

You can create a secure route with edge TLS termination for Trustee. External ingress traffic reaches the router pods as HTTPS and passes on to the Trustee pods as HTTP.

Prerequisites

  • You have installed the Confidential compute attestation Operator.

Procedure

  1. Create an edge route by running the following command:

    $ oc create route edge --service=kbs-service --port kbs-port \
      -n trustee-operator-system
    Copy to clipboard
    Note

    Note: Currently, only a route with a valid CA-signed certificate is supported. You cannot use a route with self-signed certificate.

  2. Set the TRUSTEE_HOST variable by running the following command:

    $ TRUSTEE_HOST=$(oc get route -n trustee-operator-system kbs-service \
      -o jsonpath={.spec.host})
    Copy to clipboard
  3. Verify the route by running the following command:

    $ echo $TRUSTEE_HOST
    Copy to clipboard

    Example output

    kbs-service-trustee-operator-system.apps.memvjias.eastus.aroapp.io
    Copy to clipboard

    Record this value for the peer pods config map.

7.4. About initdata

The initdata specification provides a flexible way to initialize a peer pod with sensitive or workload-specific data at runtime, avoiding the need to embed such data in the virtual machine (VM) image. This enhances security by reducing exposure of confidential information and improves flexibility by eliminating custom image builds. For example, initdata can include three configuration settings:

  • An X.509 certificate for secure communication.
  • A cryptographic key for authentication.
  • An optional Kata Agent policy.rego file to enforce runtime behavior when overriding the default Kata Agent policy.

You can apply an initdata configuration by using one of the following methods:

  • Globally by including it in the peer pods config map, setting a cluster-wide default for all pods.
  • For a specific pod when configuring a pod workload object, allowing customization for individual workloads.

    The io.katacontainers.config.runtime.cc_init_data annotation you specify when configuring a pod workload object overrides the global INITDATA setting in the peer pods config map for that specific pod. The Kata runtime handles this precedence automatically at pod creation time.

The initdata content configures the following components:

  • Attestation Agent (AA), which verifies the trustworthiness of the peer pod by sending evidence to the Trustee for attestation.
  • Confidential Data Hub (CDH), which manages secrets and secure data access within the peer pod VM.
  • Kata Agent, which enforces runtime policies and manages the lifecycle of the containers inside the pod VM.

7.5. Creating initdata

Create a TOML file with initdata and convert it to a Base64-encoded string. Use this string to specify the value in the peer pods config map, in the peer pod manifest, or in the verification-pod.yaml file.

Important

You must delete the kbs_cert setting if you configure insecure_http = true in the Trustee config map.

Procedure

  1. Create the initdata.toml configuration file:

    ```toml
    algorithm = "sha384"
    version = "0.1.0"
    
    [data]
    "aa.toml" = '''
    [token_configs]
    [token_configs.coco_as]
    url = '<url>:<port>' 1
    
    
    [token_configs.kbs]
    url = '<url>:<port>'
    cert = """
    -----BEGIN CERTIFICATE-----
    <kbs_certificate> 2
    -----END CERTIFICATE-----
    """
    '''
    
    "cdh.toml"  = '''
    socket = 'unix:///run/confidential-containers/cdh.sock'
    credentials = []
    
    [kbc]
    name = 'cc_kbc'
    url = '<url>:<port>'
    kbs_cert = """ 3
    -----BEGIN CERTIFICATE-----
    <kbs_certificate> 4
    -----END CERTIFICATE-----
    """
    '''
    
    "policy.rego" = ''' 5
    package agent_policy
    
    default AddARPNeighborsRequest := true
    default AddSwapRequest := true
    default CloseStdinRequest := true
    default CopyFileRequest := true
    default CreateContainerRequest := true
    default CreateSandboxRequest := true
    default DestroySandboxRequest := true
    default ExecProcessRequest := true
    default GetMetricsRequest := true
    default GetOOMEventRequest := true
    default GuestDetailsRequest := true
    default ListInterfacesRequest := true
    default ListRoutesRequest := true
    default MemHotplugByProbeRequest := true
    default OnlineCPUMemRequest := true
    default PauseContainerRequest := true
    default PullImageRequest := true
    default ReadStreamRequest := true
    default RemoveContainerRequest := true
    default RemoveStaleVirtiofsShareMountsRequest := true
    default ReseedRandomDevRequest := true
    default ResumeContainerRequest := true
    default SetGuestDateTimeRequest := true
    default SetPolicyRequest := true
    default SignalProcessRequest := true
    default StartContainerRequest := true
    default StartTracingRequest := true
    default StatsContainerRequest := true
    default StopTracingRequest := true
    default TtyWinResizeRequest := true
    default UpdateContainerRequest := true
    default UpdateEphemeralMountsRequest := true
    default UpdateInterfaceRequest := true
    default UpdateRoutesRequest := true
    default WaitProcessRequest := true
    default WriteStreamRequest := true
    '''
    ```
    Copy to clipboard
    1
    Specify the URL and port of the Trustee instance. If you configure the Trustee with insecure_http for testing purposes, use HTTP. Otherwise, use HTTPS. For production systems, avoid using insecure_http unless you configure your environment to handle TLS externally, for example, with a proxy.
    2
    Specify the Base64-encoded TLS certificate for the attestation agent. This is not required for testing purposes, but it is recommended for production systems.
    3
    Delete the kbs_cert setting if you configure insecure_http = true in the Trustee config map.
    4
    Specify the Base64-encoded TLS certificate for the Trustee instance.
    5
    Optional: You can specify a custom Kata Agent policy.
  2. Convert the initdata.toml file to a Base64-encoded string in a text file by running the following command:

    $ base64 -w0 initdata.toml > initdata.txt
    Copy to clipboard

7.6. Updating the peer pods config map

You must update the peer pods config map for Confidential Containers.

Note

Set Secure Boot to true to enable it by default. The default value is false, which presents a security risk.

Procedure

  1. Obtain the following values from your Azure instance:

    1. Retrieve and record the Azure resource group:

      $ AZURE_RESOURCE_GROUP=$(oc get infrastructure/cluster -o jsonpath='{.status.platformStatus.azure.resourceGroupName}') && echo "AZURE_RESOURCE_GROUP: \"$AZURE_RESOURCE_GROUP\""
      Copy to clipboard
    2. Retrieve and record the Azure VNet name:

      $ AZURE_VNET_NAME=$(az network vnet list --resource-group ${AZURE_RESOURCE_GROUP} --query "[].{Name:name}" --output tsv)
      Copy to clipboard

      This value is used to retrieve the Azure subnet ID.

    3. Retrieve and record the Azure subnet ID:

      $ AZURE_SUBNET_ID=$(az network vnet subnet list --resource-group ${AZURE_RESOURCE_GROUP} --vnet-name $AZURE_VNET_NAME --query "[].{Id:id} | [? contains(Id, 'worker')]" --output tsv) && echo "AZURE_SUBNET_ID: \"$AZURE_SUBNET_ID\""
      Copy to clipboard
    4. Retrieve and record the Azure network security group (NSG) ID:

      $ AZURE_NSG_ID=$(az network nsg list --resource-group ${AZURE_RESOURCE_GROUP} --query "[].{Id:id}" --output tsv) && echo "AZURE_NSG_ID: \"$AZURE_NSG_ID\""
      Copy to clipboard
    5. Retrieve and record the Azure region:

      $ AZURE_REGION=$(az group show --resource-group ${AZURE_RESOURCE_GROUP} --query "{Location:location}" --output tsv) && echo "AZURE_REGION: \"$AZURE_REGION\""
      Copy to clipboard
  2. Create a peer-pods-cm.yaml manifest file according to the following example:

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: peer-pods-cm
      namespace: openshift-sandboxed-containers-operator
    data:
      CLOUD_PROVIDER: "azure"
      VXLAN_PORT: "9000"
      AZURE_INSTANCE_SIZE: "Standard_DC2as_v5" 1
      AZURE_INSTANCE_SIZES: "Standard_DC2as_v5, Standard_DC4as_v5, Standard_DC8as_v5" 2
      AZURE_SUBNET_ID: "<azure_subnet_id>" 3
      AZURE_NSG_ID: "<azure_nsg_id>" 4
      PROXY_TIMEOUT: "5m"
      AZURE_IMAGE_ID: "<azure_image_id>" 5
      AZURE_REGION: "<azure_region>" 6
      AZURE_RESOURCE_GROUP: "<azure_resource_group>" 7
      PEERPODS_LIMIT_PER_NODE: "10" 8
      TAGS: "key1=value1,key2=value2" 9
      INITDATA: "<base64_encoded_initdata>" 10
      ENABLE_SECURE_BOOT: "true" 11
      DISABLECVM: "false"
    Copy to clipboard
    1
    The "Standard_DC2as_v5" value is the default if an instance size is not defined in the workload. Ensure the instance type supports the trusted environment. The default "Standard_DC2as_v5" value is for AMD SEV-SNP. If your TEE is Intel TDX, specify Standard_EC4eds_v5.
    2
    Lists all of the instance sizes you can specify when creating the pod. This allows you to define smaller instance sizes for workloads that need less memory and fewer CPUs or larger instance sizes for larger workloads. For Intel TDX, specify "Standard_EC4eds_v5, Standard_EC8eds_v5, Standard_EC16eds_v5".
    3
    Specify the AZURE_SUBNET_ID value that you retrieved.
    4
    Specify the AZURE_NSG_ID value that you retrieved.
    5
    Optional: By default, this value is populated when you run the KataConfig CR, using an Azure image ID based on your cluster credentials. If you create your own Azure image, specify the correct image ID.
    6
    Specify the AZURE_REGION value you retrieved.
    7
    Specify the AZURE_RESOURCE_GROUP value you retrieved.
    8
    Specify the maximum number of peer pods that can be created per node. The default value is 10.
    9
    You can configure custom tags as key:value pairs for pod VM instances to track peer pod costs or to identify peer pods in different clusters.
    10
    Specify the Base64-encoded string you created in the initdata.txt file.
    11
    Specify true to enable Secure Boot by default.
  3. Create the config map by running the following command:

    $ oc apply -f peer-pods-cm.yaml
    Copy to clipboard
  4. Restart the ds/osc-caa-ds daemon set by running the following command:

    $ oc set env ds/osc-caa-ds \
      -n openshift-sandboxed-containers-operator REBOOT="$(date)"
    Copy to clipboard

7.7. Customizing the Kata agent policy

The Kata agent policy is a security mechanism that controls agent API requests for pods running with the Kata runtime. Written in Rego and enforced by the Kata agent within the pod virtual machine (VM), this policy determines which operations are allowed or denied.

By default, the Kata agent policy disables the exec and log APIs, as they might transmit or receive unencrypted data through the control plane, which is insecure.

You can override the default policy with a custom one for specific use cases, such as development and testing where security is not a concern. For example, you might run in an environment where the control plane can be trusted. You can apply a custom policy in several ways:

  • Embedding it in the pod VM image.
  • Patching the peer pods config map.
  • Adding an annotation to the workload pod YAML.

For production systems, the preferred method is to use initdata to override the Kata agent policy. The following procedure applies a custom policy to an individual pod using the io.katacontainers.config.agent.policy annotation. The policy is provided in Base64-encoded Rego format. This approach overrides the default policy at pod creation without modifying the pod VM image.

Important

Enabling the exec or log APIs in Confidential Containers workloads might expose sensitive information. Do not enable these APIs in production environments.

Note

A custom policy replaces the default policy entirely. To modify only specific APIs, include the full policy and adjust the relevant rules.

Procedure

  1. Create a policy.rego file with your custom policy. The following example shows all configurable APIs, with exec and log enabled for demonstration:

    package agent_policy
    
    import future.keywords.in
    import input
    
    default CopyFileRequest := false
    default CreateContainerRequest := false
    default CreateSandboxRequest := true
    default DestroySandboxRequest := true
    default ExecProcessRequest := true  # Enabled to allow exec API
    default GetOOMEventRequest := true
    default GuestDetailsRequest := true
    default OnlineCPUMemRequest := true
    default PullImageRequest := true
    default ReadStreamRequest := true   # Enabled to allow log API
    default RemoveContainerRequest := true
    default RemoveStaleVirtiofsShareMountsRequest := true
    default SignalProcessRequest := true
    default StartContainerRequest := true
    default StatsContainerRequest := true
    default TtyWinResizeRequest := true
    default UpdateEphemeralMountsRequest := true
    default UpdateInterfaceRequest := true
    default UpdateRoutesRequest := true
    default WaitProcessRequest := true
    default WriteStreamRequest := false
    Copy to clipboard

    This policy enables the exec (ExecProcessRequest) and log (ReadStreamRequest) APIs. Adjust the true or false values to customize the policy further based on your needs.

  2. Convert the policy.rego file to a Base64-encoded string by running the following command:

    $ base64 -w0 policy.rego
    Copy to clipboard

    Save the output for use in the yaml file.

  3. Add the Base64-encoded policy to a my-pod.yaml pod specification file:

    apiVersion: v1
    kind: Pod
    metadata:
      name: <pod_name>
      annotations:
        io.katacontainers.config.agent.policy: <base64_encoded_policy>
    spec:
      runtimeClassName: kata-remote
      containers:
      - name: <container_name>
        image: registry.access.redhat.com/ubi9/ubi:latest
        command:
        - sleep
        - "36000"
        securityContext:
          privileged: false
          seccompProfile:
            type: RuntimeDefault
    Copy to clipboard
  4. Apply the pod manifest by running the following command:

    $ oc apply -f my-pod.yaml
    Copy to clipboard

7.8. Deleting the KataConfig custom resource

You can delete the KataConfig custom resource (CR) by using the command line.

Deleting the KataConfig CR removes the runtime and its related resources from your cluster.

Important

Deleting the KataConfig CR automatically reboots the worker nodes. The reboot can take from 10 to more than 60 minutes. Factors that impede reboot time are as follows:

  • A larger OpenShift Container Platform deployment with a greater number of worker nodes.
  • Activation of the BIOS and Diagnostics utility.
  • Deployment on a hard drive rather than an SSD.
  • Deployment on physical nodes such as bare metal, rather than on virtual nodes.
  • A slow CPU and network.

Prerequisites

  • You have installed the OpenShift CLI (oc).
  • You have access to the cluster as a user with the cluster-admin role.

Procedure

  1. Delete the KataConfig CR by running the following command:

    $ oc delete kataconfig example-kataconfig
    Copy to clipboard

    The OpenShift sandboxed containers Operator removes all resources that were initially created to enable the runtime on your cluster.

    Important

    When you delete the KataConfig CR, the CLI stops responding until all worker nodes reboot. You must wait for the deletion process to complete before performing the verification.

  2. Verify that the custom resource was deleted by running the following command:

    $ oc get kataconfig example-kataconfig
    Copy to clipboard

    Example output

    No example-kataconfig instances exist
    Copy to clipboard

Important

When uninstalling OpenShift sandboxed containers deployed using a cloud provider, you must delete all of the pods. Any remaining pod resources might result in an unexpected bill from your cloud provider.

7.9. Selecting a custom peer pod VM image

You can select a custom peer pod virtual machine (VM) image, tailored to your workload requirements by adding an annotation to the pod manifest. The custom image overrides the default image specified in the peer pods config map.

Prerequisites

  • The ID of the custom pod VM image to use, compatible with the cloud provider or hypervisor, is available.

Procedure

  1. Edit the pod manifest by adding the io.katacontainers.config.hypervisor.image annotation and save it in a pod-manifest.yaml file:

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-manifest
      annotations:
        io.katacontainers.config.hypervisor.image: "<custom_image_id>" 1
    spec:
      runtimeClassName: kata-remote 2
      containers:
      - name: <example_container> 3
        image: registry.access.redhat.com/ubi9/ubi:9.3
        command: ["sleep", "36000"]
    Copy to clipboard
    1
    Specify the custom peer pod image ID.
    2
    Ensure that the runtimeClassName field is set to kata-remote to create a peer pod.
    3
    Specify the container name.
  2. Create the pod by running the following command:

    $ oc apply -f pod-manifest.yaml
    Copy to clipboard

7.10. Re-creating the KataConfig custom resource

You must re-create the KataConfig custom resource (CR) for Confidential Containers.

Important

Creating the KataConfig CR automatically reboots the worker nodes. The reboot can take from 10 to more than 60 minutes. Factors that impede reboot time are as follows:

  • A larger OpenShift Container Platform deployment with a greater number of worker nodes.
  • Activation of the BIOS and Diagnostics utility.
  • Deployment on a hard disk drive rather than an SSD.
  • Deployment on physical nodes such as bare metal, rather than on virtual nodes.
  • A slow CPU and network.

Prerequisites

  • You have access to the cluster as a user with the cluster-admin role.

Procedure

  1. Create an example-kataconfig.yaml manifest file according to the following example:

    apiVersion: kataconfiguration.openshift.io/v1
    kind: KataConfig
    metadata:
      name: example-kataconfig
    spec:
      enablePeerPods: true
      logLevel: info
    #  kataConfigPoolSelector:
    #    matchLabels:
    #      <label_key>: '<label_value>' 1
    Copy to clipboard
    1
    Optional: If you have applied node labels to install kata-remote on specific nodes, specify the key and value, for example, cc: 'true'.
  2. Create the KataConfig CR by running the following command:

    $ oc apply -f example-kataconfig.yaml
    Copy to clipboard

    The new KataConfig CR is created and installs kata-remote as a runtime class on the worker nodes.

    Wait for the kata-remote installation to complete and the worker nodes to reboot before verifying the installation.

  3. Monitor the installation progress by running the following command:

    $ watch "oc describe kataconfig | sed -n /^Status:/,/^Events/p"
    Copy to clipboard

    When the status of all workers under kataNodes is installed and the condition InProgress is False without specifying a reason, the kata-remote is installed on the cluster.

  4. Verify the daemon set by running the following command:

    $ oc get -n openshift-sandboxed-containers-operator ds/osc-caa-ds
    Copy to clipboard
  5. Verify the runtime classes by running the following command:

    $ oc get runtimeclass
    Copy to clipboard

    Example output

    NAME             HANDLER          AGE
    kata             kata             152m
    kata-remote      kata-remote      152m
    Copy to clipboard

7.11. Creating the Trustee authentication secret

You must create the authentication secret for Trustee.

Prerequisites

  • You have installed the OpenShift CLI (oc).
  • You have access to the cluster as a user with the cluster-admin role.

Procedure

  1. Create a private key by running the following command:

    $ openssl genpkey -algorithm ed25519 > privateKey
    Copy to clipboard
  2. Create a public key by running the following command:

    $ openssl pkey -in privateKey -pubout -out publicKey
    Copy to clipboard
  3. Create a secret by running the following command:

    $ oc create secret generic kbs-auth-public-key --from-file=publicKey -n trustee-operator-system
    Copy to clipboard
  4. Verify the secret by running the following command:

    $ oc get secret -n trustee-operator-system
    Copy to clipboard

7.12. Creating the Trustee config map

You must create the config map to configure the Trustee server.

Note

The following configuration example turns off security features to enable demonstration of Technology Preview features. It is not meant for a production environment.

Prerequisites

  • You have created a route for Trustee.

Procedure

  1. Create a kbs-config-cm.yaml manifest file:

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: kbs-config-cm
      namespace: trustee-operator-system
    data:
      kbs-config.json: |
        {
          "insecure_http" : true,
          "sockets": ["0.0.0.0:8080"],
          "auth_public_key": "/etc/auth-secret/publicKey",
          "attestation_token_config": {
            "attestation_token_type": "CoCo"
          },
          "repository_config": {
            "type": "LocalFs",
            "dir_path": "/opt/confidential-containers/kbs/repository"
          },
          "as_config": {
            "work_dir": "/opt/confidential-containers/attestation-service",
            "policy_engine": "opa",
            "attestation_token_broker": "Simple",
              "attestation_token_config": {
              "duration_min": 5
              },
            "rvps_config": {
              "store_type": "LocalJson",
              "store_config": {
                "file_path": "/opt/confidential-containers/rvps/reference-values/reference-values.json"
              }
             }
          },
          "policy_engine_config": {
            "policy_path": "/opt/confidential-containers/opa/policy.rego"
          }
        }
    Copy to clipboard
  2. Create the config map by running the following command:

    $ oc apply -f kbs-config-cm.yaml
    Copy to clipboard

7.13. Configuring Trustee values, policies, and secrets

You can configure the following values, policies, and secrets for Trustee:

  • Optional: Reference values for the Reference Value Provider Service.
  • Optional: Attestation policy.
  • Provisioning Certificate Caching Service for Intel Trust Domain Extensions (TDX).
  • Optional: Secret for custom keys for Trustee clients.
  • Optional: Secret for container image signature verification.
  • Container image signature verification policy. This policy is mandatory. If you do not use container image signature verification, you must create a policy that does not verify signatures.
  • Resource access policy.

7.13.1. Configuring reference values

You can configure reference values for the Reference Value Provider Service (RVPS) by specifying the trusted digests of your hardware platform.

The client collects measurements from the running software, the Trusted Execution Environment (TEE) hardware and firmware and it submits a quote with the claims to the Attestation Server. These measurements must match the trusted digests registered to the Trustee. This process ensures that the confidential VM (CVM) is running the expected software stack and has not been tampered with.

Procedure

  1. Create an rvps-configmap.yaml manifest file:

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: rvps-reference-values
      namespace: trustee-operator-system
    data:
      reference-values.json: |
        [ 1
        ]
    Copy to clipboard
    1
    Specify the trusted digests for your hardware platform if required. Otherwise, leave it empty.
  2. Create the RVPS config map by running the following command:

    $ oc apply -f rvps-configmap.yaml
    Copy to clipboard

7.13.2. Creating an attestation policy

You can create an attestation policy that overrides the default attestation policy.

Procedure

  1. Create an attestation-policy.yaml manifest file according to the following example:

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: attestation-policy
      namespace: trustee-operator-system
    data:
      default.rego: |
        package policy 1
        import future.keywords.every
    
        default allow = false
    
        allow {
          every k, v in input {
              judge_field(k, v)
          }
        }
    
        judge_field(input_key, input_value) {
          has_key(data.reference, input_key)
          reference_value := data.reference[input_key]
          match_value(reference_value, input_value)
        }
    
        judge_field(input_key, input_value) {
          not has_key(data.reference, input_key)
        }
    
        match_value(reference_value, input_value) {
          not is_array(reference_value)
          input_value == reference_value
        }
    
        match_value(reference_value, input_value) {
          is_array(reference_value)
          array_include(reference_value, input_value)
        }
    
        array_include(reference_value_array, input_value) {
          reference_value_array == []
        }
    
        array_include(reference_value_array, input_value) {
          reference_value_array != []
          some i
          reference_value_array[i] == input_value
        }
    
        has_key(m, k) {
          _ = m[k]
        }
    Copy to clipboard
    1
    The attestation policy follows the Open Policy Agent specification. In this example, the attestation policy compares the claims provided in the attestation report to the reference values registered in the RVPS database. The attestation process is successful only if all the values match.
  2. Create the attestation policy config map by running the following command:

    $ oc apply -f attestation-policy.yaml
    Copy to clipboard

7.13.3. Configuring PCCS for TDX

If you use Intel Trust Domain Extensions (TDX), you must configure Trustee to use the Provisioning Certificate Caching Service (PCCS).

The PCCS retrieves the Provisioning Certification Key (PCK) certificates and caches them in a local database.

Important

Do not use the public Intel PCCS service. Use a local caching service on-premise or on the public cloud.

Procedure

  1. Create a tdx-config.yaml manifest file according to the following example:

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: tdx-config
      namespace: trustee-operator-system
    data:
      sgx_default_qcnl.conf: | \
          {
            "collateral_service": "https://api.trustedservices.intel.com/sgx/certification/v4/",
            "pccs_url": "<pccs_url>" 1
          }
    Copy to clipboard
    1
    Specify the PCCS URL, for example, https://localhost:8081/sgx/certification/v4/.
  2. Create the TDX config map by running the following command:

    $ oc apply -f tdx-config.yaml
    Copy to clipboard

7.13.4. Creating a secret with custom keys for clients

You can create a secret that contains one or more custom keys for Trustee clients.

In this example, the kbsres1 secret has two entries (key1, key2), which the clients retrieve. You can add additional secrets according to your requirements by using the same format.

Prerequisites

  • You have created one or more custom keys.

Procedure

  • Create a secret for the custom keys according to the following example:

    $ oc apply secret generic kbsres1 \
      --from-literal key1=<custom_key1> \ 1
      --from-literal key2=<custom_key2> \
      -n trustee-operator-system
    Copy to clipboard
    1
    Specify a custom key.

    The kbsres1 secret is specified in the spec.kbsSecretResources key of the KbsConfig custom resource.

7.13.5. Creating a secret for container image signature verification

If you use container image signature verification, you must create a secret that contains the public container image signing key.

The Confidential compute attestation Operator uses the secret to verify the signature, ensuring that only trusted and authenticated container images are deployed in your environment.

You can use Red Hat Trusted Artifact Signer or other tools to sign container images.

Procedure

  1. Create a secret for container image signature verification by running the following command:

    $ oc apply secret generic <type> \ 1
      --from-file=<tag>=./<public_key_file> \ 2
      -n trustee-operator-system
    Copy to clipboard
    1
    Specify the KBS secret type, for example, img-sig.
    2
    Specify the secret tag, for example, pub-key, and the public container image signing key.
  2. Record the <type> value. You must add this value to the spec.kbsSecretResources key when you create the KbsConfig custom resource.

7.13.6. Creating the container image signature verification policy

You create the container image signature verification policy because signature verification is always enabled. If this policy is missing, the pods will not start.

If you are not using container image signature verification, you create the policy without signature verification.

For more information, see containers-policy.json 5.

Procedure

  1. Create a security-policy-config.json file according to the following examples:

    • Without signature verification:

      {
        "default": [
        {
          "type": "insecureAcceptAnything"
        }],
        "transports": {}
      }
      Copy to clipboard
    • With signature verification:

      {
        "default": [
            {
            "type": "insecureAcceptAnything"
            }
        ],
        "transports": {
            "<transport>": { 1
                "<registry>/<image>": 2
                [
                    {
                        "type": "sigstoreSigned",
                        "keyPath": "kbs:///default/<type>/<tag>" 3
                    }
                ]
            }
        }
      }
      Copy to clipboard
      1
      Specify the image repository for transport, for example, "docker":. For more information, see containers-transports 5.
      2
      Specify the container registry and image, for example, "quay.io/my-image".
      3
      Specify the type and tag of the container image signature verification secret that you created, for example, img-sig/pub-key.
  2. Create the security policy by running the following command:

    $ oc apply secret generic security-policy \
      --from-file=osc=./<security-policy-config.json> \
      -n trustee-operator-system
    Copy to clipboard

    Do not alter the secret type, security-policy, or the key, osc.

    The security-policy secret is specified in the spec.kbsSecretResources key of the KbsConfig custom resource.

7.13.7. Creating the resource access policy

You configure the resource access policy for the Trustee policy engine. This policy determines which resources Trustee can access.

Note

The Trustee policy engine is different from the Attestation Service policy engine, which determines the validity of TEE evidence.

Procedure

  1. Create a resourcepolicy-configmap.yaml manifest file:

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: resource-policy
      namespace: trustee-operator-system
    data:
      policy.rego: | 1
        package policy 2
        default allow = false
        allow {
          input["tee"] != "sample"
        }
    Copy to clipboard
    1
    The name of the resource policy, policy.rego, must match the resource policy defined in the Trustee config map.
    2
    The resource policy follows the Open Policy Agent specification. This example allows the retrieval of all resources when the TEE is not the sample attester.
  2. Create the resource policy config map by running the following command:

    $ oc apply -f resourcepolicy-configmap.yaml
    Copy to clipboard

7.14. Creating the KbsConfig custom resource

You create the KbsConfig custom resource (CR) to launch Trustee.

Then, you check the Trustee pods and pod logs to verify the configuration.

Procedure

  1. Create a kbsconfig-cr.yaml manifest file:

    apiVersion: confidentialcontainers.org/v1alpha1
    kind: KbsConfig
    metadata:
      labels:
        app.kubernetes.io/name: kbsconfig
        app.kubernetes.io/instance: kbsconfig
        app.kubernetes.io/part-of: trustee-operator
        app.kubernetes.io/managed-by: kustomize
        app.kubernetes.io/created-by: trustee-operator
      name: kbsconfig
      namespace: trustee-operator-system
    spec:
      kbsConfigMapName: kbs-config-cm
      kbsAuthSecretName: kbs-auth-public-key
      kbsDeploymentType: AllInOneDeployment
      kbsRvpsRefValuesConfigMapName: rvps-reference-values
      kbsSecretResources: ["kbsres1", "security-policy", "<type>"] 1
      kbsResourcePolicyConfigMapName: resource-policy
    # tdxConfigSpec:
    #   kbsTdxConfigMapName: tdx-config 2
    # kbsAttestationPolicyConfigMapName: attestation-policy 3
    # kbsServiceType: <service_type> 4
    Copy to clipboard
    1
    Optional: Specify the type value of the container image signature verification secret if you created the secret, for example, img-sig. If you did not create the secret, set the kbsSecretResources value to ["kbsres1", "security-policy"].
    2
    Uncomment tdxConfigSpec.kbsTdxConfigMapName: tdx-config for Intel Trust Domain Extensions.
    3
    Uncomment kbsAttestationPolicyConfigMapName: attestation-policy if you create a customized attestation policy.
    4
    Uncomment kbsServiceType: <service_type> if you create a service type, other than the default ClusterIP service, to expose applications within the cluster external traffic. You can specify NodePort, LoadBalancer, or ExternalName.
  2. Create the KbsConfig CR by running the following command:

    $ oc apply -f kbsconfig-cr.yaml
    Copy to clipboard

7.15. Verifying the Trustee configuration

You verify the Trustee configuration by checking the Trustee pods and logs.

Procedure

  1. Set the default project by running the following command:

    $ oc project trustee-operator-system
    Copy to clipboard
  2. Check the Trustee pods by running the following command:

    $ oc get pods -n trustee-operator-system
    Copy to clipboard

    Example output

    NAME                                                   READY   STATUS    RESTARTS   AGE
    trustee-deployment-8585f98449-9bbgl                    1/1     Running   0          22m
    trustee-operator-controller-manager-5fbd44cd97-55dlh   2/2     Running   0          59m
    Copy to clipboard

  3. Set the POD_NAME environmental variable by running the following command:

    $ POD_NAME=$(oc get pods -l app=kbs -o jsonpath='{.items[0].metadata.name}' -n trustee-operator-system)
    Copy to clipboard
  4. Check the pod logs by running the following command:

    $ oc logs -n trustee-operator-system $POD_NAME
    Copy to clipboard

    Example output

    [2024-05-30T13:44:24Z INFO  kbs] Using config file /etc/kbs-config/kbs-config.json
    [2024-05-30T13:44:24Z WARN  attestation_service::rvps] No RVPS address provided and will launch a built-in rvps
    [2024-05-30T13:44:24Z INFO  attestation_service::token::simple] No Token Signer key in config file, create an ephemeral key and without CA pubkey cert
    [2024-05-30T13:44:24Z INFO  api_server] Starting HTTPS server at [0.0.0.0:8080]
    [2024-05-30T13:44:24Z INFO  actix_server::builder] starting 12 workers
    [2024-05-30T13:44:24Z INFO  actix_server::server] Tokio runtime found; starting in existing Tokio runtime
    Copy to clipboard

7.16. Verifying the attestation process

You can verify the attestation process by creating a test pod and retrieving its secret.

Important

This procedure is an example to verify that attestation is working. Do not write sensitive data to standard I/O because the data can be captured by using a memory dump. Only data written to memory is encrypted.

By default, the Kata agent policy, embedded in the pod virtual machine (VM) image, disables the exec and log APIs for a Confidential Containers pod. This policy prevents the cluster admin from executing processes inside the pod to exfiltrate sensitive data while also blocking accidental writes of sensitive data to standard I/O.

In a test scenario, you can override the restriction at runtime by adding a policy annotation to the pod. For Technology Preview, runtime policy annotations are not verified by remote attestation.

Prerequisites

  • You have created a route if the Trustee server and the test pod are not running in the same cluster.

Procedure

  1. Create a verification-pod.yaml manifest file:

    apiVersion: v1
    kind: Pod
    metadata:
      name: ocp-cc-pod
      labels:
        app: ocp-cc-pod
      annotations:
        io.katacontainers.config.agent.policy: <base64_encoded_policy> 1
        io.katacontainers.config.runtime.cc_init_data: <base64_initdata> 2
    spec:
      runtimeClassName: kata-remote
      containers:
        - name: skr-openshift
          image: registry.access.redhat.com/ubi9/ubi:9.3
          command:
            - sleep
            - "36000"
          securityContext:
            privileged: false
            seccompProfile:
              type: RuntimeDefault
    Copy to clipboard
    1
    This pod annotation overrides the default agent policy that prevents sensitive data from being written to standard I/O.
    2
    This pod annotation overrides the Base64-encoded string of the initdata.toml file set globally in the peer pods config map.

    If you specify both the io.katacontainers.config.agent.policy annotation and the io.katacontainers.config.runtime.cc_init_data annotation with an agent policy, the initdata annotation takes precedence over the agent policy annotation.

  2. Create the pod by running the following command:

    $ oc create -f verification-pod.yaml
    Copy to clipboard
  3. Connect to the Bash shell of the ocp-cc-pod by running the following command:

    $ oc exec -it ocp-cc-pod -- bash
    Copy to clipboard
  4. Fetch the pod secret by running the following command:

    $ curl http://127.0.0.1:8006/cdh/resource/default/kbsres1/key1
    Copy to clipboard

    Example output

    res1val1
    Copy to clipboard

    The Trustee server returns the secret only if the attestation is successful.

Back to top
Red Hat logoGithubredditYoutubeTwitter

Learn

Try, buy, & sell

Communities

About Red Hat Documentation

We help Red Hat users innovate and achieve their goals with our products and services with content they can trust. Explore our recent updates.

Making open source more inclusive

Red Hat is committed to replacing problematic language in our code, documentation, and web properties. For more details, see the Red Hat Blog.

About Red Hat

We deliver hardened solutions that make it easier for enterprises to work across platforms and environments, from the core datacenter to the network edge.

Theme

© 2025 Red Hat, Inc.