From 73f7853c62794d7c60b6eb42c40e42ca449c79ec Mon Sep 17 00:00:00 2001 From: hjh189 Date: Mon, 5 Feb 2024 17:07:21 +0800 Subject: [PATCH] Update e2e test 1.Create a pod for test. And run the test cases in the container instead of VM. 2.Expand the test framework for golang SDK. 3.The test cases replay rtmrs and support ima. Signed-off-by: hjh189 --- test/e2e-test/ci-clean.sh | 7 +- test/e2e-test/ci-e2e-test.sh | 67 ++++++++- test/e2e-test/ci-setup.sh | 23 ++- test/e2e-test/go-test/go.mod | 22 +++ test/e2e-test/go-test/go.sum | 26 ++++ .../go-test/test-cases/gosdk_utils.go | 117 +++++++++++++++ .../e2e-test/go-test/test-cases/rtmrs_test.go | 52 +++++++ test/e2e-test/profile/Dockerfile | 30 ++++ test/e2e-test/profile/ccnp-test-node.yaml | 60 ++++++++ test/e2e-test/{ => profile}/kind-config.yaml | 3 + .../{ => profile}/repo-configmap.yaml | 0 test/e2e-test/py-test/pysdk_utils.py | 133 ++++++++++++++++++ test/e2e-test/py-test/test_rtmrs.py | 49 +++++++ test/e2e-test/test_eventlog.py | 35 ----- test/e2e-test/test_tdquote.py | 18 --- test/e2e-test/test_tdreport.py | 13 -- 16 files changed, 568 insertions(+), 87 deletions(-) create mode 100644 test/e2e-test/go-test/go.mod create mode 100644 test/e2e-test/go-test/go.sum create mode 100644 test/e2e-test/go-test/test-cases/gosdk_utils.go create mode 100644 test/e2e-test/go-test/test-cases/rtmrs_test.go create mode 100644 test/e2e-test/profile/Dockerfile create mode 100644 test/e2e-test/profile/ccnp-test-node.yaml rename test/e2e-test/{ => profile}/kind-config.yaml (80%) rename test/e2e-test/{ => profile}/repo-configmap.yaml (100%) create mode 100644 test/e2e-test/py-test/pysdk_utils.py create mode 100644 test/e2e-test/py-test/test_rtmrs.py delete mode 100755 test/e2e-test/test_eventlog.py delete mode 100755 test/e2e-test/test_tdquote.py delete mode 100755 test/e2e-test/test_tdreport.py diff --git a/test/e2e-test/ci-clean.sh b/test/e2e-test/ci-clean.sh index 00fd1f3..db9ff65 100755 --- a/test/e2e-test/ci-clean.sh +++ b/test/e2e-test/ci-clean.sh @@ -7,14 +7,11 @@ DEVICE_PLUGIN=localhost:5001/ccnp-device-plugin:latest QUOTE=localhost:5001/ccnp-quote-server:latest MEASUREMENT=localhost:5001/ccnp-measurement-server:latest EVENTLOG=localhost:5001/ccnp-eventlog-server:latest +TEST_NODE=localhost:5001/ccnp-test-node:latest kind delete cluster --name $CLUSTER_NAME rm /run/ccnp/uds/* docker stop $REG_NAME docker rm $REG_NAME sleep 1m -docker rmi ${DEVICE_PLUGIN} ${QUOTE} ${MEASUREMENT} ${EVENTLOG} -for i in $(docker images | grep "none" | awk '{print $3}');do - docker rmi "$i" -done -pip uninstall -y ccnp +docker rmi ${DEVICE_PLUGIN} ${QUOTE} ${MEASUREMENT} ${EVENTLOG} ${TEST_NODE} diff --git a/test/e2e-test/ci-e2e-test.sh b/test/e2e-test/ci-e2e-test.sh index 20e0b7b..14c9d05 100755 --- a/test/e2e-test/ci-e2e-test.sh +++ b/test/e2e-test/ci-e2e-test.sh @@ -3,11 +3,70 @@ set -o errexit : ' This is an E2E test script. If you want to run the script , please run the ci-setup.sh script in advance to set up the ci test environment. + +Please enter parameters to select the test case you want to run: +-a, run all test cases. +-s py/go, run python/golang sdk test cases, such as "-s py" means running python test cases. ' +PY_WORK_DIR='test/e2e-test/py-test' +GO_WORK_DIR='test/e2e-test/go-test/test-cases' +PY_FLAG=false +GO_FLAG=false + + +while getopts "as:" opt_name +do + case $opt_name in + a) PY_FLAG=true + GO_FLAG=true + echo "Run all test cases" + ;; + s) parameter=$OPTARG + array=(${parameter//,/ }) + for var in ${array[@]} + do + if [[ $var =~ "py" ]] + then + PY_FLAG=true + echo "Run py test cases" + elif [[ $var =~ "go" ]] + then + GO_FLAG=true + echo "Run go test cases" + fi + done + ;; + ?) echo "Invalid input" +      ;; + esac +done + + + + +for i in {1..3} +do + POD_NAME=$(kubectl get po | grep ccnp-test-node | grep Running | awk '{ print $1 }') + if [[ -z "$POD_NAME" ]] + then + sleep 2 + echo "Retrying $i time ..." + else + break + fi +done + + -WORK_DIR=$(cd "$(dirname "$0")"; pwd) +#Run python E2E test cases +if $PY_FLAG +then + kubectl exec -it "$POD_NAME" -- pytest -v ${PY_WORK_DIR} +fi -#Run E2E test cases -pip install pytest pytdxattest -pytest "${WORK_DIR}/test_eventlog.py" "${WORK_DIR}/test_tdquote.py" "${WORK_DIR}/test_tdreport.py" +#Run golang E2E test cases +if $GO_FLAG +then + kubectl exec -it "$POD_NAME" -- bash -c "pushd ${GO_WORK_DIR};go test -v;popd " +fi diff --git a/test/e2e-test/ci-setup.sh b/test/e2e-test/ci-setup.sh index fed0cf2..0bc8cfd 100755 --- a/test/e2e-test/ci-setup.sh +++ b/test/e2e-test/ci-setup.sh @@ -5,18 +5,19 @@ This is a script for setting up the CCNP ci environment. If you want to run the 1 According to the CCNP documentation, create a TDVM 2 Install helm and kind on the TDVM 3 Follow the CCNP documentation to pre-configure the TDVM,including modifying the file "/etc/udev/rules.d/90-tdx.rules" and creating the folder "/run/ccnp/uds" -4 Git clone CCNP and run this script +4 Run this script ' CLUSTER_NAME=my-cluster -KIND_CONFIG=kind-config.yaml +KIND_CONFIG=profile/kind-config.yaml DEVICE_PLUGIN=localhost:5001/ccnp-device-plugin:latest QUOTE=localhost:5001/ccnp-quote-server:latest MEASUREMENT=localhost:5001/ccnp-measurement-server:latest EVENTLOG=localhost:5001/ccnp-eventlog-server:latest +TEST_NODE=localhost:5001/ccnp-test-node:latest REG_NAME=kind-registry REG_PORT=5001 -REPO_CONFIGMAP=repo-configmap.yaml +REPO_CONFIGMAP=profile/repo-configmap.yaml LOCAL_REPO=localhost:5001 DOCKER_REPO=docker.io/library NFD_URL=https://kubernetes-sigs.github.io/node-feature-discovery/charts @@ -37,7 +38,6 @@ create_file(){ if [ ! -e "$1" ];then touch "$1" fi - } @@ -79,15 +79,20 @@ docker build --build-arg http_proxy="$HTTP_PROXY" --build-arg https_proxy="$HTTP --build-arg no_proxy="$NO_PROXY" -t $EVENTLOG -f container/ccnp-eventlog-server/Dockerfile . docker build --build-arg http_proxy="$HTTP_PROXY" --build-arg https_proxy="$HTTPS_PROXY" \ --build-arg no_proxy="$NO_PROXY" -t $DEVICE_PLUGIN -f container/ccnp-device-plugin/Dockerfile . +docker build --build-arg http_proxy="$HTTP_PROXY" --build-arg https_proxy="$HTTPS_PROXY" \ + --build-arg no_proxy="$NO_PROXY" -t $TEST_NODE -f test/e2e-test/profile/Dockerfile . docker push ${DEVICE_PLUGIN} docker push ${QUOTE} docker push ${MEASUREMENT} docker push ${EVENTLOG} +docker push ${TEST_NODE} sed -i "s#${DOCKER_REPO}#${LOCAL_REPO}#g" deployment/manifests/* sed -i "s#${DOCKER_REPO}#${LOCAL_REPO}#g" device-plugin/ccnp-device-plugin/deploy/helm/ccnp-device-plugin/values.yaml +sed -i "s#${DOCKER_REPO}#${LOCAL_REPO}#g" test/e2e-test/profile/ccnp-test-node.yaml + #Deploy CCNP Dependencies helm repo add nfd $NFD_URL @@ -101,14 +106,8 @@ kubectl create -f deployment/manifests/namespace.yaml kubectl create -f deployment/manifests/eventlog-server-deployment.yaml kubectl create -f deployment/manifests/measurement-server-deployment.yaml kubectl create -f deployment/manifests/quote-server-deployment.yaml - +kubectl create -f test/e2e-test/profile/ccnp-test-node.yaml #Wait for all pods and services to be ready -sleep 2m +sleep 1m -#Install SDK -pushd sdk/python3/ -pip install -r requirements.txt -pip install . -popd popd - diff --git a/test/e2e-test/go-test/go.mod b/test/e2e-test/go-test/go.mod new file mode 100644 index 0000000..c3c17a0 --- /dev/null +++ b/test/e2e-test/go-test/go.mod @@ -0,0 +1,22 @@ +module gosdk-test + +go 1.20 + +require ( + github.com/golang/protobuf v1.5.3 // indirect + github.com/intel/confidential-cloud-native-primitives/service/eventlog-server v0.0.0-20240131020930-fcd202dd676e // indirect + github.com/pkg/errors v0.9.1 // indirect + golang.org/x/net v0.18.0 // indirect + golang.org/x/sys v0.14.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect + google.golang.org/grpc v1.61.0 // indirect + google.golang.org/protobuf v1.32.0 // indirect +) + +require ( + ccnp v0.0.0-00010101000000-000000000000 + github.com/intel/confidential-cloud-native-primitives/sdk/golang/ccnp v0.0.0-20240131020930-fcd202dd676e +) + +replace ccnp => ../../../sdk/golang/ccnp diff --git a/test/e2e-test/go-test/go.sum b/test/e2e-test/go-test/go.sum new file mode 100644 index 0000000..3a775eb --- /dev/null +++ b/test/e2e-test/go-test/go.sum @@ -0,0 +1,26 @@ +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/intel/confidential-cloud-native-primitives/sdk/golang/ccnp v0.0.0-20240131020930-fcd202dd676e h1:DaaWyQatEN+KQzrmT2xWTY5RMkqnPqOcbNq/LgBqv8M= +github.com/intel/confidential-cloud-native-primitives/sdk/golang/ccnp v0.0.0-20240131020930-fcd202dd676e/go.mod h1:0dSZ/QSICtHucjtiP8yb8ouQU7m//ZPd/dsMehE8CrU= +github.com/intel/confidential-cloud-native-primitives/service/eventlog-server v0.0.0-20240131020930-fcd202dd676e h1:/FUVfJrpfbtHoaqrYRyGwovr8UGzohvEP89qBHeIW9Q= +github.com/intel/confidential-cloud-native-primitives/service/eventlog-server v0.0.0-20240131020930-fcd202dd676e/go.mod h1:u9nIX7H+etWAv/h9YKWs3gspzVDPMSgEJj83tc5YuZQ= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA= +google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0= +google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= diff --git a/test/e2e-test/go-test/test-cases/gosdk_utils.go b/test/e2e-test/go-test/test-cases/gosdk_utils.go new file mode 100644 index 0000000..a76adf5 --- /dev/null +++ b/test/e2e-test/go-test/test-cases/gosdk_utils.go @@ -0,0 +1,117 @@ +// Define some structures and functions to support golang SDK test. + +package gosdk_test + +import ( + "ccnp/eventlog" + "log" + "strings" + "crypto/sha512" + "encoding/hex" + "io/ioutil" + "strconv" + ) + + +const rtmrCount int = 4 +const rtmrLength int = 48 +const runtimeRegister uint32 = 2 +const imaFile string = "/run/security/integrity/ima/ascii_runtime_measurements" + +type rtmr struct{ + data []byte +} + + +type eventLogActor struct{ + bootTimeEventlogs []eventlog.CCEventLogEntry + runTimeEventlogs []string +} + + +func newEventLogActor() *eventLogActor { + bootTimeEventlogs,err := eventlog.GetPlatformEventlog() + if err != nil { + log.Fatalf("Get eventlog error: %v", err) + } + + imaFlag := true + f, err := ioutil.ReadFile("/proc/cmdline") + if err != nil { + log.Fatalf("Get cmdline error: %v", err) + } + cmdline := string(f) + if !strings.Contains(cmdline, "ima_hash=sha384"){ + imaFlag = false + } + + var runTimeEventlogs []string + if imaFlag == true{ + f, err := ioutil.ReadFile(imaFile) + if err != nil { + log.Fatalf("Read file: %v error: %v", imaFile, err) + } + imaStr := string(f) + for _, runTimeEventlog := range strings.Split(imaStr, "\n") { + runTimeEventlogs = append(runTimeEventlogs, runTimeEventlog) + } + } + + return &eventLogActor{bootTimeEventlogs: bootTimeEventlogs,runTimeEventlogs: runTimeEventlogs} +} + + +func (e eventLogActor) replayBootTime(index uint32) *rtmr { + rtmrVal := make([]byte,rtmrLength) + + + for _,bootTimeEventlog := range e.bootTimeEventlogs{ + if bootTimeEventlog.RegIdx == index { + var digestHex string + digest := string(bootTimeEventlog.Digest) + for _, val := range strings.Split(strings.Trim(digest, "[]"), " ") { + valInt,_ := strconv.Atoi(val) + valHex := strconv.FormatInt(int64(valInt), 16) + if len(valHex) == 1 { + valHex = "0" + valHex + } + digestHex = digestHex+valHex + } + + h := sha512.New384() + rtmrValHex := hex.EncodeToString(rtmrVal) + combVal,_ := hex.DecodeString(rtmrValHex+digestHex) + h.Write(combVal) + rtmrVal = h.Sum(nil) + }} + + return &rtmr{data:rtmrVal} +} + +func (e eventLogActor) replayRunTime(baseRtmr *rtmr) *rtmr { + extendVal := baseRtmr.data + for _,runTimeEventlog := range e.runTimeEventlogs{ + extendValHex := hex.EncodeToString(extendVal) + elements := strings.Fields(runTimeEventlog) + if len(elements) != 0{ + h := sha512.New384() + combVal,_:=hex.DecodeString(extendValHex + elements[1]) + h.Write(combVal) + extendVal = h.Sum(nil) + } + } + + return &rtmr{data:extendVal} +} + + + +func (e eventLogActor) replay(index uint32) []byte { + rtmrValue := e.replayBootTime(index) + if index == runtimeRegister && len(e.runTimeEventlogs) != 0{ + rtmrValue = e.replayRunTime(rtmrValue) + } + return rtmrValue.data +} + + diff --git a/test/e2e-test/go-test/test-cases/rtmrs_test.go b/test/e2e-test/go-test/test-cases/rtmrs_test.go new file mode 100644 index 0000000..4a935ff --- /dev/null +++ b/test/e2e-test/go-test/test-cases/rtmrs_test.go @@ -0,0 +1,52 @@ +/* +RTMR test: +1. Fetch boot time event logs using CCNP sdk and fetch runtime event logs(from IMA) in kernel memory +2. Re-calcuate the overall digest +3. Fetch measurements using CCNP sdk +4. Compare the recalculated values with the rtmrs in the measurements +*/ + +package gosdk_test + +import ( + "github.com/intel/confidential-cloud-native-primitives/sdk/golang/ccnp/measurement" + pb "github.com/intel/confidential-cloud-native-primitives/sdk/golang/ccnp/measurement/proto" + "testing" + "bytes" + ) + +func TestRtmr(t *testing.T) { + t.Run("rtmr0", func(t *testing.T) { + eventLogActor := newEventLogActor() + replayVal0 := eventLogActor.replay(0) + val0,_ := measurement.GetPlatformMeasurement(measurement.WithMeasurementType(pb.CATEGORY_TDX_RTMR), measurement.WithRegisterIndex(0)) + if !bytes.Equal(replayVal0, val0.(measurement.TDXRtmrInfo).TDXRtmrRaw){ + t.Error("rtmr0 replay:fail") + }}) + + t.Run("rtmr1", func(t *testing.T) { + eventLogActor := newEventLogActor() + replayVal1 := eventLogActor.replay(1) + val1,_ := measurement.GetPlatformMeasurement(measurement.WithMeasurementType(pb.CATEGORY_TDX_RTMR), measurement.WithRegisterIndex(1)) + if !bytes.Equal(replayVal1, val1.(measurement.TDXRtmrInfo).TDXRtmrRaw){ + t.Error("rtmr1 replay:fail") + }}) + t.Run("rtmr2", func(t *testing.T) { + eventLogActor := newEventLogActor() + replayVal2 := eventLogActor.replay(2) + val2,_ := measurement.GetPlatformMeasurement(measurement.WithMeasurementType(pb.CATEGORY_TDX_RTMR), measurement.WithRegisterIndex(2)) + if !bytes.Equal(replayVal2, val2.(measurement.TDXRtmrInfo).TDXRtmrRaw){ + t.Error("rtmr2 replay:fail") + }}) + + t.Run("rtmr3", func(t *testing.T) { + eventLogActor := newEventLogActor() + replayVal3 := eventLogActor.replay(3) + val3,_ := measurement.GetPlatformMeasurement(measurement.WithMeasurementType(pb.CATEGORY_TDX_RTMR), measurement.WithRegisterIndex(3)) + if !bytes.Equal(replayVal3, val3.(measurement.TDXRtmrInfo).TDXRtmrRaw){ + t.Error("rtmr3 replay:fail") + }}) + + + } + diff --git a/test/e2e-test/profile/Dockerfile b/test/e2e-test/profile/Dockerfile new file mode 100644 index 0000000..0878aa1 --- /dev/null +++ b/test/e2e-test/profile/Dockerfile @@ -0,0 +1,30 @@ +FROM python:3.12.1-alpine3.19 + +ARG USER=ccnp +ARG UID=1000 +ARG GID=1000 +ARG GROUP=ccnp + +WORKDIR /run/confidential-cloud-native-primitives +COPY ./ ./ +RUN addgroup -S -g $GID $GROUP && adduser -S -u $UID -D -G $GROUP $USER +RUN chown -R $USER:$GROUP ./ + +RUN apk update && apk add bash vim +<<<<<<< HEAD +RUN pip install -r sdk/python3/requirements.txt && pip install pytest pytdxattest +======= +RUN pip install -r sdk/python3/requirements.txt && pip install pytest +>>>>>>> ddd915b (update e2e test) + +USER $UID + +COPY --from=golang:1.20-alpine /usr/local/go/ /usr/local/go/ +ENV PATH="/usr/local/go/bin:${PATH}" +ENV GOROOT="/usr/local/go" +ENV GOPATH="/home/ccnp/gopath" + +RUN pip install ./sdk/python3/ +RUN cd ./test/e2e-test/go-test && go mod tidy && cd ../../.. + +ENTRYPOINT ["tail", "-f", "/dev/null"] diff --git a/test/e2e-test/profile/ccnp-test-node.yaml b/test/e2e-test/profile/ccnp-test-node.yaml new file mode 100644 index 0000000..f3da3ff --- /dev/null +++ b/test/e2e-test/profile/ccnp-test-node.yaml @@ -0,0 +1,60 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ccnp-test-node +spec: + selector: + matchLabels: + app: ccnp-test + template: + metadata: + labels: + app: ccnp-test + spec: + initContainers: + - name: change-permissions + image: busybox + imagePullPolicy: IfNotPresent + resources: + limits: + memory: "2048Mi" + cpu: "2600m" + command: + - sh + - -c + - > + chown -R 1000:1000 /run/security/integrity/ima/ascii_runtime_measurements && + chmod -R 0544 /run/security/integrity/ima/ascii_runtime_measurements && + chown -R 1000:1000 /run/ccnp-eventlog + volumeMounts: + - name: runtime-eventlog-data + mountPath: /run/security/integrity/ima/ascii_runtime_measurements + - name: fetched-eventlog-data + mountPath: /run/ccnp-eventlog + containers: + - name: ccnp-test-node + image: "localhost:5001/ccnp-test-node:latest" + imagePullPolicy: IfNotPresent + resources: + limits: + memory: "2048Mi" + cpu: "2600m" + tdx.intel.com/tdx-guest: 1 + requests: + tdx.intel.com/tdx-guest: 1 + volumeMounts: + - name: runtime-eventlog-data + mountPath: /run/security/integrity/ima/ascii_runtime_measurements + - name: fetched-eventlog-data + mountPath: /run/ccnp-eventlog + volumes: + - name: runtime-eventlog-data + hostPath: + path: /sys/kernel/security/integrity/ima/ascii_runtime_measurements + type: File + - name: fetched-eventlog-data + hostPath: + path: /run/ccnp-eventlog/ + type: Directory + nodeSelector: + intel.feature.node.kubernetes.io/tdx-guest: enabled diff --git a/test/e2e-test/kind-config.yaml b/test/e2e-test/profile/kind-config.yaml similarity index 80% rename from test/e2e-test/kind-config.yaml rename to test/e2e-test/profile/kind-config.yaml index 61ce53d..f7bb312 100755 --- a/test/e2e-test/kind-config.yaml +++ b/test/e2e-test/profile/kind-config.yaml @@ -11,4 +11,7 @@ nodes: containerPath: /run/ccnp - hostPath: /run/ccnp-eventlog containerPath: /run/ccnp-eventlog + - hostPath: /sys/kernel/security + containerPath: /sys/kernel/security + diff --git a/test/e2e-test/repo-configmap.yaml b/test/e2e-test/profile/repo-configmap.yaml similarity index 100% rename from test/e2e-test/repo-configmap.yaml rename to test/e2e-test/profile/repo-configmap.yaml diff --git a/test/e2e-test/py-test/pysdk_utils.py b/test/e2e-test/py-test/pysdk_utils.py new file mode 100644 index 0000000..d5b90c3 --- /dev/null +++ b/test/e2e-test/py-test/pysdk_utils.py @@ -0,0 +1,133 @@ +""" +Define some classes and methods to support python SDK test. +""" +import os +import logging +import string +from hashlib import sha384 +from ccnp import Eventlog + + +LOG = logging.getLogger("Python SDK test") + +class BinaryBlob: + """ + Manage the binary blob. + """ + def __init__(self, data): + self._data = data + + @property + def length(self): + return len(self._data) + + @property + def data(self): + return self._data + + def get_bytes(self, pos, count): + if count == 0: + return None + assert pos + count <= self.length + return (self.data[pos:pos + count], pos + count) + +class RTMR(BinaryBlob): + """ + Data structure for RTMR registers. + A RTMR register manages a 48-bytes (384-bits) hash value. + """ + RTMR_COUNT = 4 + RTMR_LENGTH_BY_BYTES = 48 + + def __init__(self, data: bytearray = bytearray(RTMR_LENGTH_BY_BYTES)): + super().__init__(data) + + def __eq__(self, other): + bytearray_1, _ = self.get_bytes(0, RTMR.RTMR_LENGTH_BY_BYTES) + bytearray_2, _ = other.get_bytes(0, RTMR.RTMR_LENGTH_BY_BYTES) + + return bytearray(bytearray_1) == bytearray(bytearray_2) + +class CCEventLogActor: + """Event log actor + The actor to process event logs and do replay + """ + RUNTIME_REGISTER = 2 + + def __init__(self): + self._boot_time_event_logs = [] + self._runtime_event_logs = [] + + def _fetch_boot_time_event_logs(self): + self._boot_time_event_logs = Eventlog.get_platform_eventlog() + + def _fetch_runtime_event_logs(self): + ima_measurement_file = "/run/security/integrity/ima/ascii_runtime_measurements" + with open(ima_measurement_file, encoding="utf-8") as f: + for line in f: + self._runtime_event_logs.append(line) + + @staticmethod + def _replay_single_boot_time_rtmr(event_logs) -> RTMR: + """Replay single RTMR for boot time events""" + rtmr = bytearray(RTMR.RTMR_LENGTH_BY_BYTES) + + for event_log in event_logs: + digest = list(map(int, event_log.digest.strip('[]').split(' '))) + # pylint: disable-next=consider-using-f-string + digest_hex = ''.join('{:02x}'.format(i) for i in digest) + sha384_algo = sha384() + sha384_algo.update(bytes.fromhex(rtmr.hex() + digest_hex)) + rtmr = sha384_algo.digest() + + return RTMR(rtmr) + + @staticmethod + def _replay_runtime_rtmr(event_logs, base: RTMR) -> RTMR: + """Replay runtime measurements based on the runtime event logs""" + rtmr = bytearray(RTMR.RTMR_LENGTH_BY_BYTES) + + val = base.data.hex() + for event_log in event_logs: + elements = event_log.split(" ") + extend_val = val + elements[2] + sha384_algo = sha384() + sha384_algo.update(bytes.fromhex(extend_val)) + val = sha384_algo.hexdigest() + + rtmr = sha384_algo.digest() + return RTMR(rtmr) + + def replay(self, index:int) -> RTMR: + """Replay event logs including boot time event logs and runtime event logs to + generate RTMR values for verification. + """ + self._fetch_boot_time_event_logs() + boot_time_event_logs_by_index = {} + boot_time_event_logs_by_index[index] = [] + + for event_log in self._boot_time_event_logs: + if event_log.reg_idx==index: + boot_time_event_logs_by_index[event_log.reg_idx].append(event_log) + # replay boot time event logs + rtmr_value = CCEventLogActor._replay_single_boot_time_rtmr(boot_time_event_logs_by_index[index]) + ima_flag= True + with open("/proc/cmdline", encoding="utf-8") as proc_f: + cmdline = proc_f.read().splitlines() + if "ima_hash=sha384" not in cmdline[0].split(" "): + # pylint: disable-next=line-too-long + LOG.info("IMA over RTMR not enabled. Verify basic boot measurements.") + ima_flag = False + + # fetch and replay the runtime event logs + if CCEventLogActor.RUNTIME_REGISTER == index and ima_flag: + ima_measurement_file = \ + "/run/security/integrity/ima/ascii_runtime_measurements" + assert os.path.exists(ima_measurement_file), \ + f"Could not find the IMA measurement file {ima_measurement_file}" + LOG.info("IMA event logs found in the system.\n") + self._fetch_runtime_event_logs() + concat_rtmr_value = CCEventLogActor._replay_runtime_rtmr( + self._runtime_event_logs, rtmr_value) + rtmr_value = concat_rtmr_value + return rtmr_value diff --git a/test/e2e-test/py-test/test_rtmrs.py b/test/e2e-test/py-test/test_rtmrs.py new file mode 100644 index 0000000..98006b2 --- /dev/null +++ b/test/e2e-test/py-test/test_rtmrs.py @@ -0,0 +1,49 @@ +""" +RTMR test: +1. Fetch boot time event logs using CCNP sdk and fetch runtime event logs(from IMA) in kernel memory +2. Re-calcuate the overall digest +3. Fetch measurements using CCNP sdk +4. Compare the recalculated values with the rtmrs in the measurements +""" + +import pysdk_utils +import logging +import base64 +from ccnp import Measurement +from ccnp import MeasurementType + +class TestRtmr: + + def test_rtmr0(self): + cc_event_log_actor = pysdk_utils.CCEventLogActor() + replay_val0 = cc_event_log_actor.replay(index=0) + val0 = Measurement.get_platform_measurement( + MeasurementType.TYPE_TDX_RTMR, None, 0) + rtmr_val0 = pysdk_utils.RTMR(bytearray(base64.b64decode(val0))) + assert rtmr_val0 == replay_val0 + + + def test_rtmr1(self): + cc_event_log_actor = pysdk_utils.CCEventLogActor() + replay_val1 = cc_event_log_actor.replay(index=1) + val1 = Measurement.get_platform_measurement( + MeasurementType.TYPE_TDX_RTMR, None, 1) + rtmr_val1 = pysdk_utils.RTMR(bytearray(base64.b64decode(val1))) + assert rtmr_val1 == replay_val1 + + def test_rtmr2(self): + cc_event_log_actor = pysdk_utils.CCEventLogActor() + replay_val2 = cc_event_log_actor.replay(index=2) + val2 = Measurement.get_platform_measurement( + MeasurementType.TYPE_TDX_RTMR, None, 2) + rtmr_val2 = pysdk_utils.RTMR(bytearray(base64.b64decode(val2))) + assert rtmr_val2 == replay_val2 + + def test_rtmr3(self): + cc_event_log_actor = pysdk_utils.CCEventLogActor() + replay_val3 = cc_event_log_actor.replay(index=3) + val3 = Measurement.get_platform_measurement( + MeasurementType.TYPE_TDX_RTMR, None, 3) + rtmr_val3 = pysdk_utils.RTMR(bytearray(base64.b64decode(val3))) + assert rtmr_val3 == replay_val3 + diff --git a/test/e2e-test/test_eventlog.py b/test/e2e-test/test_eventlog.py deleted file mode 100755 index e2301f7..0000000 --- a/test/e2e-test/test_eventlog.py +++ /dev/null @@ -1,35 +0,0 @@ -import pytest -import base64 -from ccnp import Eventlog -from ccnp import EventlogType -from pytdxattest.actor import TDEventLogActor -from pytdxattest.ccel import CCEL - - -class Testeventlog: - def test_every_event(self): - logs = Eventlog.get_platform_eventlog() - ccelobj = CCEL.create_from_acpi_file() - if ccelobj is None: - return - ccelobj.dump() - actor = TDEventLogActor(ccelobj.log_area_start_address,ccelobj.log_area_minimum_length) - actor.process() - for i in range(0,len(logs)): - assert base64.b64decode(logs[i].event) == actor._event_logs[i]._event - - def test_every_digest(self): - logs = Eventlog.get_platform_eventlog() - ccelobj = CCEL.create_from_acpi_file() - if ccelobj is None: - return - ccelobj.dump() - actor = TDEventLogActor(ccelobj.log_area_start_address,ccelobj.log_area_minimum_length) - actor.process() - assert len(logs)==len(actor._event_logs) - for i in range(0,len(logs)): - digest=[] - for j in range(0,len((actor._event_logs[i]._digests)[0])): - digest.append((actor._event_logs[i]._digests)[0][j]) - assert str(digest).replace(",","") == logs[i].digest - diff --git a/test/e2e-test/test_tdquote.py b/test/e2e-test/test_tdquote.py deleted file mode 100755 index d9bf805..0000000 --- a/test/e2e-test/test_tdquote.py +++ /dev/null @@ -1,18 +0,0 @@ -import pytest -from ccnp import Quote -from pytdxattest.tdreport import TdReport - -class Testquote: - def test_integral_quote(self): - quote1 = Quote.get_quote() - quote2 = Quote.get_quote() - assert quote1.quote_type==quote2.quote_type - assert quote1.quote!=quote2.quote - def test_quote_rtmrs(self): - quote = Quote.get_quote() - report=TdReport.get_td_report() - report_rtmrs=report.td_info.rtmr_0+report.td_info.rtmr_1+report.td_info.rtmr_2+report.td_info.rtmr_3 - assert len(quote._rtmrs) == len (report_rtmrs) - for i in range(0,len(quote._rtmrs)): - assert quote._rtmrs[i] == report_rtmrs[i] - diff --git a/test/e2e-test/test_tdreport.py b/test/e2e-test/test_tdreport.py deleted file mode 100755 index 0d4ceb8..0000000 --- a/test/e2e-test/test_tdreport.py +++ /dev/null @@ -1,13 +0,0 @@ -import pytest -import base64 -from ccnp import Measurement -from ccnp import MeasurementType -from pytdxattest.tdreport import TdReport - - -class TestTdreport: - def test_without_data(self): - ccnp_report = Measurement.get_platform_measurement() - py_report = TdReport.get_td_report() - assert base64.b64decode(ccnp_report) == py_report.data -