summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMullvad build server <app@mullvad.net>2022-11-01 16:14:23 +0100
committerLinus Färnstrand <linus@mullvad.net>2022-11-30 14:45:46 +0100
commitbb179fae8829547e8b077a844a99cb45ed9410ed (patch)
tree8a2d85bea4bd1e6e172b88ed8364b2852b924d5c
parent5cb79e2a278c9e7ee94e27f2fa63e1cd83e42f82 (diff)
downloadmullvadvpn-bb179fae8829547e8b077a844a99cb45ed9410ed.tar.xz
mullvadvpn-bb179fae8829547e8b077a844a99cb45ed9410ed.zip
Add initial build-and-publish.sh script and readme for build containers
-rw-r--r--.github/workflows/verify-locked-down-signatures.yml4
-rw-r--r--building/Dockerfile (renamed from Dockerfile)0
-rw-r--r--building/README.md68
-rwxr-xr-xbuilding/build-and-publish.sh95
4 files changed, 167 insertions, 0 deletions
diff --git a/.github/workflows/verify-locked-down-signatures.yml b/.github/workflows/verify-locked-down-signatures.yml
index 9ca5088e19..0912fa6104 100644
--- a/.github/workflows/verify-locked-down-signatures.yml
+++ b/.github/workflows/verify-locked-down-signatures.yml
@@ -11,6 +11,10 @@ on:
- ios/MullvadVPN.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
- android/gradle/verification-metadata.xml
- android/gradle/wrapper/gradle-wrapper.properties
+ - building/build-and-publish.sh
+ - building/mullvad-app-container-signing.asc
+ - building/linux-container-image-tag.txt
+ - building/android-container-image-tag.txt
workflow_dispatch:
jobs:
verify-signatures:
diff --git a/Dockerfile b/building/Dockerfile
index e036834cc1..e036834cc1 100644
--- a/Dockerfile
+++ b/building/Dockerfile
diff --git a/building/README.md b/building/README.md
new file mode 100644
index 0000000000..c85776d368
--- /dev/null
+++ b/building/README.md
@@ -0,0 +1,68 @@
+# Mullvad VPN app build containers
+
+Substitute `${repo}` with the actual absolute path to this repository
+
+## Building and publishing a 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`:
+```yml
+docker:
+ ghcr.io/mullvad:
+ sigstore-staging: file://${repo}/building/sigstore
+```
+
+Build and publish the container image. Tag it with the github hash of the current commit
+```
+git checkout -b update-build-container
+
+./build-and-publish.sh (linux|android)
+
+git push # And create a PR
+```
+
+
+## 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`:
+```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`:
+```yml
+docker:
+ ghcr.io/mullvad:
+ sigstore: file://${repo}/building/sigstore
+```
+
diff --git a/building/build-and-publish.sh b/building/build-and-publish.sh
new file mode 100755
index 0000000000..777c644d51
--- /dev/null
+++ b/building/build-and-publish.sh
@@ -0,0 +1,95 @@
+#!/usr/bin/env bash
+
+# This script will build, sign and publish the Mullvad VPN app build image(s)
+# for either linux or android, depending on the first argument.
+# Please see `README.md` for setup instructions *before* running this script
+
+set -eu
+
+CONTAINER_SIGNING_KEY_FINGERPRINT=1E551687D67F5FD820BEF2C4D7C17F87A0D3D215
+REGISTRY_HOST="ghcr.io"
+REGISTRY_ORG="mullvad"
+
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+REPO_DIR="$( cd "$SCRIPT_DIR/.." && pwd )"
+cd "$REPO_DIR"
+
+source "$REPO_DIR/scripts/utils/log"
+
+tag="$(git rev-parse --short HEAD)"
+
+case ${1-:""} in
+ linux)
+ container_name="mullvadvpn-app-build"
+ containerfile_path="$SCRIPT_DIR/Dockerfile"
+ container_context_dir="$REPO_DIR"
+ container_image_tag_path="$SCRIPT_DIR/linux-container-image-tag.txt"
+ ;;
+ android)
+ container_name="mullvadvpn-app-build-android"
+ containerfile_path="$REPO_DIR/android/docker/Dockerfile"
+ container_context_dir="$REPO_DIR/android/docker/"
+ container_image_tag_path="$SCRIPT_DIR/android-container-image-tag.txt"
+ ;;
+ *)
+ log_error "Invalid platform. Specify 'linux' or 'android' as first argument"
+ exit 1
+esac
+full_container_name="$REGISTRY_HOST/$REGISTRY_ORG/$container_name"
+
+log_header "Building $full_container_name tagged as '$tag' and 'latest'"
+podman build -f "$containerfile_path" "$container_context_dir" --no-cache \
+ -t "$full_container_name:$tag" \
+ -t "$full_container_name:latest"
+
+# Temporary directory to store image digest and signature in.
+# This is a hack since two consecutive `podman push` seems
+# to overwrite the signatures. We want to keep the signature
+# for both the 'latest' and '$tag' tags.
+tmp_signature_dir=$(mktemp -d)
+
+function delete_tmp_signature_dir {
+ rm -rf "$tmp_signature_dir"
+}
+trap 'delete_tmp_signature_dir' EXIT
+
+log_header "Pushing $full_container_name:latest"
+podman push "$full_container_name:latest" \
+ --sign-by $CONTAINER_SIGNING_KEY_FINGERPRINT \
+ --digestfile "$tmp_signature_dir/digest_latest"
+
+digest=$(cat "$tmp_signature_dir/digest_latest")
+log_success "Pushed image with digest $digest"
+# Backup the signature so we can restore it after the second podman push later
+signature_dir="$SCRIPT_DIR/sigstore/$REGISTRY_ORG/$container_name@${digest/:/=}"
+if [[ -f "$signature_dir/signature-2" ]]; then
+ log_error "Did not expect $signature_dir/signature-2 to exist"
+ exit 1
+fi
+cp "$signature_dir/signature-1" "$tmp_signature_dir/signature-2"
+
+log_header "Pushing $full_container_name:$tag"
+podman push "$full_container_name:$tag" \
+ --sign-by $CONTAINER_SIGNING_KEY_FINGERPRINT \
+ --digestfile "$tmp_signature_dir/digest_$tag"
+
+if ! cmp -s "$tmp_signature_dir/digest_latest" "$tmp_signature_dir/digest_$tag"; then
+ log_error "Digests differ between 'latest' and '$tag' pushes"
+ exit 1
+fi
+
+cp "$tmp_signature_dir/signature-2" "$signature_dir/"
+
+log_info "Storing container tag to $container_image_tag_path"
+echo "$tag" > "$container_image_tag_path"
+
+log_header "Commiting signatures and new tag name to git"
+git add "$container_image_tag_path" "$signature_dir"
+GPG_TTY=$(tty) git commit -S -m "Updating build container for $1 to $tag"
+
+log_success "***********************"
+log_success ""
+log_success "Done building and pushing $full_container_name with tags '$tag' and 'latest'"
+log_success "Make sure to push the changes to git"
+log_success ""
+log_success "***********************"