mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-21 07:44:59 +00:00
Docs: restore gateway security baseline and apply staged capability notes
This commit is contained in:
@@ -7,6 +7,22 @@ title: "Security"
|
||||
|
||||
# Security 🔒
|
||||
|
||||
> [!WARNING]
|
||||
> **Personal assistant trust model:** this guidance assumes one trusted operator boundary per gateway (single-user/personal assistant model).
|
||||
> OpenClaw is **not** a hostile multi-tenant security boundary for multiple adversarial users sharing one agent/gateway.
|
||||
> If you need mixed-trust or adversarial-user operation, split trust boundaries (separate gateway + credentials, ideally separate OS users/hosts).
|
||||
|
||||
## Scope first: personal assistant security model
|
||||
|
||||
OpenClaw security guidance assumes a **personal assistant** deployment: one trusted operator boundary, potentially many agents.
|
||||
|
||||
- Supported security posture: one user/trust boundary per gateway (prefer one OS user/host/VPS per boundary).
|
||||
- Not a supported security boundary: one shared gateway/agent used by mutually untrusted or adversarial users.
|
||||
- If adversarial-user isolation is required, split by trust boundary (separate gateway + credentials, and ideally separate OS users/hosts).
|
||||
- If multiple untrusted users can message one tool-enabled agent, treat them as sharing the same delegated tool authority for that agent.
|
||||
|
||||
This page explains hardening **within that model**. It does not claim hostile multi-tenant isolation on one shared gateway.
|
||||
|
||||
## Quick check: `openclaw security audit`
|
||||
|
||||
See also: [Formal Verification (Security Models)](/security/formal-verification/)
|
||||
@@ -37,6 +53,94 @@ OpenClaw assumes the host and config boundary are trusted:
|
||||
- If someone can modify Gateway host state/config (`~/.openclaw`, including `openclaw.json`), treat them as a trusted operator.
|
||||
- Running one Gateway for multiple mutually untrusted/adversarial operators is **not a recommended setup**.
|
||||
- For mixed-trust teams, split trust boundaries with separate gateways (or at minimum separate OS users/hosts).
|
||||
- OpenClaw can run multiple gateway instances on one machine, but recommended operations favor clean trust-boundary separation.
|
||||
- Recommended default: one user per machine/host (or VPS), one gateway for that user, and one or more agents in that gateway.
|
||||
- If multiple users want OpenClaw, use one VPS/host per user.
|
||||
|
||||
### Practical consequence (operator trust boundary)
|
||||
|
||||
Inside one Gateway instance, authenticated operator access is a trusted control-plane role, not a per-user tenant role.
|
||||
|
||||
- Operators with read/control-plane access can inspect gateway session metadata/history by design.
|
||||
- Session identifiers (`sessionKey`, session IDs, labels) are routing selectors, not authorization tokens.
|
||||
- Example: expecting per-operator isolation for methods like `sessions.list`, `sessions.preview`, or `chat.history` is outside this model.
|
||||
- If you need adversarial-user isolation, run separate gateways per trust boundary.
|
||||
- Multiple gateways on one machine are technically possible, but not the recommended baseline for multi-user isolation.
|
||||
|
||||
## Personal assistant model (not a multi-tenant bus)
|
||||
|
||||
OpenClaw is designed as a personal assistant security model: one trusted operator boundary, potentially many agents.
|
||||
|
||||
- If several people can message one tool-enabled agent, each of them can steer that same permission set.
|
||||
- Per-user session/memory isolation helps privacy, but does not convert a shared agent into per-user host authorization.
|
||||
- If users may be adversarial to each other, run separate gateways (or separate OS users/hosts) per trust boundary.
|
||||
|
||||
### Shared Slack workspace: real risk
|
||||
|
||||
If "everyone in Slack can message the bot," the core risk is delegated tool authority:
|
||||
|
||||
- any allowed sender can induce tool calls (`exec`, browser, network/file tools) within the agent's policy;
|
||||
- prompt/content injection from one sender can cause actions that affect shared state, devices, or outputs;
|
||||
- if one shared agent has sensitive credentials/files, any allowed sender can potentially drive exfiltration via tool usage.
|
||||
|
||||
Use separate agents/gateways with minimal tools for team workflows; keep personal-data agents private.
|
||||
|
||||
### Company-shared agent: acceptable pattern
|
||||
|
||||
This is acceptable when everyone using that agent is in the same trust boundary (for example one company team) and the agent is strictly business-scoped.
|
||||
|
||||
- run it on a dedicated machine/VM/container;
|
||||
- use a dedicated OS user + dedicated browser/profile/accounts for that runtime;
|
||||
- do not sign that runtime into personal Apple/Google accounts or personal password-manager/browser profiles.
|
||||
|
||||
If you mix personal and company identities on the same runtime, you collapse the separation and increase personal-data exposure risk.
|
||||
|
||||
## Gateway and node trust concept
|
||||
|
||||
Treat Gateway and node as one operator trust domain, with different roles:
|
||||
|
||||
- **Gateway** is the control plane and policy surface (`gateway.auth`, tool policy, routing).
|
||||
- **Node** is remote execution surface paired to that Gateway (commands, device actions, host-local capabilities).
|
||||
- A caller authenticated to the Gateway is trusted at Gateway scope. After pairing, node actions are trusted operator actions on that node.
|
||||
- `sessionKey` is routing/context selection, not per-user auth.
|
||||
- Exec approvals (allowlist + ask) are guardrails for operator intent, not hostile multi-tenant isolation.
|
||||
|
||||
If you need hostile-user isolation, split trust boundaries by OS user/host and run separate gateways.
|
||||
|
||||
## Trust boundary matrix
|
||||
|
||||
Use this as the quick model when triaging risk:
|
||||
|
||||
| Boundary or control | What it means | Common misread |
|
||||
| ------------------------------------------- | ------------------------------------------------- | ----------------------------------------------------------------------------- |
|
||||
| `gateway.auth` (token/password/device auth) | Authenticates callers to gateway APIs | "Needs per-message signatures on every frame to be secure" |
|
||||
| `sessionKey` | Routing key for context/session selection | "Session key is a user auth boundary" |
|
||||
| Prompt/content guardrails | Reduce model abuse risk | "Prompt injection alone proves auth bypass" |
|
||||
| `canvas.eval` / browser evaluate | Intentional operator capability when enabled | "Any JS eval primitive is automatically a vuln in this trust model" |
|
||||
| Local TUI `!` shell | Explicit operator-triggered local execution | "Local shell convenience command is remote injection" |
|
||||
| Node pairing and node commands | Operator-level remote execution on paired devices | "Remote device control should be treated as untrusted user access by default" |
|
||||
|
||||
## Not vulnerabilities by design
|
||||
|
||||
These patterns are commonly reported and are usually closed as no-action unless a real boundary bypass is shown:
|
||||
|
||||
- Prompt-injection-only chains without a policy/auth/sandbox bypass.
|
||||
- Claims that assume hostile multi-tenant operation on one shared host/config.
|
||||
- Claims that classify normal operator read-path access (for example `sessions.list`/`sessions.preview`/`chat.history`) as IDOR in a shared-gateway setup.
|
||||
- Localhost-only deployment findings (for example HSTS on loopback-only gateway).
|
||||
- Discord inbound webhook signature findings for inbound paths that do not exist in this repo.
|
||||
- "Missing per-user authorization" findings that treat `sessionKey` as an auth token.
|
||||
|
||||
## Researcher preflight checklist
|
||||
|
||||
Before opening a GHSA, verify all of these:
|
||||
|
||||
1. Repro still works on latest `main` or latest release.
|
||||
2. Report includes exact code path (`file`, function, line range) and tested version/commit.
|
||||
3. Impact crosses a documented trust boundary (not just prompt injection).
|
||||
4. Claim is not listed in [Out of Scope](https://github.com/openclaw/openclaw/blob/main/SECURITY.md#out-of-scope).
|
||||
5. Existing advisories were checked for duplicates (reuse canonical GHSA when applicable).
|
||||
6. Deployment assumptions are explicit (loopback/local vs exposed, trusted vs untrusted operators).
|
||||
|
||||
## Hardened baseline in 60 seconds
|
||||
|
||||
@@ -84,7 +188,7 @@ If more than one person can DM your bot:
|
||||
- **Browser control exposure** (remote nodes, relay ports, remote CDP endpoints).
|
||||
- **Local disk hygiene** (permissions, symlinks, config includes, “synced folder” paths).
|
||||
- **Plugins** (extensions exist without an explicit allowlist).
|
||||
- **Policy drift/misconfig** (sandbox docker settings configured but sandbox mode off; ineffective `gateway.nodes.denyCommands` patterns; dangerous `gateway.nodes.allowCommands` entries; global `tools.profile="minimal"` overridden by per-agent profiles; extension plugin tools reachable under permissive tool policy).
|
||||
- **Policy drift/misconfig** (sandbox docker settings configured but sandbox mode off; ineffective `gateway.nodes.denyCommands` patterns because matching is exact command-name only (for example `system.run`) and does not inspect shell text; dangerous `gateway.nodes.allowCommands` entries; global `tools.profile="minimal"` overridden by per-agent profiles; extension plugin tools reachable under permissive tool policy).
|
||||
- **Runtime expectation drift** (for example `tools.exec.host="sandbox"` while sandbox mode is off, which runs directly on the gateway host).
|
||||
- **Model hygiene** (warn when configured models look legacy; not a hard block).
|
||||
|
||||
@@ -98,8 +202,11 @@ Use this when auditing access or deciding what to back up:
|
||||
- **Telegram bot token**: config/env or `channels.telegram.tokenFile`
|
||||
- **Discord bot token**: config/env (token file not yet supported)
|
||||
- **Slack tokens**: config/env (`channels.slack.*`)
|
||||
- **Pairing allowlists**: `~/.openclaw/credentials/<channel>-allowFrom.json` or `~/.openclaw/credentials/<channel>-<accountId>-allowFrom.json`
|
||||
- **Pairing allowlists**:
|
||||
- `~/.openclaw/credentials/<channel>-allowFrom.json` (default account)
|
||||
- `~/.openclaw/credentials/<channel>-<accountId>-allowFrom.json` (non-default accounts)
|
||||
- **Model auth profiles**: `~/.openclaw/agents/<agentId>/agent/auth-profiles.json`
|
||||
- **File-backed secrets payload (optional)**: `~/.openclaw/secrets.json`
|
||||
- **Legacy OAuth import**: `~/.openclaw/credentials/oauth.json`
|
||||
|
||||
## Security Audit Checklist
|
||||
@@ -117,31 +224,39 @@ When the audit prints findings, treat this as a priority order:
|
||||
|
||||
High-signal `checkId` values you will most likely see in real deployments (not exhaustive):
|
||||
|
||||
| `checkId` | Severity | Why it matters | Primary fix key/path | Auto-fix |
|
||||
| -------------------------------------------------- | ------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | -------- |
|
||||
| `fs.state_dir.perms_world_writable` | critical | Other users/processes can modify full OpenClaw state | filesystem perms on `~/.openclaw` | yes |
|
||||
| `fs.config.perms_writable` | critical | Others can change auth/tool policy/config | filesystem perms on `~/.openclaw/openclaw.json` | yes |
|
||||
| `fs.config.perms_world_readable` | critical | Config can expose tokens/settings | filesystem perms on config file | yes |
|
||||
| `gateway.bind_no_auth` | critical | Remote bind without shared secret | `gateway.bind`, `gateway.auth.*` | no |
|
||||
| `gateway.loopback_no_auth` | critical | Reverse-proxied loopback may become unauthenticated | `gateway.auth.*`, proxy setup | no |
|
||||
| `gateway.http.no_auth` | warn/critical | Gateway HTTP APIs reachable with `auth.mode="none"` | `gateway.auth.mode`, `gateway.http.endpoints.*` | no |
|
||||
| `gateway.tools_invoke_http.dangerous_allow` | warn/critical | Re-enables dangerous tools over HTTP API | `gateway.tools.allow` | no |
|
||||
| `gateway.nodes.allow_commands_dangerous` | warn/critical | Enables high-impact node commands (camera/screen/contacts/calendar/SMS) | `gateway.nodes.allowCommands` | no |
|
||||
| `gateway.tailscale_funnel` | critical | Public internet exposure | `gateway.tailscale.mode` | no |
|
||||
| `gateway.control_ui.insecure_auth` | warn | Insecure-auth compatibility toggle enabled | `gateway.controlUi.allowInsecureAuth` | no |
|
||||
| `gateway.control_ui.device_auth_disabled` | critical | Disables device identity check | `gateway.controlUi.dangerouslyDisableDeviceAuth` | no |
|
||||
| `config.insecure_or_dangerous_flags` | warn | Any insecure/dangerous debug flags enabled | multiple keys (see finding detail) | no |
|
||||
| `hooks.token_too_short` | warn | Easier brute force on hook ingress | `hooks.token` | no |
|
||||
| `hooks.request_session_key_enabled` | warn/critical | External caller can choose sessionKey | `hooks.allowRequestSessionKey` | no |
|
||||
| `hooks.request_session_key_prefixes_missing` | warn/critical | No bound on external session key shapes | `hooks.allowedSessionKeyPrefixes` | no |
|
||||
| `logging.redact_off` | warn | Sensitive values leak to logs/status | `logging.redactSensitive` | yes |
|
||||
| `sandbox.docker_config_mode_off` | warn | Sandbox Docker config present but inactive | `agents.*.sandbox.mode` | no |
|
||||
| `tools.exec.host_sandbox_no_sandbox_defaults` | warn | `exec host=sandbox` resolves to host exec when sandbox is off | `tools.exec.host`, `agents.defaults.sandbox.mode` | no |
|
||||
| `tools.exec.host_sandbox_no_sandbox_agents` | warn | Per-agent `exec host=sandbox` resolves to host exec when sandbox is off | `agents.list[].tools.exec.host`, `agents.list[].sandbox.mode` | no |
|
||||
| `security.exposure.open_groups_with_runtime_or_fs` | critical/warn | Open groups can reach command/file tools without sandbox/workspace guards | `channels.*.groupPolicy`, `tools.profile/deny`, `tools.fs.workspaceOnly`, `agents.*.sandbox.mode` | no |
|
||||
| `tools.profile_minimal_overridden` | warn | Agent overrides bypass global minimal profile | `agents.list[].tools.profile` | no |
|
||||
| `plugins.tools_reachable_permissive_policy` | warn | Extension tools reachable in permissive contexts | `tools.profile` + tool allow/deny | no |
|
||||
| `models.small_params` | critical/info | Small models + unsafe tool surfaces raise injection risk | model choice + sandbox/tool policy | no |
|
||||
| `checkId` | Severity | Why it matters | Primary fix key/path | Auto-fix |
|
||||
| -------------------------------------------------- | ------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | -------- |
|
||||
| `fs.state_dir.perms_world_writable` | critical | Other users/processes can modify full OpenClaw state | filesystem perms on `~/.openclaw` | yes |
|
||||
| `fs.config.perms_writable` | critical | Others can change auth/tool policy/config | filesystem perms on `~/.openclaw/openclaw.json` | yes |
|
||||
| `fs.config.perms_world_readable` | critical | Config can expose tokens/settings | filesystem perms on config file | yes |
|
||||
| `gateway.bind_no_auth` | critical | Remote bind without shared secret | `gateway.bind`, `gateway.auth.*` | no |
|
||||
| `gateway.loopback_no_auth` | critical | Reverse-proxied loopback may become unauthenticated | `gateway.auth.*`, proxy setup | no |
|
||||
| `gateway.http.no_auth` | warn/critical | Gateway HTTP APIs reachable with `auth.mode="none"` | `gateway.auth.mode`, `gateway.http.endpoints.*` | no |
|
||||
| `gateway.tools_invoke_http.dangerous_allow` | warn/critical | Re-enables dangerous tools over HTTP API | `gateway.tools.allow` | no |
|
||||
| `gateway.nodes.allow_commands_dangerous` | warn/critical | Enables high-impact node commands (camera/screen/contacts/calendar/SMS) | `gateway.nodes.allowCommands` | no |
|
||||
| `gateway.tailscale_funnel` | critical | Public internet exposure | `gateway.tailscale.mode` | no |
|
||||
| `gateway.control_ui.allowed_origins_required` | critical | Non-loopback Control UI without explicit browser-origin allowlist | `gateway.controlUi.allowedOrigins` | no |
|
||||
| `gateway.control_ui.host_header_origin_fallback` | warn/critical | Enables Host-header origin fallback (DNS rebinding hardening downgrade) | `gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback` | no |
|
||||
| `gateway.control_ui.insecure_auth` | warn | Insecure-auth compatibility toggle enabled | `gateway.controlUi.allowInsecureAuth` | no |
|
||||
| `gateway.control_ui.device_auth_disabled` | critical | Disables device identity check | `gateway.controlUi.dangerouslyDisableDeviceAuth` | no |
|
||||
| `gateway.real_ip_fallback_enabled` | warn/critical | Trusting `X-Real-IP` fallback can enable source-IP spoofing via proxy misconfig | `gateway.allowRealIpFallback`, `gateway.trustedProxies` | no |
|
||||
| `discovery.mdns_full_mode` | warn/critical | mDNS full mode advertises `cliPath`/`sshPort` metadata on local network | `discovery.mdns.mode`, `gateway.bind` | no |
|
||||
| `config.insecure_or_dangerous_flags` | warn | Any insecure/dangerous debug flags enabled | multiple keys (see finding detail) | no |
|
||||
| `hooks.token_too_short` | warn | Easier brute force on hook ingress | `hooks.token` | no |
|
||||
| `hooks.request_session_key_enabled` | warn/critical | External caller can choose sessionKey | `hooks.allowRequestSessionKey` | no |
|
||||
| `hooks.request_session_key_prefixes_missing` | warn/critical | No bound on external session key shapes | `hooks.allowedSessionKeyPrefixes` | no |
|
||||
| `logging.redact_off` | warn | Sensitive values leak to logs/status | `logging.redactSensitive` | yes |
|
||||
| `sandbox.docker_config_mode_off` | warn | Sandbox Docker config present but inactive | `agents.*.sandbox.mode` | no |
|
||||
| `sandbox.dangerous_network_mode` | critical | Sandbox Docker network uses `host` or `container:*` namespace-join mode | `agents.*.sandbox.docker.network` | no |
|
||||
| `tools.exec.host_sandbox_no_sandbox_defaults` | warn | `exec host=sandbox` resolves to host exec when sandbox is off | `tools.exec.host`, `agents.defaults.sandbox.mode` | no |
|
||||
| `tools.exec.host_sandbox_no_sandbox_agents` | warn | Per-agent `exec host=sandbox` resolves to host exec when sandbox is off | `agents.list[].tools.exec.host`, `agents.list[].sandbox.mode` | no |
|
||||
| `tools.exec.safe_bins_interpreter_unprofiled` | warn | Interpreter/runtime bins in `safeBins` without explicit profiles broaden exec risk | `tools.exec.safeBins`, `tools.exec.safeBinProfiles`, `agents.list[].tools.exec.*` | no |
|
||||
| `security.exposure.open_groups_with_elevated` | critical | Open groups + elevated tools create high-impact prompt-injection paths | `channels.*.groupPolicy`, `tools.elevated.*` | no |
|
||||
| `security.exposure.open_groups_with_runtime_or_fs` | critical/warn | Open groups can reach command/file tools without sandbox/workspace guards | `channels.*.groupPolicy`, `tools.profile/deny`, `tools.fs.workspaceOnly`, `agents.*.sandbox.mode` | no |
|
||||
| `security.trust_model.multi_user_heuristic` | warn | Config looks multi-user while gateway trust model is personal-assistant | split trust boundaries, or shared-user hardening (`sandbox.mode`, tool deny/workspace scoping) | no |
|
||||
| `tools.profile_minimal_overridden` | warn | Agent overrides bypass global minimal profile | `agents.list[].tools.profile` | no |
|
||||
| `plugins.tools_reachable_permissive_policy` | warn | Extension tools reachable in permissive contexts | `tools.profile` + tool allow/deny | no |
|
||||
| `models.small_params` | critical/info | Small models + unsafe tool surfaces raise injection risk | model choice + sandbox/tool policy | no |
|
||||
|
||||
## Control UI over HTTP
|
||||
|
||||
@@ -158,13 +273,40 @@ keep it off unless you are actively debugging and can revert quickly.
|
||||
|
||||
## Insecure or dangerous flags summary
|
||||
|
||||
`openclaw security audit` includes `config.insecure_or_dangerous_flags` when any
|
||||
insecure/dangerous debug switches are enabled. This warning aggregates the exact
|
||||
keys so you can review them in one place (for example
|
||||
`gateway.controlUi.allowInsecureAuth=true`,
|
||||
`gateway.controlUi.dangerouslyDisableDeviceAuth=true`,
|
||||
`hooks.gmail.allowUnsafeExternalContent=true`, or
|
||||
`tools.exec.applyPatch.workspaceOnly=false`).
|
||||
`openclaw security audit` includes `config.insecure_or_dangerous_flags` when
|
||||
known insecure/dangerous debug switches are enabled. That check currently
|
||||
aggregates:
|
||||
|
||||
- `gateway.controlUi.allowInsecureAuth=true`
|
||||
- `gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback=true`
|
||||
- `gateway.controlUi.dangerouslyDisableDeviceAuth=true`
|
||||
- `hooks.gmail.allowUnsafeExternalContent=true`
|
||||
- `hooks.mappings[<index>].allowUnsafeExternalContent=true`
|
||||
- `tools.exec.applyPatch.workspaceOnly=false`
|
||||
|
||||
Complete `dangerous*` / `dangerously*` config keys defined in OpenClaw config
|
||||
schema:
|
||||
|
||||
- `gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback`
|
||||
- `gateway.controlUi.dangerouslyDisableDeviceAuth`
|
||||
- `browser.ssrfPolicy.dangerouslyAllowPrivateNetwork`
|
||||
- `channels.discord.dangerouslyAllowNameMatching`
|
||||
- `channels.discord.accounts.<accountId>.dangerouslyAllowNameMatching`
|
||||
- `channels.slack.dangerouslyAllowNameMatching`
|
||||
- `channels.slack.accounts.<accountId>.dangerouslyAllowNameMatching`
|
||||
- `channels.googlechat.dangerouslyAllowNameMatching`
|
||||
- `channels.googlechat.accounts.<accountId>.dangerouslyAllowNameMatching`
|
||||
- `channels.msteams.dangerouslyAllowNameMatching`
|
||||
- `channels.irc.dangerouslyAllowNameMatching` (extension channel)
|
||||
- `channels.irc.accounts.<accountId>.dangerouslyAllowNameMatching` (extension channel)
|
||||
- `channels.mattermost.dangerouslyAllowNameMatching` (extension channel)
|
||||
- `channels.mattermost.accounts.<accountId>.dangerouslyAllowNameMatching` (extension channel)
|
||||
- `agents.defaults.sandbox.docker.dangerouslyAllowReservedContainerTargets`
|
||||
- `agents.defaults.sandbox.docker.dangerouslyAllowExternalBindSources`
|
||||
- `agents.defaults.sandbox.docker.dangerouslyAllowContainerNamespaceJoin`
|
||||
- `agents.list[<index>].sandbox.docker.dangerouslyAllowReservedContainerTargets`
|
||||
- `agents.list[<index>].sandbox.docker.dangerouslyAllowExternalBindSources`
|
||||
- `agents.list[<index>].sandbox.docker.dangerouslyAllowContainerNamespaceJoin`
|
||||
|
||||
## Reverse Proxy Configuration
|
||||
|
||||
@@ -199,6 +341,15 @@ Bad reverse proxy behavior (append/preserve untrusted forwarding headers):
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
```
|
||||
|
||||
## HSTS and origin notes
|
||||
|
||||
- OpenClaw gateway is local/loopback first. If you terminate TLS at a reverse proxy, set HSTS on the proxy-facing HTTPS domain there.
|
||||
- If the gateway itself terminates HTTPS, you can set `gateway.http.securityHeaders.strictTransportSecurity` to emit the HSTS header from OpenClaw responses.
|
||||
- Detailed deployment guidance is in [Trusted Proxy Auth](/gateway/trusted-proxy-auth#tls-termination-and-hsts).
|
||||
- For non-loopback Control UI deployments, `gateway.controlUi.allowedOrigins` is required by default.
|
||||
- `gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback=true` enables Host-header origin fallback mode; treat it as a dangerous operator-selected policy.
|
||||
- Treat DNS rebinding and proxy-host header behavior as deployment hardening concerns; keep `trustedProxies` tight and avoid exposing the gateway directly to the public internet.
|
||||
|
||||
## Local session logs live on disk
|
||||
|
||||
OpenClaw stores session transcripts on disk under `~/.openclaw/agents/<agentId>/sessions/*.jsonl`.
|
||||
@@ -215,18 +366,6 @@ If a macOS node is paired, the Gateway can invoke `system.run` on that node. Thi
|
||||
- Controlled on the Mac via **Settings → Exec approvals** (security + ask + allowlist).
|
||||
- If you don’t want remote execution, set security to **deny** and remove node pairing for that Mac.
|
||||
|
||||
## Skill security
|
||||
|
||||
Community skills (installed from ClawHub) are subject to runtime security enforcement:
|
||||
|
||||
- **Capabilities**: Skills declare what system access they need (`shell`, `filesystem`, `network`, `browser`, `sessions`, `messaging`, `scheduling`) in `metadata.openclaw.capabilities`. No capabilities = read-only metadata declaration. Capability rollout is staged; declarations are currently used for visibility and policy checks.
|
||||
- **SKILL.md scanning**: Content is scanned for prompt injection patterns, capability inflation, and boundary spoofing before entering the system prompt. Skills with critical findings are blocked from loading.
|
||||
- **Trust tiers**: Skills are classified as `builtin`, `community`, or `local`. Only `community` skills (installed from ClawHub) are subject to enforcement — builtin and local skills are exempt. Author verification may be introduced in a future release to provide an additional trust signal.
|
||||
- **Command dispatch gating**: Community skills using `command-dispatch: tool` can't dispatch to dangerous tools without declaring the matching capability.
|
||||
- **Audit logging**: All security events are tagged with `category: "security"` and include session context.
|
||||
|
||||
Use `openclaw skills check` for a security overview and `openclaw skills info <name>` for per-skill details. See [Skills CLI](/cli/skills) for full command reference.
|
||||
|
||||
## Dynamic skills (watcher / remote nodes)
|
||||
|
||||
OpenClaw can refresh the skills list mid-session:
|
||||
@@ -234,7 +373,15 @@ OpenClaw can refresh the skills list mid-session:
|
||||
- **Skills watcher**: changes to `SKILL.md` can update the skills snapshot on the next agent turn.
|
||||
- **Remote nodes**: connecting a macOS node can make macOS-only skills eligible (based on bin probing).
|
||||
|
||||
Restrict who can modify skill folders. Community skills are subject to scanning and phased capability-policy rollout (see above), but local and workspace skills are treated as trusted — if someone can write to your skill folders, they can inject instructions into the system prompt.
|
||||
Community skills (installed from ClawHub) are subject to runtime security controls:
|
||||
|
||||
- **Capabilities**: skills declare required system access (`shell`, `filesystem`, `network`, `browser`, `sessions`, `messaging`, `scheduling`) in `metadata.openclaw.capabilities`. No capabilities means read-only metadata declaration; capability rollout is staged and currently used for visibility and policy checks.
|
||||
- **SKILL.md scanning**: content is scanned for prompt injection patterns, capability inflation, and boundary spoofing before entering the system prompt. Skills with critical findings are blocked from loading.
|
||||
- **Trust tiers**: `community` skills are enforced, while `builtin` and local/workspace skills are treated as trusted by default.
|
||||
- **Command dispatch gating**: community skills using `command-dispatch: tool` cannot dispatch to dangerous tools without declaring the matching capability.
|
||||
- **Audit logging**: security events are tagged with `category: "security"` and include session context.
|
||||
|
||||
Treat skill folders as **trusted code** and restrict who can modify them.
|
||||
|
||||
## The Threat Model
|
||||
|
||||
@@ -342,6 +489,7 @@ This is a messaging-context boundary, not a host-admin boundary. If users are mu
|
||||
Treat the snippet above as **secure DM mode**:
|
||||
|
||||
- Default: `session.dmScope: "main"` (all DMs share one session for continuity).
|
||||
- Local CLI onboarding default: writes `session.dmScope: "per-channel-peer"` when unset (keeps existing explicit values).
|
||||
- Secure DM mode: `session.dmScope: "per-channel-peer"` (each channel+sender pair gets an isolated DM context).
|
||||
|
||||
If you run multiple accounts on the same channel, use `per-account-channel-peer` instead. If the same person contacts you on multiple channels, use `session.identityLinks` to collapse those DM sessions into one canonical identity. See [Session Management](/concepts/session) and [Configuration](/gateway/configuration).
|
||||
@@ -351,7 +499,7 @@ If you run multiple accounts on the same channel, use `per-account-channel-peer`
|
||||
OpenClaw has two separate “who can trigger me?” layers:
|
||||
|
||||
- **DM allowlist** (`allowFrom` / `channels.discord.allowFrom` / `channels.slack.allowFrom`; legacy: `channels.discord.dm.allowFrom`, `channels.slack.dm.allowFrom`): who is allowed to talk to the bot in direct messages.
|
||||
- When `dmPolicy="pairing"`, approvals are written to `~/.openclaw/credentials/<channel>-allowFrom.json` or `~/.openclaw/credentials/<channel>-<accountId>-allowFrom.json` for account-scoped channels (merged with config allowlists).
|
||||
- When `dmPolicy="pairing"`, approvals are written to the account-scoped pairing allowlist store under `~/.openclaw/credentials/` (`<channel>-allowFrom.json` for default account, `<channel>-<accountId>-allowFrom.json` for non-default accounts), merged with config allowlists.
|
||||
- **Group allowlist** (channel-specific): which groups/channels/guilds the bot will accept messages from at all.
|
||||
- Common patterns:
|
||||
- `channels.whatsapp.groups`, `channels.telegram.groups`, `channels.imessage.groups`: per-group defaults like `requireMention`; when set, it also acts as a group allowlist (include `"*"` to keep allow-all behavior).
|
||||
@@ -546,9 +694,10 @@ Set a token so **all** WS clients must authenticate:
|
||||
|
||||
Doctor can generate one for you: `openclaw doctor --generate-gateway-token`.
|
||||
|
||||
Note: in local mode, OpenClaw still accepts `gateway.remote.token` / `.password`
|
||||
as fallback credentials when `gateway.auth.*` is unset. Prefer setting
|
||||
`gateway.auth.token` (or password mode) explicitly so auth behavior is clear.
|
||||
Note: in local mode, OpenClaw still accepts `gateway.remote.token` /
|
||||
`gateway.remote.password` as fallback credentials when `gateway.auth.*` is
|
||||
unset. Prefer setting `gateway.auth.token` (or password mode) explicitly so
|
||||
auth behavior is clear.
|
||||
Optional: pin remote TLS with `gateway.remote.tlsFingerprint` when using `wss://`.
|
||||
|
||||
Local device pairing:
|
||||
@@ -622,7 +771,9 @@ Assume anything under `~/.openclaw/` (or `$OPENCLAW_STATE_DIR/`) may contain sec
|
||||
|
||||
- `openclaw.json`: config may include tokens (gateway, remote gateway), provider settings, and allowlists.
|
||||
- `credentials/**`: channel credentials (example: WhatsApp creds), pairing allowlists, legacy OAuth imports.
|
||||
- `agents/<agentId>/agent/auth-profiles.json`: API keys + OAuth tokens (imported from legacy `credentials/oauth.json`).
|
||||
- `agents/<agentId>/agent/auth-profiles.json`: API keys, token profiles, OAuth tokens, and optional `keyRef`/`tokenRef`.
|
||||
- `secrets.json` (optional): file-backed secret payload used by `file` SecretRef providers (`secrets.providers`).
|
||||
- `agents/<agentId>/agent/auth.json`: legacy compatibility file. Static `api_key` entries are scrubbed when discovered.
|
||||
- `agents/<agentId>/sessions/**`: session transcripts (`*.jsonl`) + routing metadata (`sessions.json`) that can contain private messages and tool output.
|
||||
- `extensions/**`: installed plugins (plus their `node_modules/`).
|
||||
- `sandboxes/**`: tool sandbox workspaces; can accumulate copies of files you read/write inside the sandbox.
|
||||
@@ -700,7 +851,8 @@ We may add a single `readOnlyMode` flag later to simplify this configuration.
|
||||
Additional hardening options:
|
||||
|
||||
- `tools.exec.applyPatch.workspaceOnly: true` (default): ensures `apply_patch` cannot write/delete outside the workspace directory even when sandboxing is off. Set to `false` only if you intentionally want `apply_patch` to touch files outside the workspace.
|
||||
- `tools.fs.workspaceOnly: true` (optional): restricts `read`/`write`/`edit`/`apply_patch` paths to the workspace directory (useful if you allow absolute paths today and want a single guardrail).
|
||||
- `tools.fs.workspaceOnly: true` (optional): restricts `read`/`write`/`edit`/`apply_patch` paths and native prompt image auto-load paths to the workspace directory (useful if you allow absolute paths today and want a single guardrail).
|
||||
- Keep filesystem roots narrow: avoid broad roots like your home directory for agent workspaces/sandbox workspaces. Broad roots can expose sensitive local files (for example state/config under `~/.openclaw`) to filesystem tools.
|
||||
|
||||
### 5) Secure baseline (copy/paste)
|
||||
|
||||
@@ -765,6 +917,30 @@ access those accounts and data. Treat browser profiles as **sensitive state**:
|
||||
- Disable browser proxy routing when you don’t need it (`gateway.nodes.browser.mode="off"`).
|
||||
- Chrome extension relay mode is **not** “safer”; it can take over your existing Chrome tabs. Assume it can act as you in whatever that tab/profile can reach.
|
||||
|
||||
### Browser SSRF policy (trusted-network default)
|
||||
|
||||
OpenClaw’s browser network policy defaults to the trusted-operator model: private/internal destinations are allowed unless you explicitly disable them.
|
||||
|
||||
- Default: `browser.ssrfPolicy.dangerouslyAllowPrivateNetwork: true` (implicit when unset).
|
||||
- Legacy alias: `browser.ssrfPolicy.allowPrivateNetwork` is still accepted for compatibility.
|
||||
- Strict mode: set `browser.ssrfPolicy.dangerouslyAllowPrivateNetwork: false` to block private/internal/special-use destinations by default.
|
||||
- In strict mode, use `hostnameAllowlist` (patterns like `*.example.com`) and `allowedHostnames` (exact host exceptions, including blocked names like `localhost`) for explicit exceptions.
|
||||
- Navigation is checked before request and best-effort re-checked on the final `http(s)` URL after navigation to reduce redirect-based pivots.
|
||||
|
||||
Example strict policy:
|
||||
|
||||
```json5
|
||||
{
|
||||
browser: {
|
||||
ssrfPolicy: {
|
||||
dangerouslyAllowPrivateNetwork: false,
|
||||
hostnameAllowlist: ["*.example.com", "example.com"],
|
||||
allowedHostnames: ["localhost"],
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## Per-agent access profiles (multi-agent)
|
||||
|
||||
With multi-agent routing, each agent can have its own sandbox + tool policy:
|
||||
@@ -896,7 +1072,7 @@ If your AI does something bad:
|
||||
|
||||
1. Rotate Gateway auth (`gateway.auth.token` / `OPENCLAW_GATEWAY_PASSWORD`) and restart.
|
||||
2. Rotate remote client secrets (`gateway.remote.token` / `.password`) on any machine that can call the Gateway.
|
||||
3. Rotate provider/API credentials (WhatsApp creds, Slack/Discord tokens, model/API keys in `auth-profiles.json`).
|
||||
3. Rotate provider/API credentials (WhatsApp creds, Slack/Discord tokens, model/API keys in `auth-profiles.json`, and encrypted secrets payload values when used).
|
||||
|
||||
### Audit
|
||||
|
||||
|
||||
Reference in New Issue
Block a user