Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 58 additions & 54 deletions deployments/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,54 +1,58 @@
x-health-retry:
&health-retry
interval: 10s
timeout: 10s
retries: 30

services:
enclave:
image: "nitro-enclave-signer-internal:local"
environment:
APP_ENV: ${APP_ENV:-dev}
AWS_PROFILE: ${AWS_PROFILE:-}
AWS_ACCESS_KEY_ID: foo
AWS_SECRET_ACCESS_KEY: bar
APP_PROVIDER_AWSKMS_LOCALSTACK_ENABLED: true
APP_PROVIDER_AWSKMS_LOCALSTACK_ENDPOINT: http://localstack:4566
APP_PROVIDER_AWSKMS_LOCALSTACK_REGION: us-east-1
APP_PROVIDER_AWSKMS_ARNS: arn:aws:kms:us-east-1:000000000000:alias/dev-multi-region-crypto,arn:aws:kms:us-west-2:000000000000:alias/dev-multi-region-crypto
APP_NITROENCLAVE_ENABLED: false
ulimits:
memlock: -1
ports:
- "10350:10350"
healthcheck:
test: ["CMD", "grpc_health_probe", "-addr=127.0.0.1:10350", "-service=arc.enclave.v1.EnclaveService"]
<<: *health-retry
entrypoint: ["/usr/local/circle/run_enclave.dev.sh"]
depends_on:
localstack:
condition: service_healthy

######################
##### Localstack #####
######################
localstack:
container_name: localstack
image: localstack/localstack:3
ports:
- "127.0.0.1:4566:4566" # LocalStack Gateway
- "127.0.0.1:4510-4559:4510-4559" # external services port range
environment:
DEBUG: ${DEBUG-}
DOCKER_HOST: unix:///var/run/docker.sock
LOCALSTACK_HOST: localstack
LOCALSTACK_PORT: 4566
volumes:
- "${LOCALSTACK_VOLUME_DIR:-./volume}:/var/lib/localstack"
- "/var/run/docker.sock:/var/run/docker.sock"
- "./localstack_scripts/create-kms-keys.sh:/etc/localstack/init/ready.d/create-kms-keys.sh" # runs script when localstack is ready
- "./localstack_scripts/create-secret-manager-key.sh:/etc/localstack/init/ready.d/create-secret-manager-key.sh" # runs script when localstack is ready
- './localstack_scripts/health-check.sh:/opt/health-check.sh' # health check script
healthcheck:
test: ['CMD', 'bash', '/opt/health-check.sh']
<<: *health-retry
x-health-retry:
&health-retry
interval: 10s
timeout: 10s
retries: 30

services:
enclave:
image: "nitro-enclave-signer-internal:local"
environment:
APP_ENV: ${APP_ENV:-dev}
AWS_PROFILE: ${AWS_PROFILE:-}
AWS_ACCESS_KEY_ID: foo
AWS_SECRET_ACCESS_KEY: bar
APP_PROVIDER_AWSKMS_LOCALSTACK_ENABLED: true
APP_PROVIDER_AWSKMS_LOCALSTACK_ENDPOINT: http://localstack:4566
APP_PROVIDER_AWSKMS_LOCALSTACK_REGION: us-east-1
APP_PROVIDER_AWSKMS_ARNS: arn:aws:kms:us-east-1:000000000000:alias/dev-multi-region-crypto,arn:aws:kms:us-west-2:000000000000:alias/dev-multi-region-crypto
APP_NITROENCLAVE_ENABLED: false
ulimits:
memlock: -1
ports:
- "127.0.0.1:10350:10350"
healthcheck:
test: ["CMD", "grpc_health_probe", "-addr=127.0.0.1:10350", "-service=arc.enclave.v1.EnclaveService"]
<<: *health-retry
entrypoint: ["/usr/local/circle/run_enclave.dev.sh"]
depends_on:
localstack:
condition: service_healthy

######################
##### Localstack #####
######################
localstack:
container_name: localstack
image: localstack/localstack:3
ports:
- "127.0.0.1:4566:4566" # LocalStack Gateway
- "127.0.0.1:4510-4559:4510-4559" # external services port range
environment:
DEBUG: ${DEBUG-}
LOCALSTACK_HOST: localstack
LOCALSTACK_PORT: 4566
volumes:
- "${LOCALSTACK_VOLUME_DIR:-./volume}:/var/lib/localstack"
# Security fix: `/var/run/docker.sock` was bind-mounted into the LocalStack container,
# which grants it full control of the host Docker daemon - effectively root on the host.
# LocalStack only needs the daemon to run Lambda, which this compose file does not enable,
# and it executes repo-supplied `./localstack_scripts/*.sh` at startup, so any change to
# those scripts escalated to host root. The socket mount is not required for KMS/Secrets
# Manager emulation and has been removed.
- "./localstack_scripts/create-kms-keys.sh:/etc/localstack/init/ready.d/create-kms-keys.sh" # runs script when localstack is ready
- "./localstack_scripts/create-secret-manager-key.sh:/etc/localstack/init/ready.d/create-secret-manager-key.sh" # runs script when localstack is ready
- './localstack_scripts/health-check.sh:/opt/health-check.sh' # health check script
healthcheck:
test: ['CMD', 'bash', '/opt/health-check.sh']
<<: *health-retry
185 changes: 97 additions & 88 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,88 +1,97 @@
ARG RUST_IMAGE=rust:1.95-bookworm@sha256:adab7941580c74513aa3347f2d2a1f975498280743d29ec62978ba12e3540d3a
ARG DEBIAN_IMAGE=debian:bookworm-slim@sha256:f9c6a2fd2ddbc23e336b6257a5245e31f996953ef06cd13a59fa0a1df2d5c252
ARG NITRO_CLI_VERSION=v1.4.4
# SHA256 of the source tarball for ${NITRO_CLI_VERSION}. Verified at build
# time so a retagged upstream cannot silently change the binary we ship.
ARG NITRO_CLI_SHA256=5f8182119496a07403b755e1c7e62780acc71ba136639226cdbb508482836e01

### Build grpc-health-probe from master (404552c) to get grpc v1.79.3 (CVE-2026-33186 fix).
### TODO: Revert to `COPY --from=ghcr.io/grpc-ecosystem/grpc-health-probe:<tag>` once a tagged release includes grpc >= v1.79.3.
FROM golang:1.25 AS grpc-health-probe-builder

RUN --mount=type=bind,from=certs,target=/usr/local/share/ca-certificates/ \
update-ca-certificates && \
CGO_ENABLED=0 GOBIN=/ go install github.com/grpc-ecosystem/grpc-health-probe@404552c

### Build nitro-cli from source. Only the binary is needed at runtime; blobs
### are only used when building EIFs (handled by the shared CI pipeline).
FROM ${RUST_IMAGE} AS nitro-cli-builder

ARG NITRO_CLI_VERSION
ARG NITRO_CLI_SHA256

RUN --mount=type=bind,from=certs,target=/usr/local/share/ca-certificates/ \
update-ca-certificates && \
apt-get update && \
apt-get install -y --no-install-recommends musl-tools && \
rm -rf /var/lib/apt/lists/*

SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN curl -sSfL -o /tmp/nitro-cli.tar.gz \
"https://github.com/aws/aws-nitro-enclaves-cli/archive/refs/tags/${NITRO_CLI_VERSION}.tar.gz" && \
echo "${NITRO_CLI_SHA256} /tmp/nitro-cli.tar.gz" | sha256sum -c - && \
tar xzf /tmp/nitro-cli.tar.gz --strip-components=1 && \
rm /tmp/nitro-cli.tar.gz && \
make nitro-cli-native && \
install -D -m 0755 build/nitro_cli/release/nitro-cli /out/usr/bin/nitro-cli && \
mkdir -p /out/var/log/nitro_enclaves /out/run/nitro_enclaves

### This is the base image that all other images inherit from
FROM ${DEBIAN_IMAGE} AS base

ARG TARGETARCH

WORKDIR /usr/local/circle

COPY --chmod=0750 bin/$TARGETARCH/app ./app
COPY configs ./configs
COPY deployments ./deployments

## Copy CA certs from the builder so apt trusts the configured CA bundle
## (e.g. when the build runs behind a proxy with a custom root CA).
COPY --from=grpc-health-probe-builder /etc/ssl/certs /etc/ssl/certs

## Install common dependencies for all targets
RUN apt-get update && \
apt-get install -y --no-install-recommends \
socat \
jq && \
rm -rf /var/lib/apt/lists/*

COPY --from=grpc-health-probe-builder /grpc-health-probe /usr/local/bin/grpc_health_probe

### This is the image that runs outside the nitro enclave, but does not include the enclave binary (EIF file)
FROM base AS signer

COPY --chmod=0750 docker/run_proxy.sh ./run_proxy.sh
COPY --chmod=0750 docker/run_enclave.sh ./run_enclave.sh
COPY --chmod=0750 docker/run_enclave.dev.sh ./run_enclave.dev.sh
ENTRYPOINT ["/usr/local/circle/run_proxy.sh"]

### This is the image that runs outside the nitro enclave, including the enclave binary (EIF file)
FROM signer AS signer-with-enclave

ARG ENCLAVE_EIF
ARG ENCLAVE_PCR0=""
ARG ENCLAVE_PCR1=""
ARG ENCLAVE_PCR2=""

LABEL enclave.pcr0="${ENCLAVE_PCR0}" \
enclave.pcr1="${ENCLAVE_PCR1}" \
enclave.pcr2="${ENCLAVE_PCR2}"

COPY --from=nitro-cli-builder /out/ /

COPY $ENCLAVE_EIF ./enclave.eif

COPY --chmod=0750 docker/run.sh ./run.sh
ENTRYPOINT ["/usr/local/circle/run.sh"]
ARG RUST_IMAGE=rust:1.95-bookworm@sha256:adab7941580c74513aa3347f2d2a1f975498280743d29ec62978ba12e3540d3a
ARG DEBIAN_IMAGE=debian:bookworm-slim@sha256:f9c6a2fd2ddbc23e336b6257a5245e31f996953ef06cd13a59fa0a1df2d5c252
ARG NITRO_CLI_VERSION=v1.4.4
# SHA256 of the source tarball for ${NITRO_CLI_VERSION}. Verified at build
# time so a retagged upstream cannot silently change the binary we ship.
ARG NITRO_CLI_SHA256=5f8182119496a07403b755e1c7e62780acc71ba136639226cdbb508482836e01

### Build grpc-health-probe from master (404552c) to get grpc v1.79.3 (CVE-2026-33186 fix).
### TODO: Revert to `COPY --from=ghcr.io/grpc-ecosystem/grpc-health-probe:<tag>` once a tagged release includes grpc >= v1.79.3.
FROM golang:1.25 AS grpc-health-probe-builder

RUN --mount=type=bind,from=certs,target=/usr/local/share/ca-certificates/ \
update-ca-certificates && \
CGO_ENABLED=0 GOBIN=/ go install github.com/grpc-ecosystem/grpc-health-probe@404552c

### Build nitro-cli from source. Only the binary is needed at runtime; blobs
### are only used when building EIFs (handled by the shared CI pipeline).
FROM ${RUST_IMAGE} AS nitro-cli-builder

ARG NITRO_CLI_VERSION
ARG NITRO_CLI_SHA256

RUN --mount=type=bind,from=certs,target=/usr/local/share/ca-certificates/ \
update-ca-certificates && \
apt-get update && \
apt-get install -y --no-install-recommends musl-tools && \
rm -rf /var/lib/apt/lists/*

SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN curl -sSfL -o /tmp/nitro-cli.tar.gz \
"https://github.com/aws/aws-nitro-enclaves-cli/archive/refs/tags/${NITRO_CLI_VERSION}.tar.gz" && \
echo "${NITRO_CLI_SHA256} /tmp/nitro-cli.tar.gz" | sha256sum -c - && \
tar xzf /tmp/nitro-cli.tar.gz --strip-components=1 && \
rm /tmp/nitro-cli.tar.gz && \
make nitro-cli-native && \
install -D -m 0755 build/nitro_cli/release/nitro-cli /out/usr/bin/nitro-cli && \
mkdir -p /out/var/log/nitro_enclaves /out/run/nitro_enclaves

### This is the base image that all other images inherit from
FROM ${DEBIAN_IMAGE} AS base

ARG TARGETARCH

WORKDIR /usr/local/circle

COPY --chmod=0750 bin/$TARGETARCH/app ./app
COPY configs ./configs
COPY deployments ./deployments

## Copy CA certs from the builder so apt trusts the configured CA bundle
## (e.g. when the build runs behind a proxy with a custom root CA).
COPY --from=grpc-health-probe-builder /etc/ssl/certs /etc/ssl/certs

## Install common dependencies for all targets
RUN apt-get update && \
apt-get install -y --no-install-recommends \
socat \
jq && \
rm -rf /var/lib/apt/lists/*

COPY --from=grpc-health-probe-builder /grpc-health-probe /usr/local/bin/grpc_health_probe

### This is the image that runs outside the nitro enclave, but does not include the enclave binary (EIF file)
FROM base AS signer

COPY --chmod=0750 docker/run_proxy.sh ./run_proxy.sh
COPY --chmod=0750 docker/run_enclave.sh ./run_enclave.sh
COPY --chmod=0750 docker/run_enclave.dev.sh ./run_enclave.dev.sh

# Security fix: enforce non-root execution. The signer stage handles signing material and
# interfaces with KMS, so running as root creates an unnecessary privilege escalation surface.
RUN groupadd --system -g 1000 circle && \
useradd --system -u 1000 -g circle -d /usr/local/circle circle && \
chown -R circle:circle /usr/local/circle

USER circle

ENTRYPOINT ["/usr/local/circle/run_proxy.sh"]

### This is the image that runs outside the nitro enclave, including the enclave binary (EIF file)
FROM signer AS signer-with-enclave

ARG ENCLAVE_EIF
ARG ENCLAVE_PCR0=""
ARG ENCLAVE_PCR1=""
ARG ENCLAVE_PCR2=""

LABEL enclave.pcr0="${ENCLAVE_PCR0}" \
enclave.pcr1="${ENCLAVE_PCR1}" \
enclave.pcr2="${ENCLAVE_PCR2}"

COPY --from=nitro-cli-builder /out/ /

COPY $ENCLAVE_EIF ./enclave.eif

COPY --chmod=0750 docker/run.sh ./run.sh
ENTRYPOINT ["/usr/local/circle/run.sh"]
Loading