Problem: OpenClaw Has No Built-In RBAC
You deployed OpenClaw for your team but discovered anyone with channel access can execute commands, read files, or trigger browser automation — regardless of their role or department.
You'll learn:
- Map OpenClaw's security model to RBAC patterns
- Configure role-based channel access via allowlists
- Isolate agent workspaces per team/role
- Lock down elevated permissions and tool sandboxing
Time: 45 min | Level: Advanced
Why This Matters
OpenClaw wasn't designed for traditional enterprise RBAC. It's a personal AI assistant built for localhost-first usage. However, its January 2026 security incidents revealed critical gaps when deployed in multi-user environments:
Common symptoms:
- Junior devs with access to production secrets
- Marketing team members triggering database queries
- External contractors executing shell commands on your gateway
- No audit trail linking actions to specific users/roles
Real incident: A misconfigured allowlist let an intern access the company's AWS credentials stored in OpenClaw's plaintext config files, leading to a $12K bill from cryptocurrency mining.
How OpenClaw Security Works (Not RBAC)
OpenClaw uses channel allowlists and agent isolation instead of traditional role assignments:
User → Channel (WhatsApp/Slack) → Allowlist Check → Agent → Sandbox Policy → Tools
Key differences from RBAC:
- No "User" object — only phone numbers, Slack IDs, or email addresses
- No "Roles" — you map identities directly to allowlists
- No "Permissions" — you enable/disable entire tools per agent
- Agents replace roles — each department gets its own isolated agent with different tool access
This means you simulate RBAC by:
- Creating separate agents for each role (Finance Agent, HR Agent, DevOps Agent)
- Routing users to agents via channel-specific allowlists
- Restricting tools per agent using sandbox policies
Solution
Step 1: Audit Current Access
Check who can currently reach your gateway:
# View all active allowlists
cat ~/.openclaw/openclaw.json | jq '.channels | to_entries[] | {channel: .key, allowFrom: .value.allowFrom}'
Expected output:
{
"channel": "whatsapp",
"allowFrom": ["*"] // ⚠️ DANGER: Everyone allowed
}
If you see "*" anywhere:
- This is wide open — anyone who gets your number can send commands
- You MUST replace with explicit phone numbers/IDs
Step 2: Define Role → Identity Mappings
Create a mapping file (this isn't stored in OpenClaw — it's your reference):
# roles.yml (documentation only)
roles:
admin:
users:
- "+1234567890" # Alice (CEO)
- "alice@company.com"
permissions:
- elevated_bash
- browser_control
- all_tools
engineering:
users:
- "+1234567891" # Bob
- "bob.slack.U12345"
permissions:
- bash_sandbox
- read_only_files
- sessions_send
finance:
users:
- "+1234567892" # Carol
permissions:
- read_only
- no_bash
external_contractors:
users:
- "+1234567893" # Dave
permissions:
- no_tools
- chat_only
Step 3: Create Isolated Agents Per Role
OpenClaw supports multiple agents, each with separate workspaces and configs:
{
"agents": {
"list": [
{
"name": "admin",
"workspace": "~/.openclaw/workspaces/admin",
"tools": {
"elevated": {
"enabled": true,
"allowFrom": ["+1234567890"]
}
}
},
{
"name": "engineering",
"workspace": "~/.openclaw/workspaces/engineering",
"sandbox": {
"mode": "always",
"scope": "agent",
"allowlist": ["bash", "read", "write", "sessions_send"]
}
},
{
"name": "finance",
"workspace": "~/.openclaw/workspaces/finance",
"sandbox": {
"mode": "always",
"scope": "agent",
"allowlist": ["read"] // Read-only for compliance
}
}
]
}
}
Why this works:
- Each agent has its own filesystem sandbox at
workspaces/<role>/ - Finance agent can't see Engineering's secrets
- Bash tools run in Docker for non-admin roles
Step 4: Route Channels to Agents
Map phone numbers/Slack IDs to specific agents using channel routing:
{
"channels": {
"whatsapp": {
"enabled": true,
"allowFrom": ["+1234567890", "+1234567891", "+1234567892"],
"routing": {
"+1234567890": "admin",
"+1234567891": "engineering",
"+1234567892": "finance"
}
},
"slack": {
"enabled": true,
"dm": {
"policy": "allowlist",
"allowFrom": ["U12345", "U67890"]
},
"routing": {
"U12345": "engineering",
"U67890": "finance"
}
}
}
}
Critical: Remove "*" from ALL allowlists before going to production.
Step 5: Sandbox Non-Admin Tools
For roles that shouldn't run arbitrary bash on the host:
{
"agents": {
"defaults": {
"sandbox": {
"mode": "non-main", // Sandbox everything except admin's main session
"scope": "agent",
"workspaceAccess": "ro", // Read-only mount
"allowlist": [
"bash", // Runs in Docker, not host
"read",
"sessions_list"
],
"denylist": [
"browser", // Too risky for non-admins
"elevated",
"cron"
]
}
}
}
}
What this does:
mode: "non-main"= Docker sandbox for groups/channelsworkspaceAccess: "ro"= Agent can read its workspace but not modify it- Bash runs inside Docker container, NOT on your gateway host
Step 6: Lock Down Elevated Mode
Elevated mode = host-level bash execution. Restrict it:
{
"tools": {
"elevated": {
"enabled": true,
"allowFrom": ["+1234567890"], // ONLY Alice (admin)
"requireApproval": true // Forces interactive approval
}
}
}
Users toggle elevated mode via /elevated on in chat. But even if they try, the allowFrom check blocks them.
Step 7: Separate Secrets Per Agent
Each agent should have its own API keys and credentials:
# Admin agent's secrets
~/.openclaw/workspaces/admin/credentials/
├── aws-credentials.json
└── prod-db-password.txt
# Engineering agent's secrets (different keys)
~/.openclaw/workspaces/engineering/credentials/
└── dev-api-keys.json
# Finance agent (no secrets, read-only)
~/.openclaw/workspaces/finance/credentials/
└── empty
Why separate?
- If Engineering's workspace is compromised, production credentials stay safe
- Each agent can only read its own
~/.openclaw/workspaces/<agent>/directory
Verification
Test each role's access:
# As admin (Alice)
openclaw message send --to +1234567890 --message "/elevated on"
# Expected: ✅ Elevated mode enabled
# As engineer (Bob)
openclaw message send --to +1234567891 --message "/elevated on"
# Expected: ⛔ Permission denied (not in allowFrom)
# As finance (Carol)
openclaw message send --to +1234567892 --message "Read /etc/passwd"
# Expected: ✅ Returns file contents (read allowed)
openclaw message send --to +1234567892 --message "Delete database"
# Expected: ⛔ Tool 'bash' not in allowlist
You should see:
- Admins can run elevated bash on host
- Engineers run bash in Docker sandbox only
- Finance can't run bash at all
Audit Trail Setup
OpenClaw doesn't log "which user did what" by default. Add this:
{
"logging": {
"level": "info",
"redactSensitive": "tools", // Logs tool args but redacts secrets
"destination": "/var/log/openclaw/audit.log"
}
}
Then pipe logs to your SIEM:
# Stream audit logs to Splunk/Datadog
tail -F /var/log/openclaw/audit.log | \
jq 'select(.type == "tool_call") | {user: .session.channel, tool: .tool, timestamp: .timestamp}'
Limitation: OpenClaw only logs the phone number/Slack ID, not the human name. You'll need to maintain the roles.yml mapping externally.
What You Learned
OpenClaw's security model is identity-scoped allowlists, not traditional RBAC:
- Agents simulate roles — each team gets an isolated agent
- Channel routing replaces role assignment — map phone numbers to agents
- Sandbox policies replace permissions — allowlist/denylist tools per agent
- No centralized user management — you track identities manually
When NOT to use this approach:
- You need 50+ roles (agent explosion)
- Users need dynamic permission changes (requires config edits)
- You want audit logs with human names (OpenClaw logs IDs only)
- You need attribute-based access control (ABAC)
Alternative: For true RBAC, consider wrapping OpenClaw behind HAProxy with Basic Auth or migrating to enterprise tools like n8n with built-in RBAC.
Common Failure Modes
Error: "User bypassed allowlist via group chat"
{
"channels": {
"whatsapp": {
"groups": {
"*": {
"activation": "mention", // Prevents group spam
"requireMention": true
}
}
}
}
}
Error: "Agent still accessing other agent's files"
- Check
sandbox.scope: "agent"is set (not"shared") - Verify each agent has separate
workspacepaths - Restart gateway:
openclaw gateway restart
Error: "Contractor with chat-only role ran bash command"
- Verify routing is set:
"routing": { "+1234567893": "contractor-agent" } - Check the contractor-agent has
"denylist": ["bash"] - Run
openclaw doctor --fixto auto-tighten configs
Production Hardening Checklist
Before deploying to real users:
- Replace ALL
"*"wildcards with explicit allowlists - Set
dmPolicy: "pairing"for all channels (no open DMs) - Enable
sandbox.mode: "non-main"for non-admin agents - Set
workspaceAccess: "ro"for read-only roles - Lock
tools.elevated.allowFromto 1-2 admin phone numbers - Store
openclaw.jsonatchmod 600(read-only for owner) - Enable audit logging to external SIEM
- Document your
roles.ymlmapping for the next admin - Run
openclaw doctorweekly to catch config drift - Set up HAProxy/Tailscale for gateway network isolation
Pro tip: Use Tailscale Serve to lock the gateway to your corporate VPN, adding network-level RBAC on top of channel allowlists.
Tested on OpenClaw v2026.2.7, Node.js 22.x, Ubuntu 24.04 + macOS 14+