diff --git a/.arg.template b/.arg.template index a27d42bd..cc400181 100644 --- a/.arg.template +++ b/.arg.template @@ -20,8 +20,30 @@ FORCE_INTERACTIVE_INSTALL=false # Interactive Install Configuration # FORCE_INTERACTIVE_INSTALL=true # Set to true to choose interactive install as the default boot option and launch the Interactive Install TUI -# If you have Ubuntu Pro, use the UBUNTU_PRO_KEY variable to activate it as part of the image build -# UBUNTU_PRO_KEY=your-key +# Ubuntu Pro (optional) +# Do NOT put your Pro token in this file or in any build arg - it would end up +# in `docker history`, the Earthly build cache, and shell history. +# +# Recommended flow (use the earthly.sh wrapper): +# 1. Uncomment the toggle below: +# UBUNTU_PRO_ATTACH=true +# 2. Run the build as usual: +# ./earthly.sh +iso # or +base-image, +build-all-images, etc. +# The script will prompt for the token (input hidden, no echo) and forward +# it to Earthly as a secret. The token never lands in `.arg`, on the +# earthly command line, in `docker history`, or in shell history. +# 3. For non-interactive runs (CI), export UBUNTU_PRO_KEY before invoking the +# script and the prompt is skipped: +# read -rs UBUNTU_PRO_KEY && export UBUNTU_PRO_KEY +# ./earthly.sh +iso +# unset UBUNTU_PRO_KEY +# +# Note: `earthly secret set ...` requires Earthly Cloud (`earthly account login`) +# and is NOT available on a stock CLI install - use the earthly.sh flow above. +# Avoid `earthly --secret UBUNTU_PRO_KEY=` on the command line: the token +# lands in your shell history and /proc//cmdline. +# +# UBUNTU_PRO_ATTACH=true # For enabling Secure Boot with Full Disk Encryption # IS_UKI=true diff --git a/Earthfile b/Earthfile index 24059a9d..6ed99489 100644 --- a/Earthfile +++ b/Earthfile @@ -49,7 +49,20 @@ ARG EDGE_CUSTOM_CONFIG=.edge-custom-config.yaml ARG ARCH ARG DISABLE_SELINUX=true ARG CIS_HARDENING=false -ARG UBUNTU_PRO_KEY +# Ubuntu Pro toggle. The token itself is NEVER passed as a build arg. +# Recommended flow: use the earthly.sh wrapper. Set UBUNTU_PRO_ATTACH=true in +# .arg (or pass --UBUNTU_PRO_ATTACH=true on the CLI), then run e.g. +# ./earthly.sh +iso +# The wrapper prompts for the token without echoing it and forwards it as an +# Earthly --secret, so the value never lands in .arg, in `docker history`, +# in build-arg metadata, in the build cache, or in shell history. +# For non-interactive (CI) runs, export UBUNTU_PRO_KEY before invoking the +# script and the prompt is skipped: +# read -rs UBUNTU_PRO_KEY && export UBUNTU_PRO_KEY +# ./earthly.sh +iso +# Note: `earthly secret set ...` requires Earthly Cloud (earthly account login) +# and is not available on a stock CLI install - use the earthly.sh flow above. +ARG UBUNTU_PRO_ATTACH=false # DRBD version for Piraeus pack ARG DRBD_VERSION="9.2.13" @@ -710,10 +723,23 @@ base-image: # OS == Ubuntu IF [ "$OS_DISTRIBUTION" = "ubuntu" ] && [ "$ARCH" = "amd64" ] - IF [ ! -z "$UBUNTU_PRO_KEY" ] - RUN sed -i '/^[[:space:]]*$/d' /etc/os-release && \ - apt update && apt-get install -y snapd && \ - pro attach $UBUNTU_PRO_KEY + IF [ "$UBUNTU_PRO_ATTACH" = "true" ] + # The token is mounted via Earthly's secret store as an env var + # that lives only for the duration of this RUN. It is materialized + # into an attach-config file using the shell builtin `printf`, so + # the value never appears in any process argv (/proc//cmdline) + # or in docker build history. The env var is unset and the temp + # file removed before the RUN exits, so nothing about the token + # survives in the resulting layer. If `--secret UBUNTU_PRO_KEY` + # is not supplied, Earthly aborts before this RUN is invoked. + RUN --secret UBUNTU_PRO_KEY \ + sed -i '/^[[:space:]]*$/d' /etc/os-release && \ + apt-get update && apt-get install -y snapd && \ + umask 077 && \ + printf 'token: %s\n' "$UBUNTU_PRO_KEY" > /tmp/.pro-attach.yaml && \ + unset UBUNTU_PRO_KEY && \ + pro attach --attach-config /tmp/.pro-attach.yaml && \ + rm -f /tmp/.pro-attach.yaml END RUN apt-get update && \ @@ -785,8 +811,15 @@ base-image: RUN /tmp/harden.sh && rm /tmp/harden.sh END - IF [ ! -z "$UBUNTU_PRO_KEY" ] - RUN pro detach --assume-yes + IF [ "$UBUNTU_PRO_ATTACH" = "true" ] + # Detach the entitlement, then scrub any on-disk traces. `pro` may + # echo or log token fragments under /var/log/ubuntu-advantage* and + # leave private state under /var/lib/ubuntu-advantage/private; we + # truncate those so the resulting image carries no residue. + RUN pro detach --assume-yes && \ + find /var/log -maxdepth 3 -name 'ubuntu-advantage*' -type f -exec sh -c ': > "$1"' _ {} \; 2>/dev/null || true && \ + rm -rf /var/lib/ubuntu-advantage/private 2>/dev/null || true && \ + rm -f /tmp/.pro-attach.yaml 2>/dev/null || true END # OS == Opensuse diff --git a/earthly.sh b/earthly.sh index 9c50e22d..0601a04d 100755 --- a/earthly.sh +++ b/earthly.sh @@ -53,10 +53,11 @@ function build_with_proxy() { -e HTTP_PROXY="$HTTP_PROXY" \ -e NO_PROXY="$NO_PROXY" \ -e no_proxy="$NO_PROXY" \ + "${DOCKER_SECRET_ENV[@]}" \ -v "$(pwd)":/workspace \ -v "$(pwd)/certs:/usr/local/share/ca-certificates:ro" \ --entrypoint /workspace/earthly-entrypoint.sh \ - "$SPECTRO_PUB_REPO"/third-party/edge/earthly/earthly:"$EARTHLY_VERSION" --allow-privileged "$@" + "$SPECTRO_PUB_REPO"/third-party/edge/earthly/earthly:"$EARTHLY_VERSION" --allow-privileged "${EARTHLY_SECRET_ARGS[@]}" "$@" } function build_without_proxy() { @@ -67,7 +68,7 @@ function build_without_proxy() { fi # Run Earthly in Docker to create artifacts Variables are passed from the .arg file - docker run --privileged ${DOCKER_CONFIG_MOUNT:+"$DOCKER_CONFIG_MOUNT"} -v /var/run/docker.sock:/var/run/docker.sock --rm --env EARTHLY_BUILD_ARGS -t -e GLOBAL_CONFIG="$global_config" -v "$(pwd)":/workspace "$SPECTRO_PUB_REPO"/third-party/edge/earthly/earthly:"$EARTHLY_VERSION" --allow-privileged "$@" + docker run --privileged ${DOCKER_CONFIG_MOUNT:+"$DOCKER_CONFIG_MOUNT"} -v /var/run/docker.sock:/var/run/docker.sock --rm --env EARTHLY_BUILD_ARGS -t -e GLOBAL_CONFIG="$global_config" "${DOCKER_SECRET_ENV[@]}" -v "$(pwd)":/workspace "$SPECTRO_PUB_REPO"/third-party/edge/earthly/earthly:"$EARTHLY_VERSION" --allow-privileged "${EARTHLY_SECRET_ARGS[@]}" "$@" } function print_os_pack() { @@ -108,6 +109,66 @@ SPECTRO_PUB_REPO=us-docker.pkg.dev/palette-images EARTHLY_VERSION=v0.8.15 source .arg +# --------------------------------------------------------------------------- +# Secret handling +# +# Some build inputs (currently only the Ubuntu Pro token) must NEVER appear in +# `docker history`, Earthly build-arg metadata, the build cache, shell history, +# or any process argv. We collect them here, prompt for any that aren't +# pre-exported, and forward them into the build via: +# - `docker run -e NAME` (no =value) -> passthrough into the earthly +# container's env +# - `earthly --secret NAME` (no =value) -> Earthly reads the value from +# its env and hands it to +# BuildKit as a secret, which +# the Earthfile consumes via +# `RUN --secret NAME ...` +# Both forms keep the value off the command line. +# --------------------------------------------------------------------------- +EARTHLY_SECRET_ARGS=() +DOCKER_SECRET_ENV=() + +if [ "${UBUNTU_PRO_ATTACH:-false}" = "true" ]; then + # Catch the legacy pattern (token sitting in `.arg`) early - that's the + # exact thing we're trying to avoid. + if [ -f .arg ] && grep -qE '^[[:space:]]*UBUNTU_PRO_KEY[[:space:]]*=' .arg; then + echo >&2 "WARNING: UBUNTU_PRO_KEY is set in .arg. Remove it - tokens placed there" + echo >&2 " leak via the build cache and process listings. Provide it via" + echo >&2 " the UBUNTU_PRO_KEY env var or the interactive prompt instead." + fi + + if [ -z "${UBUNTU_PRO_KEY:-}" ]; then + # Prompt without echoing. Prefer /dev/tty so the prompt works even when + # stdin has been redirected (common in CI wrappers). + if [ -r /dev/tty ] && [ -w /dev/tty ]; then + printf "Enter Ubuntu Pro token (input hidden): " >/dev/tty + stty -echo /dev/tty + elif [ -t 0 ]; then + read -rs -p "Enter Ubuntu Pro token (input hidden): " UBUNTU_PRO_KEY + echo + else + echo >&2 "Error: UBUNTU_PRO_ATTACH=true but UBUNTU_PRO_KEY is not set and" + echo >&2 " no terminal is available to prompt for it. Export" + echo >&2 " UBUNTU_PRO_KEY before invoking earthly.sh, e.g.:" + echo >&2 " read -rs UBUNTU_PRO_KEY && export UBUNTU_PRO_KEY" + echo >&2 " ./earthly.sh +iso" + exit 1 + fi + fi + + if [ -z "${UBUNTU_PRO_KEY:-}" ]; then + echo >&2 "Error: Empty Ubuntu Pro token. Aborting." + exit 1 + fi + + export UBUNTU_PRO_KEY + DOCKER_SECRET_ENV=(-e UBUNTU_PRO_KEY) + EARTHLY_SECRET_ARGS=(--secret UBUNTU_PRO_KEY) +fi + # Workaround to support deprecated field PROXY_CERT_PATH if [ -n "$PROXY_CERT_PATH" ]; then echo "PROXY_CERT_PATH is deprecated. Please place your certificates in the certs directory."