Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,34 @@ The script `create_vms.sh`:
scripts/create-vms.sh coreos.key.pub
```

### Create local Trustee deployment

Generate the key pair for Trustee:
```bash
scripts/gen_key.sh
```

Create trustee and helper containers for the setup:
```bash
sudo podman kube play trustee.yaml
```
The pods exposes 3 ports:
- `8080`: for the KBS and Trustee
- `8000`: serving the ignition file with the clevis configuration
- `5001`: serving the registration endpoint for the AK

The script `scripts/populate-local-kbs.sh` populate the local KBS.
```bash
scripts/populate-local-kbs.sh
```

You can now launch the VM by exposing the trustee IP (for example, using the IP of `virbr0`).
```bash
export TRUSTEE_ADDR=192.168.122.1
scripts/install_vm.sh -k <KEY> -b configs/ak.bu -i $(pwd)/coreos/fcos-qemu.x86_64.qcow2 -n <VM_NAME>
```


### Example with the Confidential Clusters operator and a local VM

If you have deployed Confidential Clusters with Trustee, and its KBS and register server are available at ports `8080` and `8000`, and the VM PCR values are configured with Trustee, you can instead run
Expand Down
41 changes: 41 additions & 0 deletions configs/ak.bu
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
variant: fcos
version: 1.7.0-experimental

ignition:
config:
merge:
- source: http://<IP>:8000/pin-trustee.ign

attestation:
attestation_key:
registration:
url: http://<IP>:5001

passwd:
users:
- name: core
ssh_authorized_keys:
- <KEY>

systemd:
units:
- name: zincati.service
enabled: false
- name: [email protected]
dropins:
- name: autologin-core.conf
contents: |
[Service]
# Override Execstart in main unit
ExecStart=
# Add new Execstart with `-` prefix to ignore failure`
ExecStart=-/usr/sbin/agetty --autologin core --noclear %I $TERM

storage:
files:
- path: /etc/profile.d/systemd-pager.sh
mode: 0644
contents:
inline: |
# Tell systemd to not use a pager when printing information
export SYSTEMD_PAGER=cat
2 changes: 1 addition & 1 deletion configs/remote-ign/pin-trustee.bu
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
variant: fcos
version: 1.6.0
version: 1.7.0-experimental
storage:
luks:
- name: root
Expand Down
2 changes: 1 addition & 1 deletion containerfiles/trustee-attester.container
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ FROM fedora:rawhide as builder

# default is 'main' but can be e.g. COMMIT=v0.11.0 (with --build-arg COMMIT=v0.11.0)
ARG COMMIT=main
ARG ATTESTERS=snp-attester,tdx-attester,az-snp-vtpm-attester,az-tdx-vtpm-attester
ARG ATTESTERS=snp-attester,tdx-attester,az-snp-vtpm-attester,az-tdx-vtpm-attester,tpm-attester

RUN dnf install -y git tss2-devel tpm2-tss-devel cargo openssl-devel perl

Expand Down
6 changes: 3 additions & 3 deletions coreos/Containerfile
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
ARG BASE
FROM quay.io/afrosi_rh/kbs-client-image:latest as kbc
FROM quay.io/confidential-clusters/trustee-attester:2025-10-21 as kbc
FROM quay.io/confidential-clusters/clevis-pin-trustee as clevis
FROM quay.io/confidential-clusters/ignition:clevis-pin-trustee as ignition
FROM quay.io/confidential-clusters/ignition:attestation as ignition
FROM $BASE

COPY ./usr /usr
COPY --from=kbc /usr/local/bin/kbs-client /usr/bin/trustee-attester
COPY --from=kbc /usr/local/bin/trustee-attester /usr/bin/trustee-attester
COPY --from=clevis /usr/bin/clevis-pin-trustee /usr/bin/clevis-pin-trustee
COPY --from=clevis /usr/bin/clevis-encrypt-trustee /usr/bin/clevis-encrypt-trustee
COPY --from=clevis /usr/bin/clevis-decrypt-trustee /usr/bin/clevis-decrypt-trustee
Expand Down
2 changes: 1 addition & 1 deletion coreos/justfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
os := "fcos"

scos_base_img:= "quay.io/okd/scos-content@sha256:3813e6608a999756931d3d621932af9662860e71a552b2670f9fe320bf0d3585"
fcos_base_img:= "quay.io/fedora/fedora-coreos:42.20250705.3.0"
fcos_base_img:= "quay.io/fedora/fedora-coreos:42.20251012.2.0"

scos_img:= "quay.io/confidential-clusters/scos"
fcos_img:= "quay.io/confidential-clusters/fcos"
Expand Down
25 changes: 25 additions & 0 deletions coreos/usr/lib/dracut/modules.d/66tmp_tools/module-setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash

check() {
return 0
}

depends() {
return 0
}

install () {
inst_multiple \
tpm2_create \
tpm2_createak \
tpm2_evictcontrol \
tpm2_getrandom \
tpm2_load \
tpm2_nvread \
tpm2_nvwrite \
tpm2_pcrread \
tpm2_unseal

# Library dependencies
inst_libdir_file "libtss2*"
}
18 changes: 18 additions & 0 deletions scripts/common.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/bash

create_remote_ign_config ()
{
IP=$1
# Setup remote ignition config
BUTANE=pin-trustee.bu
IGNITION="${BUTANE%.bu}.ign"

sed "s/<IP>/$IP/" configs/remote-ign/${BUTANE} > tmp/${BUTANE}

podman run --interactive --rm --security-opt label=disable \
--volume "$(pwd)/tmp:/pwd" \
--workdir /pwd \
quay.io/confidential-clusters/butane:clevis-pin-trustee \
--pretty --strict /pwd/$BUTANE --output "/pwd/$IGNITION"
echo "$IGNITION"
}
7 changes: 7 additions & 0 deletions scripts/gen_key.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/bash

DIR=trustee/keys
mkdir -p "$DIR"

openssl genpkey -algorithm ed25519 > $DIR/private.key
openssl pkey -in $DIR/private.key -pubout -out $DIR/public.pub
13 changes: 11 additions & 2 deletions scripts/install_vm.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
set -euo pipefail
# set -x

source scripts/common.sh

force=false
while getopts "k:b:n:fi:m:" opt; do
case $opt in
Expand All @@ -20,7 +22,7 @@ key="${key:-}"
butane="${butane:-}"
VM_NAME="${VM_NAME:-fcos-kbs}"
image="${image:-}"
RAM_MB="${RAM_MB:-2048}"
RAM_MB="${RAM_MB:-4098}"

OVMF_CODE=${OVMF_CODE:-"/usr/share/edk2/ovmf/OVMF_CODE_4M.secboot.qcow2"}
OVMF_VARS_TEMPLATE=${OVMF_VARS_TEMPLATE:-"/usr/share/edk2/ovmf/OVMF_VARS_4M.secboot.qcow2"}
Expand All @@ -33,6 +35,8 @@ VCPUS="2"
DISK_GB="20"
URL="--connect=qemu:///system"

TRUSTEE_ADDR="${TRUSTEE_ADDR:-}"

usage() {
echo "Usage: $0 -k <path-ssh-pubkey> -b <path-to-butane-config> -i <path-to-qcow2-image>"
}
Expand Down Expand Up @@ -65,10 +69,15 @@ elif [[ "$VM_NAME" == "existing-trustee" ]]; then
sed "s|<KEY>|key|g;
s|<IP>|$(ip route | grep virbr0 | cut -d' ' -f9)|g;
s|pin-trustee.ign|ignition-clevis-pin-trustee|g" "$butane" > "$bufile"
elif [ ! -z "$TRUSTEE_ADDR" ]; then
sed "s|<KEY>|key|g;
s|<IP>|$TRUSTEE_ADDR|g;" "$butane" > "$bufile"
create_remote_ign_config $TRUSTEE_ADDR
else
sed "s|<KEY>|$key|g" $butane > ${bufile}
fi


butane_args=()
if [[ -d ${butane%.bu} ]]; then
butane_args=("--files-dir" "${butane%.bu}")
Expand All @@ -77,7 +86,7 @@ podman run --interactive --rm --security-opt label=disable \
--volume "$(pwd)":/pwd \
--volume "${bufile}":/config.bu:z \
--workdir /pwd \
quay.io/confidential-clusters/butane:clevis-pin-trustee \
quay.io/confidential-clusters/butane:attestation \
--pretty --strict /config.bu --output "/pwd/${IGNITION_FILE}" \
"${butane_args[@]}"

Expand Down
72 changes: 72 additions & 0 deletions scripts/populate-local-kbs.sh
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

populate-trustee-kbs.sh regenerates the PIN Ignition config, which you probably want to share? I had an outdated one which has some very subtle implications (Ignition used old protocol version when the Clevis PIN is only implemented for v3.6).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put it in a common script to be sourced

Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/bin/bash

set -ex

KBC=kbs-client
URL=http://localhost:8080
KEY=trustee/keys/private.key

cat <<EOF >secret
{ "key_type": "oct", "key": "2b442dd5db4478367729ef8bbf2e7480" }
EOF

$KBC --url $URL config \
--auth-private-key $KEY \
set-resource --path default/machine/root \
--resource-file $(pwd)/secret

$KBC --url http://localhost:8080 config \
--auth-private-key $KEY \
set-resource-policy --allow-all


cat <<EOF >attestation-policy.rego
package policy
import rego.v1
default hardware := 97
default configuration := 36
default executables := 33

tpm_pcrs_valid if {
input.tpm.pcr04 in data.reference.tpm_pcr4
input.tpm.pcr07 in data.reference.tpm_pcr7
input.tpm.pcr14 in data.reference.tpm_pcr14
}

hardware := 2 if tpm_pcrs_valid
executables := 3 if tpm_pcrs_valid
configuration := 2 if tpm_pcrs_valid

default file_system := 0
default instance_identity := 0
default runtime_opaque := 0
default storage_opaque := 0
default sourced_data := 0
result := {
"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,
}
EOF

$KBC --url $URL config \
--auth-private-key $KEY \
set-sample-reference-value tpm_pcr4 "ff2b357be4a4bc66be796d4e7b2f1f27077dc89b96220aae60b443bcf4672525"
$KBC --url $URL config \
--auth-private-key $KEY \
set-sample-reference-value tpm_pcr7 "b3a56a06c03a65277d0a787fcabc1e293eaa5d6dd79398f2dda741f7b874c65d"
$KBC --url $URL config \
--auth-private-key $KEY \
set-sample-reference-value tpm_pcr14 "17cdefd9548f4383b67a37a901673bf3c8ded6f619d36c8007562de1d93c81cc"

$KBC --url $URL config \
--auth-private-key $KEY \
get-reference-values
$KBC --url $URL config \
--auth-private-key $KEY \
set-attestation-policy --policy-file attestation-policy.rego --id default_cpu --type rego
12 changes: 2 additions & 10 deletions scripts/populate-trustee-kbs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

set -euo pipefail
# set -x
source common.sh

if [[ "${#}" -ne 1 ]]; then
echo "Usage: $0 <path-to-ssh-public-key>"
Expand All @@ -26,16 +27,7 @@ until ssh core@$IP \
done

# Setup remote ignition config
BUTANE=pin-trustee.bu
IGNITION="${BUTANE%.bu}.ign"

sed "s/<IP>/$IP/" configs/remote-ign/${BUTANE} > tmp/${BUTANE}

podman run --interactive --rm --security-opt label=disable \
--volume "$(pwd)/tmp:/pwd" \
--workdir /pwd \
quay.io/confidential-clusters/butane:clevis-pin-trustee \
--pretty --strict /pwd/$BUTANE --output "/pwd/$IGNITION"
IGNITION=$(create_remote_ign_config)

scp -i "${KEY%.*}" \
-o StrictHostKeyChecking=no \
Expand Down
9 changes: 9 additions & 0 deletions test-server-ak/Containerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM python:3.11-slim

WORKDIR /app

COPY server.py .

EXPOSE 5001

CMD ["python", "server.py"]
40 changes: 40 additions & 0 deletions test-server-ak/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from http.server import HTTPServer, BaseHTTPRequestHandler
import json
import sys
import os

class PUTHandler(BaseHTTPRequestHandler):
def do_PUT(self):
content_length = int(self.headers['Content-Length'])
body = self.rfile.read(content_length)
print(body)
try:
data = json.loads(body)

attestation_key = data.get('attestation_key', '')

if not os.path.exists('/data/test.pub'):
os.makedirs('/data', exist_ok=True)
open('/data/test.pub', 'a').close()
print("Created /data/test.pub")

with open('/data/test.pub', 'w') as f:
f.write(attestation_key)

print(f"Successfully wrote attestation key to /data/test.pub")

self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
self.wfile.write(json.dumps({'status': 'success'}).encode())
except Exception as e:
print(f"Error: {e}")
self.send_response(500)
self.send_header('Content-type', 'application/json')
self.end_headers()
self.wfile.write(json.dumps({'status': 'error', 'message': str(e)}).encode())

if __name__ == '__main__':
server = HTTPServer(('0.0.0.0', 5001), PUTHandler)
print('Server running on port 5001...')
server.serve_forever()
Loading
Loading