feat(worker): isolate run_command children behind a uid boundary #348
No reviewers
Labels
No labels
Epic 5
MCP
P1
PR-0
PR-1
api
api-layer
architecture
area:backend
area:frontend
arrow
authentication
backend
bug
chat
client
clipboard
common-module
compose
cross-cutting
cross-cutting-requirement
data-layer
data-models
database
desktop-client
documentation
duplicate
enhancement
epic
epic-1
epic-2
epic-3
epic-4
epic-5
epic-6
epic-7
epic-llm-config
error-handling
feature
frontend
good first issue
help wanted
implementation
infrastructure
installer
invalid
kotlin
ktor
llm
medium-priority
message
multiplatform
needs-review
needs-triage
packaging
performance
phase-1
phase-2
phase-3
phase-4
phase-5
phase3
phase:3
priority: high
question
refactor
refactoring
resource-sharing
security
server
server-module
services
session
session-management
settings
setup
shutdown
state
storage
streaming
sub-issue
tests
tooling
type:task
ui
ui/ux
user-accounts
user-management
ux
viewmodel
websocket
windows
windows-integration
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Torvian.eu/chatbot!348
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/worker-command-uid-isolation"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Built-in tool commands previously ran as the same OS account as the worker, so an agent-chosen command could read
secrets.json, the service-token cache and the worker's/procentries, and could signal worker processes. This change runs commands spawned byrun_commandas a separate unprivilegedagentaccount, so ordinary file permissions and/procisolation protect the worker identity, without adding capabilities, seccomp changes, namespaces or mounts to the container. Isolation is opt-in (worker.commandIsolation.enabled = falseis the shipped default and logs a WARN; when enabled the worker refuses to start if the privilege drop cannot be provided), and the shipped worker image installs the account and the helper.Changes
worker.commandIsolationconfiguration group (enabled, user, helperPath, nested child environment) with defaults, validation, documentation in every shipped config layer, and preservation across the setup and trusted-signer config rewrites.run_command, which now spawns through the resolved launcher with an explicitly built environment, and resolves isolation once at startup; the tool's in-process tree teardown was replaced by the terminator so a timed-out or cancelled command has its process group signalled through the helper.run-as-agentsetuid helper (exec mode and signal mode, compiled in a builder stage), created theagent/sharedaccounts, tightened the secret-bearing directories, made the workspace and caches group-writable with setgid, and normalized accounts and permissions on every container start.init: true, and documented the accounts, permissions, log lines, termination behaviour and upgrade steps.Implementation notes
CommandIsolationResolutionis the single place that knows whether the privilege drop is active, which account a child runs as and how its tree is terminated; the launcher, environment provider and terminator all consume it, so the inconsistent states the earlier draft admitted are unrepresentable.TERM/KILLfor a positive process-group id, treating a non-existent group as success.safe.directoryexemption for isolated commands only, so a git the worker itself runs keeps refusing such repositories.Validation
62 suites / 567 tests / 0 failures.
Not verified in this environment (no
gcc, nodocker): the helper compile, the image build and the container steps. The recorded Docker-host checks are the isolation-active startup log line, the drop of the helper toagent, and the unreadability ofsecrets.jsonand/proc/<jvm>/environfrom an isolated command.Deployment owners must rebuild the worker image before enabling the switch, since the accounts and the helper only exist in the new image.
Risks and follow-up
enabled = falseis the shipped default, so an installation that does not set the switch keeps running commands as the worker account; the worker logs a WARN naming that consequence./app/config,/app/data,/app/logs,/app/workspaceand/app/cache, which are commonly bind-mounted; a deployment that intentionally leaves different modes on those host directories will see them reset.worker.workspace.path, anagentaccount whose home is not/home/<user>, and environment allowlist entries that are not valid variable names are known gaps recorded as follow-ups rather than part of this change.