diff --git a/.github/workflows/build_and_publish.yaml b/.github/workflows/build_and_publish.yaml index 0767e23..00ffc02 100644 --- a/.github/workflows/build_and_publish.yaml +++ b/.github/workflows/build_and_publish.yaml @@ -10,6 +10,7 @@ on: env: BASE_IMAGE_NAME: base-docker ACTION_IMAGE_NAME: base-action + UBUNTU_PRO_TOKEN: ${{ secrets.UBUNTU_PRO_TOKEN }} jobs: build: runs-on: ubuntu-latest diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 65f71ab..f261f27 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -3,6 +3,7 @@ on: pull_request: env: IMAGE_NAME: base-docker + UBUNTU_PRO_TOKEN: ${{ secrets.UBUNTU_PRO_TOKEN }} jobs: build: runs-on: ubuntu-latest diff --git a/.github/workflows/update-ubuntu-sha.yaml b/.github/workflows/update-ubuntu-sha.yaml index e25706d..0f41a2b 100644 --- a/.github/workflows/update-ubuntu-sha.yaml +++ b/.github/workflows/update-ubuntu-sha.yaml @@ -22,7 +22,7 @@ jobs: - name: Commit file run: | git status - git add *.sha + git add "*.sha" if git diff-index --quiet HEAD; then exit fi diff --git a/.gitignore b/.gitignore index 49c1224..4d000f9 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ bash.link +.secrets/ +.env diff --git a/Dockerfile b/Dockerfile index 08925d6..91ec2a1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,7 +25,9 @@ COPY docker-apt-install.sh /root/docker-apt-install.sh # install some base tools we want in all images # Caching from docs: https://docs.docker.com/reference/dockerfile/#example-cache-apt-packages # Enable full caching of apt packages and metadata, undoing the debian defaults. -RUN --mount=type=cache,target=/var/cache/apt,sharing=locked --mount=type=cache,target=/var/lib/apt,sharing=locked < /etc/apt/apt.conf.d/keep-cache UPGRADE=yes /root/docker-apt-install.sh ca-certificates sysstat lsof net-tools tcpdump vim-tiny strace file diff --git a/Justfile b/Justfile index 89db248..7490aae 100644 --- a/Justfile +++ b/Justfile @@ -1,11 +1,27 @@ +set dotenv-load := true + export ACTION_IMAGE_NAME := env_var_or_default('ACTION_IMAGE_NAME', "base-action") +export UBUNTU_PRO_TOKEN_FILE := env_var_or_default('UBUNTU_PRO_TOKEN_FILE', justfile_directory() + "/.secrets/ubuntu_pro_token") _default: @just --list +ensure-pro-token: + #!/bin/bash + set -euo pipefail + token_file="{{ UBUNTU_PRO_TOKEN_FILE }}" + if test -z "${UBUNTU_PRO_TOKEN:-}"; then + echo "UBUNTU_PRO_TOKEN is required to create $token_file" >&2 + exit 1 + fi + mkdir -p "$(dirname "$token_file")" + umask 077 + printf '%s' "$UBUNTU_PRO_TOKEN" > "$token_file" + # build all images -build *args: +build *args: ensure-pro-token #!/bin/bash + set -euo pipefail export DOCKER_BUILDKIT=1 export BASE_CREATED=$(date --utc +'%Y-%m-%dT%H:%M:%S+00:00') export BASE_GITREF=$(git rev-parse --short HEAD) @@ -15,8 +31,9 @@ clean-build: (build "--no-cache") # hadolint the Dockerfile lint: - @docker pull hadolint/hadolint @docker run --rm -i hadolint/hadolint < Dockerfile + @ls *.sh | xargs docker run --rm -v "$PWD:/mnt:ro" koalaman/shellcheck:v0.11.0 + @docker run --rm -v "$PWD:/repo:ro" --workdir /repo rhysd/actionlint:1.7.10 -color # build and test all images test: build diff --git a/README.md b/README.md index 69322a2..d229719 100644 --- a/README.md +++ b/README.md @@ -3,25 +3,28 @@ Base docker images for the OpenSAFELY framework. These provide a common, up to date base image to build on top of. -This repo produces two image: `base-docker`, and `base-action`. +This repo produces two image flavours: `base-docker`, and `base-action`. It +produces a version of these flavours for 20.04, 22.04, and 24.04, e.g. `base-docker:22.04` ## base-docker -This image is up-to-date Ubuntu 20.04 base along with common debugging tools. -(e.g. `strace`). +This image is up-to-date Ubuntu image along with common debugging tools. (e.g. +`strace`). It includes a helpful script for installing apt packages in the most docker friendly space-efficient manner. Adding this and using it in this and dependent -images saves over 100MB, typically. +images saves over 100MB, typically. This scrips also provides some +conveniences, allowing use of text file with comments to list, and also +supports enabling ESM repositories. It is rebuilt and publish weekly, so there's always a fresh base to build from. - ## base-action This is built from `base-docker` but also include a base action entrypoint, which supports the actions are used in OpenSAFELY's [project.yaml](https://docs.opensafely.org/actions-pipelines/) + This entrypoint supports invoking actions with both an explicit custom CMD or an implicit one. i.e. @@ -33,3 +36,16 @@ or Images built from base-action can define `ACTION_EXEC` env var to customise the default implicit executable used to execute. + +## ESM Support for 20.04 + +Our python:v1 and r:v1 action images are based on 20.04, which reached EOL in +Oct 2025. Because of our current backwards compatibility policy, we need to +support these images unchanged, and cannot switch them to 22.04 or later, and +so we need to continue to support the base 20.04 images. + +So we enable ESM repos via Ubuntu Pro, to provide security fixes for system +packages for 20.04 until 2030. This requires a valid Ubuntu Pro token to be +able to build the 20.04 images. In CI, this is provided via Github Actions +secret. Locally, you should add `UBUNTU_PRO_TOKEN=` in a .env file to be +able to build the images. diff --git a/docker-apt-install.sh b/docker-apt-install.sh index 8bc23cc..e412411 100755 --- a/docker-apt-install.sh +++ b/docker-apt-install.sh @@ -3,15 +3,44 @@ # It does so in the lowest footprint way possible, in a single RUN command. set -euo pipefail +pro_attached=0 +pro_token_file=/run/secrets/ubuntu_pro_token + # ensure apt lists are populated apt-get update +if grep -q 'VERSION_ID="20.04"' /etc/os-release; then + # enable ubuntu pro, based on the example in the Canonical docs: + # https://documentation.ubuntu.com/pro-client/en/docs/howtoguides/enable_in_dockerfile/ + # A file is used rather than an env var which would leak into the docker metadata. + # + # We do it in this helper script, rather than in the Dockerfile, so that + # downstream docker images can also re-use this logic and enable esm + # installations. + # + # We enable two additional archives: + # - esm-infra: core infra packages + # - esm-apps: applications and server packages + if test -s "$pro_token_file"; then + apt-get install --no-install-recommends -y ubuntu-pro-client ca-certificates + cat > /tmp/pro-attach-config.yaml < "$output" + assert_output "$output" "esm" + + # check we have not left any files with the token on the image + if test ! -e /tmp/pro-attach-config.yaml && + test ! -d /etc/ubuntu-advantage && + test ! -d /var/lib/ubuntu-advantage && + test ! -e /var/log/ubuntu-advantage.log && + ! dpkg -l | grep -q ubuntu-pro-client; then + success "no Ubuntu Pro files found" + else + failure "Found Ubuntu Pro file that should not be in the image" + fi +fi + # test script that just echos its arguments for testing script=$(mktemp /tmp/action_exec.XXXX.sh) chmod +x "$script" @@ -109,9 +128,4 @@ chmod +x "$non_exec_script" test_entrypoint "$non_exec_script" assert_output "$output" "SUCCESS" - - - - exit $failed -