Fix five real bugs found by actually installing and running the plugin
First live end-to-end install on real Unraid hardware (all 8 built
packages installed via upgradepkg, rc.podman started, containers
pulled/run/networked/port-mapped) — surfaced five genuine bugs no
amount of container-based CI testing could have caught, since none of
them exist inside the vbatts/slackware:15.0 build container:
1. rc.podman never created $PODMAN_LOG_DIR before redirecting the
podman system service's output into it, so the service failed to
even start ("No such file or directory"). Added it alongside the
existing PODMAN_RUN_DIR mkdir.
2. config/storage.conf hardcoded a [storage] table, and
podman-config.sh's `sync` step appended a second one at boot with
the real graphroot/runroot — TOML forbids defining the same table
twice. Removed the template's [storage] entirely; sync already
generates the whole thing.
3. config/policy.json had a "_comment" pseudo-field for
documentation, but containers/image's policy parser rejects any
unknown top-level key outright. JSON has no comment syntax; moved
the rationale into docs/ARCHITECTURE.md instead.
4. netavark >= 2.0 dropped its iptables firewall driver entirely
(verified: passing "iptables" is flatly rejected) — nftables or
firewalld are the only remaining options, and firewalld needs
systemd/dbus, which Unraid has neither of. Set firewall_driver =
"nftables" explicitly and documented that Unraid OS doesn't ship
the `nft` binary this needs (a slackware64 nftables package works;
not yet wired into the build/install pipeline — see follow-up).
5. Every container failed with "crun: pivot_root: Invalid argument".
Root cause: Unraid's / is permanently the kernel's initial "rootfs"
pseudo-filesystem (Unraid never pivots to a real one at boot — the
whole OS runs from RAM), and pivot_root(2) unconditionally rejects
that as the old root. This is not new: Docker/runc hits the exact
same kernel restriction on this exact host and silently falls back
to an MS_MOVE-based chroot; crun has no such fallback, only a
--no-pivot flag with no config-file equivalent. Added
plugin/sbin/crun-no-pivot.sh, a thin wrapper that scans crun's full
argument list (podman puts global flags before the subcommand, so
the subcommand isn't reliably $1) and injects --no-pivot right
after create/run, and pointed containers.conf's crun runtime at it.
Also fixed the podman.plg postinstall's chmod glob
(`podman-*.sh` -> `*.sh`), which would have skipped this new
non-podman-prefixed sbin script.
Verified end-to-end on the real host: pull, run, real network
connectivity (wget through the container's bridge), and a published
port actually serving HTTP (curl through -p 8099:80 to nginx) all
work. --no-pivot's security tradeoff (disabling one particular
container-escape mitigation) was explicitly discussed with and
approved by the user before committing, given it must be the default
for any container to start at all on this platform.
Follow-up not yet done: nftables (needed for #4) is not yet a
packages/ component in the reproducible build pipeline — it was only
installed manually on the test host for this verification run.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+26
-2
@@ -18,6 +18,30 @@
|
|||||||
# TODO: static_dir / volume_path / runroot overrides pointing at the cache-pool
|
# TODO: static_dir / volume_path / runroot overrides pointing at the cache-pool
|
||||||
# backed storage location instead of RAM-root defaults.
|
# backed storage location instead of RAM-root defaults.
|
||||||
|
|
||||||
|
[engine.runtimes]
|
||||||
|
# Unraid's / is the kernel's initial 'rootfs' pseudo-filesystem — Unraid
|
||||||
|
# never pivots to a real one during boot, the whole OS runs from RAM — and
|
||||||
|
# pivot_root(2) unconditionally rejects that as the "old root" (EINVAL).
|
||||||
|
# runc (what Docker uses) silently falls back to an MS_MOVE-based chroot
|
||||||
|
# in that situation; crun has no such fallback, only a --no-pivot flag on
|
||||||
|
# `create`/`run` with no config-file equivalent — so podman is pointed at
|
||||||
|
# a thin wrapper (installed by the unraid-podman package, see
|
||||||
|
# plugin/sbin/crun-no-pivot.sh) that injects it, instead of crun directly.
|
||||||
|
# Verified live: without this, every container fails with
|
||||||
|
# "crun: pivot_root: Invalid argument: OCI runtime error".
|
||||||
|
crun = ["/usr/local/sbin/crun-no-pivot.sh"]
|
||||||
|
|
||||||
[network]
|
[network]
|
||||||
# TODO: default network backend (netavark), default subnet range distinct from
|
# TODO: default subnet range distinct from Docker's docker0 range — see
|
||||||
# Docker's docker0 range — see docs/ARCHITECTURE.md section 8.
|
# docs/ARCHITECTURE.md section 8.
|
||||||
|
#
|
||||||
|
# netavark >= 2.0 dropped its iptables firewall driver entirely — only
|
||||||
|
# nftables and firewalld remain (verified against the actual netavark
|
||||||
|
# binary; "iptables" is rejected with "Must provide a valid firewall
|
||||||
|
# backend"). firewalld needs systemd/dbus, which Unraid has neither of, so
|
||||||
|
# nftables (netavark's own default — explicit here so that stays true even
|
||||||
|
# if netavark's default ever changes) is the only viable driver. Unraid OS
|
||||||
|
# does not ship the `nft` binary this needs — see docs/ARCHITECTURE.md
|
||||||
|
# section 8 for how it's provisioned.
|
||||||
|
network_backend = "netavark"
|
||||||
|
firewall_driver = "nftables"
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
"_comment": "Default container image signature verification policy. Placeholder: accepts all images without signature verification, matching Docker's default trust model on Unraid today. See docs/ARCHITECTURE.md section 10 (Images). Revisit before a 1.0 release if signed-image verification becomes a goal.",
|
|
||||||
"default": [
|
"default": [
|
||||||
{ "type": "insecureAcceptAnything" }
|
{ "type": "insecureAcceptAnything" }
|
||||||
],
|
],
|
||||||
|
|||||||
+7
-8
@@ -7,14 +7,13 @@
|
|||||||
#
|
#
|
||||||
# Full reference: https://github.com/containers/storage/blob/main/docs/containers-storage.conf.5.md
|
# Full reference: https://github.com/containers/storage/blob/main/docs/containers-storage.conf.5.md
|
||||||
|
|
||||||
[storage]
|
# The [storage] table itself (driver, graphroot, runroot) is intentionally
|
||||||
driver = "overlay"
|
# NOT defined here — podman-config.sh's `sync` command appends it in full
|
||||||
# runroot and graphroot are set at runtime by rc.podman based on
|
# at sync time, since graphroot depends on STORAGE_PATH from
|
||||||
# /boot/config/plugins/podman/podman.cfg (configurable storage location),
|
# /boot/config/plugins/podman/podman.cfg (configurable storage location) and
|
||||||
# not hardcoded here. TODO: document the exact substitution mechanism once
|
# can't be known statically. A second [storage] table here would be a TOML
|
||||||
# rc.podman is implemented.
|
# duplicate-key error once sync appends its own — see podman-config.sh's
|
||||||
# graphroot = "/var/lib/containers/storage"
|
# cmd_sync for the generated content.
|
||||||
# runroot = "/var/run/containers/storage"
|
|
||||||
|
|
||||||
[storage.options]
|
[storage.options]
|
||||||
# TODO: overlay-specific mount options once the loopback filesystem
|
# TODO: overlay-specific mount options once the loopback filesystem
|
||||||
|
|||||||
@@ -339,6 +339,12 @@ Unraid-Plugins.
|
|||||||
## 8. Netzwerke
|
## 8. Netzwerke
|
||||||
|
|
||||||
- **Backend**: `netavark` + `aardvark-dns` (Podman-Default seit 4.x), kein CNI in Phase 1.
|
- **Backend**: `netavark` + `aardvark-dns` (Podman-Default seit 4.x), kein CNI in Phase 1.
|
||||||
|
- **Firewall-Treiber**: `netavark` >= 2.0 hat seinen `iptables`-Treiber ersatzlos entfernt —
|
||||||
|
nur noch `nftables` oder `firewalld` (Letzteres braucht systemd/dbus, hat Unraid nicht).
|
||||||
|
`nftables` ist also zwingend, aber Unraid OS bringt das `nft`-Binary selbst nicht mit
|
||||||
|
(nur das ältere `iptables`/`iptables-nft`) — muss vom Plugin bereitgestellt werden
|
||||||
|
(verifiziert per Live-Test: `podman run` mit Port-Publishing scheitert ohne `nft`
|
||||||
|
mit „Must provide a valid firewall backend“).
|
||||||
- **Default-Bridge**: eigene Bridge `podman0` (nicht `docker0`), eigener privater
|
- **Default-Bridge**: eigene Bridge `podman0` (nicht `docker0`), eigener privater
|
||||||
Adressraum (konfigurierbar, Default-Vorschlag außerhalb von Dockers Default-Range,
|
Adressraum (konfigurierbar, Default-Vorschlag außerhalb von Dockers Default-Range,
|
||||||
um Kollisionen bei Parallelbetrieb zu vermeiden).
|
um Kollisionen bei Parallelbetrieb zu vermeiden).
|
||||||
@@ -397,6 +403,12 @@ Unraid-Plugins.
|
|||||||
- **Community-Applications-Kompatibilität**: kein automatischer Import von
|
- **Community-Applications-Kompatibilität**: kein automatischer Import von
|
||||||
Docker-Templates in Phase 1 (eigenes, separates Vorhaben); Podman-Images werden
|
Docker-Templates in Phase 1 (eigenes, separates Vorhaben); Podman-Images werden
|
||||||
zunächst über CLI/eigene, minimale Template-Definition verwaltet.
|
zunächst über CLI/eigene, minimale Template-Definition verwaltet.
|
||||||
|
- **Signatur-Policy** (`config/policy.json`): `insecureAcceptAnything` als Default —
|
||||||
|
akzeptiert Images ohne Signaturprüfung, entspricht Dockers heutigem
|
||||||
|
Standard-Vertrauensmodell auf Unraid. Vor einem 1.0-Release erneut bewerten, falls
|
||||||
|
signierte Images ein Ziel werden. Die Datei selbst darf keine Kommentarfelder
|
||||||
|
enthalten (`containers/image`s Policy-Parser lehnt unbekannte Top-Level-Keys wie
|
||||||
|
`_comment` strikt ab) — Begründung lebt deshalb hier, nicht in der Datei.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -274,7 +274,7 @@ set -u
|
|||||||
|
|
||||||
echo "Setting permissions..."
|
echo "Setting permissions..."
|
||||||
chmod 0755 /etc/rc.d/rc.podman
|
chmod 0755 /etc/rc.d/rc.podman
|
||||||
chmod 0755 /usr/local/sbin/podman-*.sh
|
chmod 0755 /usr/local/sbin/*.sh
|
||||||
chmod 0755 /usr/local/emhttp/plugins/podman/event/disks_mounted
|
chmod 0755 /usr/local/emhttp/plugins/podman/event/disks_mounted
|
||||||
chmod 0755 /usr/local/emhttp/plugins/podman/event/stopping
|
chmod 0755 /usr/local/emhttp/plugins/podman/event/stopping
|
||||||
|
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ podman_start() {
|
|||||||
# a shared process gives consistent state and event streaming.
|
# a shared process gives consistent state and event streaming.
|
||||||
# --time=0 disables the idle-shutdown timeout (this is a long-running
|
# --time=0 disables the idle-shutdown timeout (this is a long-running
|
||||||
# daemon under our process management, not an on-demand activation).
|
# daemon under our process management, not an on-demand activation).
|
||||||
mkdir -p "$PODMAN_RUN_DIR"
|
mkdir -p "$PODMAN_RUN_DIR" "$PODMAN_LOG_DIR"
|
||||||
podman_log "start: starting podman system service on unix://$PODMAN_SOCKET"
|
podman_log "start: starting podman system service on unix://$PODMAN_SOCKET"
|
||||||
nohup podman system service --time=0 "unix://$PODMAN_SOCKET" \
|
nohup podman system service --time=0 "unix://$PODMAN_SOCKET" \
|
||||||
> "$PODMAN_LOG_DIR/podman-service.log" 2>&1 &
|
> "$PODMAN_LOG_DIR/podman-service.log" 2>&1 &
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# =============================================================================
|
||||||
|
# plugin/sbin/crun-no-pivot.sh
|
||||||
|
#
|
||||||
|
# Thin OCI-runtime wrapper around crun, injecting --no-pivot on `create`/
|
||||||
|
# `run`. Unraid's / is the kernel's initial "rootfs" pseudo-filesystem —
|
||||||
|
# Unraid never pivots to a real one during boot, the whole OS runs from
|
||||||
|
# RAM — and pivot_root(2) unconditionally rejects that as the "old root"
|
||||||
|
# (EINVAL). runc (what Docker uses) silently falls back to an MS_MOVE-based
|
||||||
|
# chroot in that situation; crun has no such fallback and no config-file
|
||||||
|
# equivalent, only this per-invocation flag — so config/containers.conf
|
||||||
|
# points podman's crun runtime at this wrapper instead of crun directly.
|
||||||
|
# See docs/ARCHITECTURE.md section 8.
|
||||||
|
#
|
||||||
|
# Verified live: without this, every container fails with
|
||||||
|
# "crun: pivot_root: Invalid argument: OCI runtime error".
|
||||||
|
#
|
||||||
|
# --no-pivot is only a valid flag on crun's create/run subcommands (see
|
||||||
|
# `crun run --help`) — every other subcommand (delete, exec, kill, list,
|
||||||
|
# ...) is passed straight through unmodified. podman invokes crun with its
|
||||||
|
# own global flags BEFORE the subcommand (e.g.
|
||||||
|
# `crun --log-format=json --log <path> create --bundle <path> ...`), so
|
||||||
|
# the subcommand is not reliably $1 — scan every argument instead of only
|
||||||
|
# checking the first one, and insert --no-pivot immediately after
|
||||||
|
# create/run wherever it actually appears.
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
args=()
|
||||||
|
found=0
|
||||||
|
for arg in "$@"; do
|
||||||
|
args+=("$arg")
|
||||||
|
if [ "$found" -eq 0 ] && { [ "$arg" = create ] || [ "$arg" = run ]; }; then
|
||||||
|
args+=("--no-pivot")
|
||||||
|
found=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
exec /usr/bin/crun "${args[@]}"
|
||||||
Reference in New Issue
Block a user