Configure the Trustee operator

Now that the Trustee operator is installed, we need to set it up.

Reminder: You can assume that everything running in the trustee-operator-sysem namespace should run in a trusted, separate environment. Because of the limitations of this ARO workshop, it is not possible to set up two clusters.

Create the required keys

In order to simplify the flow, we will create here all the keys necessary to securely run Trustee.

More specifically, these keys are:

  • admin authentication keys

  • https keys

  • attestation token key

Of course the user can also bring his own existing keys. The exact purpose of these keys will be explained as they are used in the following sections.

mkdir -p trustee
cd trustee

# Admin authentication keys
openssl genpkey -algorithm ed25519 > privateKey
openssl pkey -in privateKey -pubout -out publicKey

# HTTPS keys
DOMAIN=$(oc get ingress.config/cluster -o jsonpath='{.spec.domain}')
NS=trustee-operator-system
ROUTE_NAME=kbs-service

CN_NAME=kbs-trustee-operator-system
ORG_NAME=my_org

ROUTE="${ROUTE_NAME}-${NS}.${DOMAIN}"
echo "ROUTE: $ROUTE"
echo ""

openssl req -x509 -nodes -days 365 \
  -newkey rsa:2048 \
  -keyout tls.key \
  -out tls.crt \
  -subj "/CN=${CN_NAME}/O=${ORG_NAME}$" \
  -addext "subjectAltName=DNS:${ROUTE}"

# Attestation token
openssl ecparam -name prime256v1 -genkey -noout -out token.key
openssl req -new -x509 -key token.key -out token.crt -days 365 \
	-subj "/CN=${CN_NAME}/O=${ORG_NAME}"

Create a route for Trustee

Create a secure route with passthrough TLS termination for Trustee. External ingress traffic does not get decrypted by the router but forwarded directly to Trustee pod. It is also possible to create an edge route, but then the connection between Trustee and OCP router will be in plain text (http). In this workshop, we will use passthrough.

Upload the previously self-signed certificates in the Trustee namespace:

oc create secret generic kbs-https-certificate --from-file=tls.crt -n trustee-operator-system

oc create secret generic kbs-https-key --from-file=tls.key -n trustee-operator-system

TRUSTEE_CERT=$(cat tls.crt)

clear
echo "$TRUSTEE_CERT"
echo ""
echo "ROUTE: $ROUTE"
echo ""
oc get secrets -n trustee-operator-system | grep kbs-https

Create the route and set TRUSTEE_HOST, which together with TRUSTEE_CERT will be used later when configuring the initdata.

HTTP="https://"
oc create route passthrough kbs-service \
  --service=kbs-service \
  --port=kbs-port \
  -n trustee-operator-system

TRUSTEE_ROUTE="$(oc get route -n trustee-operator-system kbs-service \
  -o jsonpath={.spec.host})"

TRUSTEE_HOST=${HTTP}${TRUSTEE_ROUTE}

echo $TRUSTEE_HOST

Example output:

https://kbs-service-trustee-operator-system.apps.rs01nyk5.eastus.aroapp.io

Create the Trustee authentication secret

Such key is not really used when the Trustee is run as Openshift operator, but it is needed in order to start the trustee application.

Create a secret with the public key:

oc create secret generic kbs-auth-public-key --from-file=./publicKey -n trustee-operator-system

Create the attestation token key

Secret retrieval works in a two-step authentication:

  • First, a confidential container sends the reference values to the Trustee

  • The Trustee checks the reference values and if they match, generates and signs an attestation token

  • The attestation token is then sent to the CoCo pod

  • The CoCo pod can now use this token without doing the attestation again to request a secret, and the same token can be reused multiple times until it expires.

The attestation token has to therefore be signed by the Trustee, otherwise a malicious CoCo pod can sign its own fake attestation token and request secrets from the Trustee bypassing the whole reference values check.

Upload the attestation token key and certificate as secret:

oc create secret generic attestation-token --from-file=token.crt --from-file=token.key -n trustee-operator-system

Create Trustee ConfigMap

Create the and apply the Trustee kbs-configmap.yaml ConfigMap. Among the others, in this ConfigMap we will enable HTTPS, authenticated admin API and signed attestation token verification.

cat > kbs-configmap.yaml <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: kbs-config-cm
  namespace: trustee-operator-system
data:
  kbs-config.toml: |
    [http_server]
    sockets = ["0.0.0.0:8080"]
    insecure_http = false
    private_key = "/etc/https-key/tls.key"
    certificate = "/etc/https-cert/tls.crt"

    [admin]
    insecure_api = false
    auth_public_key = "/etc/auth-secret/publicKey"

    [attestation_token]
    insecure_key = false
    trusted_certs_paths = ["/opt/confidential-containers/kbs/repository/default/attestation-token/token.crt"]
    attestation_token_type = "CoCo"

    [attestation_service.attestation_token_broker.signer]
    key_path = "/opt/confidential-containers/kbs/repository/default/attestation-token/token.key"
    cert_path = "/opt/confidential-containers/kbs/repository/default/attestation-token/token.crt"

    [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_config]
    duration_min = 5

    [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"
    type = "LocalFs"
    dir_path = "/opt/confidential-containers/kbs/repository"

    [policy_engine]
    policy_path = "/opt/confidential-containers/opa/policy.rego"
EOF

clear
cat kbs-configmap.yaml
oc apply -f kbs-configmap.yaml

The initdata policy

About initdata

This step will be also useful in the OSC operator setup. However, since this policy is part of the attestation and we need the expected PCR to be added in the Trustee reference values, we will create the file now.

The initdata specification provides a flexible way to initialize a CoCo 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.

We 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 under metadata:annotations: in the pod yaml spec 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.

Create the initdata policy

In this section, we will create the initdata that will be later set up as global in the OSC operator configmap.

In this policy, we will set the Trustee address in the internal CVM components. We will use TRUSTEE_HOST defined previously when configuring the Trustee.

Why do we add this to the Trustee reference values? Remember that this policy is actually added into the OSC operator Configmap, or injected as pod annotation at deployment time. Both scenarios are happening outside the trusted zone, meaning a rogue admin can simply change these values and connect the CoCo pod to a different Trustee, use insecure http and so on.
cat > initdata.toml <<EOF
algorithm = "sha256"
version = "0.1.0"

[data]
"aa.toml" = '''
[token_configs]
[token_configs.coco_as]
url = "${TRUSTEE_HOST}"

[token_configs.kbs]
url = "${TRUSTEE_HOST}"
cert = """
${TRUSTEE_CERT}
"""
'''

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

[kbc]
name = "cc_kbc"
url = "${TRUSTEE_HOST}"
kbs_cert = """
${TRUSTEE_CERT}
"""
'''

"policy.rego" = '''
package agent_policy

import future.keywords.in
import future.keywords.if

default AddARPNeighborsRequest := true
default AddSwapRequest := true
default CloseStdinRequest := true
default CopyFileRequest := true
default CreateContainerRequest := true
default CreateSandboxRequest := true
default DestroySandboxRequest := 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 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

# Enable logs, to see the output of curl
default ReadStreamRequest := true

# Restrict exec
default ExecProcessRequest := false

ExecProcessRequest if {
    input_command = concat(" ", input.process.Args)
    some allowed_command in policy_data.allowed_commands
    input_command == allowed_command
}

# Add allowed commands for exec
policy_data := {
  "allowed_commands": [
        "curl -s http://127.0.0.1:8006/cdh/resource/default/kbsres1/key1",
        "cat /sealed/secret-value/key2"
  ]
}

'''
EOF

clear
cat initdata.toml

A note on policy.rego

Under policy.rego, you can specify a custom Kata Agent policy. The default policy allows all API calls. For production environments, set ReadStreamRequest and ExecProcessRequest to false to disable the oc exec and oc log APIs, preventing unencrypted data transmission via the control plane. Adjust other true or false values to customize the policy further based on your needs. Note that if ExecProcessRequest is enabled, but ReadStreamRequest is not, the user can still inject commands, but won’t be able to see the output. This does not mean the command won’t be executed.

In this demo, we try something a bit more advanced: in order to securely allow the user to manually perform attestation, we restrict the exec commands to only allow the secret fetching, and nothing else. allowed_commands defines the only commands allowed to be exec’ed into the CoCo pod.

What the above means is that the defined command to curl key1 and cat /sealed/secret-value/key2 will work, but any other command will fail. Even oc exec -it pods/your_pod — curl http://127.0.0.1:8006/cdh/resource/default/kbsres1/key2 will not work.

This is extremely useful if the pod has to provide restricted access to an untrusted actor (admin, developer) to for example debug the application logic inside the Confidential Container.

For more information about these policy please check alternative Kata policy.

Measure the policy

Let’s convert the policy in base64 and store it in the INITDATA variable.

INITDATA=$(cat initdata.toml | gzip | base64 -w0)
echo ""
echo $INITDATA

Now, let’s calculate the expected value of PCR8, which will be given in the reference values to make sure that every CoCo pod actually uses this initdata config.

initial_pcr=0000000000000000000000000000000000000000000000000000000000000000
hash=$(sha256sum initdata.toml | cut -d' ' -f1)
PCR8_HASH=$(echo -n "$initial_pcr$hash" | xxd -r -p | sha256sum | cut -d' ' -f1)
echo ""
echo "PCR 8:" $PCR8_HASH

Configure Trustee

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

In the sections below, we will elencate how to set up all these options, but for the purpose of the workshop, we will not enforce the image signature verification.

Reference values for the Reference Value Provider Service

Purpose of this resource: In an attestation scenario, the client (CoCo) 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 (Trustee, what we are setting right now). 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. By setting reference values, the user effectively defines the trusted digest (expected values) that Trustee expects from a valid client.

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

Red Hat currently ships an official CoCo podVM image together with its measurements. Therefore we simply need to insert the values into rvps-configmap.yaml. Such values ensure that CoCo is running that specific image, with a RH kernel and specific features like initdata policy enabled.

Let’s download the measurements from Redhat and then convert them into a reference value configmap:

# 1. Prepare required files
IMAGE=$(oc get csv -n openshift-sandboxed-containers-operator -o yaml \
  | grep RELATED_IMAGE_PODVM_OCI -A1 \
  | awk '/value:/ {print $2}')

oc get -n openshift-config secret/pull-secret -o json \
| jq -r '.data.".dockerconfigjson"' \
| base64 -d \
| jq '.' > cluster-pull-secret.json

# On the ARO workshop, we don't have enough space for podman.
# Use a different folder.
sudo mkdir -p /podvm
sudo chown azure:azure /podvm

# 2. Download the measurements
podman pull --root /podvm --authfile cluster-pull-secret.json $IMAGE

cid=$(podman create --root /podvm --entrypoint /bin/true $IMAGE)
echo "CID ${cid}"
podman unshare --root /podvm sh -c '
  mnt=$(podman mount --root /podvm '"$cid"')
  echo "MNT ${mnt}"
  cp $mnt/image/measurements.json /podvm
  podman umount --root /podvm '"$cid"'
'
podman rm --root /podvm $cid
JSON_DATA=$(cat /podvm/measurements.json)

# 3. Prepare reference-values.json
REFERENCE_VALUES_JSON=$(echo "$JSON_DATA" | jq \
  --arg pcr8_val "$PCR8_HASH" '
  .measurements.sha256 | to_entries | map({
    "name": .key,
    "expiration": "2026-12-12T00:00:00Z",
    "hash-value": [
      {
        "alg": "sha256",
        "value": (.value | ltrimstr("0x"))
      }
    ]
  })
  +
  [
    {
      "name": "pcr08",
      "expiration": "2026-12-12T00:00:00Z",
      "hash-value": [
        {
          "alg": "sha256",
          "value": $pcr8_val
        }
      ]
    }
  ]
  | sort_by(.name | ltrimstr("pcr") | tonumber)
' | sed 's/^/    /')

# 4. Build the final ConfigMap
cat > rvps-configmap.yaml <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: rvps-reference-values
  namespace: trustee-operator-system
data:
  reference-values.json: |
$REFERENCE_VALUES_JSON
EOF

clear
cat rvps-configmap.yaml

Once the reference values have been added, apply the ConfigMap.

oc apply -f rvps-configmap.yaml

Attestation policy

Purpose of this resource: An attestation policy defines which part of the attestation report sent by the client (CoCo) is important for the Attester (Trustee), and how to compare the report with the reference values.

By default, Trustee has already an attestation policy. You can overwrite the default one by creating your own attestation policy.

The attestation policy follows the Open Policy Agent specification.

The policy below checks the Platform Configuration Register (PCR) values 03, 08, 09, 11, and 12 against the reference values to ensure that the Confidential Containers pod uses the specified restrictive Kata agent policy and that the Red Hat pod VM image has not been altered. The attestation process is successful only if all the values match. For details, see Linux TPM PCR Registry in the UAPI Group Specifications documentation.

cat > attestation-policy.yaml <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: attestation-policy
  namespace: trustee-operator-system
data:
  default.rego: |
    package policy

    import rego.v1
    default executables := 33
    default hardware := 97
    default configuration := 36

    ##### Azure vTPM SNP
    executables := 3 if {
      input.azsnpvtpm.tpm.pcr03 in data.reference.pcr03
      input.azsnpvtpm.tpm.pcr08 in data.reference.pcr08
      input.azsnpvtpm.tpm.pcr09 in data.reference.pcr09
      input.azsnpvtpm.tpm.pcr11 in data.reference.pcr11
      input.azsnpvtpm.tpm.pcr12 in data.reference.pcr12
    }

    hardware := 0 if {
      input.azsnpvtpm
    }

    configuration := 0 if {
      input.azsnpvtpm
    }

    ##### Azure vTPM TDX
    executables := 3 if {
      input.aztdxvtpm.tpm.pcr03 in data.reference.pcr03
      input.aztdxvtpm.tpm.pcr08 in data.reference.pcr08
      input.aztdxvtpm.tpm.pcr09 in data.reference.pcr09
      input.aztdxvtpm.tpm.pcr11 in data.reference.pcr11
      input.aztdxvtpm.tpm.pcr12 in data.reference.pcr12
    }

    hardware := 0 if {
      input.aztdxvtpm
    }

    configuration := 0 if {
      input.aztdxvtpm
    }
EOF

clear
cat attestation-policy.yaml

Once you defined your own policy, apply it.

oc apply -f attestation-policy.yaml

TDX ConfigMap

Purpose of this resource: If your TEE is Intel Trust Domain Extensions (TDX), meaning the instance size you use or plan to use is Standard_DCe*, you must create the following ConfigMap.

Enabling such configmap does not prevent CoCo to use other TEEs.

cat > tdx-config.yaml <<EOF
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/"
    }
EOF

clear
cat tdx-config.yaml

Once tdx-config.yaml is ready, apply the ConfigMap.

oc apply -f tdx-config.yaml

Container image signature verification policy

Purpose of this resource: Sets wether to enforce or not the container image signature verification feature. If enabled, all containers images not signed by the trusted certificate provided in the container image verification secret will not be run.

The Trustee Operator returns this secret to the CoCo CVM components (which will run the CoCo pod) after attestation, to make sure they will perform the intended check. The CVM components will then compare the secret with the actual pod signature to determine whether to run it or not, ensuring that only trusted and authenticated container images are deployed in your environment.

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

  1. Create a security-policy-config.json that enables or disables signature verification.

    In this example, we are going to create a policy that:

    • Ensures that the container quay.io/confidential-devhub/signed-hello-openshift (regardless of the tag) is signed, otherwise it will not be run.

    • As a global policy, reject any other registry and non-signed image. This means only the above image is allowed to run, and only if it’s signed. Such policy can be also relaxed, for example allowing any container under the confidential-devhub org (by using quay.io/confidential-devhub/*), or to restrict the image signature to only such org/container pair while allowing all the others to be unsigned (change reject to insecureAcceptAnything).

      Let’s create a secret containing the public key used to signed the above container image: because the image was built by the CoCo team, we need to download it first.

      # Download the key
      curl -L https://raw.githubusercontent.com/confidential-devhub/workshop-on-ARO-showroom/refs/heads/showroom/helpers/cosign.pub -o cosign.pub
      
      SIGNATURE_SECRET_NAME=cosign-key
      SIGNATURE_SECRET_FILE=hello-pub-key
      
      oc create secret generic $SIGNATURE_SECRET_NAME \
          --from-file=$SIGNATURE_SECRET_FILE=./cosign.pub \
          -n trustee-operator-system

      If you want to sign your own container, refer to the Red Hat Trusted Artifact Signer or other tools to sign container images. A simple guide on how to use upstream cosign is here.

      Note that $SIGNATURE_SECRET_NAME will be later used in the KbsConfig

      Then, create security-policy-config.json.

      Specify the image repository for $SECURITY_POLICY_TRANSPORT, in this case docker:. For more information, see containers-transports 5.

      Create security-policy-config.json. Specify the container registry/image in $SECURITY_POLICY_IMAGE.

      SECURITY_POLICY_TRANSPORT=docker
      SECURITY_POLICY_IMAGE=quay.io/confidential-devhub/signed-hello-openshift
      
      # TODO: this doesn't work for now!
      cat > security-policy-config-restrictive.json <<EOF
      {
        "default": [
            {
            "type": "reject"
            }
        ],
        "transports": {
            "$SECURITY_POLICY_TRANSPORT": {
                "$SECURITY_POLICY_IMAGE":
                [
                    {
                        "type": "sigstoreSigned",
                        "keyPath": "kbs:///default/$SIGNATURE_SECRET_NAME/$SIGNATURE_SECRET_FILE"
                    }
                ]
            }
        }
      }
      EOF
      
      cat > security-policy-config.json <<EOF
      {
        "default": [
            {
            "type": "insecureAcceptAnything"
            }
        ],
        "transports": {}
      }
      EOF
      
      clear
      cat security-policy-config.json
      Note how we set reject as a default global policy. As explained above, this forbids any other non-specified image from being run. Change it in insecureAcceptAnything if you want to allow also unsigned images. If you don’t care at all about container policy, leave insecureAcceptAnything and ensure "transports": {}".

      For more information on security policy, see containers-policy.json.5 and in particular this example.

  2. After security-policy-config.json is created, upload it as a secret with the following command:

    oc create secret generic security-policy \
      --from-file=osc=./security-policy-config.json \
      -n trustee-operator-system
    Do not alter the secret type, security-policy, or the key, osc.

    Note that security-policy will be later used in the KbsConfig

Resource access policy

Purpose of this resource: Resource policies control which secrets are released and are generally scoped to the workload. They allow the user define which attested workload has access to which resource, to avoid that the wrong client accesses data that it is not supposed to.

In this example below we are creating a simple policy that accepts any request that comes from an attester (client) that does use a TEE. For more information about resource access policies, and how to create stronger ones, look here

cat > resourcepolicy-configmap.yaml <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: resource-policy
  namespace: trustee-operator-system
data:
  policy.rego: |
    package policy
    import rego.v1

    default allow = false
    allow if {
      input["submods"]["cpu"]["ear.status"] == "affirming"
    }
EOF

clear
cat resourcepolicy-configmap.yaml

Once the policy has been implemented, apply the ConfigMap.

oc apply -f resourcepolicy-configmap.yaml

Add a secret to Trustee

Populate Trustee with secret(s) that are then managed by the above policies and if attestation is successful, are sent to the client(s) (CoCo). For example, a Confidential Container image/workload could be encrypted, and the key to decrypt it is stored inside the Trustee and provided only if attestation is successful. In this section, we will show how to add the key into Trustee.

Prerequisites: You have created one or more custom keys. In this workshop, we will also create 2 keys.

Define secret name and values. 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.

For the purpose of this workshop, we will show how to retrieve the key in two ways:

  1. key1 will be retrieved via lazy attestation, meaning the application has to retrieve it itself by connecting with the internal guest components (the container does not need to know the Trustee address!)

  2. key2 will be retireved via a sealed secret, meaning which allows to load confidential information in the untrusted OCP environment. What this means is that the OCP sealed secret will simply be a pointer to the desired key2 stored in Trustee, and once the container starts, the CoCo guest components will automatically perform attestation with the Trustee, retrieve the key and load it into a specified volume.

More info on this in the example section.

Create what will be uploaded as key2:

echo "This is my super secret key!" > key.bin
# Alternatively:
# openssl rand 128 > key.bin

We will add key1 as a simple string containing the text Confidential_Secret!, and key2 as key.bin.

SECRET_NAME=kbsres1

oc create secret generic $SECRET_NAME \
  --from-literal key1=Confidential_Secret! \
  --from-file key2=key.bin \
  -n trustee-operator-system

Note that kbsres1 will be later used in the KbsConfig

Now, prepare the key2 sealed secret. Note that this secret is uploaded in the namespace where the CoCo pod will run (default), not in the Trustee/OSC namespace, as it will be used directly by the CoCo pod:

SECRET=$(podman run -it quay.io/confidential-devhub/coco-tools:0.3.0 /tools/secret seal vault --resource-uri kbs:///default/${SECRET_NAME}/key2 --provider kbs | grep -v "Warning")

oc create secret generic sealed-secret --from-literal=key2=$SECRET -n default

Create the KbsConfig custom resource

To complete Trustee setup, you must create a KbsConfig.

cat > kbsconfig-cr.yaml <<EOF
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: ["$SECRET_NAME", "security-policy", "attestation-token", "$SIGNATURE_SECRET_NAME"]
  kbsResourcePolicyConfigMapName: resource-policy
  kbsAttestationPolicyConfigMapName: attestation-policy
  kbsHttpsKeySecretName: kbs-https-key
  kbsHttpsCertSecretName: kbs-https-certificate
  tdxConfigSpec:
    kbsTdxConfigMapName: tdx-config
EOF

clear
cat kbsconfig-cr.yaml

Once the KbsConfig has been configured, apply it.

oc apply -f kbsconfig-cr.yaml

Verification

Verify the Trustee configuration by checking the Trustee pods and logs.

  1. Check that both pods are up and running:

    oc get pods -n trustee-operator-system

    Expected 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
  2. Check the KBS pod logs

    POD_NAME=$(oc get pods -l app=kbs -o jsonpath='{.items[0].metadata.name}' -n trustee-operator-system)
    clear
    oc logs -n trustee-operator-system $POD_NAME

    Expected output:

[2025-08-25T12:56:21Z INFO kbs] Using config file /etc/kbs-config/kbs-config.toml
[2025-08-25T12:56:21Z WARN kbs::admin] insecure admin APIs are enabled
[2025-08-25T12:56:21Z INFO attestation_service::rvps] launch a built-in RVPS.
[2025-08-25T12:56:21Z WARN attestation_service::policy_engine::opa] Default policy file is already populated. Existing policy file will be used.
[2025-08-25T12:56:21Z INFO attestation_service::token::ear_broker] Loading default AS policy "ear_default_policy.rego"
[2025-08-25T12:56:21Z INFO attestation_service::token::ear_broker] No Token Signer key in config file, create an ephemeral key and without CA pubkey cert
[2025-08-25T12:56:21Z INFO kbs::api_server] Starting HTTPS server at [0.0.0.0:8080]
[2025-08-25T12:56:21Z INFO actix_server::builder] starting 64 workers
[2025-08-25T12:56:21Z INFO actix_server::server] Actix runtime found; starting in Actix runtime
[2025-08-25T12:56:21Z INFO actix_server::server] starting service: "actix-web-service-0.0.0.0:8080", workers: 64, listening on: 0.0.0.0:8080