diff --git a/.cargo/audit.toml b/.cargo/audit.toml index 9e353b7e..ba6b8868 100644 --- a/.cargo/audit.toml +++ b/.cargo/audit.toml @@ -4,7 +4,14 @@ [advisories] ignore = [ # rsa 0.9 Marvin timing side channel (no fixed release): a dev-dependency - # only (forge-core, forge-server and forge-e2e tests sign test JWTs with - # it); not in the shipped forge-server binary. + # (forge-core, forge-server and forge-e2e tests sign test JWTs with it), + # and linked into forge-server by rPGP (pgp, apt's signing keys), which + # never signs or decrypts with RSA there: Forge's signing keys are Ed25519, + # generated or imported (other algorithms are refused). "RUSTSEC-2023-0071", + # crypto-bigint 0.5 timing variability (no fixed 0.5 release): linked by + # rPGP (pgp) for its NIST-curve and X448 support (elliptic-curve 0.13, + # cx448), which forge-server never uses with a secret key: apt's signing + # keys are Ed25519 (curve25519-dalek), other algorithms are refused. + "RUSTSEC-2026-0387", ] diff --git a/.github/scripts/apt-e2e.sh b/.github/scripts/apt-e2e.sh new file mode 100755 index 00000000..62a3a8a7 --- /dev/null +++ b/.github/scripts/apt-e2e.sh @@ -0,0 +1,325 @@ +#!/usr/bin/env bash +# End-to-end check of apt repositories with the real apt-get (used by +# ci.yml): boots forge-server, then +# +# 1. hosted: uploads packages built with dpkg-deb (Nexus' raw-body POST +# and a PUT), refuses a redeploy and path traversal, verifies InRelease +# and Release.gpg with gpgv against the repository's key.gpg, and in a +# debian:bookworm container adds the source with signed-by and +# credentials in /etc/apt/auth.conf.d, runs apt-get update (no warning), +# installs a package, and upgrades it after a newer version is uploaded; +# 2. key rotation: adds a second signing key; InRelease and Release.gpg +# verify with either key alone, and apt-get update trusting only the +# old key still has no warning; the old key is then deleted; +# 3. proxy: a mock Debian mirror (dpkg-scanpackages, signed with its own +# key, by-hash copies) behind a proxy repository; apt-get installs +# through it, then installs again in a fresh container while the mirror +# is down. +# +# Usage: apt-e2e.sh +# Needs on PATH: curl, jq, python3, docker, dpkg-deb, dpkg-scanpackages, +# gpg, gpgv, sha256sum, gzip, openssl. Postgres and S3 come from the +# environment (defaults match ci.yml): +# FORGE_E2E_DATABASE_URL (postgres://forge:forge@localhost:5432/forge) +# FORGE_E2E_S3_ENDPOINT (http://localhost:7070), access/secret forge-e2e-key +# FORGE_E2E_PORT (8441: forge-server's) +# FORGE_E2E_MIRROR_PORT (5031: the mock mirror's) +set -euo pipefail + +server_bin="$(realpath "$1")" +results="$(mkdir -p "$2" && realpath "$2")" +db_url="${FORGE_E2E_DATABASE_URL:-postgres://forge:forge@localhost:5432/forge}" +s3_endpoint="${FORGE_E2E_S3_ENDPOINT:-http://localhost:7070}" +port="${FORGE_E2E_PORT:-8441}" +mirror_port="${FORGE_E2E_MIRROR_PORT:-5031}" +base="http://127.0.0.1:$port" +admin_password="e2e-$(openssl rand -hex 12)" +# Docker Hub images come through mirror.gcr.io (AGENTS.md), pinned. +debian_image="mirror.gcr.io/library/debian:bookworm@sha256:d74df7598108a173a621eda4d58a2adbf850b2d9a9d81bc2cca1a6c21174a880" + +work=$(mktemp -d) +server_pid="" +mirror_pid="" +cleanup() { + if [ -n "$server_pid" ]; then kill "$server_pid" 2>/dev/null || true; fi + if [ -n "$mirror_pid" ]; then kill "$mirror_pid" 2>/dev/null || true; fi + cp "$work/server.log" "$work/mirror.log" "$work"/apt-*.log "$results/" 2>/dev/null || true + rm -rf "$work" +} +trap cleanup EXIT + +step() { echo; echo "── $* ──"; } +fail() { echo "FAIL: $*" >&2; exit 1; } +expect_status() { # + local want=$1 + shift + local got + got=$(curl -sS -o "$work/body" -w '%{http_code}' "$@") + [ "$got" = "$want" ] || fail "curl $* answered $got, not $want: $(head -c 300 "$work/body")" +} + +# A binary package [section] in $work/debs. +deb() { + local name=$1 version=$2 arch=$3 section=${4:-utils} + local dir="$work/build/${name}_${version}_${arch}" + mkdir -p "$dir/DEBIAN" "$dir/usr/bin" "$work/debs" + printf '#!/bin/sh\necho "%s %s"\n' "$name" "$version" > "$dir/usr/bin/$name" + chmod 755 "$dir/usr/bin/$name" + cat > "$dir/DEBIAN/control" < +Section: $section +Priority: optional +Description: $name for the apt e2e + A package built by apt-e2e.sh. +EOF + dpkg-deb -Zxz --build --root-owner-group "$dir" "$work/debs/${name}_${version}_${arch}.deb" >/dev/null +} + +# Run