feat(agent): isolate from erik — dedicated overlord-agent user
The agent service was running as User=erik, which meant: - Sessions polluted erik's ~/.claude/projects/ - erik's .claude/settings.local.json (months of accumulated dev permissions for docker/git/dotnet/etc.) was loaded by the production agent, defeating the --allowed-tools whitelist - Subscription rate quota mingled between human-erik's interactive Claude Code use and the production assistant - Theoretical access to /home/erik/.ssh, .bash_history, .gitconfig Now: - User=overlord-agent (system account, no shell, /var/lib/overlord-agent home) - HOME=/var/lib/overlord-agent — claude state fully isolated from erik - /home/erik/.claude permissions tightened to 0700 (was 0755) - group=overlord-agent on the repo + /etc/overlord/agent.env (read-only) Project settings: - New strict committed .claude/settings.json: deny Bash/Read/Write/Edit/ Glob/Grep/NotebookEdit/WebSearch; allow only WebFetch(domain:acpedia.org) - .claude/settings.local.json now gitignored (was leaking dev permissions to the server through the deploy)
This commit is contained in:
parent
49ae4369e0
commit
f894399165
3 changed files with 42 additions and 14 deletions
19
.claude/settings.json
Normal file
19
.claude/settings.json
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"WebFetch(domain:acpedia.org)"
|
||||||
|
],
|
||||||
|
"deny": [
|
||||||
|
"Bash",
|
||||||
|
"Write",
|
||||||
|
"Edit",
|
||||||
|
"Read",
|
||||||
|
"Glob",
|
||||||
|
"Grep",
|
||||||
|
"NotebookEdit",
|
||||||
|
"WebSearch"
|
||||||
|
],
|
||||||
|
"ask": []
|
||||||
|
},
|
||||||
|
"enableAllProjectMcpServers": true
|
||||||
|
}
|
||||||
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -2,3 +2,8 @@
|
||||||
__pycache__
|
__pycache__
|
||||||
static/v2/
|
static/v2/
|
||||||
frontend/node_modules/
|
frontend/node_modules/
|
||||||
|
|
||||||
|
# Claude Code per-machine permissions (do NOT deploy to server — production
|
||||||
|
# agent must run with the strict permissions in committed .claude/settings.json)
|
||||||
|
.claude/settings.local.json
|
||||||
|
.claude/settings.local.json.*
|
||||||
|
|
|
||||||
|
|
@ -5,18 +5,23 @@ Wants=network-online.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
User=erik
|
# Dedicated unprivileged user — kernel-level isolation from `erik`.
|
||||||
Group=erik
|
# overlord-agent has NO access to /home/erik/.claude (mode 0700),
|
||||||
# Working directory MUST be the repo root so:
|
# /home/erik/.ssh, /home/erik/.bash_history, /home/erik/.gitconfig, etc.
|
||||||
# - claude -p sessions land at ~/.claude/projects/-home-erik-MosswartOverlord/
|
# Its own claude state lives at /var/lib/overlord-agent/.claude/ and its
|
||||||
# - .mcp.json is auto-loaded
|
# claude session JSONLs land there — completely separate from any
|
||||||
|
# interactive Claude Code use by the human user.
|
||||||
|
User=overlord-agent
|
||||||
|
Group=overlord-agent
|
||||||
|
# Working directory: the repo root (group-readable to overlord-agent).
|
||||||
|
# claude session JSONLs path-encode this cwd so it's important to keep
|
||||||
|
# stable across restarts.
|
||||||
WorkingDirectory=/home/erik/MosswartOverlord
|
WorkingDirectory=/home/erik/MosswartOverlord
|
||||||
# Secrets moved OUT of /home/erik/ to /etc/overlord/agent.env so
|
# HOME explicitly set so claude reads /var/lib/overlord-agent/.claude/*
|
||||||
# ProtectHome=read-only blocks their read entirely. The file is
|
# instead of trying /home/erik/.claude/* (which is now 0700, locked out).
|
||||||
# root-owned, mode 0640, group=erik.
|
Environment="HOME=/var/lib/overlord-agent"
|
||||||
|
# Secrets file (root:overlord-agent 0640).
|
||||||
EnvironmentFile=-/etc/overlord/agent.env
|
EnvironmentFile=-/etc/overlord/agent.env
|
||||||
# Backwards-compat: also try the old location during transition.
|
|
||||||
EnvironmentFile=-/home/erik/MosswartOverlord/.env
|
|
||||||
# Run inside the venv populated by install.sh.
|
# Run inside the venv populated by install.sh.
|
||||||
ExecStart=/home/erik/MosswartOverlord/agent/.venv/bin/python -m agent.service
|
ExecStart=/home/erik/MosswartOverlord/agent/.venv/bin/python -m agent.service
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
|
|
@ -37,12 +42,11 @@ ProtectHome=read-only
|
||||||
# Allow writing only to the explicit paths claude / our service need.
|
# Allow writing only to the explicit paths claude / our service need.
|
||||||
# - ~/.claude — session JSONL files
|
# - ~/.claude — session JSONL files
|
||||||
# - .venv pycache — minor pip cache writes
|
# - .venv pycache — minor pip cache writes
|
||||||
ReadWritePaths=/home/erik/.claude
|
ReadWritePaths=/var/lib/overlord-agent/.claude
|
||||||
ReadWritePaths=/home/erik/MosswartOverlord/agent/.venv
|
ReadWritePaths=/home/erik/MosswartOverlord/agent/.venv
|
||||||
ReadWritePaths=/var/log/overlord-agent
|
ReadWritePaths=/var/log/overlord-agent
|
||||||
# Keep $HOME visible to the venv python so it can find pip cache etc.
|
# StateDirectory creates/owns /var/lib/overlord-agent automatically.
|
||||||
# (read-only via ProtectHome=read-only — this writable carve-out is
|
StateDirectory=overlord-agent
|
||||||
# narrowly the .claude session dir above.)
|
|
||||||
LogsDirectory=overlord-agent
|
LogsDirectory=overlord-agent
|
||||||
LogsDirectoryMode=0755
|
LogsDirectoryMode=0755
|
||||||
PrivateTmp=true
|
PrivateTmp=true
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue