diff --git a/.github/actions/publish-oras-artifact/action.yml b/.github/actions/publish-oras-artifact/action.yml new file mode 100644 index 000000000..636ece0f2 --- /dev/null +++ b/.github/actions/publish-oras-artifact/action.yml @@ -0,0 +1,65 @@ +name: Publish ORAS artifact +description: Package a binary and publish as ORAS artifact with manifest + +inputs: + registry: + description: OCI registry hostname + required: true + image_name: + description: Repository image namespace + required: true + artifact_name: + description: Binary/artifact name + required: true + rust_target: + description: Rust target triple + required: true + git_sha: + description: Git SHA for tag + required: true + tee: + description: TEE platform suffix for tag + required: true + arch: + description: Build architecture (x86_64/aarch64/powerpc64le/...) + required: true + oci_arch: + description: OCI architecture for manifest annotation + required: true + +outputs: + image: + description: Published image name + value: ${{ steps.publish.outputs.image }} + digest: + description: Published image digest + value: ${{ steps.publish.outputs.digest }} + +runs: + using: composite + steps: + - id: publish + shell: bash + run: | + mkdir -p oras + cd oras + cp ../target/${{ inputs.rust_target }}/release/${{ inputs.artifact_name }} . + tar cJf "${{ inputs.artifact_name }}.tar.xz" "${{ inputs.artifact_name }}" + + arch="${{ inputs.arch }}" + # Align non-standard Rust arch naming with OCI conventions. + [ "$arch" = "powerpc64le" ] && arch="ppc64le" + + arch_tag="${{ inputs.git_sha }}-${{ inputs.tee }}_${arch}" + image="${{ inputs.registry }}/${{ inputs.image_name }}/${{ inputs.artifact_name }}" + tag="${{ inputs.git_sha }}-${{ inputs.tee }}" + + oras push "${image}:${arch_tag}" "${{ inputs.artifact_name }}.tar.xz" + docker manifest create "${image}:${tag}" --amend "${image}:${arch_tag}" + docker manifest annotate --arch "${{ inputs.oci_arch }}" --os linux "${image}:${tag}" "${image}:${arch_tag}" + docker manifest push "${image}:${tag}" + + # add image and digest to output for attestation + echo "image=${image}" >> "$GITHUB_OUTPUT" + digest="$(oras manifest fetch "${image}:${arch_tag}" --descriptor | jq -r .digest)" + echo "digest=${digest}" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/publish-artifacts.yml b/.github/workflows/publish-artifacts.yml index ccfe018cf..dde524a51 100644 --- a/.github/workflows/publish-artifacts.yml +++ b/.github/workflows/publish-artifacts.yml @@ -9,7 +9,7 @@ permissions: contents: read jobs: - publish-aa: + publish-aa-and-initdata-validator: permissions: contents: read packages: write @@ -70,38 +70,39 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - name: Build AA + - name: Build AA and Initdata Validator env: ARCH: ${{ matrix.platform.arch }} LIBC: ${{ matrix.platform.libc }} run: | make ./target/${{ env.RUST_TARGET }}/release/attestation-agent + make ./target/${{ env.RUST_TARGET }}/release/initdata-validator - - name: Publish with ORAS + - name: Publish AA with ORAS id: publish - env: - OCI_ARCH: ${{ matrix.platform.arch == 'x86_64' && 'amd64' || matrix.platform.arch == 'aarch64' && 'arm64' || matrix.platform.arch == 'powerpc64le' && 'ppc64le' || matrix.platform.arch }} - run: | - mkdir oras - cd oras - cp ../target/${{ env.RUST_TARGET }}/release/attestation-agent . - tar cJf attestation-agent.tar.xz attestation-agent - arch="${{ matrix.platform.arch }}" - # After building for target powerpc64le, tag and push the image as ppc64le to match standard arch naming. - [ "$arch" = "powerpc64le" ] && arch="ppc64le" - arch_tag="${{ github.sha }}-${{ matrix.platform.tee }}_${arch}" - image="${REGISTRY}/${IMAGE_NAME}/attestation-agent" - tag="${{ github.sha }}-${{ matrix.platform.tee }}" - oras push "${image}:${arch_tag}" attestation-agent.tar.xz - # We need to create the platform annotations with docker, since oras 1.2 doesn't support - # pushing with platform yet. - docker manifest create "${image}:${tag}" --amend "${image}:${arch_tag}" - docker manifest annotate --arch "$OCI_ARCH" --os linux "${image}:${tag}" "${image}:${arch_tag}" - docker manifest push "${image}:${tag}" - # add image and digest to output for attestation - echo "image=${image}" >> "$GITHUB_OUTPUT" - digest="$(oras manifest fetch "${image}:${arch_tag}" --descriptor | jq -r .digest)" - echo "digest=${digest}" >> "$GITHUB_OUTPUT" + uses: ./.github/actions/publish-oras-artifact + with: + registry: ${{ env.REGISTRY }} + image_name: ${{ env.IMAGE_NAME }} + artifact_name: attestation-agent + rust_target: ${{ env.RUST_TARGET }} + git_sha: ${{ github.sha }} + tee: ${{ matrix.platform.tee }} + arch: ${{ matrix.platform.arch }} + oci_arch: ${{ matrix.platform.arch == 'x86_64' && 'amd64' || matrix.platform.arch == 'aarch64' && 'arm64' || matrix.platform.arch == 'powerpc64le' && 'ppc64le' || matrix.platform.arch }} + + - name: Publish Initdata Validator with ORAS + id: publish-initdata-validator + uses: ./.github/actions/publish-oras-artifact + with: + registry: ${{ env.REGISTRY }} + image_name: ${{ env.IMAGE_NAME }} + artifact_name: initdata-validator + rust_target: ${{ env.RUST_TARGET }} + git_sha: ${{ github.sha }} + tee: ${{ matrix.platform.tee }} + arch: ${{ matrix.platform.arch }} + oci_arch: ${{ matrix.platform.arch == 'x86_64' && 'amd64' || matrix.platform.arch == 'aarch64' && 'arm64' || matrix.platform.arch == 'powerpc64le' && 'ppc64le' || matrix.platform.arch }} - uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 with: @@ -109,6 +110,12 @@ jobs: subject-digest: ${{ steps.publish.outputs.digest }} push-to-registry: true + - uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 + with: + subject-name: ${{ steps.publish-initdata-validator.outputs.image }} + subject-digest: ${{ steps.publish-initdata-validator.outputs.digest }} + push-to-registry: true + publish-cdh-and-asr: permissions: contents: read diff --git a/Makefile b/Makefile index 011d7ec3c..5ecb1a9fe 100644 --- a/Makefile +++ b/Makefile @@ -74,12 +74,15 @@ endif CDH := confidential-data-hub AA := attestation-agent ASR := api-server-rest +ATTESTER_DIR := attester +INITDATA_VALIDATOR := initdata-validator BUILD_DIR := target/$(ARCH)-unknown-linux-$(LIBC)/release CDH_BINARY := $(BUILD_DIR)/$(CDH) AA_BINARY := $(BUILD_DIR)/$(AA) ASR_BINARY := $(BUILD_DIR)/$(ASR) +INITDATA_VALIDATOR_BINARY := $(BUILD_DIR)/$(INITDATA_VALIDATOR) build: $(CDH_BINARY) $(ASR_BINARY) $(AA_BINARY) @echo guest components built for $(TEE_PLATFORM) succeeded! @@ -92,6 +95,10 @@ $(AA_BINARY): @echo build $(AA) for $(TEE_PLATFORM) cd $(AA) && $(MAKE) ttrpc=true ARCH=$(ARCH) LIBC=$(LIBC) ATTESTER=$(ATTESTER) +$(INITDATA_VALIDATOR_BINARY): + @echo build $(INITDATA_VALIDATOR) for $(TEE_PLATFORM) + cd $(AA)/$(ATTESTER_DIR) && $(MAKE) ARCH=$(ARCH) LIBC=$(LIBC) ATTESTER=$(ATTESTER) + $(ASR_BINARY): @echo build $(ASR) for $(TEE_PLATFORM) cd $(ASR) && $(MAKE) ARCH=$(ARCH) LIBC=$(LIBC) diff --git a/attestation-agent/attester/Cargo.toml b/attestation-agent/attester/Cargo.toml index eb16357ab..b67ef752d 100644 --- a/attestation-agent/attester/Cargo.toml +++ b/attestation-agent/attester/Cargo.toml @@ -64,6 +64,10 @@ rstest.workspace = true name = "evidence_getter" required-features = ["bin"] +[[bin]] +name = "initdata-validator" +required-features = ["bin"] + [features] default = ["all-attesters"] all-attesters = [ diff --git a/attestation-agent/attester/Makefile b/attestation-agent/attester/Makefile new file mode 100644 index 000000000..60f02e293 --- /dev/null +++ b/attestation-agent/attester/Makefile @@ -0,0 +1,26 @@ +ARCH ?= $(shell uname -m) +LIBC ?= gnu +ATTESTER ?= +DEBUG ?= + +TARGET_DIR := ../../target/$(ARCH)-unknown-linux-$(LIBC) +BIN_NAME := initdata-validator + +features := bin +ifdef ATTESTER +ifneq ($(ATTESTER), none) +features += ,$(ATTESTER) +endif +else +features += ,all-attesters +endif + +ifdef DEBUG +release := +else +release := --release +endif + +build: + @echo build $(BIN_NAME) + cargo build $(release) --no-default-features --features "$(features)" --bin $(BIN_NAME) --target $(ARCH)-unknown-linux-$(LIBC) \ No newline at end of file diff --git a/attestation-agent/attester/src/bin/initdata-validator.rs b/attestation-agent/attester/src/bin/initdata-validator.rs new file mode 100644 index 000000000..3f4f34f7b --- /dev/null +++ b/attestation-agent/attester/src/bin/initdata-validator.rs @@ -0,0 +1,39 @@ +// Copyright (c) 2026 Alibaba Cloud +// +// SPDX-License-Identifier: Apache-2.0 +// + +use anyhow::{Context, Result}; +use attester::{detect_tee_type, BoxedAttester, InitDataResult}; +use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _}; +use clap::Parser; + +#[derive(Debug, Parser)] +#[command(author)] +struct Cli { + /// URL_SAFE_NO_PAD base64 encoded initdata digest. + initdata: String, +} + +#[tokio::main(flavor = "current_thread")] +async fn main() -> Result<()> { + let cli = Cli::parse(); + let initdata = URL_SAFE_NO_PAD + .decode(cli.initdata.as_bytes()) + .context("failed to decode initdata as URL_SAFE_NO_PAD base64")?; + + let tee = detect_tee_type(); + let attester = TryInto::::try_into(tee) + .context("failed to initialize attester for current platform")?; + + match attester + .bind_init_data(&initdata) + .await + .context("failed to bind initdata")? + { + InitDataResult::Ok => println!("initdata bind success: {tee:?}"), + InitDataResult::Unsupported => println!("initdata binding is unsupported on {tee:?}"), + } + + Ok(()) +}