Introduce daemon for sudo-less operation #332
Merged
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.
This commit introduces a major new feature: a client-daemon architecture that allows
voponoto be run by unprivileged users without requiringsudofor every command. See issue #298.Previously, every
vopono execinvocation required root privileges to manipulate network namespaces, leading to frequentsudopassword prompts. This was inconvenient for interactive use and a significant barrier for scripting or system integrations (e.g., desktop shortcuts).This change addresses the problem by splitting
voponointo two main components:How It Works: The Client-Daemon Flow
Daemon Startup: A new
vopono daemonsubcommand is added. This is intended to be run as root, typically via a systemd service. The daemon creates and listens on a Unix domain socket at/run/vopono.sock.Client Execution: When a non-root user runs
vopono exec ..., the client first attempts to connect to this socket.sudoto elevate privileges.Inter-Process Communication (IPC):
ExecCommandarguments into a binary format (usingbincode) and sends it to the daemon.stdin(0),stdout(1), andstderr(2) file descriptors to the daemon using theSCM_RIGHTScontrol message over the socket. This is a kernel-level mechanism for securely transferring open file handles between processes.Daemon-Side Execution:
SO_PEERCREDto securely verify the connecting user's UID and GID. This is essential for security.Spawning the Child Process:
pre_exechook, which allows for fine-grained control before the new program starts.pre_exec, it usessetns()to move the child into the correct network namespace andunshare(CLONE_NEWNS)to give it a private mount namespace. This allows for safely bind-mounting files like/etc/resolv.confwithout affecting the host.stdin,stdout, andstderrto the file descriptors it received from the client.This design ensures that I/O flows directly between the sandboxed application and the user's terminal, with the daemon acting only as a privileged setup coordinator.
Handling Interactive Sessions and Signals
A significant challenge is correctly handling interactive applications (like shells or text editors) that require a controlling terminal (TTY).
PTY (Pseudoterminal) Creation: If the daemon detects that the
client's
stdinis a TTY, it creates a PTY pair. The child process'sstdio is connected to the PTY slave, making the application believe it's
running in a real terminal.
I/O and Signal Forwarding: The daemon bridges I/O between the client's
real terminal FDs and the PTY master. When the client traps a signal
(e.g.,
SIGINTfromCtrl+C), it sends a message to the daemon, whichthen injects the corresponding control character into the PTY. This
ensures that signals are delivered correctly to the application inside
the namespace, preserving job control and interactive behavior.
When the child process exits, the daemon sends the final exit code back to the client, which then exits with the same code, seamlessly restoring control to the user's original shell.