Skip to content

Commit 9a3b437

Browse files
authored
Fix: Improve daemon startup reliability and add secrets-in-args audit check (#75)
* fix: daemon silent death after fork + add secrets-in-process-args audit Fix two issues reported by OpenClaw customers: 1. Daemon process dies silently after fork with no logs or error output. The child was forked with stdio:'ignore', so any errors during module loading or config parsing were swallowed. Now: - stdout/stderr redirect to daemon-startup.log before logger init - IPC channel detects child readiness or early exit - uncaughtException/unhandledRejection handlers installed before main() - forkDaemon reports startup log contents on failure 2. Container secrets visible via `ps aux` (new check OC-H-064). When containers are started with `docker run -e SECRET=value`, the secret is visible to any process on the host via /proc/*/cmdline. The new audit check flags sensitive env vars passed inline and recommends Docker secrets, --env-file, or mounted files instead. * chore: update package-lock.json after npm install * fix: ensure ~/.g0 directory exists before opening startup log forkDaemon() opens the startup log file before writePid() creates the directory. If ~/.g0/ doesn't exist yet (e.g. first run without prior saveDaemonConfig call), fs.openSync would fail with ENOENT. * fix: harden daemon startup, improve secret detection, update docs Daemon robustness (process.ts, runner.ts): - resolveRunnerPath() now throws with searched paths instead of silently returning a non-existent path that causes a cryptic fork failure - Startup log truncated on each start (mode 'w') with 0o600 permissions to prevent unbounded growth and restrict access - FD leak fixed: startupLogFd is now closed if writePid() throws - Extracted settle() helper to deduplicate event handler cleanup logic - Signal handlers (SIGTERM/SIGINT) registered before IPC ready signal so a kill during initialization triggers graceful shutdown Secret detection (openclaw-deployment.ts, OC-H-064): - Added missing sensitive patterns: AWS_SECRET, GOOGLE_APPLICATION_CREDENTIALS, PASSPHRASE, OAUTH_CLIENT_SECRET, HMAC, JWT_SECRET - Added exact-match token patterns (_TOKEN$, TOKEN$) to catch GITHUB_TOKEN, GH_TOKEN etc. without false-positiving on TOKEN_ENDPOINT - Expanded safe patterns: AUTHORIZATION_TYPE, AUTH_TYPE, TOKEN_URL, TOKEN_ISSUER, TOKEN_VALIDITY, REFRESH_TOKEN_ENDPOINT, etc. - DATABASE_URL only flagged when it contains embedded credentials (user:pass@) Documentation (openclaw-deployment-guide.md): - Added OC-H-064 to the container deep audit table (now 36 checks) - Added "Never Pass Secrets via -e Flags" section with good/bad examples - Added "Daemon won't stay alive" troubleshooting section with startup log, common causes, and verification steps * chore: bump version to 1.5.0 - package.json and package-lock.json version bump - CHANGELOG.md entry for v1.5.0 (daemon fixes, OC-H-064) - Updated version references in evidence-collector test and docs * fix: restore tree-sitter entry in package-lock.json npm ci on CI was failing because the node_modules/tree-sitter@0.21.1 entry was missing from the lock file. The language-specific parsers (tree-sitter-python, etc.) were present but the base tree-sitter package was stripped by npm install on a machine without native build tools. Restored the entry from main.
1 parent dcf4de8 commit 9a3b437

12 files changed

Lines changed: 385 additions & 272 deletions

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,20 @@ All notable changes to g0 will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.5.0] - 2026-03-11
9+
10+
### Fixed
11+
12+
- **Daemon Silent Death** - `forkDaemon()` now redirects child stdout/stderr to `daemon-startup.log` and uses IPC handshake to detect early exit. Previously the child was forked with `stdio: 'ignore'`, so crashes during module loading or config parsing produced zero output. Parent now waits for a `daemon-ready` message or captures the startup log on failure
13+
- **Runner Path Resolution** - `resolveRunnerPath()` throws with searched paths instead of silently returning a non-existent path that caused cryptic fork failures
14+
- **Signal Handler Timing** - SIGTERM/SIGINT handlers moved to execute immediately after logger initialization, ensuring graceful shutdown even if later initialization steps fail
15+
- **Startup FD Leak** - `startupLogFd` is now closed if `writePid()` throws during daemon startup
16+
17+
### Added
18+
19+
- **Secrets in Process Args (OC-H-064)** - New critical audit check detects secrets (API keys, passwords, tokens) passed via Docker `-e` flags, which are visible to all users on the host via `ps aux`. Recommends Docker secrets, `--env-file`, or mounted files instead
20+
- **Global Crash Handlers** - `uncaughtException` and `unhandledRejection` handlers installed before `main()` in the daemon runner, capturing startup crashes to the startup log
21+
822
## [1.4.0] - 2026-03-10
923

1024
### Added

docs/openclaw-deployment-guide.md

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ A complete guide for securing self-hosted OpenClaw deployments with g0. Covers e
3434
## Prerequisites
3535

3636
- **OpenClaw** v2026.2.23+ (patched for CVE-2026-25253 and CVE-2026-28363)
37-
- **g0** v1.4.0+ (`npm install -g @guard0/g0`)
37+
- **g0** v1.5.0+ (`npm install -g @guard0/g0`)
3838
- **Docker** and **Docker Compose** (for containerized deployments)
3939
- **Linux host** (for iptables, auditd — macOS supported for scanning only)
4040
- Root/sudo access (for iptables and auditd rule installation)
@@ -70,7 +70,7 @@ g0 daemon start
7070

7171
## Deployment Audit Checks
7272

73-
g0 performs 35 deployment-level checks across 7 categories:
73+
g0 performs 36 deployment-level checks across 7 categories:
7474

7575
| g0 Check | Category | Description | Severity |
7676
|----------|----------|-------------|----------|
@@ -96,7 +96,7 @@ g0 performs 35 deployment-level checks across 7 categories:
9696

9797
**Categories**: NET (network), CRED (credentials), DOCK (Docker/container), DATA (data protection), O11Y (observability), SYS (system), FORNS (forensics)
9898

99-
### Container Deep Audit (OC-H-056..063)
99+
### Container Deep Audit (OC-H-056..064)
100100

101101
g0 performs deep inspection of running Docker containers:
102102

@@ -110,6 +110,7 @@ g0 performs deep inspection of running Docker containers:
110110
| **OC-H-061** | DOCK | OPENCLAW_DISABLE_BONJOUR should be set | Low |
111111
| **OC-H-062** | DOCK | Sensitive host paths should not be mounted | High |
112112
| **OC-H-063** | DOCK | Container images should be verified/signed | Medium |
113+
| **OC-H-064** | CRED | Secrets passed via `-e` flags are visible in `ps aux` | Critical |
113114

114115
---
115116

@@ -427,6 +428,34 @@ chmod 600 /data/.openclaw/agents/*/.env
427428

428429
g0 detects duplicate credential groups automatically during the deployment audit.
429430

431+
### Never Pass Secrets via `-e` Flags (OC-H-064)
432+
433+
**Finding:** Secrets passed as `docker run -e SECRET_KEY=value` are visible to **every user on the host** via `ps aux` or `/proc/{pid}/cmdline`. This is a critical exposure that g0 now detects automatically.
434+
435+
**Bad — visible in process list:**
436+
437+
```bash
438+
# Anyone on the host can see this secret:
439+
docker run -e OPENAI_API_KEY=sk-proj-abc123... openclaw-agent
440+
```
441+
442+
**Good — use Docker secrets or `--env-file`:**
443+
444+
```bash
445+
# Option 1: Docker secrets (Swarm mode)
446+
echo "sk-proj-abc123..." | docker secret create agent1_openai_key -
447+
448+
# Option 2: env-file with restricted permissions
449+
echo "OPENAI_API_KEY=sk-proj-abc123..." > /data/.openclaw/agents/agent-1/.env
450+
chmod 600 /data/.openclaw/agents/agent-1/.env
451+
docker run --env-file /data/.openclaw/agents/agent-1/.env openclaw-agent
452+
453+
# Option 3: Mount secret as a file
454+
docker run -v /data/secrets/openai_key:/run/secrets/openai_key:ro openclaw-agent
455+
```
456+
457+
g0 inspects running container environment variables and flags any sensitive values (API keys, tokens, passwords, credentials) that are passed inline rather than via files or secret stores.
458+
430459
---
431460

432461
## Step 6: Docker Socket Isolation
@@ -1357,6 +1386,33 @@ g0 looks for OpenClaw indicators in these locations:
13571386

13581387
Ensure your `agentDataPath` is correct in `~/.g0/daemon.json`.
13591388

1389+
### Daemon won't stay alive / exits immediately
1390+
1391+
If `g0 daemon start` reports a PID but the process dies immediately:
1392+
1393+
1. **Check the startup log** — errors during early initialization are captured here:
1394+
1395+
```bash
1396+
cat ~/.g0/daemon-startup.log
1397+
```
1398+
1399+
2. **Check the daemon log** — if the daemon survived past initial startup:
1400+
1401+
```bash
1402+
g0 daemon logs
1403+
```
1404+
1405+
3. **Common causes:**
1406+
- Missing Node.js modules — reinstall g0: `npm install -g @guard0/g0`
1407+
- Corrupt `daemon.json` — validate: `cat ~/.g0/daemon.json | python3 -m json.tool`
1408+
- No swap + high memory pressure — the OOM killer may be terminating the process. Check `dmesg | grep -i oom`
1409+
1410+
4. If the startup log is empty, the runner script could not be located. Verify the installation:
1411+
1412+
```bash
1413+
ls $(dirname $(which g0))/../lib/node_modules/@guard0/g0/dist/src/daemon/runner.js
1414+
```
1415+
13601416
### "iptables: Permission denied"
13611417

13621418
Egress rule application requires root/sudo. Either:
@@ -1396,7 +1452,7 @@ sudo auditctl -l # Verify rules are loaded
13961452
### False Positives in Static Scan
13971453

13981454
If you see false positives like SQL injection on logger lines:
1399-
1. g0 v1.4.0+ includes fixes for common FPs (e.g., AA-CE-012 on Python f-string logging)
1455+
1. g0 v1.5.0+ includes fixes for common FPs (e.g., AA-CE-012 on Python f-string logging)
14001456
2. Add specific rules to `exclude_rules` in `.g0.yaml`
14011457
3. Use `--min-confidence medium` (default) to hide low-confidence generic findings
14021458

0 commit comments

Comments
 (0)