Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions .github/workflows/build-enclave-eif.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# Builds the secure-enclave EIF and pushes the carrier image to GHCR.
# Pull requests build without publishing.
name: Build Enclave EIF Image

on:
pull_request:
paths:
- "secure-enclave/**"
- "shared/**"
- "Cargo.toml"
- "Cargo.lock"
- "rust-toolchain.toml"
- "scripts/build-eif.sh"
- ".github/workflows/build-enclave-eif.yml"
push:
branches:
- main
tags:
- "v*"
workflow_dispatch:
inputs:
version:
description: "Version tag to publish (including v prefix, e.g. v0.1.0)"
required: false
type: string

permissions:
contents: read

env:
NITRO_CLI_VERSION: v1.4.2

jobs:
build-enclave-eif:
name: Build enclave EIF + carrier image
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
packages: write
attestations: write
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
Comment thread
kilianglas marked this conversation as resolved.
Outdated

- name: Cache nitro-cli build
uses: actions/cache@v4
with:
path: target/eif/aws-nitro-enclaves-cli-${{ env.NITRO_CLI_VERSION }}
key: nitro-cli-${{ runner.os }}-${{ env.NITRO_CLI_VERSION }}

- name: Resolve version
id: version
if: github.ref_type == 'tag' || inputs.version != ''
env:
INPUT_VERSION: ${{ inputs.version }}
run: |
if [[ -n "$INPUT_VERSION" ]]; then
VERSION="$INPUT_VERSION"
else
VERSION="$GITHUB_REF_NAME"
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
if [[ "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "is_stable=true" >> "$GITHUB_OUTPUT"
else
echo "is_stable=false" >> "$GITHUB_OUTPUT"
fi

- name: Build EIF and PCRs
run: scripts/build-eif.sh target/eif

- name: Publish PCR measurements
run: |
{
echo '### Enclave PCR measurements'
echo '```json'
cat target/eif/pcrs.json
echo '```'
} >> "$GITHUB_STEP_SUMMARY"

- name: Upload PCR measurements
uses: actions/upload-artifact@v4
with:
name: enclave-pcrs
path: target/eif/pcrs.json
if-no-files-found: error

- name: Docker meta
id: meta
uses: docker/metadata-action@v5

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Semgrep identified an issue in your code:

Third-party action docker/metadata-action@v5 is referenced by mutable version tag instead of immutable commit SHA, allowing attackers who compromise the repository to inject malicious code into your workflow.

More details about this

The action docker/metadata-action@v5 is pinned to a version tag rather than a specific commit SHA. This means the tag reference (v5) can be updated or replaced at any time by the maintainers of the docker/metadata-action repository, potentially without your knowledge.

Here's a concrete attack scenario:

  1. An attacker compromises the docker/metadata-action repository or tricks maintainers into merging malicious code.
  2. They push a new commit and update the v5 tag to point to their compromised version (tags in Git are mutable by default).
  3. Your workflow runs and pulls docker/metadata-action@v5, which now fetches the attacker's malicious code instead of the original.
  4. The malicious action could extract secrets from the GitHub Actions environment (like ${{ secrets.GITHUB_TOKEN }}), steal repository data, or inject backdoors into your build artifacts.
  5. Since your workflow uses this action to tag and push images to ghcr.io/${{ github.repository }}-enclave-eif, the attacker could now push a compromised container image to your registry.

Using a full commit SHA (like @a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b) instead of the tag makes the reference immutable—the specific code that runs is locked in and cannot be changed retroactively.

To resolve this comment:

✨ Commit fix suggestion
  1. Replace the version tag in the uses line with the full 40-character commit SHA for docker/metadata-action instead of @v5.
  2. Keep the current major version visible in a comment so the workflow stays readable, for example: uses: docker/metadata-action@<full-commit-sha> # v5.
  3. Get the correct SHA from the docker/metadata-action repository release or tag that corresponds to v5, and pin exactly that commit. This makes the action reference immutable so the workflow cannot silently pick up different code later.
💬 Ignore this finding

Reply with Semgrep commands to ignore this finding.

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by third-party-action-not-pinned-to-commit-sha.

You can view more details about this finding in the Semgrep AppSec Platform.

with:
images: ghcr.io/${{ github.repository }}-enclave-eif
tags: |
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' || steps.version.outputs.is_stable == 'true' }}
type=sha
type=raw,value=${{ steps.version.outputs.version }},enable=${{ steps.version.outputs.version != '' }}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
Comment thread
kilianglas marked this conversation as resolved.
Outdated

- name: Login to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
Comment thread
kilianglas marked this conversation as resolved.
Outdated
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push carrier image
id: build
uses: docker/build-push-action@v6
Comment thread
kilianglas marked this conversation as resolved.
Outdated
with:
# Context is the EIF output dir so the .eif is always included.
context: target/eif
file: secure-enclave/Dockerfile.eif
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64

- name: Attest build provenance
if: github.event_name != 'pull_request'
uses: actions/attest-build-provenance@v1
with:
push-to-registry: true
subject-name: ghcr.io/${{ github.repository }}-enclave-eif
subject-digest: ${{ steps.build.outputs.digest }}
54 changes: 54 additions & 0 deletions scripts/build-eif.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/bin/bash
set -euo pipefail

# Build the secure-enclave EIF and emit its PCR measurements. Needs Linux
# x86_64 + Docker; Nitro hardware is only required to run the enclave, not
# build it.
#
# Usage: scripts/build-eif.sh [output-dir] (default: target/eif)
# Outputs: embedding-verifier-enclave.eif, pcrs.json
# Env: NITRO_CLI_VERSION (default v1.4.2), ENCLAVE_IMAGE_TAG

if [ "$(uname -s)" != "Linux" ] || [ "$(uname -m)" != "x86_64" ]; then
echo "[ERROR] EIF builds require Linux x86_64 (got $(uname -s)/$(uname -m))." >&2
exit 1
fi

NITRO_CLI_VERSION="${NITRO_CLI_VERSION:-v1.4.2}"
ENCLAVE_IMAGE_TAG="${ENCLAVE_IMAGE_TAG:-embedding-verifier-enclave:local}"

repo_root="$(git rev-parse --show-toplevel)"
cd "$repo_root"

out_dir="${1:-target/eif}"
mkdir -p "$out_dir"
out_dir="$(cd "$out_dir" && pwd)"

echo "[1/3] Building enclave container image ($ENCLAVE_IMAGE_TAG)..."
docker build -t "$ENCLAVE_IMAGE_TAG" -f secure-enclave/Dockerfile .

echo "[2/3] Building nitro-cli $NITRO_CLI_VERSION..."
nitro_cli_dir="$out_dir/aws-nitro-enclaves-cli-$NITRO_CLI_VERSION"
nitro_cli="$nitro_cli_dir/target/release/nitro-cli"
if [ ! -x "$nitro_cli" ]; then
rm -rf "$nitro_cli_dir"
git clone --depth 1 --branch "$NITRO_CLI_VERSION" \
https://github.com/aws/aws-nitro-enclaves-cli "$nitro_cli_dir"
cargo build --release --bin nitro-cli --manifest-path "$nitro_cli_dir/Cargo.toml"
fi

echo "[3/3] Converting to EIF..."
eif_path="$out_dir/embedding-verifier-enclave.eif"
build_json="$out_dir/build-enclave.json"
NITRO_CLI_BLOBS="$nitro_cli_dir/blobs/x86_64" \
NITRO_CLI_ARTIFACTS="$out_dir/artifacts" \
"$nitro_cli" build-enclave \
--docker-uri "$ENCLAVE_IMAGE_TAG" \
--output-file "$eif_path" | tee "$build_json"

jq '.Measurements' "$build_json" > "$out_dir/pcrs.json"

echo
echo "EIF: $eif_path"
echo "PCRs: $out_dir/pcrs.json"
jq . "$out_dir/pcrs.json"
32 changes: 32 additions & 0 deletions secure-enclave/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# syntax=docker/dockerfile:1
# Enclave workload image, converted to an EIF by scripts/build-eif.sh.
#
# Base images are tag-pinned and the build is --locked. Before a PRODUCTION
# deploy, pin base images by digest + apt to a snapshot.debian.org timestamp so
# PCRs are reproducible, then register the real PCRs (dev runs --debug-mode,
# which zeroes PCRs and skips attestation).

FROM rust:1.97.0-bookworm AS builder

RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
clang \
pkg-config \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /src
COPY . .

RUN cargo build --release --locked --package secure-enclave --bin secure-enclave

FROM debian:bookworm-slim

RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*

COPY --from=builder /src/target/release/secure-enclave /usr/local/bin/secure-enclave

ENV RUST_LOG=info

ENTRYPOINT ["/usr/local/bin/secure-enclave"]
Comment thread
kilianglas marked this conversation as resolved.
43 changes: 43 additions & 0 deletions secure-enclave/Dockerfile.eif
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# syntax=docker/dockerfile:1
# Carrier image that launches the enclave EIF on Nitro-enabled nodes.
# Build context: target/eif/ (produced by scripts/build-eif.sh).

FROM public.ecr.aws/amazonlinux/amazonlinux:2023

RUN dnf install -y aws-nitro-enclaves-cli aws-nitro-enclaves-cli-devel jq && \
dnf clean all

COPY embedding-verifier-enclave.eif /home/embedding-verifier-enclave.eif

ENV EIF_PATH="/home/embedding-verifier-enclave.eif" \
ENCLAVE_CPU_COUNT=2 \
ENCLAVE_MEMORY_SIZE=1024 \
ENCLAVE_DEBUG_MODE=false

COPY <<'EOF' /home/run.sh
#!/bin/bash
set -euo pipefail

echo "[run.sh] Starting enclave: eif=${EIF_PATH} cpu=${ENCLAVE_CPU_COUNT} mem=${ENCLAVE_MEMORY_SIZE}MiB debug=${ENCLAVE_DEBUG_MODE}"

DEBUG_FLAG=""
if [[ "${ENCLAVE_DEBUG_MODE}" == "true" || "${ENCLAVE_DEBUG_MODE}" == "1" ]]; then
DEBUG_FLAG="--debug-mode"
fi

run_output=$(nitro-cli run-enclave \
--cpu-count "${ENCLAVE_CPU_COUNT}" \
--memory "${ENCLAVE_MEMORY_SIZE}" \
--eif-path "${EIF_PATH}" \
${DEBUG_FLAG})

enclave_id=$(echo "${run_output}" | jq -r '.EnclaveID')
echo "[run.sh] Enclave started: ${enclave_id}"

# Blocking — keeps the container alive.
nitro-cli console --enclave-id "${enclave_id}"
EOF

RUN chmod +x /home/run.sh

CMD ["/home/run.sh"]
Loading