[PR #54] [MERGED] fix(mounts): set bind-mount propagation so containers can't break the host (#53) #55

Closed
opened 2026-10-15 12:51:40 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/games-on-whales/LXC2Docker/pull/54
Author: @JBailes
Created: 6/4/2026
Status: ✅ Merged
Merged: 6/4/2026
Merged by: @JBailes

Base: main ← Head: fix/dev-mount-propagation-leak


📝 Commits (1)

  • db1a4f5 fix(mounts): set bind-mount propagation so containers can't break the host

📊 Changes

3 files changed (+96 additions, -0 deletions)

View changed files

📝 internal/api/containers.go (+1 -0)
📝 internal/lxc/config.go (+36 -0)
📝 internal/lxc/config_test.go (+59 -0)

📄 Description

Problem

Issue #53 — running Wolf with -v /dev/:/dev/:rw broke the Proxmox web console and SSH on the host (Server refused to allocate pty, termproxy ... exit code 1).

Root cause is a mount-propagation leak. Bind mounts were emitted as:

lxc.mount.entry: <src> <dst> none bind,create=dir 0 0

with no propagation mode, so each bind inherited the host's propagation — which on a systemd/Proxmox host is shared. When Wolf bind-mounts the whole host /dev and then mounts its own devpts over /dev/pts, that new mount propagates back through the shared peer group and replaces the host's /dev/pts and /dev/ptmx. The host can then no longer allocate PTYs, killing the web console and SSH.

Docker avoids this by defaulting every bind to rprivate; we never emitted any mode.

Fix

  • Added Propagation to lxc.MountSpec and a validated mountPropagationOpt() helper (defaults to rprivate, Docker's default).
  • Both bind emitters — buildItems (raw LXC) and buildPVEItems (Proxmox CT) — now append the propagation option, so -v /dev:/dev becomes ... none bind,create=dir,rprivate 0 0. A container's own mounts under a bind can no longer leak to the host.
  • API threads Docker's BindOptions.Propagation through; legacy -v binds and volumes fall back to the safe rprivate default.

Propagation governs only mount events, not devtmpfs node creation, so GoW device/gamepad hotplug keeps working.

Tests

Added coverage in internal/lxc/config_test.go for the default, an explicit override, the PVE path, and the helper. go build ./... and go test ./... pass.

Out of scope

The reporter's secondary wolfpulseaudio mount failure (/run/user/wolf → /tmp/pulse) is a separate issue — that path is created inside the running Wolf container, not a recorded mount destination, so sibling-source translation can't resolve it. Left for a follow-up.

Fixes #53

🤖 Generated with Claude Code


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/games-on-whales/LXC2Docker/pull/54 **Author:** [@JBailes](https://github.com/JBailes) **Created:** 6/4/2026 **Status:** ✅ Merged **Merged:** 6/4/2026 **Merged by:** [@JBailes](https://github.com/JBailes) **Base:** `main` ← **Head:** `fix/dev-mount-propagation-leak` --- ### 📝 Commits (1) - [`db1a4f5`](https://github.com/games-on-whales/LXC2Docker/commit/db1a4f5f14011cdce1ceab019b534037ad4f3151) fix(mounts): set bind-mount propagation so containers can't break the host ### 📊 Changes **3 files changed** (+96 additions, -0 deletions) <details> <summary>View changed files</summary> 📝 `internal/api/containers.go` (+1 -0) 📝 `internal/lxc/config.go` (+36 -0) 📝 `internal/lxc/config_test.go` (+59 -0) </details> ### 📄 Description ## Problem Issue #53 — running Wolf with `-v /dev/:/dev/:rw` broke the Proxmox web console and SSH on the host (`Server refused to allocate pty`, `termproxy ... exit code 1`). Root cause is a **mount-propagation leak**. Bind mounts were emitted as: ``` lxc.mount.entry: <src> <dst> none bind,create=dir 0 0 ``` with **no propagation mode**, so each bind inherited the host's propagation — which on a systemd/Proxmox host is *shared*. When Wolf bind-mounts the whole host `/dev` and then mounts its own `devpts` over `/dev/pts`, that new mount propagates back through the shared peer group and replaces the **host's** `/dev/pts` and `/dev/ptmx`. The host can then no longer allocate PTYs, killing the web console and SSH. Docker avoids this by defaulting every bind to `rprivate`; we never emitted any mode. ## Fix - Added `Propagation` to `lxc.MountSpec` and a validated `mountPropagationOpt()` helper (defaults to `rprivate`, Docker's default). - Both bind emitters — `buildItems` (raw LXC) and `buildPVEItems` (Proxmox CT) — now append the propagation option, so `-v /dev:/dev` becomes `... none bind,create=dir,rprivate 0 0`. A container's own mounts under a bind can no longer leak to the host. - API threads Docker's `BindOptions.Propagation` through; legacy `-v` binds and volumes fall back to the safe `rprivate` default. Propagation governs only *mount* events, not devtmpfs node creation, so GoW device/gamepad hotplug keeps working. ## Tests Added coverage in `internal/lxc/config_test.go` for the default, an explicit override, the PVE path, and the helper. `go build ./...` and `go test ./...` pass. ## Out of scope The reporter's secondary `wolfpulseaudio` mount failure (`/run/user/wolf` → `/tmp/pulse`) is a separate issue — that path is created inside the running Wolf container, not a recorded mount destination, so sibling-source translation can't resolve it. Left for a follow-up. Fixes #53 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-15 12:51:40 +00:00
Sign in to join this conversation.
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
games-on-whales/LXC2Docker#55
No description provided.