fix: units an unprivileged container cannot run stay off there #34

Merged
marcos-mendez merged 1 commit from fix/container-units into 19.x 2026-10-03 01:27:00 -03:00
marcos-mendez commented 2026-10-02 22:58:42 -03:00 (Migrated from github.com)

On a Keel Web LXC container on Proxmox (keel-core 19.0-6, 2026-10-03), systemctl --failed listed systemd-networkd-wait-online.service, sys-kernel-config.mount and sys-kernel-debug.mount, and ntpsec logged "ntp_adjtime: Operation not permitted" every few minutes.

Cause of each

  • systemd-networkd-wait-online: the systemd package enables networkd, its socket and wait-online in the image, but the network is ifupdown with dhcpcd and there is no .network file, so wait-online waits for a link networkd never brings up and fails at its timeout. Nothing in common, inithooks, confconsole or keel asks for networkd (inithooks' fence only orders itself After=network-online.target, which ifupdown's networking.service reaches); keel inspect reports network.managed_by host or file.
  • sys-kernel-config.mount, sys-kernel-debug.mount: static units pulled in by sysinit.target.wants (and the configfs device unit). Each carries ConditionCapability=CAP_SYS_RAWIO, which passes inside a user namespace because the container init's bounding set is full there; the kernel then refuses the mount, since debugfs and configfs cannot be mounted outside the initial user namespace.
  • ntpsec.service: same shape, ConditionCapability=CAP_SYS_TIME passes, adjtimex is refused outside the initial user namespace, so ntpd runs and fails every adjustment.

Change

conf/turnkey.d/container-units disables systemd-networkd.service, systemd-networkd.socket and systemd-networkd-wait-online.service, and writes keel-container.conf with ConditionVirtualization=!container (systemd.unit(5), systemd 257 of trixie) as a drop-in of ntpsec.service, sys-kernel-config.mount and sys-kernel-debug.mount. In a container the three stay inactive with the condition unmet; on a VM or a bare install they run as before. Nothing is masked, no package is removed. ntpsec is the only time daemon the plans install (desktop's ntpdate is a one-shot).

Tests

  • tests/container-units.bats: the script against the systemctl stub and a scratch systemd tree, with trixie's packaged units as fixtures; systemd-analyze gives the verdict on the condition and on the merged units. 100 percent of the script's lines (tests/coverage.sh at 100).
  • tests/container-units-live.bats, run last by the CI job install in its booted trixie LXC container with ntpsec installed: applies the script to the machine, then reads back that networkd is disabled, that systemd loaded the drop-ins and skips each unit on systemctl start, and that starting them adds nothing to systemctl --failed --no-legend: no unit of this change in it and no new one. The CI container is not rebooted, so this is after a start of the units, not after a boot; and it has mounts of its own failing at boot (dev-mqueue, run-lock, tmp: keel-lxc-1's AppArmor profile lets it mount nothing), so the suite cannot ask for an empty list there, only the image on Proxmox can show that.

Changelog bullet in the unreleased turnkey-core-19.0 (1) entry; COVERAGE.md section.

On a Keel Web LXC container on Proxmox (keel-core 19.0-6, 2026-10-03), `systemctl --failed` listed systemd-networkd-wait-online.service, sys-kernel-config.mount and sys-kernel-debug.mount, and ntpsec logged "ntp_adjtime: Operation not permitted" every few minutes. **Cause of each** - systemd-networkd-wait-online: the systemd package enables networkd, its socket and wait-online in the image, but the network is ifupdown with dhcpcd and there is no .network file, so wait-online waits for a link networkd never brings up and fails at its timeout. Nothing in common, inithooks, confconsole or keel asks for networkd (inithooks' fence only orders itself `After=network-online.target`, which ifupdown's networking.service reaches); `keel inspect` reports network.managed_by host or file. - sys-kernel-config.mount, sys-kernel-debug.mount: static units pulled in by sysinit.target.wants (and the configfs device unit). Each carries `ConditionCapability=CAP_SYS_RAWIO`, which passes inside a user namespace because the container init's bounding set is full there; the kernel then refuses the mount, since debugfs and configfs cannot be mounted outside the initial user namespace. - ntpsec.service: same shape, `ConditionCapability=CAP_SYS_TIME` passes, `adjtimex` is refused outside the initial user namespace, so ntpd runs and fails every adjustment. **Change** `conf/turnkey.d/container-units` disables systemd-networkd.service, systemd-networkd.socket and systemd-networkd-wait-online.service, and writes `keel-container.conf` with `ConditionVirtualization=!container` (systemd.unit(5), systemd 257 of trixie) as a drop-in of ntpsec.service, sys-kernel-config.mount and sys-kernel-debug.mount. In a container the three stay inactive with the condition unmet; on a VM or a bare install they run as before. Nothing is masked, no package is removed. ntpsec is the only time daemon the plans install (desktop's ntpdate is a one-shot). **Tests** - `tests/container-units.bats`: the script against the systemctl stub and a scratch systemd tree, with trixie's packaged units as fixtures; systemd-analyze gives the verdict on the condition and on the merged units. 100 percent of the script's lines (tests/coverage.sh at 100). - `tests/container-units-live.bats`, run last by the CI job install in its booted trixie LXC container with ntpsec installed: applies the script to the machine, then reads back that networkd is disabled, that systemd loaded the drop-ins and skips each unit on `systemctl start`, and that starting them adds nothing to `systemctl --failed --no-legend`: no unit of this change in it and no new one. The CI container is not rebooted, so this is after a start of the units, not after a boot; and it has mounts of its own failing at boot (dev-mqueue, run-lock, tmp: keel-lxc-1's AppArmor profile lets it mount nothing), so the suite cannot ask for an empty list there, only the image on Proxmox can show that. Changelog bullet in the unreleased turnkey-core-19.0 (1) entry; COVERAGE.md section.
Sign in to join this conversation.
No description provided.