@@ -129,188 +129,6 @@ jobs:
129129 OPENSHELL_MCP_CONFORMANCE_CLIENT_IMAGE : ${{ format('openshell-mcp-conformance-client:{0}', inputs.image-tag) }}
130130 run : ${{ matrix.cmd }}
131131
132- e2e-podman-rootless :
133- name : E2E (rust-podman-${{ matrix.suite }}, ${{ matrix.runner }})
134- # Run directly on the Ubuntu host so the test observes the host's AppArmor
135- # and unprivileged-user-namespace policy. A privileged job container masks
136- # the restrictions that production rootless Podman installations enforce.
137- # Ubuntu 26.04 provides the supported Podman 5.x and pasta combination.
138- # Re-add older/slirp4netns environments when direct callbacks through a
139- # rootless-network namespace relay are supported.
140- runs-on : ${{ matrix.runner }}
141- timeout-minutes : 30
142- strategy :
143- fail-fast : false
144- matrix :
145- include :
146- # Keep package versions explicit so hosted-runner tool overrides
147- # cannot silently change the supported test environment.
148- - suite : rootless
149- runner : ubuntu-26.04
150- podman_major : " 5"
151- podman_package_version : " 5.7.0+ds2-3build1"
152- conmon_package_version : " 2.1.13+ds1-2"
153- cmd : " mise run --no-deps --skip-deps e2e:podman:rootless"
154- - suite : external-driver
155- runner : ubuntu-26.04
156- podman_major : " 5"
157- podman_package_version : " 5.7.0+ds2-3build1"
158- conmon_package_version : " 2.1.13+ds1-2"
159- cmd : " env -u OPENSHELL_GATEWAY_BIN mise run --no-deps --skip-deps e2e:podman:external-driver"
160- - suite : provider-refresh-keycloak
161- runner : ubuntu-26.04
162- podman_major : " 5"
163- podman_package_version : " 5.7.0+ds2-3build1"
164- conmon_package_version : " 2.1.13+ds1-2"
165- cmd : " mise run --no-deps --skip-deps e2e:provider-refresh-keycloak"
166- env :
167- IMAGE_TAG : ${{ inputs.image-tag }}
168- MISE_GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
169- OPENSHELL_REGISTRY : ghcr.io/nvidia/openshell
170- OPENSHELL_REGISTRY_HOST : ghcr.io
171- OPENSHELL_REGISTRY_NAMESPACE : nvidia/openshell
172- OPENSHELL_REGISTRY_USERNAME : ${{ github.actor }}
173- OPENSHELL_REGISTRY_PASSWORD : ${{ secrets.GITHUB_TOKEN }}
174- OPENSHELL_SUPERVISOR_IMAGE : ${{ format('ghcr.io/nvidia/openshell/supervisor:{0}', inputs.image-tag) }}
175- steps :
176- - uses : actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
177- with :
178- ref : ${{ inputs['checkout-ref'] || github.sha }}
179- persist-credentials : false
180-
181- - name : Use prebuilt OpenShell CLI
182- if : inputs.cli-artifact-prefix != ''
183- uses : ./.github/actions/setup-e2e-cli
184- with :
185- artifact-prefix : ${{ inputs.cli-artifact-prefix }}
186-
187- - name : Use prebuilt OpenShell gateway
188- if : inputs.gateway-artifact-prefix != ''
189- uses : ./.github/actions/setup-e2e-gateway
190- with :
191- artifact-prefix : ${{ inputs.gateway-artifact-prefix }}
192-
193- - name : Install mise
194- run : |
195- curl https://mise.run | MISE_VERSION=v2026.4.25 sh
196- echo "$HOME/.local/bin" >> "$GITHUB_PATH"
197- echo "$HOME/.local/share/mise/shims" >> "$GITHUB_PATH"
198-
199- - name : Install tools
200- run : mise install --locked
201-
202- - name : Install Podman and build dependencies
203- run : |
204- sudo apt-get update
205- sudo apt-get install -y --no-install-recommends \
206- apparmor \
207- build-essential \
208- clang \
209- fuse-overlayfs \
210- libssl-dev \
211- libz3-dev \
212- openssh-client \
213- passt \
214- pkg-config \
215- "conmon=${{ matrix.conmon_package_version }}" \
216- "podman=${{ matrix.podman_package_version }}" \
217- uidmap
218- # Hosted runners can place newer Podman and conmon binaries under
219- # /usr/local ahead of Ubuntu's packages. Select the distro CLI and
220- # use Podman's supported final config override for its conmon path.
221- podman_config="${RUNNER_TEMP}/openshell-containers.conf"
222- printf '%s\n' \
223- '[engine]' \
224- 'conmon_path = ["/usr/bin/conmon"]' \
225- > "${podman_config}"
226- echo "/usr/bin" >> "${GITHUB_PATH}"
227- echo "CONTAINERS_CONF_OVERRIDE=${podman_config}" >> "${GITHUB_ENV}"
228-
229- - name : Allow pasta to receive Podman stop signals
230- # Ubuntu's packaged pasta profile currently blocks this signal, forcing
231- # Podman to wait for its SIGKILL fallback. Keep this narrow allowance
232- # until the distribution package includes the upstream profile fix.
233- run : |
234- set -euo pipefail
235- profile=/etc/apparmor.d/usr.bin.pasta
236- rule=' signal (receive) peer=podman,'
237- if ! sudo grep -Fqx "${rule}" "${profile}"; then
238- sudo sed -i '\|^ include <abstractions/pasta>$|a\ signal (receive) peer=podman,' "${profile}"
239- fi
240- sudo grep -Fqx "${rule}" "${profile}"
241- sudo apparmor_parser --replace "${profile}"
242-
243- - name : Configure rootless Podman
244- run : |
245- set -euo pipefail
246- if ! grep -q "^${USER}:" /etc/subuid; then
247- sudo usermod --add-subuids 100000-165535 "$USER"
248- fi
249- if ! grep -q "^${USER}:" /etc/subgid; then
250- sudo usermod --add-subgids 100000-165535 "$USER"
251- fi
252- runtime_dir="/run/user/$(id -u)"
253- sudo install -d -m 0700 -o "$(id -u)" -g "$(id -g)" "$runtime_dir"
254- echo "XDG_RUNTIME_DIR=$runtime_dir" >> "$GITHUB_ENV"
255-
256- - name : Verify rootless Podman environment
257- run : |
258- set -euo pipefail
259- podman_version="$(podman version --format '{{.Client.Version}}')"
260- case "$podman_version" in
261- "${{ matrix.podman_major }}".*) ;;
262- *) echo "ERROR: expected Podman ${{ matrix.podman_major }}.x, found $podman_version" >&2; exit 1 ;;
263- esac
264- test "$(dpkg-query -W -f='${Version}' podman)" = "${{ matrix.podman_package_version }}"
265- test "$(dpkg-query -W -f='${Version}' conmon)" = "${{ matrix.conmon_package_version }}"
266- test "$(command -v podman)" = "/usr/bin/podman"
267- test "$(podman info --format '{{.Host.Conmon.Path}}')" = "/usr/bin/conmon"
268- test "$(podman info --format '{{.Host.Security.Rootless}}')" = "true"
269- test "$(podman info --format '{{.Host.RootlessNetworkCmd}}')" = "pasta"
270- test "$(sudo sysctl -n kernel.apparmor_restrict_unprivileged_userns)" = "1"
271- echo "=== host ==="
272- uname -a
273- echo "=== AppArmor ==="
274- cat /proc/self/attr/current
275- sudo aa-status || true
276- echo "=== Podman ==="
277- podman version
278- podman info --debug
279-
280- - name : Probe rootless capability bounding set
281- run : |
282- set -euo pipefail
283- probe="$RUNNER_TEMP/openshell-capbset-probe"
284- cc -static -O2 -Wall -Wextra -Werror \
285- e2e/support/capbset-probe.c \
286- -o "$probe"
287- podman run --rm \
288- --cap-add=SETPCAP \
289- --volume "$probe:/openshell-capbset-probe:ro" \
290- docker.io/library/alpine:3.22 \
291- /openshell-capbset-probe
292-
293- - name : Log in to GHCR with Podman
294- run : echo "${{ secrets.GITHUB_TOKEN }}" | podman login ghcr.io -u "${{ github.actor }}" --password-stdin
295-
296- - name : Run Podman E2E
297- run : ${{ matrix.cmd }}
298-
299- - name : Print AppArmor denials
300- if : always()
301- run : sudo dmesg | grep -E 'apparmor=.*DENIED|profile="unprivileged_userns"' | tail -100 || true
302-
303- - name : Fail on pasta SIGTERM AppArmor denial
304- if : always()
305- run : |
306- set -euo pipefail
307- denials="$(sudo dmesg | grep -E 'profile="pasta".*requested_mask="receive".*signal=term.*peer="podman"' || true)"
308- if [ -n "${denials}" ]; then
309- echo "::error::pasta denied Podman's SIGTERM; Podman will use its SIGKILL fallback"
310- printf '%s\n' "${denials}"
311- exit 1
312- fi
313-
314132 e2e-vm :
315133 name : E2E (rust-vm-${{ matrix.suite }})
316134 # libkrun needs KVM, so this job must run directly on a GitHub-hosted
0 commit comments