Skip to content

Commit f138b3f

Browse files
committed
Codify the creation of test containers
Add infrastructure to build, sign, encrypt, and push test container images used by the CoCo project. This includes a Makefile with targets for various image variants (unsigned, cosign-signed, GPG-signed, encrypted, and multi-arch encrypted), along with the necessary keys, configs, Dockerfiles, and a GitHub Actions workflow. The multi-arch-encrypted target builds the ccv0-ssh image for amd64, arm64, and s390x, encrypts each with the coco-keyprovider container using the ssh-demo encryption key, and assembles a multi-arch manifest at ghcr.io/confidential-containers/test-container:multi-arch-encrypted. Based-on-work-by: Chris Porter <porter@ibm.com> Signed-off-by: Fabiano Fidêncio <ffidencio@nvidia.com> Made-with: Cursor
1 parent 02e5c59 commit f138b3f

17 files changed

Lines changed: 465 additions & 0 deletions

File tree

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
name: Build Test Containers
2+
run-name: Build Test Containers
3+
4+
on:
5+
workflow_dispatch:
6+
inputs:
7+
target:
8+
description: "Make target to run (default: all)"
9+
required: false
10+
default: "all"
11+
type: choice
12+
options:
13+
- all
14+
- multi-arch-encrypted
15+
- multi-arch-encrypted-cosign-sig
16+
- unsig
17+
- cosign-sig
18+
- simple-sig
19+
- enc-unsig
20+
- enc-cosign-sig
21+
- test-container-unencrypted
22+
- test-container-encrypted
23+
- busybox
24+
push:
25+
branches:
26+
- "main"
27+
paths:
28+
- "container-images/**"
29+
- ".github/workflows/build-test-containers.yaml"
30+
31+
jobs:
32+
build-test-containers:
33+
runs-on: ubuntu-24.04
34+
permissions:
35+
contents: read
36+
packages: write
37+
38+
env:
39+
REGISTRY: ghcr.io
40+
41+
steps:
42+
- name: Checkout
43+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
44+
45+
- name: Set up QEMU
46+
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
47+
48+
- name: Set up Docker Buildx
49+
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
50+
51+
- name: Log in to GHCR
52+
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
53+
with:
54+
registry: ${{ env.REGISTRY }}
55+
username: ${{ github.actor }}
56+
password: ${{ secrets.GITHUB_TOKEN }}
57+
58+
- name: Check out guest-components
59+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
60+
with:
61+
repository: confidential-containers/guest-components
62+
ref: refs/heads/main
63+
path: ./guest-components
64+
65+
- name: Build coco-keyprovider container
66+
run: |
67+
docker build -t coco-keyprovider \
68+
-f guest-components/attestation-agent/docker/Dockerfile.keyprovider \
69+
guest-components/
70+
71+
- name: Start coco-keyprovider
72+
run: |
73+
docker run -d --rm --network host --name coco-keyprovider coco-keyprovider
74+
echo "Waiting for coco-keyprovider on localhost:50000"
75+
timeout 30 bash -c 'until nc -z localhost 50000; do sleep 1; done'
76+
echo "coco-keyprovider is ready"
77+
78+
- name: Install tools
79+
run: |
80+
sudo apt-get update
81+
sudo apt-get install -y skopeo jq
82+
83+
- name: Install cosign
84+
uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1
85+
86+
- name: Import GPG key
87+
working-directory: container-images
88+
run: gpg --batch --import keys/sign/github-runner.keys
89+
90+
- name: Run make target
91+
env:
92+
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
93+
working-directory: container-images
94+
run: make ${{ github.event.inputs.target || 'all' }}
95+
96+
- name: Stop coco-keyprovider
97+
if: always()
98+
run: docker stop coco-keyprovider 2>/dev/null || true

container-images/Makefile

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
#
2+
# This makefile's targets rebuild various container images that can be used
3+
# for development and testing in the CoCo project.
4+
# They also are intended to serve as an up-to-date reference for creating
5+
# new images.
6+
#
7+
# Note: The targets push to ghcr, which requires proper credentials and
8+
# `docker login`.
9+
#
10+
# For multi-arch targets, Docker buildx with QEMU is required:
11+
# docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
12+
# docker buildx create --name multiarch-coco --use
13+
# docker buildx inspect --bootstrap
14+
#
15+
16+
.PHONY: unsig \
17+
cosign-sig \
18+
simple-sig \
19+
enc-unsig \
20+
enc-cosign-sig \
21+
test-container-unencrypted \
22+
test-container-encrypted \
23+
multi-arch-encrypted \
24+
multi-arch-encrypted-cosign-sig \
25+
busybox \
26+
setup-buildx \
27+
coco-keyprovider \
28+
all
29+
30+
SHELL := /bin/bash
31+
32+
COCO_PKG := confidential-containers/test-container
33+
COCO_PKG_IMGRS := confidential-containers/test-container-image-rs
34+
REGISTRY := ghcr.io
35+
36+
PLATFORMS := linux/amd64 linux/arm64 linux/s390x
37+
BUILDX_BUILDER := multiarch-coco
38+
KEYPROVIDER_IMAGE := coco-keyprovider
39+
40+
SSH_DEMO_KEY_B64 := HUlOu8NWz8si11OZUzUJMnjiq/iZyHBJZMSD3BaqgMc=
41+
SSH_DEMO_KEY_ID := kbs:///default/key/ssh-demo
42+
43+
WORK_DIR := /tmp/coco-multi-arch-build
44+
45+
46+
all: \
47+
unsig \
48+
cosign-sig \
49+
simple-sig \
50+
enc-unsig \
51+
enc-cosign-sig \
52+
test-container-unencrypted \
53+
test-container-encrypted \
54+
multi-arch-encrypted \
55+
multi-arch-encrypted-cosign-sig \
56+
busybox
57+
58+
59+
# ---------------------------------------------------------------------------
60+
# Setup targets
61+
# ---------------------------------------------------------------------------
62+
63+
setup-buildx:
64+
@if ! docker buildx inspect $(BUILDX_BUILDER) >/dev/null 2>&1; then \
65+
echo "==> Setting up QEMU and buildx builder"; \
66+
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes; \
67+
docker buildx create --name $(BUILDX_BUILDER) --use; \
68+
docker buildx inspect --bootstrap; \
69+
else \
70+
docker buildx use $(BUILDX_BUILDER); \
71+
fi
72+
73+
coco-keyprovider:
74+
@if ! docker image inspect $(KEYPROVIDER_IMAGE) >/dev/null 2>&1; then \
75+
echo "==> Building coco-keyprovider from guest-components"; \
76+
if [ ! -d /tmp/guest-components ]; then \
77+
git clone --depth 1 https://github.com/confidential-containers/guest-components.git /tmp/guest-components; \
78+
fi; \
79+
docker build -t $(KEYPROVIDER_IMAGE) \
80+
-f /tmp/guest-components/attestation-agent/docker/Dockerfile.keyprovider \
81+
/tmp/guest-components/; \
82+
else \
83+
echo "==> coco-keyprovider image already exists"; \
84+
fi
85+
86+
87+
# ---------------------------------------------------------------------------
88+
# Single-arch targets (build for host arch, push directly)
89+
# ---------------------------------------------------------------------------
90+
91+
unsig:
92+
docker build \
93+
-t $(REGISTRY)/$(COCO_PKG):unsig \
94+
-f dockerfiles/alpine-with-sshd/Dockerfile \
95+
.
96+
docker push $(REGISTRY)/$(COCO_PKG):unsig
97+
98+
cosign-sig:
99+
docker build \
100+
-t $(REGISTRY)/$(COCO_PKG):cosign-sig \
101+
-f dockerfiles/alpine-with-sshd/Dockerfile \
102+
.
103+
docker push $(REGISTRY)/$(COCO_PKG):cosign-sig
104+
${CURDIR}/scripts/make-cosign-sig.sh $(COCO_PKG) cosign-sig $(REGISTRY)
105+
106+
# NOTE: This depends on a gpg key owned by git@runner.com.
107+
# Before issuing this make target:
108+
# gpg --batch --import ./keys/sign/github-runner.keys
109+
simple-sig: unsig
110+
skopeo \
111+
copy \
112+
--debug \
113+
--insecure-policy \
114+
--sign-by git@runner.com \
115+
--sign-passphrase-file $(shell pwd)/keys/sign/git-runner-password.txt \
116+
docker-daemon:$(REGISTRY)/$(COCO_PKG):unsig \
117+
docker://$(REGISTRY)/$(COCO_PKG):simple-sig
118+
119+
# NOTE: This requires coco-keyprovider running on localhost:50000.
120+
# Use `make coco-keyprovider` to build the container, then run it:
121+
# docker run --rm --network host coco-keyprovider
122+
enc-unsig: unsig
123+
OCICRYPT_KEYPROVIDER_CONFIG="$(shell pwd)/configs/ocicrypt.conf" \
124+
skopeo copy \
125+
--insecure-policy \
126+
--encryption-key provider:attestation-agent:keypath=$(shell pwd)/keys/encrypt/key1::keyid=kbs:///default/key/key_id1::algorithm=A256GCM \
127+
docker-daemon:$(REGISTRY)/$(COCO_PKG):unsig \
128+
docker://$(REGISTRY)/$(COCO_PKG):enc-unsig
129+
130+
enc-cosign-sig: cosign-sig
131+
OCICRYPT_KEYPROVIDER_CONFIG="$(shell pwd)/configs/ocicrypt.conf" \
132+
skopeo copy \
133+
--insecure-policy \
134+
--encryption-key provider:attestation-agent:keypath=$(shell pwd)/keys/encrypt/key1::keyid=kbs:///default/key/key_id1::algorithm=A256GCM \
135+
docker-daemon:$(REGISTRY)/$(COCO_PKG):cosign-sig \
136+
docker://$(REGISTRY)/$(COCO_PKG):enc-cosign-sig
137+
${CURDIR}/scripts/make-cosign-sig.sh $(COCO_PKG) enc-cosign-sig $(REGISTRY)
138+
139+
test-container-unencrypted:
140+
docker build \
141+
-t $(REGISTRY)/$(COCO_PKG):unencrypted \
142+
-f dockerfiles/alpine-with-sshd/Dockerfile \
143+
.
144+
docker push $(REGISTRY)/$(COCO_PKG):unencrypted
145+
146+
test-container-encrypted: test-container-unencrypted
147+
OCICRYPT_KEYPROVIDER_CONFIG="$(shell pwd)/configs/ocicrypt.conf" \
148+
skopeo copy \
149+
--insecure-policy \
150+
--encryption-key provider:attestation-agent:keypath=$(shell pwd)/keys/encrypt/key1::keyid=kbs:///default/key/key_id1::algorithm=A256GCM \
151+
docker-daemon:$(REGISTRY)/$(COCO_PKG):unencrypted \
152+
docker://$(REGISTRY)/$(COCO_PKG):encrypted
153+
154+
busybox:
155+
docker build \
156+
-t $(REGISTRY)/$(COCO_PKG_IMGRS):busybox \
157+
-f dockerfiles/busybox/Dockerfile \
158+
dockerfiles/busybox
159+
docker push $(REGISTRY)/$(COCO_PKG_IMGRS):busybox
160+
161+
162+
# ---------------------------------------------------------------------------
163+
# Multi-arch encrypted target
164+
#
165+
# Builds the ccv0-ssh image for each platform, encrypts with the
166+
# coco-keyprovider container, pushes per-arch images, and assembles
167+
# a multi-arch manifest.
168+
#
169+
# Encryption key: from demos/ssh-demo/aa-offline_fs_kbc-keys.json
170+
# Key ID: kbs:///default/key/ssh-demo
171+
# ---------------------------------------------------------------------------
172+
173+
multi-arch-encrypted: setup-buildx coco-keyprovider
174+
@echo "==> Building multi-arch-encrypted for: $(PLATFORMS)"
175+
@mkdir -p $(WORK_DIR)
176+
@for platform in $(PLATFORMS); do \
177+
arch=$$(echo $$platform | cut -d/ -f2); \
178+
arch_dir="$(WORK_DIR)/$$arch"; \
179+
per_arch_tag="$(REGISTRY)/$(COCO_PKG):encrypted-$$arch"; \
180+
\
181+
echo "==> [$$arch] Building unencrypted image"; \
182+
docker buildx build \
183+
--platform "$$platform" \
184+
--provenance=false \
185+
-t "ccv0-ssh:$$arch" \
186+
--load \
187+
-f dockerfiles/alpine-with-sshd/Dockerfile \
188+
. ; \
189+
\
190+
echo "==> [$$arch] Exporting to OCI directory"; \
191+
mkdir -p "$$arch_dir"/{input,output}; \
192+
skopeo copy --override-arch "$$arch" \
193+
"docker-daemon:ccv0-ssh:$$arch" \
194+
"dir:$$arch_dir/input"; \
195+
\
196+
echo "==> [$$arch] Encrypting with coco-keyprovider"; \
197+
docker run --rm \
198+
-v "$$arch_dir:/oci" \
199+
$(KEYPROVIDER_IMAGE) \
200+
/encrypt.sh \
201+
-k "$(SSH_DEMO_KEY_B64)" \
202+
-i "$(SSH_DEMO_KEY_ID)" \
203+
-s "dir:/oci/input" \
204+
-d "dir:/oci/output"; \
205+
\
206+
echo "==> [$$arch] Verifying encryption annotations"; \
207+
skopeo inspect "dir:$$arch_dir/output" \
208+
| jq -e 'any(.LayersData[]?; .Annotations?["org.opencontainers.image.enc.keys.provider.attestation-agent"] != null)' \
209+
|| { echo "ERROR: [$$arch] No encryption annotation"; exit 1; }; \
210+
\
211+
echo "==> [$$arch] Pushing encrypted image"; \
212+
skopeo copy "dir:$$arch_dir/output" "docker://$$per_arch_tag"; \
213+
done
214+
@echo "==> Creating multi-arch manifest"
215+
@docker manifest rm $(REGISTRY)/$(COCO_PKG):multi-arch-encrypted 2>/dev/null || true
216+
@docker manifest create $(REGISTRY)/$(COCO_PKG):multi-arch-encrypted \
217+
$(foreach p,$(PLATFORMS),$(REGISTRY)/$(COCO_PKG):encrypted-$(lastword $(subst /, ,$(p))))
218+
@$(foreach p,$(PLATFORMS), \
219+
docker manifest annotate $(REGISTRY)/$(COCO_PKG):multi-arch-encrypted \
220+
$(REGISTRY)/$(COCO_PKG):encrypted-$(lastword $(subst /, ,$(p))) \
221+
--os linux --arch $(lastword $(subst /, ,$(p))) ; \
222+
)
223+
@echo "==> Pushing multi-arch manifest"
224+
@docker manifest push $(REGISTRY)/$(COCO_PKG):multi-arch-encrypted
225+
@echo "==> Done! Image: $(REGISTRY)/$(COCO_PKG):multi-arch-encrypted"
226+
227+
228+
# ---------------------------------------------------------------------------
229+
# Multi-arch encrypted + cosign-signed target
230+
#
231+
# Re-tags the per-arch encrypted images under a new manifest and
232+
# cosign-signs it, producing an image that is both encrypted and
233+
# cosign-signed under a separate tag.
234+
# ---------------------------------------------------------------------------
235+
236+
multi-arch-encrypted-cosign-sig: multi-arch-encrypted
237+
@echo "==> Creating multi-arch-encrypted-cosign-sig manifest"
238+
@docker manifest rm $(REGISTRY)/$(COCO_PKG):multi-arch-encrypted-cosign-sig 2>/dev/null || true
239+
@docker manifest create $(REGISTRY)/$(COCO_PKG):multi-arch-encrypted-cosign-sig \
240+
$(foreach p,$(PLATFORMS),$(REGISTRY)/$(COCO_PKG):encrypted-$(lastword $(subst /, ,$(p))))
241+
@$(foreach p,$(PLATFORMS), \
242+
docker manifest annotate $(REGISTRY)/$(COCO_PKG):multi-arch-encrypted-cosign-sig \
243+
$(REGISTRY)/$(COCO_PKG):encrypted-$(lastword $(subst /, ,$(p))) \
244+
--os linux --arch $(lastword $(subst /, ,$(p))) ; \
245+
)
246+
@echo "==> Pushing multi-arch-encrypted-cosign-sig manifest"
247+
@docker manifest push $(REGISTRY)/$(COCO_PKG):multi-arch-encrypted-cosign-sig
248+
@echo "==> Cosign-signing multi-arch-encrypted-cosign-sig"
249+
${CURDIR}/scripts/make-cosign-sig.sh $(COCO_PKG) multi-arch-encrypted-cosign-sig $(REGISTRY)
250+
@echo "==> Done! Image: $(REGISTRY)/$(COCO_PKG):multi-arch-encrypted-cosign-sig"
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"key-providers": {
3+
"attestation-agent": {
4+
"grpc": "localhost:50000"
5+
}
6+
}
7+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
FROM alpine:3.14
2+
RUN apk add --no-cache openssh-server
3+
4+
# Use the ssh-demo image's legacy keys. To generate new ones, can do something
5+
# like:
6+
# RUN ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -P ""
7+
COPY keys/ssh/ssh_host_ed25519_key /etc/ssh/ssh_host_ed25519_key
8+
COPY keys/ssh/ssh_host_ed25519_key.pub /etc/ssh/ssh_host_ed25519_key.pub
9+
10+
# A password needs to be set for login to work. An empty password is
11+
# unproblematic as password-based login to root is not allowed.
12+
RUN passwd -d root
13+
14+
# Use the ssh-demo user/client's legacy keys. To generate new ones, can do
15+
# something like:
16+
# $ ssh-keygen -t ed25519 -f ccv0-ssh -P "" -C ""
17+
COPY keys/ssh/ccv0-ssh.pub /root/.ssh/authorized_keys
18+
RUN chmod 600 /etc/ssh/ssh_host_ed25519_key /root/.ssh/authorized_keys \
19+
&& chmod 644 /etc/ssh/ssh_host_ed25519_key.pub \
20+
&& chmod 700 /root/.ssh
21+
ENTRYPOINT ["/usr/sbin/sshd", "-D"]
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
FROM busybox:1.36
2+
3+
CMD ["sh"]

container-images/keys/encrypt/key1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
DE�L'��tOY|��&m����7���h�e�@
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
IN��V��"�S�S5 2x����pId���

0 commit comments

Comments
 (0)