Skip to content
Merged
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
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ RUN mkdir -p /usr/libexec/docker/cli-plugins && \
-o /usr/libexec/docker/cli-plugins/docker-buildx && \
chmod +x /usr/libexec/docker/cli-plugins/docker-buildx

COPY --from=ghcr.io/sigstore/cosign/cosign:v3.1.3@sha256:9e5c2f2edc34351160407ca3416c61855bdf9403c3c5936e0f0be7fc261611b8 /ko-app/cosign /usr/local/bin/cosign

RUN echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list && \
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg && \
apt update -y && apt install --no-install-recommends -y google-cloud-cli
Expand Down
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,20 @@ Requesting an image also reserves it from cleanup, permanently. If the image alr
Cleanup only touches `hexpm/elixir-amd64` and `hexpm/elixir-arm64`, which hold the single-arch images the multi-arch manifests are assembled from rather than images meant to be pulled directly. They keep the last 30 days of builds; older single-arch tags are removed, and the multi-arch image they back stays available.

Tags reserved by a build request are never removed and are flagged as `reserved`. To pin a single-arch tag indefinitely, request it at https://bob.hex.pm/request.

### Verifying images

Images are signed with [cosign](https://github.com/sigstore/cosign) using a key held in Google Cloud KMS, and every signature is recorded in the public [Rekor](https://docs.sigstore.dev/logging/overview/) transparency log. The public key is [`cosign.pub`](cosign.pub). Verify an image with:

```
curl -fsSLO https://raw.githubusercontent.com/hexpm/bob/main/cosign.pub
cosign verify --key cosign.pub hexpm/elixir:<tag>
```

Tags built before signing was added have no signature.

Each image also carries a [SLSA provenance](https://docs.docker.com/build/metadata/attestations/slsa-provenance/) attestation recording the build arguments, the Dockerfile and the digests of the base images it was built from:

```
docker buildx imagetools inspect hexpm/elixir:<tag> --format '{{json .Provenance}}'
```
4 changes: 4 additions & 0 deletions cosign.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWV4rz84tD9WropTPo3OaJPGhNw7+
4klLaSVMgijgDryNV6JBWG3ZEZ1x6qo2XPMeo9blVblN0NX1LoRQtkXP1Q==
-----END PUBLIC KEY-----
4 changes: 3 additions & 1 deletion priv/scripts/docker/elixir.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ arch=$5
tag=${elixir}-erlang-${erlang}-${os}-${os_version}
erlang_major=$(echo "${erlang}" | awk 'match($0, /^[0-9][0-9]/) { print substr( $0, RSTART, RLENGTH )}')

docker build --ulimit nofile=1024:1024 -t hexpm/elixir-${arch}:${tag} --build-arg ELIXIR=${elixir} --build-arg ERLANG=${erlang} --build-arg ERLANG_MAJOR=${erlang_major} --build-arg OS_VERSION=${os_version} --build-arg ARCH=${arch} -f ${SCRIPT_DIR}/docker/elixir-${os}.dockerfile ${SCRIPT_DIR}/docker
docker build --ulimit nofile=1024:1024 --provenance=mode=max -t hexpm/elixir-${arch}:${tag} --build-arg ELIXIR=${elixir} --build-arg ERLANG=${erlang} --build-arg ERLANG_MAJOR=${erlang_major} --build-arg OS_VERSION=${os_version} --build-arg ARCH=${arch} -f ${SCRIPT_DIR}/docker/elixir-${os}.dockerfile ${SCRIPT_DIR}/docker

# Smoke test
docker run --rm hexpm/elixir-${arch}:${tag} elixir -v
Expand All @@ -23,3 +23,5 @@ docker push docker.io/hexpm/elixir-${arch}:${tag} ||
(sleep $((20 + $RANDOM % 40)) && docker push docker.io/hexpm/elixir-${arch}:${tag}) ||
(sleep $((20 + $RANDOM % 40)) && docker push docker.io/hexpm/elixir-${arch}:${tag}) ||
(exit 1)

${SCRIPT_DIR}/docker/sign.sh hexpm/elixir-${arch} ${tag}
3 changes: 3 additions & 0 deletions priv/scripts/docker/erlang.sh
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ fi

docker build \
--ulimit nofile=1024:1024 \
--provenance=mode=max \
-t hexpm/erlang-${arch}:${tag} \
--build-arg ARCH=${arch} \
--build-arg ERLANG=${erlang} \
Expand All @@ -58,3 +59,5 @@ docker push docker.io/hexpm/erlang-${arch}:${tag} ||
(sleep $((20 + $RANDOM % 40)) && docker push docker.io/hexpm/erlang-${arch}:${tag}) ||
(sleep $((20 + $RANDOM % 40)) && docker push docker.io/hexpm/erlang-${arch}:${tag}) ||
(exit 1)

${SCRIPT_DIR}/docker/sign.sh hexpm/erlang-${arch} ${tag}
2 changes: 2 additions & 0 deletions priv/scripts/docker/manifest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,5 @@ docker buildx imagetools create -t hexpm/${kind}:${tag} ${arch_images} ||
(sleep $((10 + $RANDOM % 20)) && docker buildx imagetools create -t hexpm/${kind}:${tag} ${arch_images}) ||
(sleep $((10 + $RANDOM % 20)) && docker buildx imagetools create -t hexpm/${kind}:${tag} ${arch_images}) ||
(exit 1)

${SCRIPT_DIR}/docker/sign.sh hexpm/${kind} ${tag}
20 changes: 20 additions & 0 deletions priv/scripts/docker/sign.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/bin/bash

set -euox pipefail

repo=$1
tag=$2

# Sign the digest that was pushed, since the tag can be moved
digest=sha256:$(docker buildx imagetools inspect --raw ${repo}:${tag} | sha256sum | cut -d ' ' -f 1)

# Cloud KMS is called as the service account whose key Bob.Application writes here
export GOOGLE_APPLICATION_CREDENTIALS=/boto/keyfile.json

# This command has a tendency to intermittently fail
cosign sign --yes --key ${BOB_COSIGN_KEY} ${repo}@${digest} ||
(sleep $((20 + $RANDOM % 40)) && cosign sign --yes --key ${BOB_COSIGN_KEY} ${repo}@${digest}) ||
(sleep $((20 + $RANDOM % 40)) && cosign sign --yes --key ${BOB_COSIGN_KEY} ${repo}@${digest}) ||
(sleep $((20 + $RANDOM % 40)) && cosign sign --yes --key ${BOB_COSIGN_KEY} ${repo}@${digest}) ||
(sleep $((20 + $RANDOM % 40)) && cosign sign --yes --key ${BOB_COSIGN_KEY} ${repo}@${digest}) ||
(exit 1)
Loading