Skip to content
Open
Show file tree
Hide file tree
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
3 changes: 3 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@
# the header lives in /usr/include/z3/ rather than /usr/include/. The extra -I
# is harmless on systems where the path doesn't exist.
BINDGEN_EXTRA_CLANG_ARGS = "-I/usr/include/z3"

[alias]
xtask = "run --package xtask --"
92 changes: 8 additions & 84 deletions .github/workflows/e2e-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,24 +121,15 @@ jobs:
run: ${{ matrix.cmd }}

e2e-podman-rootless:
name: E2E (rust-podman-rootless, ${{ matrix.runner }})
# Run directly on the Ubuntu host so the test observes the host's AppArmor
# and unprivileged-user-namespace policy. A privileged job container masks
# the restrictions that production rootless Podman installations enforce.
runs-on: ${{ matrix.runner }}
timeout-minutes: 30
name: E2E (rust-podman-rootless, ${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
include:
# Ubuntu 24.04 matches the environment reported in #2069 and ships
# Podman 4.x. The probe records whether AppArmor blocks the drop.
- runner: ubuntu-24.04
podman_major: "4"
# Ubuntu 26.04 provides the supported Podman 5.x coverage for
# comparison with the Ubuntu 24.04 environment.
- runner: ubuntu-26.04
podman_major: "5"
os:
- ubuntu-24.04
- ubuntu-26.04
env:
IMAGE_TAG: ${{ inputs.image-tag }}
MISE_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down Expand Up @@ -175,76 +166,9 @@ jobs:
- name: Install tools
run: mise install --locked

- name: Install Podman and build dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
build-essential \
clang \
fuse-overlayfs \
libssl-dev \
libz3-dev \
openssh-client \
passt \
pkg-config \
podman \
slirp4netns \
uidmap

- name: Configure rootless Podman
run: |
set -euo pipefail
if ! grep -q "^${USER}:" /etc/subuid; then
sudo usermod --add-subuids 100000-165535 "$USER"
fi
if ! grep -q "^${USER}:" /etc/subgid; then
sudo usermod --add-subgids 100000-165535 "$USER"
fi
runtime_dir="/run/user/$(id -u)"
sudo install -d -m 0700 -o "$(id -u)" -g "$(id -g)" "$runtime_dir"
echo "XDG_RUNTIME_DIR=$runtime_dir" >> "$GITHUB_ENV"

- name: Verify rootless Podman environment
run: |
set -euo pipefail
podman_version="$(podman version --format '{{.Client.Version}}')"
case "$podman_version" in
"${{ matrix.podman_major }}".*) ;;
*) echo "ERROR: expected Podman ${{ matrix.podman_major }}.x, found $podman_version" >&2; exit 1 ;;
esac
test "$(podman info --format '{{.Host.Security.Rootless}}')" = "true"
test "$(sudo sysctl -n kernel.apparmor_restrict_unprivileged_userns)" = "1"
echo "=== host ==="
uname -a
echo "=== AppArmor ==="
cat /proc/self/attr/current
sudo aa-status || true
echo "=== Podman ==="
podman version
podman info --debug

- name: Probe rootless capability bounding set
run: |
set -euo pipefail
probe="$RUNNER_TEMP/openshell-capbset-probe"
cc -static -O2 -Wall -Wextra -Werror \
e2e/support/capbset-probe.c \
-o "$probe"
podman run --rm \
--cap-add=SETPCAP \
--volume "$probe:/openshell-capbset-probe:ro" \
docker.io/library/alpine:3.22 \
/openshell-capbset-probe

- name: Log in to GHCR with Podman
run: echo "${{ secrets.GITHUB_TOKEN }}" | podman login ghcr.io -u "${{ github.actor }}" --password-stdin

- name: Run rootless Podman E2E
run: mise run --no-deps --skip-deps e2e:podman:rootless

- name: Print AppArmor denials
if: always()
run: sudo dmesg | grep -E 'apparmor=.*DENIED|profile="unprivileged_userns"' | tail -100 || true
timeout-minutes: 90
run: cargo xtask e2e --suite podman --setup --os "${{ matrix.os }}"

e2e-vm:
name: E2E (rust-vm)
Expand Down
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions crates/xtask/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

[package]
name = "xtask"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
repository.workspace = true
publish = false

[dependencies]
28 changes: 28 additions & 0 deletions crates/xtask/scripts/machine/development/ubuntu.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

set -Eeuo pipefail

echo "==> Installing OpenShell development dependencies on Ubuntu"
sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y \
build-essential \
ca-certificates \
clang \
cmake \
curl \
git \
jq \
libclang-dev \
libssl-dev \
libz3-dev \
musl-tools \
openssh-client \
pkg-config \
python3 \
python3-venv \
rsync \
socat \
unzip \
xz-utils \
zstd
27 changes: 27 additions & 0 deletions crates/xtask/scripts/machine/os/ubuntu.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

set -Eeuo pipefail

if [ ! -r /etc/os-release ]; then
echo "cannot detect guest OS: /etc/os-release is missing" >&2
exit 1
fi

# shellcheck disable=SC1091
. /etc/os-release

if [ "${ID:-}" != "ubuntu" ]; then
echo "expected an Ubuntu system, found ${ID:-unknown}" >&2
exit 1
fi

if [ -n "${OPENSHELL_EXPECTED_OS:-}" ] \
&& [ "ubuntu-${VERSION_ID:-unknown}" != "${OPENSHELL_EXPECTED_OS}" ]; then
echo "expected ${OPENSHELL_EXPECTED_OS}, found ubuntu-${VERSION_ID:-unknown}" >&2
exit 1
fi

echo "==> Preparing Ubuntu ${VERSION_ID:-unknown}"
sudo apt-get update
63 changes: 63 additions & 0 deletions crates/xtask/scripts/machine/suites/podman/common.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

set -Eeuo pipefail

echo "==> Preparing rootless Podman"
if ! grep -q "^${USER}:" /etc/subuid; then
sudo usermod --add-subuids 100000-165535 "${USER}"
fi
if ! grep -q "^${USER}:" /etc/subgid; then
sudo usermod --add-subgids 100000-165535 "${USER}"
fi

runtime_dir="/run/user/$(id -u)"
sudo install -d -m 0700 -o "$(id -u)" -g "$(id -g)" "${runtime_dir}"
export XDG_RUNTIME_DIR="${runtime_dir}"

echo "==> Configuring rootless Podman"
containers_config_dir="${HOME}/.config/containers"
mkdir -p "${containers_config_dir}"
cat >"${containers_config_dir}/containers.conf" <<'EOF'
[containers]
# Use file-backed logs so Docker-compatible log reads work reliably.
log_driver = "k8s-file"

[engine]
# Use file-backed events so open Libpod streams receive lifecycle events reliably.
events_logger = "file"
EOF

if [ "${OPENSHELL_SKIP_PODMAN_SOCKET:-0}" != "1" ]; then
echo "==> Enabling the rootless Podman socket"
sudo loginctl enable-linger "$USER"
systemctl --user daemon-reload
systemctl --user enable --now podman.socket
if ! systemctl --user is-active --quiet podman.socket; then
echo "rootless Podman socket did not become active" >&2
systemctl --user status --no-pager podman.socket >&2 || true
exit 1
fi
fi

require_podman_info() {
local format="$1"
local expected="$2"
local description="$3"
local actual

if ! actual="$(podman info --format "${format}")"; then
echo "could not inspect Podman ${description}" >&2
podman info >&2 || true
exit 1
fi
if [ "${actual}" != "${expected}" ]; then
echo "expected Podman ${description} to be ${expected}, found ${actual:-<empty>}" >&2
exit 1
fi
}

require_podman_info '{{.Host.Security.Rootless}}' true "rootless mode"
require_podman_info '{{.Host.LogDriver}}' k8s-file "log driver"
require_podman_info '{{.Host.EventLogger}}' file "event logger"
11 changes: 11 additions & 0 deletions crates/xtask/scripts/machine/suites/podman/ubuntu.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

set -Eeuo pipefail

echo "==> Installing rootless Podman on Ubuntu"
sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y \
ca-certificates \
curl \
podman
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

echo "=== recent AppArmor denials ==="
sudo dmesg \
| grep -E 'apparmor=.*DENIED|profile="unprivileged_userns"' \
| tail -100 \
|| true
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

set -Eeuo pipefail

case "${OPENSHELL_EXPECTED_OS:-}" in
ubuntu-24.04) expected_podman_major=4 ;;
ubuntu-26.04) expected_podman_major=5 ;;
*)
echo "cannot determine the expected Podman version for ${OPENSHELL_EXPECTED_OS:-unknown OS}" >&2
exit 1
;;
esac

echo "=== host ==="
uname -a
echo "=== AppArmor ==="
cat /proc/self/attr/current
sudo aa-status || true
echo "=== Podman ==="
podman version
podman info --debug

podman_version="$(podman version --format '{{.Client.Version}}')"
case "${podman_version}" in
"${expected_podman_major}".*) ;;
*)
echo "expected Podman ${expected_podman_major}.x, found ${podman_version}" >&2
exit 1
;;
esac

apparmor_restrict_userns="$(sudo sysctl -n kernel.apparmor_restrict_unprivileged_userns)"
if [ "${apparmor_restrict_userns}" != "1" ]; then
echo "expected kernel.apparmor_restrict_unprivileged_userns=1, found ${apparmor_restrict_userns}" >&2
exit 1
fi

echo "==> Probing the rootless capability bounding set"
capbset_probe="$(mktemp "${TMPDIR:-/tmp}/openshell-capbset-probe.XXXXXX")"
cleanup_capbset_probe() {
rm -f "${capbset_probe}"
}
trap cleanup_capbset_probe EXIT
cc -static -O2 -Wall -Wextra -Werror \
e2e/support/capbset-probe.c \
-o "${capbset_probe}"
podman run --rm \
--cap-add=SETPCAP \
--volume "${capbset_probe}:/openshell-capbset-probe:ro" \
docker.io/library/alpine:3.22 \
/openshell-capbset-probe
cleanup_capbset_probe
trap - EXIT
Loading
Loading