diff --git a/.attestation_container_canary b/.attestation_container_canary deleted file mode 100644 index cda753c92e97..000000000000 --- a/.attestation_container_canary +++ /dev/null @@ -1,3 +0,0 @@ -<<<>??<><><><> -<>< -<>> diff --git a/.azure-pipelines-attestation-container.yml b/.azure-pipelines-attestation-container.yml deleted file mode 100644 index fa99788739d9..000000000000 --- a/.azure-pipelines-attestation-container.yml +++ /dev/null @@ -1,29 +0,0 @@ -trigger: - batch: true - branches: - include: - - main - -pr: - branches: - include: - - main - paths: - include: - - .attestation_container_canary - - attestation-container/* - - .azure_pipelines_attestation_container.yml - - .azure-pipelines-templates/* - - scripts/azure_deployment/* - -jobs: - - template: .azure-pipelines-templates/deploy_attestation_container.yml - parameters: - used_by: - - test_attestation_container - - - template: .azure-pipelines-templates/test_attestation_container.yml - parameters: - job_name: test_attestation_container - display_name: "Test Attestation Container" - depends_on: deploy_attestation_container diff --git a/.azure-pipelines-templates/configure.yml b/.azure-pipelines-templates/configure.yml index 68390075efcb..3f2ab5213dd4 100644 --- a/.azure-pipelines-templates/configure.yml +++ b/.azure-pipelines-templates/configure.yml @@ -11,7 +11,7 @@ jobs: clean: true - script: | echo "Determine if any code has changed." - if git diff --ignore-submodules=dirty --quiet origin/${SYSTEM_PULLREQUEST_TARGETBRANCH:-origin/main} -- ':!doc' ':!*.md' ':!tla' ':!scripts/azure_deployment' ':!.azure-pipelines-attestation-container.yml'; then + if git diff --ignore-submodules=dirty --quiet origin/${SYSTEM_PULLREQUEST_TARGETBRANCH:-origin/main} -- ':!doc' ':!*.md' ':!tla' ':!scripts/azure_deployment'; then echo " - Documentation change only" echo "##vso[task.setvariable variable=docOnly;isOutput=true]true" #set variable docOnly to true else diff --git a/.azure-pipelines-templates/deploy_attestation_container.yml b/.azure-pipelines-templates/deploy_attestation_container.yml deleted file mode 100644 index 95bc405f8e23..000000000000 --- a/.azure-pipelines-templates/deploy_attestation_container.yml +++ /dev/null @@ -1,90 +0,0 @@ -jobs: - - job: deploy_attestation_container - displayName: "Deploy Attestation Container" - variables: - Codeql.SkipTaskAutoInjection: true - skipComponentGovernanceDetection: true - pool: - vmImage: ubuntu-20.04 - steps: - - template: azure_cli.yml - parameters: - app_id: $(ATTESTATION_CONTAINER_CI_APP_ID) - service_principal_password: $(ATTESTATION_CONTAINER_CI_SERVICE_PRINCIPAL_PASSWORD) - tenant: $(ATTESTATION_CONTAINER_CI_TENANT) - - - template: build-ci-container.yml - parameters: - src_dir: attestation-container - image_name: attestation-container - container_registry: attestationcontainerregistry - docker_build_options: "--build-arg variant=dev" - - - script: | - set -ex - python3.8 -m venv ./scripts/azure_deployment/.env - source ./scripts/azure_deployment/.env/bin/activate - pip install -r ./scripts/azure_deployment/requirements.txt - python3.8 scripts/azure_deployment/arm_template.py deploy aci \ - --subscription-id $(CCF_AZURE_SUBSCRIPTION_ID) \ - --resource-group attestation-container \ - --aci-type dynamic-agent \ - --deployment-name ci-$(Build.BuildNumber) \ - --attestation-container-e2e \ - --generate-security-policy - name: deploy_attestation_container - displayName: "Deploy Attestation Container" - env: - CCF_AZURE_SUBSCRIPTION_ID: $(CCF_AZURE_SUBSCRIPTION_ID) - - - job: cleanup_aci - displayName: "Cleanup Attestation Container" - pool: - vmImage: ubuntu-20.04 - dependsOn: - - deploy_attestation_container - - ${{ parameters.used_by }} - condition: always() - steps: - - template: azure_cli.yml - parameters: - app_id: $(ATTESTATION_CONTAINER_CI_APP_ID) - service_principal_password: $(ATTESTATION_CONTAINER_CI_SERVICE_PRINCIPAL_PASSWORD) - tenant: $(ATTESTATION_CONTAINER_CI_TENANT) - - - script: | - set -ex - python3.8 -m venv ./scripts/azure_deployment/.env - source ./scripts/azure_deployment/.env/bin/activate - pip install -r ./scripts/azure_deployment/requirements.txt - python3.8 scripts/azure_deployment/arm_template.py remove aci \ - --subscription-id $(CCF_AZURE_SUBSCRIPTION_ID) \ - --resource-group attestation-container \ - --aci-type dynamic-agent \ - --deployment-name ci-$(Build.BuildNumber) - name: cleanup_aci - displayName: "Delete the ACI and Azure Deployment" - - - job: cleanup_container_image - displayName: "Cleanup Attestation Container image" - pool: - vmImage: ubuntu-20.04 - dependsOn: - - deploy_attestation_container - - ${{ parameters.used_by }} - variables: - image_version: $[ dependencies.deploy_attestation_container.outputs['build_ci_container.image_version'] ] - condition: always() - steps: - - template: azure_cli.yml - parameters: - app_id: $(ATTESTATION_CONTAINER_CI_APP_ID) - service_principal_password: $(ATTESTATION_CONTAINER_CI_SERVICE_PRINCIPAL_PASSWORD) - tenant: $(ATTESTATION_CONTAINER_CI_TENANT) - - - script: | - set -ex - az acr repository delete --name attestationcontainerregistry --image attestation-container:$(image_version) --yes - - name: cleanup_container_image - displayName: "Delete the container image" diff --git a/.azure-pipelines-templates/test_attestation_container.yml b/.azure-pipelines-templates/test_attestation_container.yml deleted file mode 100644 index 9e461ddd4973..000000000000 --- a/.azure-pipelines-templates/test_attestation_container.yml +++ /dev/null @@ -1,43 +0,0 @@ -parameters: - depends_on: "" - condition: "" - -jobs: - - job: ${{ parameters.job_name }} - displayName: ${{ parameters.display_name }} - dependsOn: ${{ parameters.depends_on }} - condition: ${{ parameters.condition }} - pool: - vmImage: ubuntu-20.04 - timeoutInMinutes: 120 - variables: - Codeql.SkipTaskAutoInjection: true - skipComponentGovernanceDetection: true - - steps: - - template: azure_cli.yml - parameters: - app_id: $(ATTESTATION_CONTAINER_CI_APP_ID) - service_principal_password: $(ATTESTATION_CONTAINER_CI_SERVICE_PRINCIPAL_PASSWORD) - tenant: $(ATTESTATION_CONTAINER_CI_TENANT) - - - script: | - set -ex - # The following sed is to change 20221213.48 to 2022121348 for example - echo "##vso[task.setvariable variable=BUILD_NUMBER]$(echo $(Build.BuildNumber) | sed 's/\.//g')" - name: set_build_number_var - displayName: "Set BUILD_NUMBER variable" - - # See https://github.com/Azure/azure-cli/issues/13352 for rationale for use of "script return..." - - script: | - set -ex - script --return -c "az container exec --resource-group attestation-container --name ci-$(BUILD_NUMBER)-business-logic-0 --container-name ci-$(BUILD_NUMBER)-attestation-container --exec-command 'attest.test --testdata-dir /testdata'" /dev/null - script --return -c "az container exec --resource-group attestation-container --name ci-$(BUILD_NUMBER)-business-logic-0 --container-name ci-$(BUILD_NUMBER)-attestation-container --exec-command 'uvm.test'" /dev/null - name: run_unit_test - displayName: "Unit Test in Attestation Container Instance Deployed to ACIs" - - - script: | - set -ex - script --return -c "az container exec --resource-group attestation-container --name ci-$(BUILD_NUMBER)-business-logic-0 --container-name ci-$(BUILD_NUMBER)-dummy-business-logic-container --exec-command 'attestation-container.test -addr /mnt/uds/sock -test.v'" /dev/null - name: test_attestation_container - displayName: "Test attestation container with dummy business logic container Instance Deployed to ACIs" diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 1d939a6a12e2..49c7536256b2 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -14,8 +14,3 @@ updates: directory: "/python" # Location of package manifests schedule: interval: "weekly" - - - package-ecosystem: "gomod" - directory: "/attestation-container" # Location of package manifests - schedule: - interval: "weekly" diff --git a/.github/workflows/attestation_container_image.yml b/.github/workflows/attestation_container_image.yml deleted file mode 100644 index 61a568f1a784..000000000000 --- a/.github/workflows/attestation_container_image.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: "Build and Publish Release Attestation Container Image to MCR" - -on: - release: - types: [published] - -env: - ACR_REGISTRY: attestationcontainermsrc.azurecr.io - ACR_TOKEN_NAME: attestation-container-push-token - DOCKER_BUILDKIT: 1 # https://docs.docker.com/develop/develop-images/build_enhancements/ - -jobs: - build_and_publish: - name: "Build & push" - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - - name: Get image tag from git tag (release) - run: | - echo "tag=${GITHUB_REF#refs/tags/ccf-}" >> $GITHUB_OUTPUT - id: tref - - - name: Build Attestation Container - run: docker build attestation-container -t $ACR_REGISTRY/public/ccf/attestation-container/run:${{ steps.tref.outputs.tag }} - - - name: Log in - run: | - docker login -u $ACR_TOKEN_NAME -p ${{ secrets.ACR_ATTESTATION_CONTAINER_PUSH_TOKEN_PASSWORD }} $ACR_REGISTRY - - - name: Push App container - run: docker push $ACR_REGISTRY/public/ccf/attestation-container/run:${{ steps.tref.outputs.tag }} diff --git a/.gitignore b/.gitignore index 103290ed9ec8..1bd183227b3f 100644 --- a/.gitignore +++ b/.gitignore @@ -44,5 +44,3 @@ tests/perf-system/analyzer/*.png **/*.ipynb* scripts/azure_deployment/.env tests/external_executor/executors/ccf/protobuf/*.proto -tests/external_executor/executors/attestation_container/protobuf/*.proto -src/apps/external_executor/protobuf/attestation-container.proto diff --git a/CMakeLists.txt b/CMakeLists.txt index 3203e0bd4f6b..bcbc7e60394c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -830,9 +830,6 @@ add_custom_target( # Add sample apps add_subdirectory(${CCF_DIR}/samples) -# Include attestation container to copy .proto files -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/attestation-container) - # Add external_executor app add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/apps/external_executor) diff --git a/attestation-container/.dockerignore b/attestation-container/.dockerignore deleted file mode 100644 index fdaf16a4d128..000000000000 --- a/attestation-container/.dockerignore +++ /dev/null @@ -1 +0,0 @@ -**/*.test \ No newline at end of file diff --git a/attestation-container/.gitignore b/attestation-container/.gitignore deleted file mode 100644 index f347f7189e3d..000000000000 --- a/attestation-container/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -attestation-container -attestation-container.test -attest/attest.test diff --git a/attestation-container/CMakeLists.txt b/attestation-container/CMakeLists.txt deleted file mode 100644 index 85a18f07a232..000000000000 --- a/attestation-container/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the Apache 2.0 License. - -# For now, copy .proto files across to external executor test -configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/protobuf/attestation-container.proto - ${CCF_DIR}/tests/external_executor/executors/attestation_container/protobuf/attestation-container.proto - COPYONLY -) - -# Note: can be removed once all external executors are containerised -# and ccf/registration.py is no longer imported by main test code -configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/protobuf/attestation-container.proto - ${CCF_DIR}/src/apps/external_executor/protobuf/attestation-container.proto - COPYONLY -) \ No newline at end of file diff --git a/attestation-container/Dockerfile b/attestation-container/Dockerfile deleted file mode 100644 index 98072d128bef..000000000000 --- a/attestation-container/Dockerfile +++ /dev/null @@ -1,38 +0,0 @@ -# Based on https://hub.docker.com/_/golang/ - -# To run this: -# docker build -t attestation-container . [--build-arg variant=dev] -# docker run -it --rm attestation-container - -# Two variants: run (production) or dev (development) -ARG variant=run - -# Build -FROM mcr.microsoft.com/oss/go/microsoft/golang:1.20-fips-cbl-mariner2.0 AS builder-app -WORKDIR /usr/src/app - -# pre-copy/cache go.mod for pre-downloading dependencies and only redownloading them in subsequent builds if they change -COPY . . -RUN go mod download && go mod verify - -# Note: Use CGO_ENABLED=0 to build statically-linked binaries deployable in scratch image -RUN CGO_ENABLED=0 go build -v -o /usr/local/bin/app . - -FROM builder-app AS builder-test -WORKDIR /usr/src/app -## Pre-compile tests to avoid I/O errors in ACI -RUN CGO_ENABLED=0 go test -c && cd attest && CGO_ENABLED=0 go test -c && cd ../uvm && CGO_ENABLED=0 go test -c - -# Run -FROM mcr.microsoft.com/cbl-mariner/distroless/minimal:2.0 AS app-run -COPY --from=builder-app /usr/local/bin/app /usr/local/bin/app - -FROM app-run AS app-dev -COPY --from=builder-test /usr/src/app/attestation-container.test* /usr/local/bin/attestation-container.test -COPY --from=builder-test /usr/src/app/attest/attest.test* /usr/local/bin/attest.test -COPY --from=builder-test /usr/src/app/attest/testdata/host_amd_certificate_env* /testdata/ -COPY --from=builder-test /usr/src/app/uvm/uvm.test* /usr/local/bin/uvm.test - -# Final -FROM app-${variant} AS final -CMD ["app"] diff --git a/attestation-container/README.md b/attestation-container/README.md deleted file mode 100644 index 674ec77ddd60..000000000000 --- a/attestation-container/README.md +++ /dev/null @@ -1,89 +0,0 @@ -# Attestation Container - -This is a gRPC server application to fetch SEV-SNP attestation and its endorsement. - -## Environment - -This application needs to run on [SEV-SNP VM](https://www.amd.com/system/files/TechDocs/SEV-SNP-strengthening-vm-isolation-with-integrity-protection-and-more.pdf). - -## Dependencies - -- [Go](https://go.dev/doc/install) -- [gRPC](https://grpc.io/docs/languages/go/quickstart/) - -## How to start the app - -The following command starts the gRPC server application (must be inside SEV-SNP VM). - -```bash -# In the same directory as this README.md -go run . -``` - -You can use insecure virtual mode to run the application on non SEV-SNP VM. -(**Not secure. Do not use it in production**). - -```bash -go run . --insecure-virtual -``` - -You can find the details of the flag and other flags by running `go run . --help`. - -## Build - -Since it's a go application, you can build the application before running it. - -```bash -go build -./attestation-container -``` - -## API - -The gPRC API is defined in [attestation-container.proto](https://github.com/microsoft/CCF/blob/main/attestation-container/protobuf/attestation-container.proto). - -Note that gPRC communication is used over [Unix domain sockets (UDS)](https://en.wikipedia.org/wiki/Unix_domain_socket). You can find an example client code in [the E2E test](https://github.com/microsoft/CCF/blob/main/attestation-container/attestation-container_test.go). - -## Test - -Unit test: - -```bash -cd attest -go test # Test for attest package - -cd ../uvm -go test # Test for uvm package -``` - -E2E test: - -```bash -# Run the app first -go run . - -# In another terminal -go test -``` - -## Development and maintenance - -### Update protobuf - -When you edit `.proto` file, you also need to update `.pb.go` files by: - -```bash -protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative protobuf/attestation-container.proto -``` - -### Upgrade dependencies - -PRs to upgrade the dependencies are created automatically by [Dependabot](https://docs.github.com/en/code-security/dependabot/working-with-dependabot) (The setting is done [here](https://github.com/microsoft/CCF/blob/main/.github/dependabot.yml)). - -However, when Dependabot creates multiple PRs at the same time, go.mod file can be corrupted. -In that case, you still need to fix go.mod using `go` command manually. - -```bash -go get -u -go mod tidy -``` diff --git a/attestation-container/attest/attestation.go b/attestation-container/attest/attestation.go deleted file mode 100644 index 939d43b57dc6..000000000000 --- a/attestation-container/attest/attestation.go +++ /dev/null @@ -1,152 +0,0 @@ -package attest - -import ( - "bytes" - "encoding/binary" - "fmt" - "unsafe" - - "golang.org/x/sys/unix" -) - -// Data structures are based on SEV-SNP Firmware ABI Specification -// https://www.amd.com/en/support/tech-docs/sev-secure-nested-paging-firmware-abi-specification - -const ( - ATTESTATION_REPORT_SIZE = 1184 // Size of ATTESTATION_REPORT (Table 21) - REPORT_DATA_SIZE = 64 // Size of REPORT_DATA_SIZE in ATTESTATION_REPORT - REPORTED_TCB_OFFSET = 384 - REPORTED_TCB_SIZE = 8 - CHIP_ID_OFFSET = 416 - CHIP_ID_SIZE = 64 - REPORT_REQ_SIZE = 96 // Size of MSG_REPORT_REQ (Table 20) - REPORT_RSP_SIZE = 1280 // Size of MSG_REPORT_RSP (Table 23) - PAYLOAD_SIZE = 40 // Size of sev_snp_guest_request struct from sev-snp driver include/uapi/linux/psp-sev-guest.h -) - -// Message Type Encodings (Table 100) -const ( - MSG_REPORT_REQ = 5 - MSG_REPORT_RSP = 6 -) - -// From sev-snp driver include/uapi/linux/psp-sev-guest.h -const SEV_SNP_GUEST_MSG_REPORT = 3223868161 - -const SNP_DEVICE_PATH = "/dev/sev" - -/* -Creates and returns MSG_REPORT_REQ message bytes (SEV-SNP Firmware ABI Specification Table 20) -*/ -func createReportReqBytes(reportData [REPORT_DATA_SIZE]byte) [REPORT_REQ_SIZE]byte { - reportReqBytes := [REPORT_REQ_SIZE]byte{} - copy(reportReqBytes[0:REPORT_DATA_SIZE], reportData[:]) - return reportReqBytes -} - -/* -Creates and returns byte array of the following C struct - -// From sev-snp driver include/uapi/linux/psp-sev-guest.h -// struct sev_snp_guest_request { -// uint8_t req_msg_type; -// uint8_t rsp_msg_type; -// uint8_t msg_version; -// uint16_t request_len; -// uint64_t request_uaddr; -// uint16_t response_len; -// uint64_t response_uaddr; -// uint32_t error; // firmware error code on failure (see psp-sev.h) -// }; - -The padding is based on Section 3.1.2 of System V ABI for AMD64 -https://www.uclibc.org/docs/psABI-x86_64.pdf -*/ -func createPayloadBytes(reportReqPtr uintptr, ReportRespPtr uintptr) ([PAYLOAD_SIZE]byte, error) { - payload := [PAYLOAD_SIZE]byte{} - var buf bytes.Buffer - // req_msg_type - if err := binary.Write(&buf, binary.LittleEndian, uint8(MSG_REPORT_REQ)); err != nil { - return payload, err - } - // rsp_msg_type - if err := binary.Write(&buf, binary.LittleEndian, uint8(MSG_REPORT_RSP)); err != nil { - return payload, err - } - // msg_version - if err := binary.Write(&buf, binary.LittleEndian, uint8(1)); err != nil { - return payload, err - } - // Padding - if err := binary.Write(&buf, binary.LittleEndian, uint8(0)); err != nil { - return payload, err - } - // request_len - if err := binary.Write(&buf, binary.LittleEndian, uint16(REPORT_REQ_SIZE)); err != nil { - return payload, err - } - // Padding - if err := binary.Write(&buf, binary.LittleEndian, uint16(0)); err != nil { - return payload, err - } - // request_uaddr - if err := binary.Write(&buf, binary.LittleEndian, uint64(reportReqPtr)); err != nil { - return payload, err - } - // response_len - if err := binary.Write(&buf, binary.LittleEndian, uint16(REPORT_RSP_SIZE)); err != nil { - return payload, err - } - // Padding - if err := binary.Write(&buf, binary.LittleEndian, [3]uint16{}); err != nil { - return payload, err - } - // response_uaddr - if err := binary.Write(&buf, binary.LittleEndian, uint64(ReportRespPtr)); err != nil { - return payload, err - } - // error - if err := binary.Write(&buf, binary.LittleEndian, uint32(0)); err != nil { - return payload, err - } - // Padding - if err := binary.Write(&buf, binary.LittleEndian, uint32(0)); err != nil { - return payload, err - } - for i, x := range buf.Bytes() { - payload[i] = x - } - return payload, nil -} - -func FetchAttestationReportByte(reportData [64]byte) ([]byte, error) { - fd, err := unix.Open(SNP_DEVICE_PATH, unix.O_RDWR|unix.O_CLOEXEC, 0) - if err != nil { - return nil, fmt.Errorf("Error opening SNP device %s: %s", SNP_DEVICE_PATH, err) - } - - reportReqBytes := createReportReqBytes(reportData) - // MSG_REPORT_RSP message bytes (SEV-SNP Firmware Firmware ABI Specification Table 23) - reportRspBytes := [REPORT_RSP_SIZE]byte{} - payload, err := createPayloadBytes(uintptr(unsafe.Pointer(&reportReqBytes[0])), uintptr(unsafe.Pointer(&reportRspBytes[0]))) - if err != nil { - return nil, err - } - - _, _, errno := unix.Syscall( - unix.SYS_IOCTL, - uintptr(fd), - uintptr(SEV_SNP_GUEST_MSG_REPORT), - uintptr(unsafe.Pointer(&payload[0])), - ) - - if errno != 0 { - return nil, fmt.Errorf("ioctl failed:%v", errno) - } - - if status := binary.LittleEndian.Uint32(reportRspBytes[0:4]); status != 0 { - return nil, fmt.Errorf("fetching attestation report failed. status: %v", status) - } - const SNP_REPORT_OFFSET = 32 - return reportRspBytes[SNP_REPORT_OFFSET : SNP_REPORT_OFFSET+ATTESTATION_REPORT_SIZE], nil -} diff --git a/attestation-container/attest/attestation_endorsement.go b/attestation-container/attest/attestation_endorsement.go deleted file mode 100644 index f4ae2419bdad..000000000000 --- a/attestation-container/attest/attestation_endorsement.go +++ /dev/null @@ -1,175 +0,0 @@ -package attest - -import ( - "crypto/x509" - "encoding/base64" - "encoding/binary" - "encoding/hex" - "encoding/json" - "encoding/pem" - "fmt" - "io/ioutil" - "math" - "math/rand" - "net/http" - "os" - "path" - "time" -) - -const ( - defaultBaseSec = 2 - defaultMaxRetries = 5 -) - -const ( - AMD_ENDORSEMENT_HOST = "https://kdsintf.amd.com" - AZURE_ENDORSEMENT_HOST = "https://global.acccache.azure.net" - - DEFAULT_SECURITY_CONTEXT_ENVVAR = "UVM_SECURITY_CONTEXT_DIR" // SEV-SNP ACI deployments - UVM_ENDORSEMENTS_FILE_NAME = "reference-info-base64" - REPORT_ENDORSEMENTS_FILE_NAME = "host-amd-cert-base64" -) - -type ACIEndorsements struct { - CacheControl string `json:"cacheControl"` - VcekCert string `json:"vcekCert"` - CertificateChain string `json:"certificateChain"` - Tcbm string `json:"tcbm"` -} - -func fetchWithRetry(requestURL string, baseSec int, maxRetries int) ([]byte, error) { - if maxRetries < 0 { - return nil, fmt.Errorf("invalid `maxRetries` value") - } - var err error - retryCount := 0 - for retryCount <= maxRetries { - if retryCount > 0 { - // Exponential backoff - maxDelay := math.Pow(float64(baseSec), float64(retryCount)) - delaySec := rand.Float64() * maxDelay - delaySecInt := math.Min(math.MaxInt64, delaySec) - time.Sleep(time.Duration(delaySecInt) * time.Second) - } - res, err := http.Get(requestURL) - if err != nil { - retryCount++ - continue - } - if 200 <= res.StatusCode && res.StatusCode < 300 { - // Got successful status code 2xx - defer res.Body.Close() - resBody, err := ioutil.ReadAll(res.Body) - if err != nil { - retryCount++ - continue - } - return resBody, nil - } else if res.StatusCode == 408 || res.StatusCode == 429 || 500 <= res.StatusCode { - // Got status code that is worth to retry - retryCount++ - continue - } else { - // Got status code that is not worth to retry - defer res.Body.Close() - resBody, err := ioutil.ReadAll(res.Body) - if err != nil { - return nil, fmt.Errorf("got error while handling non successful response with status code %d: %s", res.StatusCode, err) - } - return nil, fmt.Errorf("GET request failed with status code %d: %s", res.StatusCode, resBody) - } - } - return nil, err -} - -func fetchAttestationEndorsementAzure(reportedTCBBytes [REPORTED_TCB_SIZE]byte, chipID string) ([]byte, error) { - // Fetch attestation endorsement from Azure endpoint - reportedTCB := binary.LittleEndian.Uint64(reportedTCBBytes[:]) - reportedTCBHex := fmt.Sprintf("%x", reportedTCB) - requestURL := fmt.Sprintf("%s/SevSnpVM/certificates/%s/%s?api-version=2020-10-15-preview", AZURE_ENDORSEMENT_HOST, chipID, reportedTCBHex) - return fetchWithRetry(requestURL, defaultBaseSec, defaultMaxRetries) -} - -func fetchAttestationEndorsementAMD(reportedTCBBytes [REPORTED_TCB_SIZE]byte, chipID string) ([]byte, error) { - // Fetch attestation endorsement from AMD endpoint - // https://www.amd.com/en/support/tech-docs/versioned-chip-endorsement-key-vcek-certificate-and-kds-interface-specification - - boot_loader := reportedTCBBytes[0] - tee := reportedTCBBytes[1] - snp := reportedTCBBytes[6] - microcode := reportedTCBBytes[7] - const PRODUCT_NAME = "Milan" - requestURL := fmt.Sprintf("%s/vcek/v1/%s/%s?blSPL=%d&teeSPL=%d&snpSPL=%d&ucodeSPL=%d", AMD_ENDORSEMENT_HOST, PRODUCT_NAME, chipID, boot_loader, tee, snp, microcode) - vcekCertDER, err := fetchWithRetry(requestURL, defaultBaseSec, defaultMaxRetries) - if err != nil { - return nil, err - } - - vcek, err := x509.ParseCertificate(vcekCertDER) - if err != nil { - return nil, fmt.Errorf("Could not decode VCEK: %s", err) - } - endorsement := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: vcek.Raw}) - - requestURLChain := fmt.Sprintf("%s/vcek/v1/%s/cert_chain", AMD_ENDORSEMENT_HOST, PRODUCT_NAME) - endorsementCertChain, err := fetchWithRetry(requestURLChain, defaultBaseSec, defaultMaxRetries) - if err != nil { - return nil, err - } - return append(endorsement, endorsementCertChain...), nil -} - -/* -Fetches attestation endorsements of SEV-SNP VM. - -The endorsements are concatenation of VCEK, ASK, and ARK certificates (PEM format, in that order). -https://www.amd.com/en/support/tech-docs/versioned-chip-endorsement-key-vcek-certificate-and-kds-interface-specification -*/ -func FetchAttestationEndorsement(server string, reportedTCBBytes []byte, chipIDBytes []byte) ([]byte, error) { - if server != "AMD" && server != "Azure" { - return nil, fmt.Errorf("invalid endorsement server: %s", server) - } - if len(reportedTCBBytes) != REPORTED_TCB_SIZE { - return nil, fmt.Errorf("Length of reportedTCBBytes should be %d", REPORTED_TCB_SIZE) - } - if len(chipIDBytes) != CHIP_ID_SIZE { - return nil, fmt.Errorf("Length of chipIDBytes should be %d", CHIP_ID_SIZE) - } - - reportedTCB := [REPORTED_TCB_SIZE]byte{} - copy(reportedTCB[:], reportedTCBBytes) - chipID := hex.EncodeToString(chipIDBytes) - if server == "Azure" { - return fetchAttestationEndorsementAzure(reportedTCB, chipID) - } else { - return fetchAttestationEndorsementAMD(reportedTCB, chipID) - } -} - -func ParseEndorsementACI(endorsementACIBase64 string) (ACIEndorsements, error) { - endorsementsRaw, err := base64.StdEncoding.DecodeString(endorsementACIBase64) - if err != nil { - return ACIEndorsements{}, fmt.Errorf("Failed to decode ACI endorsements: %s", err) - } - - endorsements := ACIEndorsements{} - err = json.Unmarshal([]byte(endorsementsRaw), &endorsements) - if err != nil { - return ACIEndorsements{}, fmt.Errorf("Failed to unmarshal JSON ACI endorsements: %s", err) - } - return endorsements, nil -} - -func ParseEndorsementACIFromSecurityContextDirectory(securityContextDirectory string) (ACIEndorsements, error) { - endorsementsBase64, err := os.ReadFile(path.Join(securityContextDirectory, REPORT_ENDORSEMENTS_FILE_NAME)) - if err != nil { - return ACIEndorsements{}, err - } - - endorsement, err := ParseEndorsementACI(string(endorsementsBase64)) - if err != nil { - return ACIEndorsements{}, err - } - return endorsement, nil -} diff --git a/attestation-container/attest/attestation_endorsement_test.go b/attestation-container/attest/attestation_endorsement_test.go deleted file mode 100644 index bdfefaaa2d04..000000000000 --- a/attestation-container/attest/attestation_endorsement_test.go +++ /dev/null @@ -1,106 +0,0 @@ -package attest - -import ( - "flag" - "fmt" - "os" - "path/filepath" - "strings" - "testing" -) - -var ( - testDataDir = flag.String("testdata-dir", "testdata/", "Path to testdata directory") -) - -func getReportedTCBAndChipID(t *testing.T) ([]byte, []byte) { - // Get attestation report to get chip ID and reported TCB - // Report data for test - reportData := [REPORT_DATA_SIZE]byte{} - for i := 0; i < REPORT_DATA_SIZE; i++ { - reportData[i] = byte(i) - } - - reportBytes, err := FetchAttestationReportByte(reportData) - if err != nil { - t.Fatalf("Fetching report failed: %v", err) - } - reportedTCBBytes := reportBytes[REPORTED_TCB_OFFSET : REPORTED_TCB_OFFSET+REPORTED_TCB_SIZE] - chipIDBytes := reportBytes[CHIP_ID_OFFSET : CHIP_ID_OFFSET+CHIP_ID_SIZE] - return reportedTCBBytes, chipIDBytes -} - -func TestFetchAttestationEndorsementFromAzure(t *testing.T) { - reportedTCBBytes, chipIDBytes := getReportedTCBAndChipID(t) - endorsement, err := FetchAttestationEndorsement("Azure", reportedTCBBytes, chipIDBytes) - if err != nil || len(endorsement) == 0 { - t.Fatalf("Fetching attestation endorsement failed: %s", err) - } -} - -func TestFetchAttestationEndorsementFromAMD(t *testing.T) { - reportedTCBBytes, chipIDBytes := getReportedTCBAndChipID(t) - endorsement, err := FetchAttestationEndorsement("AMD", reportedTCBBytes, chipIDBytes) - if err != nil || len(endorsement) == 0 { - t.Fatalf("Fetching attestation endorsement failed: %s", err) - } -} - -func TestInvalidServer(t *testing.T) { - reportedTCBBytes, chipIDBytes := getReportedTCBAndChipID(t) - server := "Invalid Server Type" - _, err := FetchAttestationEndorsement(server, reportedTCBBytes, chipIDBytes) - if err.Error() != fmt.Sprintf("invalid endorsement server: %s", server) { - t.Fatalf("Should return error for invalid server") - } -} - -func TestInvalidReportedTCBBytes(t *testing.T) { - reportedTCBBytes, chipIDBytes := getReportedTCBAndChipID(t) - reportedTCBBytes = []byte{} - _, err := FetchAttestationEndorsement("Azure", reportedTCBBytes, chipIDBytes) - if err.Error() != fmt.Sprintf("Length of reportedTCBBytes should be %d", REPORTED_TCB_SIZE) { - t.Fatalf("Should return error for invalid length of reportedTCBBytes") - } -} - -func TestInvalidChipID(t *testing.T) { - reportedTCBBytes, chipIDBytes := getReportedTCBAndChipID(t) - chipIDBytes = []byte{} - _, err := FetchAttestationEndorsement("Azure", reportedTCBBytes, chipIDBytes) - if err.Error() != fmt.Sprintf("Length of chipIDBytes should be %d", CHIP_ID_SIZE) { - t.Fatalf("Should return error for invalid length of chipIDBytes") - } -} -func TestGetAttestationEndorsementFromEnvironment(t *testing.T) { - flag.Parse() - testDataHostAmdCertificate := filepath.Join(*testDataDir, "host_amd_certificate_env") - endorsement, err := os.ReadFile(testDataHostAmdCertificate) - if err != nil { - t.Fatalf("Could not open file %s", testDataHostAmdCertificate) - } - - // Valid - _, err = ParseEndorsementACI(string(endorsement)) - if err != nil { - t.Fatalf("Could not parse ACI endorsement: %s", err) - } - - // Empty - _, err = ParseEndorsementACI("") - if !strings.Contains(err.Error(), "unexpected end of JSON input") { - t.Fatalf("Could not parse ACI endorsement: %s", err) - } - - // Invalid base64 - _, err = ParseEndorsementACI(string(endorsement[:len(endorsement)-1])) - if !strings.Contains(err.Error(), "illegal base64 data") { - t.Fatalf("Could not parse ACI endorsement: %s", err) - } - - // Invalid JSON - _, err = ParseEndorsementACI(string(endorsement[:len(endorsement)-4])) - if !strings.Contains(err.Error(), "unexpected end of JSON input") { - t.Fatalf("Could not parse ACI endorsement: %s", err) - } -} diff --git a/attestation-container/attest/attestation_test.go b/attestation-container/attest/attestation_test.go deleted file mode 100644 index 9d62b528d303..000000000000 --- a/attestation-container/attest/attestation_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package attest - -import ( - "encoding/hex" - "testing" -) - -func assertEqual[T comparable](t *testing.T, description string, expect T, actual T) { - if expect != actual { - t.Fatalf("%s: Expected %v, but got %v", description, expect, actual) - } -} - -func TestFetchReport(t *testing.T) { - // Report data for test - reportData := [REPORT_DATA_SIZE]byte{} - for i := 0; i < REPORT_DATA_SIZE; i++ { - reportData[i] = byte(i) - } - - reportBytes, err := FetchAttestationReportByte(reportData) - if err != nil { - t.Fatalf("Fetching report failed: %v", err) - } - expectedByteString := hex.EncodeToString(reportData[:]) - // Confirm `report data` (user provided 64 byte data) is correct - // Offset of `report data` is specified in SEV-SNP Firmware ABI Specification Table 21 - // https://www.amd.com/en/support/tech-docs/sev-secure-nested-paging-firmware-abi-specification - const REPORT_DATA_OFFSET = 80 - assertEqual(t, "Check report data", expectedByteString, hex.EncodeToString(reportBytes[REPORT_DATA_OFFSET:REPORT_DATA_OFFSET+REPORT_DATA_SIZE])) -} diff --git a/attestation-container/attest/testdata/host_amd_certificate_env b/attestation-container/attest/testdata/host_amd_certificate_env deleted file mode 100644 index f12f87d9e4a8..000000000000 --- a/attestation-container/attest/testdata/host_amd_certificate_env +++ /dev/null @@ -1 +0,0 @@ -eyJ2Y2VrQ2VydCI6Ii0tLS0tQkVHSU4gQ0VSVElGSUNBVEUtLS0tLVxuTUlJRlREQ0NBdnVnQXdJQkFnSUJBREJHQmdrcWhraUc5dzBCQVFvd09hQVBNQTBHQ1dDR1NBRmxBd1FDQWdVQVxub1J3d0dnWUpLb1pJaHZjTkFRRUlNQTBHQ1dDR1NBRmxBd1FDQWdVQW9nTUNBVENqQXdJQkFUQjdNUlF3RWdZRFxuVlFRTERBdEZibWRwYm1WbGNtbHVaekVMTUFrR0ExVUVCaE1DVlZNeEZEQVNCZ05WQkFjTUMxTmhiblJoSUVOc1xuWVhKaE1Rc3dDUVlEVlFRSURBSkRRVEVmTUIwR0ExVUVDZ3dXUVdSMllXNWpaV1FnVFdsamNtOGdSR1YyYVdObFxuY3pFU01CQUdBMVVFQXd3SlUwVldMVTFwYkdGdU1CNFhEVEl5TVRJeU5qQTFNell3TkZvWERUSTVNVEl5TmpBMVxuTXpZd05Gb3dlakVVTUJJR0ExVUVDd3dMUlc1bmFXNWxaWEpwYm1jeEN6QUpCZ05WQkFZVEFsVlRNUlF3RWdZRFxuVlFRSERBdFRZVzUwWVNCRGJHRnlZVEVMTUFrR0ExVUVDQXdDUTBFeEh6QWRCZ05WQkFvTUZrRmtkbUZ1WTJWa1xuSUUxcFkzSnZJRVJsZG1salpYTXhFVEFQQmdOVkJBTU1DRk5GVmkxV1EwVkxNSFl3RUFZSEtvWkl6ajBDQVFZRlxuSzRFRUFDSURZZ0FFQUxXTm1vajZvc2w4UDg2UCtNNHN3aHEyN1VkT3JxQnplejRPcFdPeHpTTGJQSWNoaW1NOVxubk54OEtCQ0l2M1VvdE4zOGpsa1ZJdkt0RFlwNnZHbzZTOVEvd2RkcGVLL2J3eGtwdSs5OWVZMS9ZeVd6bnlzaFxuVUtJM1l5UitiWHFTbzRJQkZqQ0NBUkl3RUFZSkt3WUJCQUdjZUFFQkJBTUNBUUF3RndZSkt3WUJCQUdjZUFFQ1xuQkFvV0NFMXBiR0Z1TFVJd01CRUdDaXNHQVFRQm5IZ0JBd0VFQXdJQkF6QVJCZ29yQmdFRUFaeDRBUU1DQkFNQ1xuQVFBd0VRWUtLd1lCQkFHY2VBRURCQVFEQWdFQU1CRUdDaXNHQVFRQm5IZ0JBd1VFQXdJQkFEQVJCZ29yQmdFRVxuQVp4NEFRTUdCQU1DQVFBd0VRWUtLd1lCQkFHY2VBRURCd1FEQWdFQU1CRUdDaXNHQVFRQm5IZ0JBd01FQXdJQlxuQ0RBUkJnb3JCZ0VFQVp4NEFRTUlCQU1DQVhNd1RRWUpLd1lCQkFHY2VBRUVCRUJNMjZpSk9jUkNVZmk2V3Z1T1xuK3lIV1o3SUhQdm5HV2VHQkIzTXJyd2E5aWxoYVhCUDRHWEt2eTV3L2oydkROMU9pVENmbUtiREZNRjBpVjJCVlxuMDVjQU1FWUdDU3FHU0liM0RRRUJDakE1b0E4d0RRWUpZSVpJQVdVREJBSUNCUUNoSERBYUJna3Foa2lHOXcwQlxuQVFnd0RRWUpZSVpJQVdVREJBSUNCUUNpQXdJQk1LTURBZ0VCQTRJQ0FRQjFGYklad2RBaHNObUtObGJJMm1tU1xuRHNuUmxocWpnY1hJb3JFM2lQMzhlUzEvc2diS25ZN2ZEMVNUMzJ3WVZiN1NKZ0MwNG9hTlgxRktwaXJnQTdvSVxuNjlEREthR0ZVSXJ3Vmp5YTQvRi9rS2greVBwT3BrUjhVQTkwbnNmNFFQREJjeHB6MXpwU2JJdXJZOFV4WHNDelxubHBSRUYyQzk3WkZad09oQWxVUTVoNHF5RnF1d3Y1UFlzSml4NU9TbkpnSDAyMlZ6VEVxL3pHQittdGhwOGV3ZFxuOTRGck5NL0J2UW1jQ0lDUU1yYk8xWU9yODF5YSswaDV4ZEsxQktPcTlGd1NsbVZBTkxEK1g5ZW5BZ2lJZUFJRlxueW1GMnUyUXgrNXF6aThXUkdWTzVFTThaMzlXU09rWnhyL3hBQzBoV2VJTUNSQVM2Q2JLc1cxamRaclgzakRYN1xuQUw4Um1NSEM5V1Nzd3Z2ei9wRGxXanN1VEVNaXlqTlk1Yk5tRVl5NFUzcktlbEJYZlJjNWk0azhUNmtqSlRlTFxudENhUUR3ZXVENTdsTHgyRFVQazVTQlhxMzNWdlRnQS9KSHZnYmxBZW1LTldSVmF0N2YzYzRoLzRzbFpjeUM2UFxuVXFxVTBwcGdDbXhIaGxDVDl3Wkd0SlkvcUZYQTc4Y3JwQWc5RnpJa1VsTlhtbmZEV1I0OFo3T1hnS3JnVHhUMVxuQUt6ZFJmbHpMQ2FjQUJYL0VLT2ZqRkhrWi9NK3pTVFd0Q0UxcDZoVVRSOFJidUZUeDRnU2VsNG5mNlpYWG9wU1xuTVRySDdjQlVuSmxvVTl0MmdjbzRXNE9iMDBBNG82bzVFRlhSK2owRndVQVhBbE1iV29nMm9EZk81YTJWT29qdlxuS0xaS3F4dEVSdDZuZEV4TmsyNWVGQT09XG4tLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tXG4iLCJ0Y2JtIjoiNzMwODAwMDAwMDAwMDAwMyIsImNlcnRpZmljYXRlQ2hhaW4iOiItLS0tLUJFR0lOIENFUlRJRklDQVRFLS0tLS1cbk1JSUdpVENDQkRpZ0F3SUJBZ0lEQVFBQk1FWUdDU3FHU0liM0RRRUJDakE1b0E4d0RRWUpZSVpJQVdVREJBSUNcbkJRQ2hIREFhQmdrcWhraUc5dzBCQVFnd0RRWUpZSVpJQVdVREJBSUNCUUNpQXdJQk1LTURBZ0VCTUhzeEZEQVNcbkJnTlZCQXNNQzBWdVoybHVaV1Z5YVc1bk1Rc3dDUVlEVlFRR0V3SlZVekVVTUJJR0ExVUVCd3dMVTJGdWRHRWdcblEyeGhjbUV4Q3pBSkJnTlZCQWdNQWtOQk1SOHdIUVlEVlFRS0RCWkJaSFpoYm1ObFpDQk5hV055YnlCRVpYWnBcblkyVnpNUkl3RUFZRFZRUUREQWxCVWtzdFRXbHNZVzR3SGhjTk1qQXhNREl5TVRneU5ESXdXaGNOTkRVeE1ESXlcbk1UZ3lOREl3V2pCN01SUXdFZ1lEVlFRTERBdEZibWRwYm1WbGNtbHVaekVMTUFrR0ExVUVCaE1DVlZNeEZEQVNcbkJnTlZCQWNNQzFOaGJuUmhJRU5zWVhKaE1Rc3dDUVlEVlFRSURBSkRRVEVmTUIwR0ExVUVDZ3dXUVdSMllXNWpcblpXUWdUV2xqY204Z1JHVjJhV05sY3pFU01CQUdBMVVFQXd3SlUwVldMVTFwYkdGdU1JSUNJakFOQmdrcWhraUdcbjl3MEJBUUVGQUFPQ0FnOEFNSUlDQ2dLQ0FnRUFuVTJkcnJOVGZiaE5RSWxsZitXMnkrUk9DYlN6SWQxYUtaZnRcbjJUOXpqWlFPempHY2NsMTdpMW1JS1dsN05UY0IwVllYdDNKeFpTek9aanNqTE5WQUVOMk1HajlUaWVkTCtRZXdcbktaWDBKbVFFdVlqbStXS2tzTHR4Z2RMcDlFN0VaTndORHFWMXIwcVJQNXRCOE9Xa3lRYklkTGV1NGFDejdqL1NcbmwxRmtCeXRldjlzYkZHenQ3Y3duanppOW03bm9xc2srdVJWQnAzK0luMzVRUGRjajhZZmxFbW5IQk52dVVESmhcbkxDSk1XOEtPalA2KytQaGJzM2lDaXRKY0FORXRXNHFUTkZvS1czQ0hsYmNTQ2pUTThLc05iVXgzQThlazVFVkxcbmpaV0gxcHQ5RTNUZnBSNlh5ZlFLblk2a2w1YUVJUHdkVzNlRllhcUNGUHJJbzlwUVQ2V3VEU1A0SkNZSmJabmVcbktLSWJaanpYa0p0M05RRzMyRXVrWUltQmI5U0NrbTkrZlM1TFpGZzlvanp1Yk1YMytOa0JvU1hJN09Qdm5ITXhcbmp1cDltdzVzZTZRVVY3R3FwQ0EyVE55cG9sbXVRK2NBYXhWN0pxSEU4ZGw5cFdmK1kzYXJiKzlpaUZDd0Z0NGxcbkFsSnc1RDBDVFJUQzFZNVlXRkRCQ3JBL3ZHbm1UbnFHOEMrampVQVM3Y2pqUjhxNE9QaHlEbUpSUG5hQy9aRzVcbnVQMEswejZHb08vM3Vlbjl3cXNoQ3VIZWdMVHBPZUhFSlJLclFGcjRQVkl3Vk9CMCtlYk81RmdveU93NDNueUZcbkQ1VUtCRHhFQjRCS28vMHVBaUtITFJ2dmdMYk9SYlU4S0FSSXMxRW9xRWptRjhVdHJtUVdWMmhVand6cXd2SEZcbmVpOHJQeE1DQXdFQUFhT0JvekNCb0RBZEJnTlZIUTRFRmdRVU84WnVHQ3JEL1QxaVpFaWI0N2RITExUOHYvZ3dcbkh3WURWUjBqQkJnd0ZvQVVoYXdhMFVQM3lLeFYxTVVkUVVpcjFYaEsxRk13RWdZRFZSMFRBUUgvQkFnd0JnRUJcbi93SUJBREFPQmdOVkhROEJBZjhFQkFNQ0FRUXdPZ1lEVlIwZkJETXdNVEF2b0MyZ0s0WXBhSFIwY0hNNkx5OXJcblpITnBiblJtTG1GdFpDNWpiMjB2ZG1ObGF5OTJNUzlOYVd4aGJpOWpjbXd3UmdZSktvWklodmNOQVFFS01EbWdcbkR6QU5CZ2xnaGtnQlpRTUVBZ0lGQUtFY01Cb0dDU3FHU0liM0RRRUJDREFOQmdsZ2hrZ0JaUU1FQWdJRkFLSURcbkFnRXdvd01DQVFFRGdnSUJBSWdlVVFTY0FmM2xEWXFnV1UxVnRsRGJtSU44UzJkQzVrbVF6c1ovSHRBalFuTEVcblBJMWpoM2dKYkx4TDZnZjNLOGp4Y3R6T1dua1ljYmRmTU9PcjI4S1QzNUlhQVIyMHJla0tSRnB0VEhoZStERnJcbjNBRnpaTEREN2NXSzI5L0dwUGl0UEpES0N2STdBNFVnMDZyazdKMHpCZTFmei9xZTRpMi9GMTJydmZ3Q0dZaGNcblJ4UHk3UUYzcThmUjZHQ0pkQjFVUTVTbHdDakZ4RDR1ZXpVUnp0SWxJQWpNa3Q3REZ2S1JoKzJ6Sys1cGxWR0dcbkZzakRKdE16MnVkOXkwcHZPRTRqM2RINUlXOWpHeGFTR1N0cU5yYWJubnBGMjM2RVRyMS9hNDNiOEZGS0w1UU5cbm10OFZyOXhuWFJwem5xQ1J2cWpyK2tWcmI2ZGxmdVRsbGlYZVFUTWxCb1JXRkpPUkw4QWNCSnhHWjRLMm1YZnRcbmwxalU1VExlaDVLWEw5Tlc3YS9xQU9JVXMyRmlPaHFydHpBaEpSZzlJajhRa1E5UGsrY0tHenc2RWwzVDNrRnJcbkVnNnpreG12TXVhYlpPc2RLZlJrV2ZoSDJaS2NUbERmbUgxSDB6cTBRMmJHM3V2YVZkaUN0RlkxTGxXeUIzOEpcblMyZk5zUi9QeTZ0NWJyRUpDRk52emFEa3k2S2VDNGlvbi9jVmdVYWk3enpTM2JHUVd6S0RLVTM1U3FOVTJXa1Bcbkk4eENaMDBXdElpS0tGblhXVVF4dmxLbW1nWkJJWVBlMDF6RDBOOGF0RnhtV2lTbmZKbDY5MEI5ckpwTlIvZklcbmFqeENXM1NlaXdzNnIxWm0rdEN1VmJNaU50cFM5VGhqTlg0dXZlNXRoeWZFMkRnb3hSRnZZMUNzb0Y1TVxuLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLVxuLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tXG5NSUlHWXpDQ0JCS2dBd0lCQWdJREFRQUFNRVlHQ1NxR1NJYjNEUUVCQ2pBNW9BOHdEUVlKWUlaSUFXVURCQUlDXG5CUUNoSERBYUJna3Foa2lHOXcwQkFRZ3dEUVlKWUlaSUFXVURCQUlDQlFDaUF3SUJNS01EQWdFQk1Ic3hGREFTXG5CZ05WQkFzTUMwVnVaMmx1WldWeWFXNW5NUXN3Q1FZRFZRUUdFd0pWVXpFVU1CSUdBMVVFQnd3TFUyRnVkR0VnXG5RMnhoY21FeEN6QUpCZ05WQkFnTUFrTkJNUjh3SFFZRFZRUUtEQlpCWkhaaGJtTmxaQ0JOYVdOeWJ5QkVaWFpwXG5ZMlZ6TVJJd0VBWURWUVFEREFsQlVrc3RUV2xzWVc0d0hoY05NakF4TURJeU1UY3lNekExV2hjTk5EVXhNREl5XG5NVGN5TXpBMVdqQjdNUlF3RWdZRFZRUUxEQXRGYm1kcGJtVmxjbWx1WnpFTE1Ba0dBMVVFQmhNQ1ZWTXhGREFTXG5CZ05WQkFjTUMxTmhiblJoSUVOc1lYSmhNUXN3Q1FZRFZRUUlEQUpEUVRFZk1CMEdBMVVFQ2d3V1FXUjJZVzVqXG5aV1FnVFdsamNtOGdSR1YyYVdObGN6RVNNQkFHQTFVRUF3d0pRVkpMTFUxcGJHRnVNSUlDSWpBTkJna3Foa2lHXG45dzBCQVFFRkFBT0NBZzhBTUlJQ0NnS0NBZ0VBMExkNTJSSk9kZWlKbHFLMkpkc1ZtRDdGa3R1b3RXd1gxZk5nXG5XNDFYWTlYejFIRWhTVW1oTHo5Q3U5REhSbHZnSlNOeGJlWVlzbkpmdnlqeDFNZlUwVjV0a0tpVTFFZXNORnRhXG4xa1RBMHN6TmlzZFljOWlzcWs3bVhUNStLZkdSYmZjNFYvOXpSSWNFOGpsSE42MVMxanU4WDkzKzZkeERVckcyXG5TenhxSjRCaHF5WW1VRHJ1UFhKU1g0dlVjMDFQN2o5OE1wcU9TOTVyT1JkR0hlSTUyTmF6NW0yQitPK3Zqc0MwXG42MGQzN2pZOUxGZXVPUDRNZXJpOHFnZmkyUzVrS3FnL2FGNmFQdHVBWlFWUjd1M0tGWVhQNTlYbUpndGNvZzA1XG5nbUkwVC9PaXRMaHV6VnZwWmNMcGgwb2RoLzFJUFhxeDMrTW5qRDk3QTdmWHBxR2QveThLeFg3amtzVEV6QU9nXG5iS0FlYW0zbG0rM3lLSWNUWU1sc1JNWFBjak5iSXZtc0J5a0QvL3hTbml1c3VIQmtnbmxFTkVXeDFVY2JRUXJzXG4rZ1ZEa3VWUGhzbnpJUk5nWXZNNDhZKzdMR2lKWW5ybUU4eGNyZXhla0J4cnZhMlY5VEpRcW5OM1E1M2t0NXZpXG5RaTMrZ0NmbWt3QzBGMHRpcklaYkxrWFByUHd6WjBNOWVOeGhJeVNiMm5wSmZnbnF6NTVJMHUzM3doNHIwWk5RXG5lVEdmdzAzTUJVdHl1ekdlc0drY3crbG9xTWFxMXFSNHRqR2JQWXhDdnBDcTcrT2dwQ0NvTU5pdDJ1TG85TTE4XG5mSHoxMGxPTVQ4bldBVXZSWkZ6dGVYQ20rN1BIZFlQbG1Rd1V3M0x2ZW5KL0lMWG9RUEhmYmtIMEN5UGZobDFqXG5XaEpGWmFzQ0F3RUFBYU4rTUh3d0RnWURWUjBQQVFIL0JBUURBZ0VHTUIwR0ExVWREZ1FXQkJTRnJCclJRL2ZJXG5yRlhVeFIxQlNLdlZlRXJVVXpBUEJnTlZIUk1CQWY4RUJUQURBUUgvTURvR0ExVWRId1F6TURFd0w2QXRvQ3VHXG5LV2gwZEhCek9pOHZhMlJ6YVc1MFppNWhiV1F1WTI5dEwzWmpaV3N2ZGpFdlRXbHNZVzR2WTNKc01FWUdDU3FHXG5TSWIzRFFFQkNqQTVvQTh3RFFZSllJWklBV1VEQkFJQ0JRQ2hIREFhQmdrcWhraUc5dzBCQVFnd0RRWUpZSVpJXG5BV1VEQkFJQ0JRQ2lBd0lCTUtNREFnRUJBNElDQVFDNm0wa0RwNnp2NE9qZmd5K3psZWVoc3g2b2wwb2NnVmVsXG5FVG9icHgrRXVDc3FWRlJQSzFqWjFzcC9seWQ5KzBmUTByNjZuN2thZ1JrNENhMzlnNjZXR1RKTWVKZHFZcml3XG5TVGpqRENLVlBTZXNXWFlQVkF5RGhtUDVuMnYrQllpcFpXaHB2cXBhaU8rRUdLNUlCUCs1NzhRZVcvc1Nva3JLXG5kSGFMQXhHMkxoWnhqOWFGNzNmcUM3T0FKWjVhUG9udzRSRTI5OUZWYXJoMVR4MmVUM3dTZ2tEZ3V0Q1RCMVlxXG56VDVEdXd2QWUrY28yQ0lWSXpNRGFtWXVTRmpQTjBCQ2dvamw3VitiVG91N2RNc3FJdS9UVy9yUENYOS9FVWNwXG5LR0txUFEzUCtOOXIxaGpFRlkxcGxCZzkzdDUzT09vNDlHTkkrVjF6dlhQTEk2eElGVnNoK210bzJSdGdFWC9lXG5wbU1LVE5ONnBzVzg4cWc3YzFoVFd0TjZNYlJ1UTB2bStPKy8ydEtCRjJoOFRIYjk0T3Z2SEhvRkRwYkNFTGxxXG5IbklZaHh5MFlLWEd5YVcxTmpmVUx4cnJteFZXNHdjbjVFOEdkZG12TmE2eVltOHNjSmFnRWkxM21oR3U0SnFoXG4zUVUzc2Y4aVVTVXIwOXhRRHdIdE9RVVZJcXg0bWFCWlBCdFNNZitxVUR0alhTU3E4bGZXY2Q4YkxyOW1kc1VuXG5KWkowK3R1UE1LbUJuU0g4NjBsbEtrK1ZwVlFzZ3FiekRJdk9MdkQ2VzFVbXEyNWJveENZSitUdUJvYTRzK0hIXG5DVmlBdmdUOWtmL3JCcTFkK2l2ajZza2tIeHV6Y3hiazF4djZaR3hydGVKeFZIN0tsWDdZUmRaNmVBUkt3TGU0XG5BRlpFQXdvS0NRPT1cbi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS1cbiIsImNhY2hlQ29udHJvbCI6Ijg2NDAwIn0= \ No newline at end of file diff --git a/attestation-container/attestation-container.go b/attestation-container/attestation-container.go deleted file mode 100644 index ece88821b79a..000000000000 --- a/attestation-container/attestation-container.go +++ /dev/null @@ -1,137 +0,0 @@ -package main - -import ( - "context" - "errors" - "flag" - "log" - "net" - "os" - "path/filepath" - - "microsoft/attestation-container/attest" - pb "microsoft/attestation-container/protobuf" - "microsoft/attestation-container/uvm" - - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -var ( - socketAddress = flag.String("socket-address", "/tmp/attestation-container.sock", "The socket address of Unix domain socket (UDS)") - securityContextDirectoryEnvVar = flag.String("security-context-directory-envvar", attest.DEFAULT_SECURITY_CONTEXT_ENVVAR, "Name of environment variable specifying name of directory containing confidential ACI security context") - attestationEndorsementServer = flag.String("attestation-endorsement-server", "", "Server to fetch attestation endorsement. If set, endorsements contained in security context directory are ignored. Value is either 'Azure' or 'AMD'") - insecureVirtual = flag.Bool("insecure-virtual", false, "If set, dummy attestation is returned (INSECURE: do not use in production)") - - attestationEndorsementValue *attest.ACIEndorsements = nil - uvmEndorsementEnvVarValue []byte = nil -) - -type server struct { - pb.UnimplementedAttestationContainerServer -} - -func (s *server) FetchAttestation(ctx context.Context, in *pb.FetchAttestationRequest) (*pb.FetchAttestationReply, error) { - reportData := [attest.REPORT_DATA_SIZE]byte{} - if len(in.GetReportData()) > attest.REPORT_DATA_SIZE { - return nil, status.Errorf(codes.InvalidArgument, "`report_data` needs to be smaller than %d bytes. size: %d bytes", attest.REPORT_DATA_SIZE, len(in.GetReportData())) - } - copy(reportData[:], in.GetReportData()) - if *insecureVirtual { - log.Println("Serving virtual attestation report") - return &pb.FetchAttestationReply{}, nil - } - - reportBytes, err := attest.FetchAttestationReportByte(reportData) - if err != nil { - return nil, status.Errorf(codes.Internal, "failed to fetch attestation report: %s", err) - } - - var attestationEndorsement []byte - if attestationEndorsementValue == nil { - reportedTCBBytes := reportBytes[attest.REPORTED_TCB_OFFSET : attest.REPORTED_TCB_OFFSET+attest.REPORTED_TCB_SIZE] - chipIDBytes := reportBytes[attest.CHIP_ID_OFFSET : attest.CHIP_ID_OFFSET+attest.CHIP_ID_SIZE] - attestationEndorsement, err = attest.FetchAttestationEndorsement(*attestationEndorsementServer, reportedTCBBytes, chipIDBytes) - if err != nil { - return nil, status.Errorf(codes.Internal, "failed to fetch attestation endorsement: %s", err) - } - } else { - attestationEndorsement = append(attestationEndorsement, attestationEndorsementValue.VcekCert...) - attestationEndorsement = append(attestationEndorsement, attestationEndorsementValue.CertificateChain...) - } - - return &pb.FetchAttestationReply{Attestation: reportBytes, AttestationEndorsements: attestationEndorsement, UvmEndorsements: uvmEndorsementEnvVarValue}, nil -} - -func validateFlags() { - if *attestationEndorsementServer != "" && *attestationEndorsementServer != "AMD" && *attestationEndorsementServer != "Azure" { - log.Fatalf("invalid --attestation-endorsement-server value %s (valid values: 'AMD', 'Azure')", *attestationEndorsementServer) - } -} - -func main() { - flag.Parse() - validateFlags() - - log.Println("Attestation container started.") - - if *insecureVirtual { - log.Printf("Warning: INSECURE virtual: do not use in production!") - } else { - if _, err := os.Stat(attest.SNP_DEVICE_PATH); err == nil { - log.Printf("%s is detected\n", attest.SNP_DEVICE_PATH) - } else if errors.Is(err, os.ErrNotExist) { - log.Fatalf("%s is not detected", attest.SNP_DEVICE_PATH) - } else { - log.Fatalf("Unknown error: %s", err) - } - - securityContextDirectory, ok := os.LookupEnv(*securityContextDirectoryEnvVar) - if !ok { - log.Fatalf("Security context directory %s is not specified", *securityContextDirectoryEnvVar) - } - - if *attestationEndorsementServer == "" { - attestationEndorsementValue = new(attest.ACIEndorsements) - var err error - *attestationEndorsementValue, err = attest.ParseEndorsementACIFromSecurityContextDirectory(securityContextDirectory) - if err != nil { - log.Fatalf(err.Error()) - } - } else { - log.Printf("Attestation report endorsement will be retrieved from server %s", *attestationEndorsementServer) - } - - var err error - uvmEndorsementEnvVarValue, err = uvm.ParseUVMEndorsement(securityContextDirectory) - if err != nil { - log.Fatalf(err.Error()) - } - } - - // Cleanup - if _, err := os.Stat(*socketAddress); err == nil { - if err := os.RemoveAll(*socketAddress); err != nil { - log.Fatalf("Failed to clean up socket: %s", err) - } - } - - // Create parent directory for socketAddress - socketDir := filepath.Dir(*socketAddress) - // os.MkdirAll doesn't return error when the directory already exists - if err := os.MkdirAll(socketDir, os.ModePerm); err != nil { - log.Fatalf("Failed to create directory for Unix domain socket: %s", err) - } - - lis, err := net.Listen("unix", *socketAddress) - if err != nil { - log.Fatalf("failed to listen: %v", err) - } - s := grpc.NewServer() - pb.RegisterAttestationContainerServer(s, &server{}) - log.Printf("Server listening at %v", lis.Addr()) - if err := s.Serve(lis); err != nil { - log.Fatalf("failed to serve: %v", err) - } -} diff --git a/attestation-container/attestation-container_test.go b/attestation-container/attestation-container_test.go deleted file mode 100644 index bf11f174aa36..000000000000 --- a/attestation-container/attestation-container_test.go +++ /dev/null @@ -1,106 +0,0 @@ -package main - -import ( - "context" - "encoding/hex" - "encoding/pem" - "flag" - "log" - "net" - "testing" - "time" - - pb "microsoft/attestation-container/protobuf" - - "google.golang.org/grpc" -) - -var ( - addr = flag.String("addr", "/tmp/attestation-container.sock", "the Unix domain socket address to connect to") -) - -const TIMEOUT_IN_SEC = 10 - -func splitPemChain(pemChain []byte) [][]byte { - var chain [][]byte - var certDERBlock *pem.Block - for { - certDERBlock, pemChain = pem.Decode(pemChain) - if certDERBlock == nil { - break - } - if certDERBlock.Type == "CERTIFICATE" { - chain = append(chain, certDERBlock.Bytes) - } - } - return chain -} - -func TestFetchReport(t *testing.T) { - flag.Parse() - // Set up a connection to the server. - dialer := func(addr string, t time.Duration) (net.Conn, error) { - return net.Dial("unix", addr) - } - conn, err := grpc.Dial(*addr, grpc.WithInsecure(), grpc.WithDialer(dialer)) - if err != nil { - log.Fatalf("did not connect: %v", err) - } - defer conn.Close() - c := pb.NewAttestationContainerClient(conn) - - // Contact the server and print out its response. - ctx, cancel := context.WithTimeout(context.Background(), TIMEOUT_IN_SEC*time.Second) - defer cancel() - // public key bytes in UTF-8 (https://go.dev/blog/strings) - publicKey := []byte("public-key-contents") - r, err := c.FetchAttestation(ctx, &pb.FetchAttestationRequest{ReportData: publicKey}) - if err != nil { - log.Fatalf("could not get attestation: %v", err) - } - // Verify attestation - attestation := r.GetAttestation() - if len(attestation) == 0 { - log.Fatalf("attestation is empty") - } - log.Printf("Attestation: %v", hex.EncodeToString(attestation)) - - // Verify endorsements - endorsementCertificates := r.GetAttestationEndorsements() - if len(endorsementCertificates) == 0 { - log.Fatalf("endorsementCertificates is empty") - } - chainLen := len(splitPemChain(endorsementCertificates)) - if chainLen != 3 { - // Expecting VCEK, ASK and ARK - log.Fatalf("endorsementCertificates does not contain 3 certificates, found %d", chainLen) - } - log.Printf("Attestation endorsement certificates: %v", hex.EncodeToString(endorsementCertificates)) - - if len(r.GetUvmEndorsements()) == 0 { - log.Fatalf("UVM endorsement is empty") - } - log.Printf("UVM endorsement: %s", r.GetUvmEndorsements()) -} - -func TestInputError(t *testing.T) { - flag.Parse() - // Set up a connection to the server. - dialer := func(addr string, t time.Duration) (net.Conn, error) { - return net.Dial("unix", addr) - } - conn, err := grpc.Dial(*addr, grpc.WithInsecure(), grpc.WithDialer(dialer)) - if err != nil { - log.Fatalf("did not connect: %v", err) - } - defer conn.Close() - c := pb.NewAttestationContainerClient(conn) - - // Contact the server and print out its response. - ctx, cancel := context.WithTimeout(context.Background(), TIMEOUT_IN_SEC*time.Second) - defer cancel() - publicKey := []byte("too long (longer than 64 bytes in utf-8) ------------------------") - if _, err := c.FetchAttestation(ctx, &pb.FetchAttestationRequest{ReportData: publicKey}); err == nil { - log.Fatalf("server should return input error for too large input") - } -} diff --git a/attestation-container/go.mod b/attestation-container/go.mod deleted file mode 100644 index bd5bdb76e880..000000000000 --- a/attestation-container/go.mod +++ /dev/null @@ -1,16 +0,0 @@ -module microsoft/attestation-container - -go 1.20 - -require ( - golang.org/x/sys v0.7.0 - google.golang.org/grpc v1.54.0 - google.golang.org/protobuf v1.30.0 -) - -require ( - github.com/golang/protobuf v1.5.2 // indirect - golang.org/x/net v0.8.0 // indirect - golang.org/x/text v0.8.0 // indirect - google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44 // indirect -) diff --git a/attestation-container/go.sum b/attestation-container/go.sum deleted file mode 100644 index 2a7b98116850..000000000000 --- a/attestation-container/go.sum +++ /dev/null @@ -1,20 +0,0 @@ -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/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.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44 h1:EfLuoKW5WfkgVdDy7dTK8qSbH37AX5mj/MFh+bGPz14= -google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA= -google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= -google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= -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.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= diff --git a/attestation-container/protobuf/attestation-container.pb.go b/attestation-container/protobuf/attestation-container.pb.go deleted file mode 100644 index 80beb2e079c2..000000000000 --- a/attestation-container/protobuf/attestation-container.pb.go +++ /dev/null @@ -1,249 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.28.1 -// protoc v3.6.1 -// source: protobuf/attestation-container.proto - -package protobuf - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type FetchAttestationRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Defined in Table 21 of SEV-SNP firmware ABI specification - // https://www.amd.com/en/support/tech-docs/sev-secure-nested-paging-firmware-abi-specification - ReportData []byte `protobuf:"bytes,1,opt,name=report_data,json=reportData,proto3" json:"report_data,omitempty"` -} - -func (x *FetchAttestationRequest) Reset() { - *x = FetchAttestationRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_protobuf_attestation_container_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *FetchAttestationRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FetchAttestationRequest) ProtoMessage() {} - -func (x *FetchAttestationRequest) ProtoReflect() protoreflect.Message { - mi := &file_protobuf_attestation_container_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FetchAttestationRequest.ProtoReflect.Descriptor instead. -func (*FetchAttestationRequest) Descriptor() ([]byte, []int) { - return file_protobuf_attestation_container_proto_rawDescGZIP(), []int{0} -} - -func (x *FetchAttestationRequest) GetReportData() []byte { - if x != nil { - return x.ReportData - } - return nil -} - -type FetchAttestationReply struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // ATTESTATION_REPORT Structure defined in Table 21 of SEV-SNP firmware ABI specification - Attestation []byte `protobuf:"bytes,1,opt,name=attestation,proto3" json:"attestation,omitempty"` - // Concatenation of VCEK, ASK, and ARK certificates (PEM format, in that order). - // https://www.amd.com/en/support/tech-docs/versioned-chip-endorsement-key-vcek-certificate-and-kds-interface-specification - AttestationEndorsements []byte `protobuf:"bytes,2,opt,name=attestation_endorsements,json=attestationEndorsements,proto3" json:"attestation_endorsements,omitempty"` - UvmEndorsements []byte `protobuf:"bytes,3,opt,name=uvm_endorsements,json=uvmEndorsements,proto3" json:"uvm_endorsements,omitempty"` -} - -func (x *FetchAttestationReply) Reset() { - *x = FetchAttestationReply{} - if protoimpl.UnsafeEnabled { - mi := &file_protobuf_attestation_container_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *FetchAttestationReply) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FetchAttestationReply) ProtoMessage() {} - -func (x *FetchAttestationReply) ProtoReflect() protoreflect.Message { - mi := &file_protobuf_attestation_container_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FetchAttestationReply.ProtoReflect.Descriptor instead. -func (*FetchAttestationReply) Descriptor() ([]byte, []int) { - return file_protobuf_attestation_container_proto_rawDescGZIP(), []int{1} -} - -func (x *FetchAttestationReply) GetAttestation() []byte { - if x != nil { - return x.Attestation - } - return nil -} - -func (x *FetchAttestationReply) GetAttestationEndorsements() []byte { - if x != nil { - return x.AttestationEndorsements - } - return nil -} - -func (x *FetchAttestationReply) GetUvmEndorsements() []byte { - if x != nil { - return x.UvmEndorsements - } - return nil -} - -var File_protobuf_attestation_container_proto protoreflect.FileDescriptor - -var file_protobuf_attestation_container_proto_rawDesc = []byte{ - 0x0a, 0x24, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, - 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x15, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x22, 0x3a, 0x0a, - 0x17, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x70, 0x6f, - 0x72, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x72, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x22, 0x9f, 0x01, 0x0a, 0x15, 0x46, 0x65, - 0x74, 0x63, 0x68, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x70, 0x6c, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x39, 0x0a, 0x18, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x73, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x17, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, 0x6f, 0x72, 0x73, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, - 0x12, 0x29, 0x0a, 0x10, 0x75, 0x76, 0x6d, 0x5f, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x73, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x75, 0x76, 0x6d, 0x45, - 0x6e, 0x64, 0x6f, 0x72, 0x73, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x32, 0x8a, 0x01, 0x0a, 0x14, - 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x12, 0x72, 0x0a, 0x10, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x74, 0x74, - 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x2e, 0x61, 0x74, 0x74, 0x65, 0x73, - 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, - 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x61, 0x74, 0x74, 0x65, 0x73, - 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, - 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x2a, 0x5a, 0x28, 0x6d, 0x69, 0x63, 0x72, - 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_protobuf_attestation_container_proto_rawDescOnce sync.Once - file_protobuf_attestation_container_proto_rawDescData = file_protobuf_attestation_container_proto_rawDesc -) - -func file_protobuf_attestation_container_proto_rawDescGZIP() []byte { - file_protobuf_attestation_container_proto_rawDescOnce.Do(func() { - file_protobuf_attestation_container_proto_rawDescData = protoimpl.X.CompressGZIP(file_protobuf_attestation_container_proto_rawDescData) - }) - return file_protobuf_attestation_container_proto_rawDescData -} - -var file_protobuf_attestation_container_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_protobuf_attestation_container_proto_goTypes = []interface{}{ - (*FetchAttestationRequest)(nil), // 0: attestation_container.FetchAttestationRequest - (*FetchAttestationReply)(nil), // 1: attestation_container.FetchAttestationReply -} -var file_protobuf_attestation_container_proto_depIdxs = []int32{ - 0, // 0: attestation_container.AttestationContainer.FetchAttestation:input_type -> attestation_container.FetchAttestationRequest - 1, // 1: attestation_container.AttestationContainer.FetchAttestation:output_type -> attestation_container.FetchAttestationReply - 1, // [1:2] is the sub-list for method output_type - 0, // [0:1] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name -} - -func init() { file_protobuf_attestation_container_proto_init() } -func file_protobuf_attestation_container_proto_init() { - if File_protobuf_attestation_container_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_protobuf_attestation_container_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FetchAttestationRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_protobuf_attestation_container_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FetchAttestationReply); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_protobuf_attestation_container_proto_rawDesc, - NumEnums: 0, - NumMessages: 2, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_protobuf_attestation_container_proto_goTypes, - DependencyIndexes: file_protobuf_attestation_container_proto_depIdxs, - MessageInfos: file_protobuf_attestation_container_proto_msgTypes, - }.Build() - File_protobuf_attestation_container_proto = out.File - file_protobuf_attestation_container_proto_rawDesc = nil - file_protobuf_attestation_container_proto_goTypes = nil - file_protobuf_attestation_container_proto_depIdxs = nil -} diff --git a/attestation-container/protobuf/attestation-container.proto b/attestation-container/protobuf/attestation-container.proto deleted file mode 100644 index 71b090c35014..000000000000 --- a/attestation-container/protobuf/attestation-container.proto +++ /dev/null @@ -1,27 +0,0 @@ -syntax = "proto3"; - -option go_package = "microsoft/attestation-container/protobuf"; - -package attestation_container; - -// attestation_container service definition. -service AttestationContainer { - // Fetches and returns attestation report and its endorsements. - // In future it returns Certificate Revocation List (CRL) as well. - rpc FetchAttestation (FetchAttestationRequest) returns (FetchAttestationReply) {} -} - -message FetchAttestationRequest { - // Defined in Table 21 of SEV-SNP firmware ABI specification - // https://www.amd.com/en/support/tech-docs/sev-secure-nested-paging-firmware-abi-specification - bytes report_data = 1; -} - -message FetchAttestationReply { - // ATTESTATION_REPORT Structure defined in Table 21 of SEV-SNP firmware ABI specification - bytes attestation = 1; - // Concatenation of VCEK, ASK, and ARK certificates (PEM format, in that order). - // https://www.amd.com/en/support/tech-docs/versioned-chip-endorsement-key-vcek-certificate-and-kds-interface-specification - bytes attestation_endorsements = 2; - bytes uvm_endorsements = 3; -} diff --git a/attestation-container/protobuf/attestation-container_grpc.pb.go b/attestation-container/protobuf/attestation-container_grpc.pb.go deleted file mode 100644 index ea13ca5ba176..000000000000 --- a/attestation-container/protobuf/attestation-container_grpc.pb.go +++ /dev/null @@ -1,109 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. -// versions: -// - protoc-gen-go-grpc v1.2.0 -// - protoc v3.6.1 -// source: protobuf/attestation-container.proto - -package protobuf - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 - -// AttestationContainerClient is the client API for AttestationContainer service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type AttestationContainerClient interface { - // Fetches and returns attestation report and its endorsements. - // In future it returns Certificate Revocation List (CRL) as well. - FetchAttestation(ctx context.Context, in *FetchAttestationRequest, opts ...grpc.CallOption) (*FetchAttestationReply, error) -} - -type attestationContainerClient struct { - cc grpc.ClientConnInterface -} - -func NewAttestationContainerClient(cc grpc.ClientConnInterface) AttestationContainerClient { - return &attestationContainerClient{cc} -} - -func (c *attestationContainerClient) FetchAttestation(ctx context.Context, in *FetchAttestationRequest, opts ...grpc.CallOption) (*FetchAttestationReply, error) { - out := new(FetchAttestationReply) - err := c.cc.Invoke(ctx, "/attestation_container.AttestationContainer/FetchAttestation", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// AttestationContainerServer is the server API for AttestationContainer service. -// All implementations must embed UnimplementedAttestationContainerServer -// for forward compatibility -type AttestationContainerServer interface { - // Fetches and returns attestation report and its endorsements. - // In future it returns Certificate Revocation List (CRL) as well. - FetchAttestation(context.Context, *FetchAttestationRequest) (*FetchAttestationReply, error) - mustEmbedUnimplementedAttestationContainerServer() -} - -// UnimplementedAttestationContainerServer must be embedded to have forward compatible implementations. -type UnimplementedAttestationContainerServer struct { -} - -func (UnimplementedAttestationContainerServer) FetchAttestation(context.Context, *FetchAttestationRequest) (*FetchAttestationReply, error) { - return nil, status.Errorf(codes.Unimplemented, "method FetchAttestation not implemented") -} -func (UnimplementedAttestationContainerServer) mustEmbedUnimplementedAttestationContainerServer() {} - -// UnsafeAttestationContainerServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to AttestationContainerServer will -// result in compilation errors. -type UnsafeAttestationContainerServer interface { - mustEmbedUnimplementedAttestationContainerServer() -} - -func RegisterAttestationContainerServer(s grpc.ServiceRegistrar, srv AttestationContainerServer) { - s.RegisterService(&AttestationContainer_ServiceDesc, srv) -} - -func _AttestationContainer_FetchAttestation_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(FetchAttestationRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(AttestationContainerServer).FetchAttestation(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/attestation_container.AttestationContainer/FetchAttestation", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(AttestationContainerServer).FetchAttestation(ctx, req.(*FetchAttestationRequest)) - } - return interceptor(ctx, in, info, handler) -} - -// AttestationContainer_ServiceDesc is the grpc.ServiceDesc for AttestationContainer service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var AttestationContainer_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "attestation_container.AttestationContainer", - HandlerType: (*AttestationContainerServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "FetchAttestation", - Handler: _AttestationContainer_FetchAttestation_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "protobuf/attestation-container.proto", -} diff --git a/attestation-container/uvm/uvm_endorsement.go b/attestation-container/uvm/uvm_endorsement.go deleted file mode 100644 index e7d4268d9df0..000000000000 --- a/attestation-container/uvm/uvm_endorsement.go +++ /dev/null @@ -1,29 +0,0 @@ -package uvm - -import ( - "encoding/base64" - "fmt" - "os" - "path" -) - -const ( - UVM_ENDORSEMENTS_FILE_NAME = "reference-info-base64" -) - -/* -Gets UVM endorsement from environment variable as base64 encoded string and returns as []byte. -*/ -func ParseUVMEndorsement(securityContextDirectory string) ([]byte, error) { - uvmEndorsementsBase64, err := os.ReadFile(path.Join(securityContextDirectory, UVM_ENDORSEMENTS_FILE_NAME)) - if err != nil { - return nil, err - } - - uvmEndorsement, err := base64.StdEncoding.DecodeString(string(uvmEndorsementsBase64)) - if err != nil { - return nil, fmt.Errorf("Failed to decode base64 string: %s", err) - } - - return uvmEndorsement, nil -} diff --git a/attestation-container/uvm/uvm_endorsement_test.go b/attestation-container/uvm/uvm_endorsement_test.go deleted file mode 100644 index 0a6ced69b8a0..000000000000 --- a/attestation-container/uvm/uvm_endorsement_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package uvm - -import ( - "encoding/base64" - "io/ioutil" - "log" - "os" - "path" - "strings" - "testing" -) - -func TestUVMEndorsement(t *testing.T) { - // Prepare for test - securityContextDirectory, err := ioutil.TempDir("", "security-context") - if err != nil { - log.Fatal(err) - } - defer os.RemoveAll(securityContextDirectory) - - log.Print(securityContextDirectory) - - const testString = "test" - testStringBase64 := base64.StdEncoding.EncodeToString([]byte(testString)) - - // Valid - content := []byte(testStringBase64) - err = ioutil.WriteFile(path.Join(securityContextDirectory, UVM_ENDORSEMENTS_FILE_NAME), content, 0666) - if err != nil { - log.Fatal(err) - } - uvmEndorsement, err := ParseUVMEndorsement(securityContextDirectory) - if err != nil { - t.Fatalf("Couldn't get UVM endorsement: %s", err) - } - if string(uvmEndorsement) != testString { - t.Fatalf("Value doesn't match: '%s' was expected but got '%s'", testString, uvmEndorsement) - } - - // Invalid Based 64 - content = content[1:] - err = ioutil.WriteFile(path.Join(securityContextDirectory, UVM_ENDORSEMENTS_FILE_NAME), content, 0666) - if err != nil { - log.Fatal(err) - } - uvmEndorsement, err = ParseUVMEndorsement(securityContextDirectory) - if !strings.Contains(err.Error(), "Failed to decode base64 string: illegal base64 data") { - t.Fatalf("Couldn't get UVM endorsement: %s", err) - } -} diff --git a/scripts/azure_deployment/arm_aci.py b/scripts/azure_deployment/arm_aci.py index 63aecdd58db8..e4fe2a3bf581 100644 --- a/scripts/azure_deployment/arm_aci.py +++ b/scripts/azure_deployment/arm_aci.py @@ -205,12 +205,6 @@ def parse_aci_args(parser: ArgumentParser) -> Namespace: type=str, default=None, ) - parser.add_argument( - "--attestation-container-e2e", - help="Deploy attestation container for its E2E test if this flag is true. Default=False", - default=False, - action="store_true", - ) parser.add_argument( "--aci-storage-account-key", @@ -240,45 +234,20 @@ def make_aci_deployment(args: Namespace) -> Deployment: } for i in range(args.count): - if not args.attestation_container_e2e: - deployment_name = args.deployment_name - container_name = args.deployment_name - container_image = args.aci_image - command = make_dev_container_command(args) - containers = [ - make_dev_container( - i, - container_name, - container_image, - command, - args.ports, - with_volume=True, - ) - ] - else: - # Attestation container E2E test requires two ports as `args.ports`: [, ] - container_image = f"attestationcontainerregistry.azurecr.io/attestation-container:{args.deployment_name}" - deployment_name = f"{args.deployment_name}-business-logic" - container_name = f"{args.deployment_name}-attestation-container" - command = make_attestation_container_command() - container_name_dummy_blc = ( - f"{args.deployment_name}-dummy-business-logic-container" + deployment_name = args.deployment_name + container_name = args.deployment_name + container_image = args.aci_image + command = make_dev_container_command(args) + containers = [ + make_dev_container( + i, + container_name, + container_image, + command, + args.ports, + with_volume=True, ) - command_dummy_blc = make_dummy_business_logic_container_command() - containers = [ - make_attestation_container( - container_name, - container_image, - command, - with_volume=True, - ), - make_dummy_business_logic_container( - container_name_dummy_blc, - container_image, # Same image for now to run existing end-to-end test - command_dummy_blc, - with_volume=True, - ), - ] + ] container_group_properties = { "sku": "Standard" if args.non_confidential else "Confidential", @@ -439,43 +408,41 @@ def check_aci_deployment( args.resource_group, container_group_name ) - if not args.attestation_container_e2e: - # Check that container commands have been completed - start_time = time.time() - end_time = start_time + args.aci_setup_timeout - current_time = start_time + start_time = time.time() + end_time = start_time + args.aci_setup_timeout + current_time = start_time - while current_time < end_time: - try: - print( - f"Attempting SSH connection to container {container_group.ip_address.ip}" + while current_time < end_time: + try: + print( + f"Attempting SSH connection to container {container_group.ip_address.ip}" + ) + assert ( + subprocess.check_output( + [ + "ssh", + f"agent@{container_group.ip_address.ip}", + "-o", + "StrictHostKeyChecking=no", + "-o", + "ConnectTimeout=100", + "echo test", + ] ) - assert ( - subprocess.check_output( - [ - "ssh", - f"agent@{container_group.ip_address.ip}", - "-o", - "StrictHostKeyChecking=no", - "-o", - "ConnectTimeout=100", - "echo test", - ] + == b"test\n" + ) + if args.out: + with open(os.path.expanduser(args.out), "w") as f: + f.write( + f"{container_group_name}, {container_group.ip_address.ip}{os.linesep}" ) - == b"test\n" - ) - if args.out: - with open(os.path.expanduser(args.out), "w") as f: - f.write( - f"{container_group_name}, {container_group.ip_address.ip}{os.linesep}" - ) - print(container_group_name, container_group.ip_address.ip) - break - except Exception as e: - print(f"Error during SSH connection: {e}") - time.sleep(5) - current_time = time.time() - - assert ( - current_time < end_time - ), "Timed out waiting for container commands to run" + print(container_group_name, container_group.ip_address.ip) + break + except Exception as e: + print(f"Error during SSH connection: {e}") + time.sleep(5) + current_time = time.time() + + assert ( + current_time < end_time + ), "Timed out waiting for container commands to run" diff --git a/scripts/ci-checks.sh b/scripts/ci-checks.sh index 2cc84bea2561..b79bb8e9375c 100755 --- a/scripts/ci-checks.sh +++ b/scripts/ci-checks.sh @@ -124,44 +124,4 @@ endgroup group "Python types" git ls-files python/ | grep -e '\.py$' | xargs mypy -endgroup - -group "Go dependencies" -GO_VERSION="1.20" -if command -v go &> /dev/null -then - # go is found - if ! go version | grep go$GO_VERSION &> /dev/null - then - echo "Wrong version of go is installed. Please make sure version $GO_VERSION.x is installed." - echo -n "Current install version: " - go version - exit 1 - fi -else - # go is not found - # Install the latest bugfix version of GO_VERSION - # https://github.com/golang/go/issues/36898 - install_version=$(curl -sL 'https://go.dev/dl/?mode=json&include=all' | jq -r '.[].version' | grep -m 1 go$GO_VERSION) - tar_filename=$install_version.linux-amd64.tar.gz - curl -sLO "https://go.dev/dl/$tar_filename" - function clean_up_tar { - rm "$tar_filename" - } - trap clean_up_tar EXIT - tar -C /usr/local -xzf "$tar_filename" - # shellcheck disable=SC2016,SC1090 - echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc && source ~/.bashrc -fi - -group "Go format" -if [ $FIX -ne 0 ]; then - git ls-files attestation-container/ | grep -e '\.go$' | xargs gofmt -w -else - GOFMT_RES=$(git ls-files attestation-container/ | grep -e '\.go$' | xargs gofmt -d) - if [ "$GOFMT_RES" != "" ]; - then - echo "Format of go codes is broken" - echo "$GOFMT_RES" - fi -fi +endgroup \ No newline at end of file diff --git a/tests/external_executor/executors/Dockerfile b/tests/external_executor/executors/Dockerfile deleted file mode 100644 index 969901e160fa..000000000000 --- a/tests/external_executor/executors/Dockerfile +++ /dev/null @@ -1,34 +0,0 @@ -# Dockerfile used to generate images for Python-based CCF external executors - -# Build -FROM mcr.microsoft.com/cbl-mariner/base/python:3 as builder - -ARG ccf_dir="ccf" -ARG attestation_container_dir="attestation_container" -COPY "$ccf_dir"/protobuf/ protobuf/ -COPY "$attestation_container_dir"/protobuf/ protobuf/ - -RUN pip install "grpcio-tools==1.44.0" \ - && mkdir -p generated \ - && python3 -m grpc_tools.protoc \ - -I protobuf/ \ - --python_out generated/ \ - --grpc_python_out generated/ \ - $(find protobuf/ -iname "*.proto") - -# Run -FROM mcr.microsoft.com/cbl-mariner/base/python:3 - -ARG ccf_dir="ccf" -ARG app_dir="logging_app" - -WORKDIR /app - -COPY --from=builder /generated . -COPY app.py . -COPY "$app_dir" "$app_dir" -COPY "$ccf_dir"/executors/ "$ccf_dir"/executors/ - -RUN pip install --upgrade pip && pip3 install -r "$app_dir"/requirements.txt - -CMD ["python3", "app.py"] \ No newline at end of file diff --git a/tests/external_executor/executors/app.py b/tests/external_executor/executors/app.py index 4802b29b6772..5d7f7b62a4dd 100644 --- a/tests/external_executor/executors/app.py +++ b/tests/external_executor/executors/app.py @@ -21,6 +21,7 @@ ccf_address, service_certificate_bytes, LoggingExecutor.get_supported_endpoints(), + with_attestation_container=False, ) e = LoggingExecutor(ccf_address, credentials) signal.signal(signal.SIGTERM, e.terminate) diff --git a/tests/external_executor/executors/ccf/executors/registration.py b/tests/external_executor/executors/ccf/executors/registration.py index fecbd9ea3a54..aafb66af181c 100644 --- a/tests/external_executor/executors/ccf/executors/registration.py +++ b/tests/external_executor/executors/ccf/executors/registration.py @@ -1,7 +1,6 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the Apache 2.0 License. import grpc -import time # pylint: disable=import-error import executor_registration_pb2 as ExecutorRegistration @@ -9,11 +8,6 @@ # pylint: disable=import-error import executor_registration_pb2_grpc as RegistrationService -# pylint: disable=import-error -import attestation_container_pb2_grpc as AttestationContainerService - -# pylint: disable=import-error -import attestation_container_pb2 as AttestationContainer from cryptography.hazmat.primitives.asymmetric import ec from cryptography import x509 @@ -32,7 +26,6 @@ from loguru import logger as LOG DEFAULT_VALIDITY_PERIOD_DAYS = 90 -ATTESTATION_CONTAINER_UNIX_DOMAIN_SOCKET = "unix:///tmp/attestation-container.sock" def generate_ec_keypair(curve=ec.SECP256R1): @@ -90,46 +83,16 @@ def register_new_executor( node_public_rpc_address, service_certificate_bytes, supported_endpoints=None, - with_attestation_container=True, # Note: remove when all executors are containerised + with_attestation_container=False, # Note: remove when all executors are containerised timeout=3, ): # Generate a new executor identity key_priv_pem, _ = generate_ec_keypair() cert = generate_self_signed_cert(key_priv_pem) - # Retrieve attestation report and endorsements from attestation container - # Note: As containers (in the same container group) may startup at different speeds, - # wait a reasonable timeout until the attestation container is up. - if with_attestation_container: - end_time = time.time() + timeout - while True: - with grpc.insecure_channel( - target=ATTESTATION_CONTAINER_UNIX_DOMAIN_SOCKET, - ) as channel: - message = AttestationContainer.FetchAttestationRequest() - message.report_data = b"" # Note: unset for now - stub = AttestationContainerService.AttestationContainerStub(channel) - - try: - reply = stub.FetchAttestation(message) - except grpc.RpcError as e: - if time.time() > end_time: - raise TimeoutError( - f"Attestation container could not be reached after {timeout}s. Stopping." - ) from e - LOG.trace("Attestation container starting up, retrying...") - time.sleep(0.1) - continue - else: - break - # Create a default NewExecutor message message = ExecutorRegistration.NewExecutor() message.attestation.format = ExecutorRegistration.Attestation.AMD_SEV_SNP_V1 - if with_attestation_container: - message.attestation.attestation = reply.attestation - message.attestation.attestation_endorsements = reply.attestation_endorsements - message.attestation.uvm_endorsements = reply.uvm_endorsements if supported_endpoints: for method, uri in supported_endpoints: diff --git a/tests/external_executor/executors/utils/executor_container.py b/tests/external_executor/executors/utils/executor_container.py index 951d19cb86b2..cdd928699bb2 100644 --- a/tests/external_executor/executors/utils/executor_container.py +++ b/tests/external_executor/executors/utils/executor_container.py @@ -75,7 +75,6 @@ def __init__(self, executor: str, node: Node, network: Network): self._name = f"{executor}_{self._executors_count[executor]}" self._dir = os.path.join(self._node.remote.remote.root, self._name) self.executor_container = None - self.attestation_container = None # Build external executor image_name = executor @@ -83,28 +82,6 @@ def __init__(self, executor: str, node: Node, network: Network): image_name, os.path.join(CCF_DIR, "tests/external_executor/executors") ) - # Build attestation container - attestation_container_image_name = "attestation_container" - self._build_image( - attestation_container_image_name, - os.path.join(CCF_DIR, "attestation-container"), - ) - - # Create shared volume for attestation container unix domain socket - self._shared_volume = self._client.volumes.create(name="shared_volume") - - # Create attestation container - attestation_container_name = f"ac_{self._name}" - self._cleanup_container(attestation_container_name) - self._attestation_container = self._client.containers.create( - image=attestation_container_image_name, - name=attestation_container_name, - publish_all_ports=True, - command="app --insecure-virtual", # Remove insecure argument when we run this in SNP ACI - auto_remove=True, - volumes={self._shared_volume.name: {"bind": "/tmp", "mode": "rw"}}, - ) - # Create external executor container # self._cleanup_container(self._name) service_certificate_bytes = open( @@ -117,7 +94,7 @@ def __init__(self, executor: str, node: Node, network: Network): "CCF_CORE_NODE_RPC_ADDRESS": node.get_public_rpc_address(), "CCF_CORE_SERVICE_CERTIFICATE": b64encode(service_certificate_bytes), }, - volumes_from=[attestation_container_name], + volumes_from=[], publish_all_ports=True, auto_remove=True, ) @@ -128,9 +105,6 @@ def __init__(self, executor: str, node: Node, network: Network): def start(self): LOG.debug(f"Starting container {self._name}...") self.executor_container = ScopedContainer(self._container, self._dir) - self.attestation_container = ScopedContainer( - self._attestation_container, self._dir, "ac.out" - ) LOG.info(f"Container {self._name} started") def wait_for_registration(self, timeout=10): @@ -157,7 +131,6 @@ def wait_for_registration(self, timeout=10): def terminate(self): LOG.debug(f"Terminating container {self._name}...") self.executor_container.stop() - self.attestation_container.stop() LOG.info(f"Container {self._name} stopped")