feat(doctor): audit config + state permissions
This commit is contained in:
parent
13ddd40a59
commit
884e734809
@ -2,6 +2,7 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Doctor: check config/state permissions and offer to tighten them. — thanks @steipete
|
||||
- Doctor/Daemon: audit supervisor configs, add --repair/--force flows, surface service config audits in daemon status, and document user vs system services. — thanks @steipete
|
||||
- Daemon: align generated systemd unit with docs for network-online + restart delay. (#479) — thanks @azade-c
|
||||
- Outbound: default Telegram account selection for config-only tokens; remove heartbeat-specific accountId handling. (follow-up #516) — thanks @YuriNachos
|
||||
|
||||
@ -59,6 +59,7 @@ cat ~/.clawdbot/clawdbot.json
|
||||
- Legacy config migration and normalization.
|
||||
- Legacy on-disk state migration (sessions/agent dir/WhatsApp auth).
|
||||
- State integrity and permissions checks (sessions, transcripts, state dir).
|
||||
- Config file permission checks (chmod 600) when running locally.
|
||||
- Legacy workspace dir detection (`~/clawdis`, `~/clawdbot`).
|
||||
- Sandbox image repair when sandboxing is enabled.
|
||||
- Legacy service migration and extra gateway detection.
|
||||
@ -129,6 +130,8 @@ Doctor checks:
|
||||
split between installs).
|
||||
- **Remote mode reminder**: if `gateway.mode=remote`, doctor reminds you to run
|
||||
it on the remote host (the state lives there).
|
||||
- **Config file permissions**: warns if `~/.clawdbot/clawdbot.json` is
|
||||
group/world readable and offers to tighten to `600`.
|
||||
|
||||
### 5) Sandbox image repair
|
||||
When sandboxing is enabled, doctor checks Docker images and offers to build or
|
||||
|
||||
@ -95,6 +95,14 @@ This is social engineering 101. Create distrust, encourage snooping.
|
||||
|
||||
## Configuration Hardening (examples)
|
||||
|
||||
### 0) File permissions
|
||||
|
||||
Keep config + state private on the gateway host:
|
||||
- `~/.clawdbot/clawdbot.json`: `600` (user read/write only)
|
||||
- `~/.clawdbot`: `700` (user only)
|
||||
|
||||
`clawdbot doctor` can warn and offer to tighten these permissions.
|
||||
|
||||
### 1) DMs: pairing by default
|
||||
|
||||
```json5
|
||||
|
||||
@ -123,6 +123,7 @@ function findOtherStateDirs(stateDir: string): string[] {
|
||||
export async function noteStateIntegrity(
|
||||
cfg: ClawdbotConfig,
|
||||
prompter: DoctorPrompterLike,
|
||||
configPath?: string,
|
||||
) {
|
||||
const warnings: string[] = [];
|
||||
const changes: string[] = [];
|
||||
@ -186,6 +187,49 @@ export async function noteStateIntegrity(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (stateDirExists && process.platform !== "win32") {
|
||||
try {
|
||||
const stat = fs.statSync(stateDir);
|
||||
if ((stat.mode & 0o077) !== 0) {
|
||||
warnings.push(
|
||||
`- State directory permissions are too open (${stateDir}). Recommend chmod 700.`,
|
||||
);
|
||||
const tighten = await prompter.confirmSkipInNonInteractive({
|
||||
message: `Tighten permissions on ${stateDir} to 700?`,
|
||||
initialValue: true,
|
||||
});
|
||||
if (tighten) {
|
||||
fs.chmodSync(stateDir, 0o700);
|
||||
changes.push(`- Tightened permissions on ${stateDir} to 700`);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
warnings.push(`- Failed to read ${stateDir} permissions: ${String(err)}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (configPath && existsFile(configPath) && process.platform !== "win32") {
|
||||
try {
|
||||
const stat = fs.statSync(configPath);
|
||||
if ((stat.mode & 0o077) !== 0) {
|
||||
warnings.push(
|
||||
`- Config file is group/world readable (${configPath}). Recommend chmod 600.`,
|
||||
);
|
||||
const tighten = await prompter.confirmSkipInNonInteractive({
|
||||
message: `Tighten permissions on ${configPath} to 600?`,
|
||||
initialValue: true,
|
||||
});
|
||||
if (tighten) {
|
||||
fs.chmodSync(configPath, 0o600);
|
||||
changes.push(`- Tightened permissions on ${configPath} to 600`);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
warnings.push(
|
||||
`- Failed to read config permissions (${configPath}): ${String(err)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (stateDirExists) {
|
||||
const dirCandidates = new Map<string, string>();
|
||||
|
||||
@ -146,7 +146,7 @@ export async function doctorCommand(
|
||||
}
|
||||
}
|
||||
|
||||
await noteStateIntegrity(cfg, prompter);
|
||||
await noteStateIntegrity(cfg, prompter, snapshot.path ?? CONFIG_PATH_CLAWDBOT);
|
||||
|
||||
cfg = await maybeRepairSandboxImages(cfg, runtime, prompter);
|
||||
noteSandboxScopeWarnings(cfg);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user