The image reference files, and the Dockerfiles that build on our own images, named only a tag. Tags are mutable: whoever can push to the registry can point one at a different image, and then everybody silently builds in something else. We never reuse a tag, since it is the commit the image was built from, but that is a promise the files themselves do not make and nobody downstream can check. Name the images by digest instead, everywhere they are referenced. The build environment is an input to a reproducible build just as much as the source is, so it has to be named as precisely. test/scripts/Dockerfile loses its `latest` default rather than gaining a digest. Its container-run.sh always passes the image in, read from building/linux-container-image.txt, so the default only existed to be silently wrong. Which commit an image was built from is still in git: the signed commit adding its signatures names the tag in its message and the digest in the directory it adds under building/sigstore/. build-and-publish-container-image.sh now also prints the line to put in the reference file, so the digest never has to be looked up by hand.
4.6 KiB
Mullvad VPN app build containers
Substitute ${repo} with the actual absolute path to this repository
Building and publishing a production container image
These instructions describe how to set up the trusted machine that builds, signs and publishes the container images to ghcr.io.
If you sudo into a build account that do the builds, you need to set the permissions on the tty,
so podman can ask for the passphrase for the gpg key:
realuser@server $ sudo chown build:build $(tty)
realuser@server $ sudo -u build -i
Configure podman to store signatures when building and pushing images. ~/.config/containers/registries.d/mullvad.yaml:
docker:
ghcr.io/mullvad:
sigstore-staging: file://${repo}/building/sigstore
Sign in to ghcr.io with a classic auth token. Read more here: https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry#authenticating-with-a-personal-access-token-classic. The TLDR is basically:
- Go to https://github.com/settings/tokens/new?scopes=write:packages
- Create a new token named
build@app-build-linux3withwrite:packagesas the only permission. Set a very short expiry time on the token, since it's tied to your personal account. - Log podman in to ghcr.io with the new token. :
This login session is valid as long as the token is valid. You can revoke the token on github as soon as the container building and pushing is done.
$ podman login ghcr.io -u <YOUR_PERSONAL_GITHUB_USERNAME> Password: <Here you paste the token generated above>
Build and publish the container image. Tag it with the github hash of the current commit. This also adds the container GPG signatures to the sigstore and commits that to git. The single sigstore addition (signed) commit can be pushed directly to the main branch without PR.
# Builds a new container image, pushes it to the registry, signs it and commits the signatures in git
./build-and-publish-container-image.sh (linux|android)
# Pushes the new sigstore entry
GIT_SSH_COMMAND="ssh -i ~/.ssh/id_ed25519-mullvadvpn-app-deploy" git push
When satisfied with how the new image works, the building/{linux,android}-container-image.txt
files can be updated to point to the new image. They name it by digest:
ghcr.io/mullvad/mullvadvpn-app-build@sha256:<digest>
Not by tag. A tag can be repointed at a different image, which would silently change what everyone builds in. A digest cannot.
build-and-publish-container-image.sh prints the exact line to use when it is done. To go the
other way, from a digest to the commit the image was built from, find the signed commit that added
building/sigstore/mullvad/<image>@sha256=<digest>/. Its message names the tag.
This update is usually done in a separate PR by a developer
Building and publishing a development image container image
These instructions describe how to set up a development machine to build, sign and publish container
images. The purpose of this is mainly to verify the build-and-publish-container-image.sh
script as well as the built images.
Set the following environment variables to override the default values:
REGISTRY_HOSTREGISTRY_ORGCONTAINER_SIGNING_KEY_FINGERPRINT
Configure podman to store signatures when building and pushing images (substitute ${testorg}). ~/.config/containers/registries.d/$testorg.yaml:
docker:
ghcr.io/$testorg:
sigstore-staging: file://${repo}/building/sigstore
In order to verify the signature of the development images, you'll also need to follow the pull and verification steps with some slight adjustments.
Pulling, verifying and using build images
These instructions describe how anyone can pull the images and verify them with GPG before using them.
Copy the Mullvad app container signing GPG key to somewhere outside the repository (so a git pull can't overwrite it with a malicious key):
cp ${repo}/building/mullvad-app-container-signing.asc /path/to/mullvad-app-container-signing.asc
Configure a strict policy for podman when pulling from ghcr.io/mullvad. ~/.config/containers/policy.json:
{
"default": [{ "type": "insecureAcceptAnything" }],
"transports": {
"docker": {
"ghcr.io/mullvad": [
{
"type": "signedBy",
"keyType": "GPGKeys",
"keyPath": "/path/to/mullvad-app-container-signing.asc"
}
]
}
}
}
Configure podman to fetch image signatures from the in-repo sigstore directory. ~/.config/containers/registries.d/mullvad.yaml:
docker:
ghcr.io/mullvad:
sigstore: file://${repo}/building/sigstore