Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 107 additions & 18 deletions .github/scripts/install-podman.sh
Original file line number Diff line number Diff line change
@@ -1,33 +1,122 @@
#!/usr/bin/env bash
# Pin and install podman, working around a crun incompatibility on some
# runner images.
# Install a pinned podman static bundle from mgoltzsche/podman-static.
#
# Runner images occasionally pre-install podman 5.x paired with a crun too
# old to support it, breaking sandbox creation with "crun: unknown version
# specified". Pin to the 4.x series, which matches the crun shipped on these
# images, until GitHub Actions runner images ship a compatible crun pairing.
# `apt-get install -y podman` alone does not downgrade an already-installed
# newer version, so the pin's Pin-Priority authorizes the downgrade and
# --allow-downgrades permits apt to execute it.
# Runner images ship a static podman 5.8.4 bundle under /usr/local/, which
# wins on PATH over anything apt installs at /usr/bin/ -- apt pinning to
# 4.x (#5738) never actually took effect, so podman --version kept
# reporting 5.8.4 regardless. This script installs the *same* version the
# image already ships, explicitly and verifiably, instead of fighting it:
# pinning our own copy over the same /usr/local location guarantees a
# known-good version+checksum regardless of what a future runner image
# update silently changes underneath us. No version change from what's
# already running in the image; smoke-tested end to end via
# functional-tests (real sandbox creation through the OpenShell
# gateway/API socket, not just the diagnostic `podman ps`/`logs` calls
# in internal/sandbox).
#
# See #5733. Remove this pin once runner images ship crun >= 1.15
# (podman 5.x's requirement) as standard.
# This approach mirrors how runner-images itself installs podman
# (self-contained static tarball bundling podman + crun/runc + conmon +
# netavark + aardvark-dns + pasta) and is portable across distros.
#
# See #5733, #5742.
#
# Usage:
# .github/scripts/install-podman.sh
set -euo pipefail

sudo install -d /etc/apt/preferences.d
printf 'Package: podman\nPin: version 4.*\nPin-Priority: 1001\n' \
| sudo tee /etc/apt/preferences.d/podman-pin >/dev/null
sudo apt-get update
sudo apt-get install -y --allow-downgrades podman
# Pinned podman-static release tag -- matches the version actions/runner-images
# itself pins for the same bundle, so we're never fighting the image's own
# install, just making it explicit and verified.
PODMAN_STATIC_TAG="v5.8.4"

# SHA-256 checksums for the pinned release archives, independently verified
# against the upstream release. These match the values actions/runner-images
# itself pins for this same tag. To update: download each archive and run
# `sha256sum podman-linux-<arch>.tar.gz`.
declare -A EXPECTED_SHA256=(
[amd64]="a58765fe8be6ab3fb79f892f1a027b4ce4a7e8eb589df1ef960c167cbde08d69"
[arm64]="a2f6b73cc0f7018e2e8518338a4ec27db70148e1af86e16719235605aefd1df3"
)

case "$(uname -m)" in
x86_64) arch="amd64" ;;
aarch64) arch="arm64" ;;
*)
echo "::error::Unsupported architecture: $(uname -m)"
exit 1
;;
esac

archive_url="https://github.com/mgoltzsche/podman-static/releases/download/${PODMAN_STATIC_TAG}/podman-linux-${arch}.tar.gz"
archive_path="$(mktemp)"
trap 'rm -f "${archive_path}"' EXIT

echo "Downloading podman static ${PODMAN_STATIC_TAG} (${arch})..."
curl -fsSL --retry 3 --retry-delay 5 -o "${archive_path}" "${archive_url}"
Comment thread
waynesun09 marked this conversation as resolved.

# Verify the downloaded archive against the pinned checksum before
# extracting anything as root. Fail loudly on mismatch.
expected="${EXPECTED_SHA256[${arch}]}"
echo "${expected} ${archive_path}" | sha256sum -c --strict
echo "SHA-256 checksum verified for podman-linux-${arch}.tar.gz"

# The archive contains a top-level podman-linux-<arch>/ directory with
# usr/ and etc/ sub-trees. Extract only usr/ (the binaries): the bundled
# etc/containers/*.conf are generic Fedora-oriented defaults (e.g. a
# deprecated v1-format registries.conf) that would silently override
# whatever the runner image already has correctly configured for its own
# static bundle -- actions/runner-images re-applies its own post-install
# config fixes after this same extraction for exactly this reason, and
# that follow-up config has already changed upstream once. Skipping
# etc/ avoids re-chasing that moving target: existing config is left
# untouched, and podman falls back to sane compiled-in defaults if none
# is present.
sudo tar -xzf "${archive_path}" -C / --strip-components=1 \
"podman-linux-${arch}/usr"

# Without the bundled etc/containers/containers.conf (intentionally not
# extracted above), podman falls back to the base OS default at
# /usr/share/containers/containers.conf -- on runner images that also
# have a distro-packaged podman, that default points at the distro's
# own crun (/usr/bin/crun), not our freshly-extracted one. Confirmed via
# `podman info`: conmon/netavark/aardvark-dns/pasta all correctly
# resolved to the new bundle, but ociRuntime still resolved to
# /usr/bin/crun at the old distro version. Force both possible
# resolution paths to the same verified binary rather than depending on
# podman's config-driven runtime search order.
sudo ln -sf /usr/local/bin/crun /usr/bin/crun
if [[ "$(readlink -f /usr/bin/crun)" != "/usr/local/bin/crun" ]]; then
echo "::error::/usr/bin/crun does not resolve to our pinned /usr/local/bin/crun (see #5742)"
exit 1
fi

# On Ubuntu >= 23.10, AppArmor restricts unprivileged user namespaces by
# default. The distro-packaged podman ships an /etc/apparmor.d/podman
# profile granting the `userns` permission; this static binary has none,
# so rootless podman fails with "failed to reexec: Permission denied"
# without it. Mirrors actions/runner-images' own install script.
# `flags=(unconfined)` is intentional and provides no sandboxing of
# podman itself beyond the explicit `userns` grant -- it exists solely
# to satisfy the kernel's "process must be AppArmor-confined" gate.
if [[ "$(cat /proc/sys/kernel/apparmor_restrict_unprivileged_userns 2>/dev/null)" == "1" ]]; then
sudo tee /etc/apparmor.d/podman >/dev/null <<'EOF'
abi <abi/4.0>,
include <tunables/global>

profile podman /usr/{bin,local/bin}/podman flags=(unconfined) {
userns,

include if exists <local/podman>
}
EOF
sudo apparmor_parser -r -W /etc/apparmor.d/podman
fi

installed_version="$(podman --version)"
case "${installed_version}" in
*"version 4."*) ;;
*"version ${PODMAN_STATIC_TAG#v}"*) ;;
*)
echo "::error::Failed to pin podman to the 4.x series (see #5733); got: ${installed_version}"
echo "::error::Failed to install podman ${PODMAN_STATIC_TAG} (see #5742); got: ${installed_version}"
exit 1
;;
esac
Expand Down
Loading