Run LTspice as non-root wineuser, support --user=arbitrary uid - #9
Merged
Conversation
Move WINEPREFIX from /root/.wine to /home/wineuser/.wine and run the container as uid 1000 by default. The prefix tree is chmod a+rwX'd at build time so callers can also pass --user=$(id -u):$(id -g) and drop --cap-add=DAC_OVERRIDE on native Linux. test.sh now runs the simulation twice — once with the image's default user and once hardened (--user + --cap-drop=ALL) — and asserts the output .raw file is owned by the host uid.
The previous "default user" variant ran the container without --user, relying on the image's built-in uid 1000. On native Linux that fails when the host uid differs (GH Actions runner is uid 1001) because the in-container user can't write to the bind mount without DAC_OVERRIDE. The hardened variant (--user=$(id -u):$(id -g) + --cap-drop=ALL) is the supported consumption pattern and is the only one worth testing — keeping the default variant required either chmod'ing the test dir or testing a configuration nobody will use.
Wine refuses to use a prefix not owned by the running uid (hard exit: "is not owned by you"). Without CAP_CHOWN we cannot fix the owner at runtime, so chmod-only does not satisfy the check. Restructure: the build-time prefix becomes a uid-agnostic template at /opt/wineprefix-template, and LTspice's 1.7 GB install is moved to /opt/ltspice with a relative symlink left behind so the registry entries written by msiexec still resolve. The entrypoint cp -a's the template into $WINEPREFIX (default /tmp/wine-prefix) on every fresh container start; the copy inherits the current uid, satisfying Wine. LOGNAME/USER are pinned to "wineuser" so getpwuid()-based lookups for arbitrary uids without /etc/passwd entries do not surprise Wine.
cp -a --no-preserve=ownership preserves mode bits, so a 0444/0555 template chmod'd a+rX produced a per-uid prefix copy that the running uid could not write to. Wine then hit "I/O error : Permission denied" trying to update userdef.reg / registry timestamps. Switch the template chmod to a+rwX so the copy is writable regardless of uid.
Three additional write paths were unreachable for arbitrary --user uids: - /opt/ltspice was a+rX; LTspice writes a config XML into its install dir, which fails when the install is read-only. - /home/wineuser was 0755 owned by uid 1000; the container's default CWD (and any temp files written there) was unwritable for any other uid. - Build-time Xvfb left /tmp/.X11-unix and /tmp/.X99-lock in the image with uid-1000 ownership, blocking runtime Xvfb when running as a different uid. Clean those at end of build.
Document the new defaults: container runs as wineuser (uid 1000) instead of root, supports --user=$(id -u):$(id -g) for any host uid, and works with --cap-drop=ALL. Add a "sandboxed invocation" example for the canonical hardened Linux flow, and troubleshooting notes for the bind-mount permission case and the per-run prefix-copy cost.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Move LTspice + Wine off root inside the container so consumers can drop
--cap-add=DAC_OVERRIDEand use--user=$(id -u):$(id -g)like a normal sandboxed simulator.wineuser(uid 1000) by default. Works underdocker run --user=$(id -u):$(id -g) --cap-drop=ALLfor any host uid./opt/wineprefix-template; entrypointcp -as it to/tmp/wine-prefixon each container start so the copy is owned by the running uid (satisfies Wine's prefix-owner check withoutCAP_CHOWN)./opt/ltspicewith a symlink left in the prefix template — keeps the per-run copy small (~150 MB).test.shruns the hardened invocation (--user=$(id -u):$(id -g) --cap-drop=ALL) and verifies the produced.rawfile is owned by the host uid.docs/wineprefix.mdexplains the prefix split.macos-latest.ymlpublishes the Wine-9 image. Each run pushes two tags: an immutabledevel-YYYYMMDD-wine-X.Yaudit tag and a rollingmacos-latestpointer (overrideable per run).CI green on amd64-linux and arm64-linux. Wine version pinning per tag (
latest= Wine latest stable,macos-latest= Wine 9) unchanged.Test plan
.rawfile produced under hardened invocation is owned by the host uid (asserted bytest.sh)