Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #195

Merged
merged 6 commits into from
Jul 25, 2024
Merged

Fix #195

Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: 1.20.5
go-version: 1.22.5

- name: Build
run: |
Expand Down
1 change: 0 additions & 1 deletion cmc/sgx.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,5 @@ package cmc
import "github.com/Fraunhofer-AISEC/cmc/sgxdriver"

func init() {
log.Info("Adding SGX driver to supported drivers")
drivers["sgx"] = &sgxdriver.Sgx{}
}
1 change: 0 additions & 1 deletion cmc/snp.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,5 @@ package cmc
import "github.com/Fraunhofer-AISEC/cmc/snpdriver"

func init() {
log.Info("Adding SNP driver to supported drivers")
drivers["snp"] = &snpdriver.Snp{}
}
1 change: 0 additions & 1 deletion cmc/sw.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,5 @@ package cmc
import "github.com/Fraunhofer-AISEC/cmc/swdriver"

func init() {
log.Info("Adding SW driver to supported drivers")
drivers["sw"] = &swdriver.Sw{}
}
1 change: 0 additions & 1 deletion cmc/tpm.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,5 @@ package cmc
import "github.com/Fraunhofer-AISEC/cmc/tpmdriver"

func init() {
log.Info("Adding TPM driver to supported drivers")
drivers["tpm"] = &tpmdriver.Tpm{}
}
1 change: 1 addition & 0 deletions example-setup/setup-cmc
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ mkdir -p "${data}"

# Install dependencies
sudo apt install -y moreutils golang-cfssl build-essential zlib1g-dev libssl-dev jq
sudo snap install yq

# Intall tpm-pcr-tools
git clone https://github.com/Fraunhofer-AISEC/tpm-pcr-tools.git "${data}/tpm-pcr-tools"
Expand Down
185 changes: 109 additions & 76 deletions example-setup/update-container-manifest-live
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ source "${dir}/utils.sh"
export PATH=${PATH}:${HOME}/go/bin

if [[ "$#" -lt 5 ]]; then
echo "Usage: ./update-container-manifest-live <container-name> <data-folder> <cmc-folder> <cbor|json> <container-client> [<container arguments>]"
exit 1
# The target depends on the client, examples:
# docker/ctr: docker.io/library/ubuntu:22.04
# docker compose: docker-compose.yaml
# runc: /path/to/runtime-bundle
echo "Usage: ./update-container-manifest-live <target> <data-folder> <cmc-folder> <cbor|json> <container-client> [<container arguments>]"
exit 1
fi

# Extract the -env parameters
Expand All @@ -28,7 +32,7 @@ for arg in "$@"; do
done

# Set all parameters
container="$1"
target="$1"
shift
data=$(set -e; abs_path "$1")
shift
Expand All @@ -44,105 +48,134 @@ input="${data}/metadata-raw"
tmp="${data}/metadata-tmp"
output="${data}/metadata-signed"
runtime="${cmc}/tools/containerd-shim-cmc-v1/containerd-shim-cmc-v1"
container="${container%/}"
container_name=$(echo "${container}-oci" | sed 's:.*/::' | tr : -)
target="${target%/}"


echo "Creating reference values for container: ${container} with args ${args}"
echo "Creating reference values for container: ${target} with args ${args}"
echo "Using ${data} as directory for local data"

# Delete temporary manifests
rm -rf "${tmp}"/${container_name}.manifest.*
rm -rf "${tmp}"/*

# Calculate the container reference values
sudo rm -f /tmp/container-refs

echo "Generating reference values for ${client} client"
if [[ "${client}" == "ctr" ]]; then
sudo ctr image pull ${container}
containers=("${target}")
sudo ctr image pull ${target}
set +e
sudo ctr run --detach ${args} ${container} REF_CONTAINER
sudo ctr run --detach ${args} ${target} REF_CONTAINER
sudo ctr task kill -s SIGKILL REF_CONTAINER
sudo ctr container delete REF_CONTAINER
set -e
elif [[ "${client}" == "shim" ]]; then
sudo ctr image pull ${container}
containers=("${target}")
sudo ctr image pull ${target}
set +e
sudo ctr run --runtime ${runtime} -t --rm ${args} ${container} CMC_GENERATE_APP_MANIFEST
sudo ctr run --runtime ${runtime} -t --rm ${args} ${target} CMC_GENERATE_APP_MANIFEST
set -e
elif [[ "${client}" == "docker" ]]; then
sudo docker run ${args} ${container}
containers=("${target}")
sudo docker run ${args} ${target}
elif [[ "${client}" == "docker-compose" ]]; then
containers=($(yq eval '.services | keys | .[]' "${target}"))
set +e
docker compose -f "${target}" up &
echo "Sleeping for 10s to enable container start"
sleep 10
echo "Stopping containers"
docker compose -f "${target}" stop
sleep 3
set -e
elif [[ "${client}" == "runc" ]]; then
cd ${container}
containers=("${target}")
cd ${target}
sudo runc create references
sudo runc delete references
cd -
else
echo "Client ${client} not supported. Only 'docker', 'ctr' and 'runc' supported for now."
echo "Client ${client} not supported. Only 'docker', 'docker-compose', 'ctr' and 'runc' supported for now."
exit
fi

refvals=$(cat /tmp/container-refs)

# App Manifest: Replace existing reference values with new reference values in the App Manifest
json=$(cat "${input}/app.manifest.json")
json=$(echo "${json}" | jq 'del(.referenceValues[])')
json=$(echo "${json}" | jq --argjson ver "${refvals}" '.referenceValues += $ver')

# App Manifest: Extract the reference value and add it to the file name to not overwrite the same
# container with different arguments such as environment variables
refval=$(echo "${json}" | jq -r '.referenceValues[0].sha256')

# App Manifest: Set name and reference value name
json=$(echo "${json}" | jq ".name = \"${container}-${refval}\"")
json=$(echo "${json}" | jq ".referenceValues[0].name += \": ${container}\"")

# App Manifest: Store
echo "Writing ${input}/${container_name}-${refval}.manifest.json"
printf "%s\n" "${json}" > "${input}/${container_name}-${refval}.manifest.json"

# App Description: Create corresponding app description
appdesc=$(cat "${input}/app.description.json")
appdesc=$(echo "${appdesc}" | jq ".name = \"${container}-${refval}.description\"")
appdesc=$(echo "${appdesc}" | jq ".appManifest = \"${container}-${refval}\"")
echo "Adding environment variables"
for i in "${!keys[@]}"; do
envs="{\"key\": \"${keys[$i]}\", \"value\": \"${values[$i]}\"}"
echo "Adding $envs"
appdesc=$(echo "${appdesc}" | jq --argjson envs "${envs}" '.environment += [$envs]')
done

# Device Description: Add/replace app description to/in device description
devdesc=$(cat "${input}/device.description.json")
exists=$(echo "${devdesc}" | jq "any(.appDescriptions[]; .name == \"${container}-${refval}.description\")")
if [[ "${exists}" = false ]]; then
echo "Adding app description to device description"
else
echo "Replacing existing app description"
devdesc=$(echo "$devdesc" | jq ".appDescriptions |= map(select(.name != \"${container}-${refval}.description\"))")
fi
devdesc=$(echo "${devdesc}" | jq --argjson desc "[${appdesc}]" '.appDescriptions += $desc')

# Device Description: Store
echo "Writing ${input}/device.description.json"
printf "%s\n" "${devdesc}" > "${input}/device.description.json"

# Sign the metadata*
key="${data}/pki/signing-cert-key.pem"
chain="${data}/pki/signing-cert.pem,${data}/pki/ca.pem"

# Convert to CBOR if specified
if [[ "${ser,,}" = "json" ]]; then
echo "using json serialization"
cp "${input}/${container_name}-${refval}.manifest.json" "${tmp}/${container_name}-${refval}.manifest.json"
cp "${input}/device.description.json" "${tmp}/device.description.json"
elif [[ "${ser,,}" = "cbor" ]]; then
echo "using cbor serialiation"
cmc-converter -in "${input}/${container_name}-${refval}.manifest.json" -out "${tmp}/${container_name}-${refval}.manifest.cbor" -outform cbor
cmc-converter -in "${input}/device.description.json" -out "${tmp}/device.description.cbor" -outform cbor
else
echo "serialization format ${ser} is not supported"
exit 1
fi
# if containers not set, set it to the single container

# In some scenarios, e.g. docker-compose, we have more than one container and have to iterate over all
num_containers=$(echo "${refvals}" | jq length)
for ((i = 0; i < num_containers; i++)); do
echo "Creating manifest for container $i"

container=${containers[$i]}
container_name=$(echo "${container}-oci" | sed 's:.*/::' | tr : -)

refval=$(echo "${refvals}" | jq -r ".[$i]")

# App Manifest: Replace existing reference values with new reference values in the App Manifest
json=$(cat "${input}/app.manifest.json")
json=$(echo "${json}" | jq 'del(.referenceValues[])')
json=$(echo "${json}" | jq --argjson ver "[${refval}]" '.referenceValues += $ver')

# App Manifest: Extract the reference value sha256 and add it to the file name to not overwrite the same
# container with different arguments such as environment variables
sha256=$(echo "${json}" | jq -r '.referenceValues[0].sha256')

echo "Refval sha256: ${sha256}"

# App Manifest: Set name and reference value name
json=$(echo "${json}" | jq ".name = \"${container}-${sha256}\"")
json=$(echo "${json}" | jq ".referenceValues[0].name += \": ${container}\"")

# App Manifest: Store
echo "Writing ${input}/${container_name}-${sha256}.manifest.json"
printf "%s\n" "${json}" > "${input}/${container_name}-${sha256}.manifest.json"

# App Description: Create corresponding app description
appdesc=$(cat "${input}/app.description.json")
appdesc=$(echo "${appdesc}" | jq ".name = \"${container}-${sha256}.description\"")
appdesc=$(echo "${appdesc}" | jq ".appManifest = \"${container}-${sha256}\"")
echo "Adding environment variables"
for i in "${!keys[@]}"; do
envs="{\"key\": \"${keys[$i]}\", \"value\": \"${values[$i]}\"}"
echo "Adding $envs"
appdesc=$(echo "${appdesc}" | jq --argjson envs "${envs}" '.environment += [$envs]')
done

# Device Description: Add/replace app description to/in device description
devdesc=$(cat "${input}/device.description.json")
exists=$(echo "${devdesc}" | jq "any(.appDescriptions[]; .name == \"${container}-${sha256}.description\")")
if [[ "${exists}" = false ]]; then
echo "Adding app description to device description"
else
echo "Replacing existing app description"
devdesc=$(echo "$devdesc" | jq ".appDescriptions |= map(select(.name != \"${container}-${sha256}.description\"))")
fi
devdesc=$(echo "${devdesc}" | jq --argjson desc "[${appdesc}]" '.appDescriptions += $desc')

# Device Description: Store
echo "Writing ${input}/device.description.json"
printf "%s\n" "${devdesc}" > "${input}/device.description.json"

# Sign the metadata*
key="${data}/pki/signing-cert-key.pem"
chain="${data}/pki/signing-cert.pem,${data}/pki/ca.pem"

# Convert to CBOR if specified
if [[ "${ser,,}" = "json" ]]; then
echo "using json serialization"
cp "${input}/${container_name}-${sha256}.manifest.json" "${tmp}/${container_name}-${sha256}.manifest.json"
cp "${input}/device.description.json" "${tmp}/device.description.json"
elif [[ "${ser,,}" = "cbor" ]]; then
echo "using cbor serialiation"
cmc-converter -in "${input}/${container_name}-${sha256}.manifest.json" -out "${tmp}/${container_name}-${sha256}.manifest.cbor" -outform cbor
cmc-converter -in "${input}/device.description.json" -out "${tmp}/device.description.cbor" -outform cbor
else
echo "serialization format ${ser} is not supported"
exit 1
fi

cmc-signing-tool -in "${tmp}/${container_name}-${refval}.manifest.${ser}" -out "${output}/${container_name}-${refval}.manifest.${ser}" -keys "${key}" -x5cs "${chain}"
cmc-signing-tool -in "${tmp}/device.description.${ser}" -out "${output}/device.description.${ser}" -keys "${key}" -x5cs "${chain}"
cmc-signing-tool -in "${tmp}/${container_name}-${sha256}.manifest.${ser}" -out "${output}/${container_name}-${sha256}.manifest.${ser}" -keys "${key}" -x5cs "${chain}"
cmc-signing-tool -in "${tmp}/device.description.${ser}" -out "${output}/device.description.${ser}" -keys "${key}" -x5cs "${chain}"
done
26 changes: 24 additions & 2 deletions example-setup/update-platform
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ fi

echo "Using ${data} as directory for local data"

# Retrieve high level details
set +e
firmware="Lenovo"
bootloader=$(grub-install --version)
kernel=$(uname -r)
os=$(lsb_release -sd 2>/dev/null)
set -e

# Replace existing app description in the device description (will be added through
# update-app-manifest scripts)
json=$(cat "${input}/device.description.json")
Expand All @@ -34,17 +42,31 @@ printf "%s\n" "${json}" > "${input}/device.description.json"
# Parse the values of the RTM PCRs from the kernel's binary bios measurements as reference values
referenceValues=$(sudo parse-srtm-pcrs -p 0,1,2,3,4,5,6,7 -f json -e)

# Replace existing reference values with new reference values in the RTM Manifest
# Load RTM manifest
json=$(cat "${input}/rtm.manifest.json")

# Insert high-level details
json=$(echo "${json}" | jq ".details.firmware = \"${firmware}\"")
json=$(echo "${json}" | jq ".details.bootloader = \"${bootloader}\"")

# Replace existing reference values with new reference values in the RTM Manifest
json=$(echo "${json}" | jq 'del(.referenceValues[])')
json=$(echo "${json}" | jq --argjson ver "${referenceValues}" '.referenceValues += $ver')

# Save the RTM manifest
printf "%s\n" "${json}" > "${input}/rtm.manifest.json"

# Parse the values of the OS PCRs from the kernel's binary bios measurements as reference values
referenceValues=$(sudo parse-srtm-pcrs -p 8,9,12,13,14,15 -f json -e)

# Replace existing reference values with new reference values in the RTM Manifest
# Load OS manifest
json=$(cat "${input}/os.manifest.json")

# Insert high-level details
json=$(echo "${json}" | jq ".details.kernel = \"${kernel}\"")
json=$(echo "${json}" | jq ".details.os = \"${os}\"")

# Replace existing reference values with new reference values in the OS Manifest
json=$(echo "${json}" | jq 'del(.referenceValues[])')
json=$(echo "${json}" | jq --argjson ver "${referenceValues}" '.referenceValues += $ver')
printf "%s\n" "${json}" > "${input}/os.manifest.json"
Expand Down
6 changes: 5 additions & 1 deletion measure/rtconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"encoding/json"
"fmt"
"path/filepath"
"slices"
"strings"

"github.com/opencontainers/runtime-spec/specs-go"
Expand Down Expand Up @@ -78,7 +79,7 @@ func normalize(id string, configData []byte) ([]byte, string, error) {
return nil, "", fmt.Errorf("failed to evaluate dockerd symlink: %w", err)
}

// TODO FIXME The docker network controller ID is generated randomly (// github.com/moby/moby/libnetwork/controller.go:New())
// TODO FIXME The docker network controller ID is generated randomly (github.com/moby/moby/libnetwork/controller.go:New())
// and then the truncated daemon.netController.ID() is added as an argument in the prestart
// hooks (github.com/moby/moby/daemon/oci_linux.go:withLibnetwork()). This is a very hacky
// way to make it reproducible, ideally, the actual network ID controller ID should be
Expand Down Expand Up @@ -107,6 +108,9 @@ func normalize(id string, configData []byte) ([]byte, string, error) {
rootfs := config.Root.Path
config.Root = nil

// List environment variables alphabetically to guarantee reproducibility
slices.Sort(config.Process.Env)

data, err := json.Marshal(config)
if err != nil {
return nil, "", fmt.Errorf("failed to marshal config: %w", err)
Expand Down
9 changes: 7 additions & 2 deletions measure/rtconfig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@ func TestGetConfigMeasurement(t *testing.T) {
id: "mycontainer",
configData: configData,
},
want: []byte{0x17, 0x76, 0xdf, 0xed, 0x31, 0x7c, 0xa1, 0x03, 0xd1, 0x49, 0x13, 0xd6, 0x5e, 0x86, 0x2e, 0x8a, 0xa8, 0x0a, 0x3c, 0x73, 0x87, 0xeb, 0x3f, 0x36, 0x20, 0xd3, 0xe3, 0x2d, 0x46, 0x4c, 0x54, 0x62},
want: []byte{
0xaf, 0xd6, 0xa3, 0x51, 0xca, 0x87, 0xb4, 0x1c,
0x84, 0x82, 0x5d, 0x76, 0x7a, 0x71, 0x91, 0x80,
0x3d, 0x08, 0x77, 0xc3, 0x33, 0xf1, 0xf1, 0xee,
0xaa, 0xd0, 0xa5, 0x9a, 0xf5, 0x01, 0x11, 0x0e,
},
wantErr: false,
},
}
Expand Down Expand Up @@ -92,7 +97,7 @@ func Test_normalize(t *testing.T) {
}

var (
normalizedDockerConfig = []byte(`{"hooks":{"prestart":[{"args":["libnetwork-setkey","-exec-root=/var/run/docker","<container-id>","<docker-network-controller-id>"],"path":"/usr/bin/dockerd"}]},"hostname":"<container-id>","linux":{"cgroupsPath":"system.slice:docker:<container-id>","maskedPaths":["/proc/asound","/sys/devices/virtual/powercap"],"namespaces":[{"type":"mount"}],"readonlyPaths":["/proc/bus","/proc/sysrq-trigger"],"resources":{"blockIO":{},"devices":[{"access":"rwm","allow":false}]},"seccomp":{"architectures":["SCMP_ARCH_X86_64","SCMP_ARCH_X32"],"defaultAction":"SCMP_ACT_ERRNO","defaultErrnoRet":1,"syscalls":[{"action":"SCMP_ACT_ALLOW","names":["chroot"]}]},"sysctl":{"net.ipv4.ip_unprivileged_port_start":"0","net.ipv4.ping_group_range":"0 2147483647"}},"mounts":[{"destination":"/etc/resolv.conf","options":["rbind","rprivate"],"source":"/var/lib/docker/containers/<container-id>/resolv.conf","type":"bind"}],"ociVersion":"1.2.0","process":{"args":["/bin/sh"],"capabilities":{"permitted":["CAP_CHOWN","CAP_DAC_OVERRIDE"]},"cwd":"/","env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","HOSTNAME=<container-id>","TERM=xterm"],"user":{"gid":0,"uid":0}}}`)
normalizedDockerConfig = []byte(`{"hooks":{"prestart":[{"args":["libnetwork-setkey","-exec-root=/var/run/docker","<container-id>","<docker-network-controller-id>"],"path":"/usr/bin/dockerd"}]},"hostname":"<container-id>","linux":{"cgroupsPath":"system.slice:docker:<container-id>","maskedPaths":["/proc/asound","/sys/devices/virtual/powercap"],"namespaces":[{"type":"mount"}],"readonlyPaths":["/proc/bus","/proc/sysrq-trigger"],"resources":{"blockIO":{},"devices":[{"access":"rwm","allow":false}]},"seccomp":{"architectures":["SCMP_ARCH_X86_64","SCMP_ARCH_X32"],"defaultAction":"SCMP_ACT_ERRNO","defaultErrnoRet":1,"syscalls":[{"action":"SCMP_ACT_ALLOW","names":["chroot"]}]},"sysctl":{"net.ipv4.ip_unprivileged_port_start":"0","net.ipv4.ping_group_range":"0 2147483647"}},"mounts":[{"destination":"/etc/resolv.conf","options":["rbind","rprivate"],"source":"/var/lib/docker/containers/<container-id>/resolv.conf","type":"bind"}],"ociVersion":"1.2.0","process":{"args":["/bin/sh"],"capabilities":{"permitted":["CAP_CHOWN","CAP_DAC_OVERRIDE"]},"cwd":"/","env":["HOSTNAME=<container-id>","PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","TERM=xterm"],"user":{"gid":0,"uid":0}}}`)

dockerConfig = []byte(`
{
Expand Down
3 changes: 2 additions & 1 deletion testtool/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,8 @@ func handleConnection(conn net.Conn) {

msg, err := r.ReadString('\n')
if err != nil {
log.Errorf("Failed to read: %v", err)
log.Warnf("Failed to read: %v", err)
return
}
log.Infof("Received from peer: %v", msg)

Expand Down
Loading