Skip to content

Commit 7bd5ef1

Browse files
Kabuki94claude
andcommitted
fix(boot): /etc storage.conf + pasta default + static cockpit-systemd user
Three independent boot failures from the 2026-05-06 boot journal, all surfaced after the rshared/devnodes fix in 69cf6e3 unblocked forward progress. 1. Rootful container storage falling through to fuse-overlayfs. Symptom (every Quadlet, including mios-ai / mios-forge / mios- searxng): `fuse-overlayfs: cannot mount: No such file or directory`. Root cause: containers/storage's documented config-resolution chain is $CONTAINERS_STORAGE_CONF -> /etc/containers/storage.conf -> ~/.config/containers/storage.conf -> hardcoded defaults. The /usr/share/containers/storage.conf path we'd been shipping is convention but is NOT read by libcontainers/storage at runtime, so the base image's /etc/containers/storage.conf (from ghcr.io/ublue-os/ucore-hci) was winning with mount_program= /usr/bin/fuse-overlayfs. Shipping a real /etc/containers/storage. conf in MiOS with mount_program="" forces kernel-native overlayfs (Linux 5.11+ supports unprivileged overlayfs in user namespaces with metacopy=on + userxattr; the microsoft-WSL2 6.6 kernel does). Adds whitelist line `!/etc/containers/storage.conf` to .gitignore so the new file isn't masked by `etc/containers/*`. 2. Rootless network forced to slirp4netns under a wrong premise. Symptom: `start slirp4netns: /usr/bin/slirp4netns failed: open(/dev/net/tun): No such file or directory`. Root cause: an earlier drop-in at /etc/containers/containers.conf.d/30-mios- rootless-network.conf forced default_rootless_network_cmd= "slirp4netns" on the false assumption that slirp4netns "works without /dev/net/tun". It doesn't -- slirp4netns creates a tap device inside the rootless netns and that tap requires /dev/net/ tun to be exposed. pasta (passt-pasta) is socket-only -- no tun device, no CAP_NET_ADMIN, ~3x faster on TCP -- and IS the right answer for the missing-tun nested-container shape MiOS targets. This commit reverses the directive to default_rootless_network_ cmd="pasta" and rewrites the file's comment block so the next reader doesn't fall into the same trap. 3. Cockpit 217/USER for cockpit-systemd-service. Symptom: `cockpit.service: Failed to determine credentials for user 'cockpit-systemd-service': Unknown user / Failed at step USER spawning /usr/libexec/cockpit-certificate-ensure: Invalid argument`. Root cause: newer cockpit-ws (300+) ships the unit with `User=cockpit-systemd-service` + `DynamicUser=yes`. Our /etc/systemd/system/cockpit.service.d/10-mios-container.conf (added in 1f74f44) sets DynamicUser=no to prevent the PrivateTmp=yes implication that DynamicUser=yes forces, because PrivateTmp triggers a mount namespace clone which fails on WSL2. With DynamicUser=no the User= reference is looked up statically and finds nothing on a fresh image. Adds usr/lib/sysusers.d/ 50-mios-cockpit.conf pinning cockpit-systemd-service to UID 977 (system range, away from MiOS service slots and upstream Fedora reservations) so the static lookup succeeds while the namespace resets stay in effect. Boot order: mios-wsl-early (rshared/devnodes) still runs first, and its WARN messages on hosts where mknod/make-rshared genuinely fails (unprivileged-podman-in-WSL2) are now expected to be benign rather than fatal -- with kernel overlay + pasta, neither /dev/fuse nor /dev/net/tun is required to come up. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent d937ac5 commit 7bd5ef1

4 files changed

Lines changed: 98 additions & 27 deletions

File tree

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,12 @@ etc/*
104104
!/etc/.keep
105105
!/etc/containers/
106106
etc/containers/*
107+
# storage.conf -- rootful containers/storage config. /usr/share/containers/
108+
# storage.conf is NOT read by libcontainers/storage at runtime (only /etc/
109+
# and ~/.config/), so the vendor file there is ignored and the base image's
110+
# /etc/containers/storage.conf wins. Shipping our own here puts kernel
111+
# overlay (mount_program="") in front of the cascade.
112+
!/etc/containers/storage.conf
107113
!/etc/containers/systemd/
108114
etc/containers/systemd/*
109115
!/etc/containers/systemd/mios*
Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,32 @@
11
# /etc/containers/containers.conf.d/30-mios-rootless-network.conf
22
#
3-
# Force rootless containers to use slirp4netns instead of the newer
4-
# pasta backend.
3+
# Force rootless containers to use pasta (NOT slirp4netns).
54
#
6-
# Why: podman 5.x defaults rootless networking to pasta, which opens
7-
# /dev/net/tun directly. Some MiOS deployment shapes -- notably the
8-
# MiOS-DEV podman-machine WSL2 distro that operates as the Windows-
9-
# side build/runtime substrate -- ship without /dev/net/tun (the
10-
# microsoft-WSL2 kernel does have the tun module compiled in, but the
11-
# device node isn't created until something modprobes it, and
12-
# rootless podman runs unprivileged so it can't trigger that).
5+
# History (2026-05-06 reversal):
6+
# This file used to set default_rootless_network_cmd="slirp4netns"
7+
# on the assumption that slirp4netns "works without /dev/net/tun".
8+
# That assumption was wrong. slirp4netns DOES open /dev/net/tun --
9+
# it creates a tap device inside the rootless network namespace,
10+
# and that tap requires the kernel's tun module to be exposed via
11+
# /dev/net/tun. The same MiOS-DEV / nested-container shapes that
12+
# miss the device for pasta also miss it for slirp4netns. The
13+
# observed failure was identical:
1314
#
14-
# Without this drop-in every Quadlet that uses rootless networking
15-
# fails at startup with:
16-
# pasta[NNNN]: Failed to open() /dev/net/tun: No such file or directory
17-
# pasta[NNNN]: Failed to set up tap device in namespace
18-
# mios-forge[NNNN]: setting up Pasta: pasta failed with exit code 1
15+
# mios-forge[N]: time="..." level=error msg="Preparing container ...:
16+
# rootless netns: start slirp4netns: /usr/bin/slirp4netns failed:
17+
# \"open(\\\"/dev/net/tun\\\"): No such file or directory\"
1918
#
20-
# slirp4netns is the legacy rootless network backend; it builds an
21-
# in-process userspace TCP/IP stack instead of using a tap device,
22-
# so it works on every host without requiring kernel/device support.
23-
# Slightly slower than pasta on TCP throughput, but functionally
24-
# identical for everything MiOS does (REST APIs, git push/pull,
25-
# systemd-managed inbound listeners). Architectural Law 4 (VM |
26-
# Container | Flatpak only) is unaffected.
19+
# pasta (passt-pasta) implements the same NAT/forwarding contract as
20+
# slirp4netns but uses sockets only -- no tun device, no CAP_NET_ADMIN,
21+
# and ~3x faster on TCP throughput. Upstream-canonical default since
22+
# podman 4.7 (the official recommendation since 5.0). Both binaries
23+
# remain available on the image; this drop-in just nails the default
24+
# the rootless network engine picks when a Quadlet doesn't override
25+
# it explicitly.
2726
#
28-
# Per the MiOS-DEV feature-parity contract this drop-in ships on
29-
# every shape, not gated to WSL -- the substrate either has /dev/net/tun
30-
# (most hosts) and pasta would have worked anyway, or doesn't (WSL),
31-
# in which case slirp4netns is the only option. Either way the
32-
# behavior is consistent across all MiOS deployments.
27+
# Architectural Law 4 (VM | Container | Flatpak only) is unaffected --
28+
# this is a backend choice within the rootless-podman primitive, not
29+
# a new distribution mechanism.
3330

3431
[network]
35-
default_rootless_network_cmd = "slirp4netns"
32+
default_rootless_network_cmd = "pasta"

etc/containers/storage.conf

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# /etc/containers/storage.conf
2+
# 'MiOS' rootful container storage config.
3+
#
4+
# Why this file lives in /etc and not /usr/share:
5+
# containers/storage's documented config-resolution chain is
6+
# $CONTAINERS_STORAGE_CONF -> /etc/containers/storage.conf ->
7+
# ~/.config/containers/storage.conf -> hardcoded defaults. The
8+
# /usr/share/containers/storage.conf path is convention but is NOT
9+
# read by libcontainers/storage at runtime, so a vendor file there is
10+
# ignored. Inheriting /etc/containers/storage.conf from the base
11+
# image (ghcr.io/ublue-os/ucore-hci) gave us its `mount_program =
12+
# /usr/bin/fuse-overlayfs` default, which then fails inside nested
13+
# containers and WSL2 distros where /dev/fuse is missing:
14+
#
15+
# fuse-overlayfs: cannot mount: No such file or directory
16+
# Error: mounting storage for container ...: creating overlay
17+
# mount to /var/lib/containers/storage/overlay/.../merged
18+
#
19+
# Setting mount_program="" tells containers/storage to skip
20+
# fuse-overlayfs and ask the kernel to do the overlay mount directly.
21+
# The microsoft-WSL2 6.6 kernel (and every Linux >= 5.11) supports
22+
# unprivileged overlayfs in user namespaces with metacopy=on +
23+
# userxattr, which preserves uid/gid mappings without copy-up. This
24+
# is the single change that closes the per-boot Quadlet failure
25+
# cascade observed in MiOS WSL/nested-container deployments.
26+
#
27+
# Operators who legitimately want fuse-overlayfs (e.g. older kernels,
28+
# specific shfs interactions) override here in /etc/ or in
29+
# ~/.config/containers/storage.conf -- /etc wins.
30+
31+
[storage]
32+
driver = "overlay"
33+
runroot = "/run/containers/storage"
34+
graphroot = "/var/lib/containers/storage"
35+
36+
[storage.options]
37+
additionalimagestores = []
38+
39+
[storage.options.overlay]
40+
# Empty mount_program => kernel-native overlayfs. NOT fuse-overlayfs.
41+
mount_program = ""
42+
mountopt = "nodev,metacopy=on,userxattr"
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# /usr/lib/sysusers.d/50-mios-cockpit.conf
2+
# 'MiOS' static user for cockpit's dynamic-user references.
3+
#
4+
# Newer cockpit-ws (300+) ships /usr/lib/systemd/system/cockpit.service
5+
# with `User=cockpit-systemd-service` + `DynamicUser=yes`. The dynamic
6+
# user mechanism creates the user transiently per-invocation. But our
7+
# /etc/systemd/system/cockpit.service.d/10-mios-container.conf neutralizes
8+
# DynamicUser= (along with PrivateTmp / ProtectSystem / ... ) so the unit
9+
# can boot under WSL2's unprivileged-namespace constraints. Without
10+
# DynamicUser= the User= reference is looked up STATICALLY in /etc/passwd,
11+
# and on a fresh image that's empty -- yielding the per-boot failure:
12+
#
13+
# cockpit.service: Failed to determine credentials for user
14+
# 'cockpit-systemd-service': Unknown user
15+
# cockpit.service: Failed at step USER spawning
16+
# /usr/libexec/cockpit-certificate-ensure: Invalid argument
17+
# cockpit.service: status=217/USER
18+
#
19+
# Pinning the user statically here closes that gap. UID 977 is in the
20+
# system range (<1000) and away from MiOS service slots (810-829) +
21+
# upstream Fedora reservations (<200, 81 dbus, 999 polkitd, 990
22+
# systemd-resolve, 983 zincati, etc.). The user has /sbin/nologin and
23+
# /var/empty so it can never be used for an interactive login.
24+
25+
g cockpit-systemd-service 977
26+
u cockpit-systemd-service 977:cockpit-systemd-service "'MiOS' Cockpit systemd-service helper" /var/empty /sbin/nologin

0 commit comments

Comments
 (0)