title: "OpenClaw Security Hardening: A Complete Guide" date: "2026-02-10" description: "Secure your OpenClaw deployment with this comprehensive security hardening guide. Covers API key management, network security, access controls, and monitoring." category: "Security" author: "OpenClaw Team" tags: ["security", "hardening", "production", "best-practices"] readTime: "13 min"
Running OpenClaw in production means your AI agents have access to API keys, internal systems, databases, and potentially sensitive data. That's a significant attack surface. This guide covers every layer of a hardened OpenClaw deployment — from the keys that authenticate your agents to the incident response plan you'll be glad you wrote before you needed it.
This is not a theoretical checklist. Every recommendation here includes concrete configuration, commands, and code you can apply today.
What This Guide Covers
- API key rotation and secrets management
- Environment variable handling
- Network security (firewall rules, VPN, TLS)
- Access control and authentication
- Input validation and output sanitization
- Audit logging
- Security monitoring and alerting
- Backup strategies
- Incident response planning
Prerequisites
Before you begin, make sure you have:
- OpenClaw installed (installation guide)
- Admin access to your server or cloud environment
- A secrets management solution (HashiCorp Vault, AWS Secrets Manager, or similar)
- Basic familiarity with Linux system administration
Step 1: API Key Management
API keys are the most common source of security incidents in AI deployments. A leaked key means an attacker can impersonate your agents, rack up your bill, and exfiltrate data.
Never Put Keys in Config Files
# WRONG — do not do this
echo "ANTHROPIC_API_KEY=sk-ant-abc123" >> .openclaw/config.yaml
# RIGHT — use environment variables
export ANTHROPIC_API_KEY=$(vault kv get -field=key secret/openclaw/anthropic)
openclaw start
Use a Secrets Manager
HashiCorp Vault is a solid choice for self-hosted deployments:
# Store your API key in Vault
vault kv put secret/openclaw/anthropic key=sk-ant-abc123...
# Configure OpenClaw to pull secrets from Vault at startup
# .openclaw/config.yaml
secrets:
provider: vault
address: "https://vault.your-company.com"
auth_method: kubernetes # or approle, aws, token
paths:
anthropic_api_key: "secret/data/openclaw/anthropic#key"
openai_api_key: "secret/data/openclaw/openai#key"
database_url: "secret/data/openclaw/database#url"
For AWS deployments, use AWS Secrets Manager:
secrets:
provider: aws_secrets_manager
region: us-east-1
secrets:
anthropic_api_key: "openclaw/prod/anthropic-key"
database_url: "openclaw/prod/database"
Rotate Keys Regularly
Set up automated key rotation so compromised keys expire quickly:
# .openclaw/maintenance.yaml — schedule key rotation
jobs:
rotate_api_keys:
schedule: "0 3 1 * *" # First day of each month at 3 AM UTC
type: maintenance
action: rotate_secrets
secrets:
- anthropic_api_key
- openai_api_key
notify:
slack: "#security-ops"
After rotation, always verify that your agents can still connect:
openclaw health check --verify-credentials
Step 2: Environment Variable Management
Even with a secrets manager, you still need to be careful about how environment variables are handled at the OS level.
# Prevent environment variables from appearing in process listings
# Set this in your systemd service or Docker config
openclaw start --no-env-in-ps
# Audit which environment variables OpenClaw can see
openclaw debug env --redact-sensitive
In production, restrict who can read the environment of running processes:
# /etc/systemd/system/openclaw.service
[Service]
User=openclaw
Group=openclaw
# Only the openclaw user and root can read /proc/PID/environ
PrivateTmp=true
ProtectSystem=strict
NoNewPrivileges=true
For containerized deployments, never pass secrets as Docker environment variables in plain text. Use Docker secrets or an init container that fetches secrets at startup:
# docker-compose.yaml
services:
openclaw:
image: openclaw/openclaw:latest
secrets:
- anthropic_key
environment:
- ANTHROPIC_API_KEY_FILE=/run/secrets/anthropic_key
secrets:
anthropic_key:
file: ./secrets/anthropic.key # Kept out of version control
Step 3: Network Security
Firewall Rules
Restrict inbound and outbound traffic to only what OpenClaw needs:
# Allow only necessary inbound ports (example using ufw)
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp comment "SSH"
ufw allow 443/tcp comment "HTTPS for webhook/API"
ufw allow from 10.0.0.0/8 to any port 3001 comment "Internal webhook only"
ufw enable
# Verify the rules
ufw status verbose
For outbound, restrict which external APIs OpenClaw can reach:
# .openclaw/network.yaml
egress:
allowed_hosts:
- "api.anthropic.com"
- "api.openai.com"
- "api.github.com"
- "your-internal-api.company.com"
denied_hosts:
- "*" # Deny everything else (allowlist approach)
dns_over_https: true # Prevent DNS leakage
Enforce TLS Everywhere
# .openclaw/config.yaml
tls:
min_version: "1.2" # Never allow TLS 1.0 or 1.1
preferred_version: "1.3"
verify_certificates: true # Never set this to false in production
ca_bundle: "/etc/ssl/certs/ca-certificates.crt"
# If you run OpenClaw's own HTTPS server
server:
cert: "/etc/openclaw/tls/cert.pem"
key: "/etc/openclaw/tls/key.pem"
auto_renew: true # Uses Let's Encrypt
Use a VPN for Internal Services
If your agents access internal databases or APIs, route that traffic through a VPN rather than exposing those services to the internet:
# Connect to WireGuard VPN before starting OpenClaw
wg-quick up wg0
# Verify connectivity
openclaw network test --endpoint https://internal-api.company.internal
Step 4: Access Control and Authentication
Principle of Least Privilege
Create a dedicated system user for OpenClaw with minimal permissions:
# Create a dedicated user with no login shell
useradd --system --no-create-home --shell /usr/sbin/nologin openclaw
# Give it ownership of only what it needs
chown -R openclaw:openclaw /opt/openclaw /var/log/openclaw /etc/openclaw
chmod 750 /etc/openclaw # No world-readable config
chmod 700 /etc/openclaw/secrets/
API Authentication for OpenClaw's Own API
If you expose OpenClaw's management API, secure it with API keys and rate limiting:
# .openclaw/api.yaml
api:
auth:
type: bearer_token
tokens:
- id: admin_token
value: "$OPENCLAW_ADMIN_TOKEN" # From secrets manager
permissions: ["read", "write", "admin"]
- id: ci_token
value: "$OPENCLAW_CI_TOKEN"
permissions: ["read"] # CI can only read status
rate_limiting:
enabled: true
requests_per_minute: 60
burst: 10
ip_allowlist:
enabled: true
cidrs:
- "10.0.0.0/8" # Internal network only
- "192.168.1.0/24" # Office VPN
Multi-Factor Authentication for the Dashboard
# .openclaw/config.yaml
dashboard:
auth:
provider: oidc
issuer: "https://your-idp.company.com"
client_id: "$OIDC_CLIENT_ID"
require_mfa: true
allowed_groups: ["engineering", "devops"]
Step 5: Input Validation
Agents that accept user-supplied input can be manipulated through prompt injection. Validate and sanitize inputs before they reach your agents.
from openclaw import Agent, InputValidator, ValidationError
validator = InputValidator(
max_length=4096, # Reject suspiciously long inputs
allow_html=False, # Strip HTML tags
allow_markdown=True,
blocked_patterns=[
r"ignore (all )?(previous|prior) instructions",
r"you are now",
r"system prompt",
r"<\|.*\|>", # Token injection attempts
]
)
def handle_user_request(user_input: str) -> str:
try:
clean_input = validator.validate(user_input)
except ValidationError as e:
# Log the attempt and reject it
log_security_event("input_validation_failed", {
"input_preview": user_input[:100],
"reason": str(e)
})
return "Invalid input. Please try again."
agent = Agent(model="claude-3-sonnet")
return agent.run(clean_input)
For YAML and JSON inputs in automated pipelines, use strict schema validation:
import jsonschema
TASK_SCHEMA = {
"type": "object",
"required": ["task_type", "parameters"],
"additionalProperties": False,
"properties": {
"task_type": {
"type": "string",
"enum": ["report", "analysis", "summary"] # Only known task types
},
"parameters": {
"type": "object",
"maxProperties": 10
}
}
}
def validate_task(task_input: dict) -> None:
jsonschema.validate(instance=task_input, schema=TASK_SCHEMA)
Step 6: Output Sanitization
Agents can produce outputs that get rendered in other systems. Sanitize outputs before passing them downstream.
from openclaw import Agent
import bleach
ALLOWED_TAGS = ["p", "br", "strong", "em", "ul", "ol", "li", "code", "pre"]
ALLOWED_ATTRIBUTES = {}
def run_safe_agent(prompt: str) -> str:
agent = Agent(model="claude-3-sonnet")
raw_output = agent.run(prompt)
# Sanitize HTML if the output will be rendered in a browser
clean_output = bleach.clean(
raw_output,
tags=ALLOWED_TAGS,
attributes=ALLOWED_ATTRIBUTES,
strip=True
)
# Strip any secrets that might have leaked into the output
clean_output = redact_secrets(clean_output)
return clean_output
def redact_secrets(text: str) -> str:
import re
patterns = [
(r"sk-[a-zA-Z0-9]{32,}", "[REDACTED_API_KEY]"),
(r"AKIA[0-9A-Z]{16}", "[REDACTED_AWS_KEY]"),
(r"ghp_[a-zA-Z0-9]{36}", "[REDACTED_GH_TOKEN]"),
]
for pattern, replacement in patterns:
text = re.sub(pattern, replacement, text)
return text
Step 7: Audit Logging
Every action your agents take should be logged with enough context to reconstruct what happened and why.
# .openclaw/config.yaml
audit:
enabled: true
log_path: /var/log/openclaw/audit.log
format: json
include:
- agent_invocations
- tool_calls
- api_requests
- auth_events
- config_changes
- job_executions
redact:
- api_keys
- passwords
- tokens
retention_days: 365 # Keep a full year for compliance
tamper_protection:
enabled: true
method: sha256_chain # Each log entry includes a hash of the previous entry
A sample audit log entry:
{
"timestamp": "2026-02-10T14:23:01.452Z",
"event_type": "tool_call",
"agent_id": "daily_report",
"tool": "web_search",
"input_summary": "query: 'Q4 2025 metrics'",
"result_summary": "5 results returned",
"duration_ms": 1240,
"model": "claude-3-sonnet",
"tokens_used": 843,
"user": "system/cron",
"trace_id": "abc-123-xyz",
"prev_hash": "sha256:a1b2c3..."
}
Ship audit logs to a tamper-resistant destination:
# Stream audit logs to S3 (example with Fluentd)
openclaw audit stream --format json | \
fluentd --config /etc/fluentd/openclaw-audit.conf
Step 8: Security Monitoring and Alerts
Detecting an attack in progress is as important as preventing one.
# .openclaw/monitoring.yaml
security_alerts:
- name: high_token_usage
description: "Unusual spike in token consumption — possible prompt injection"
condition: "tokens_per_hour > 500000"
action: slack_alert
channel: "#security-ops"
severity: warning
- name: repeated_auth_failures
description: "Multiple failed auth attempts — possible brute force"
condition: "auth_failures_per_minute > 10"
action: pagerduty
severity: critical
auto_block: true # Automatically block the source IP
- name: unexpected_tool_call
description: "Agent called a tool not in its configured allowlist"
condition: "tool_not_in_allowlist == true"
action: multi
destinations:
- type: pagerduty
severity: high
- type: slack
channel: "#security-ops"
auto_disable_agent: true # Stop the agent immediately
- name: data_exfiltration_pattern
description: "Agent output contains large amounts of structured data"
condition: "output_contains_pii == true OR output_size_kb > 500"
action: human_review_queue
Set up a regular security summary report (see scheduling AI tasks with cron jobs for the scheduling setup):
jobs:
daily_security_digest:
schedule: "0 7 * * *" # 7 AM daily
type: security_report
include:
- failed_auth_events
- unusual_tool_calls
- high_cost_runs
- suppressed_alerts
output:
type: email
to: "security@yourcompany.com"
Step 9: Backup Strategies
Protect your OpenClaw configuration, agent definitions, and audit logs.
#!/bin/bash
# /opt/openclaw/scripts/backup.sh
BACKUP_DIR="/mnt/backups/openclaw"
DATE=$(date +%Y%m%d-%H%M%S)
BACKUP_PATH="$BACKUP_DIR/$DATE"
mkdir -p "$BACKUP_PATH"
# Back up configuration (secrets excluded — they live in Vault)
tar -czf "$BACKUP_PATH/config.tar.gz" \
/etc/openclaw/*.yaml \
/opt/openclaw/agents/ \
/opt/openclaw/workflows/
# Back up audit logs
tar -czf "$BACKUP_PATH/audit-logs.tar.gz" \
/var/log/openclaw/audit.log*
# Encrypt the backup
gpg --encrypt \
--recipient "backup-key@yourcompany.com" \
--output "$BACKUP_PATH/config.tar.gz.gpg" \
"$BACKUP_PATH/config.tar.gz"
rm "$BACKUP_PATH/config.tar.gz"
# Upload to offsite storage
aws s3 cp "$BACKUP_PATH/" "s3://your-backup-bucket/openclaw/$DATE/" \
--recursive \
--storage-class GLACIER
echo "Backup completed: $BACKUP_PATH"
Schedule this script and test restores quarterly:
# Test that a restore actually works
openclaw restore --from-backup s3://your-backup-bucket/openclaw/2026-02-10-030000/ \
--target /tmp/restore-test/ \
--dry-run
Step 10: Incident Response Planning
Document your incident response before you need it. A security incident is the wrong time to figure out who to call.
# .openclaw/incident-response.yaml
contacts:
primary_oncall: "oncall@yourcompany.com"
security_team: "security@yourcompany.com"
pagerduty_service: "PXXXXXX"
playbooks:
api_key_compromised:
steps:
- "Immediately rotate the compromised key in Vault"
- "Run: openclaw audit search --api-key-prefix $PREFIX --last 30d"
- "Review all agent runs that used the compromised key"
- "Check for unusual outputs or data access"
- "File incident report within 24 hours"
- "Review how the key leaked and add controls"
agent_acting_unexpectedly:
steps:
- "Run: openclaw agent disable $AGENT_ID"
- "Preserve logs: openclaw logs $AGENT_ID --last 24h > incident-$(date +%s).log"
- "Review recent tool calls and outputs in audit log"
- "Check for prompt injection in recent inputs"
- "Do not re-enable until root cause is identified"
data_breach_suspected:
steps:
- "Immediately stop all agent runs: openclaw pause --all"
- "Preserve all logs"
- "Notify security team via PagerDuty (do not use Slack for sensitive incidents)"
- "Engage legal team if customer data may be involved"
- "Begin forensic analysis of audit logs"
Practice your incident response with a tabletop exercise every six months. Walk through a scenario — a leaked API key, a prompt injection, an agent accessing data it shouldn't — and verify your playbook works.
Security Checklist
Before going to production, verify each item:
| Category | Check | Status | |----------|-------|--------| | Secrets | API keys stored in secrets manager, not config files | | | Secrets | Key rotation scheduled | | | Network | Firewall restricts inbound to necessary ports only | | | Network | TLS 1.2+ enforced everywhere | | | Access | OpenClaw runs as dedicated low-privilege user | | | Access | Dashboard requires MFA | | | Input | Input length and content validation enabled | | | Output | Output sanitization strips HTML and redacts secrets | | | Audit | Audit logging enabled with 1-year retention | | | Monitoring | Security alerts configured for key threat scenarios | | | Backup | Encrypted backups tested and offsite | | | Incident | Response playbooks documented and practiced | |
Next Steps
Security is an ongoing practice, not a one-time configuration. Stay current with:
- OpenClaw for code review — automatically scan your OpenClaw agent code for security issues
- Scheduling AI tasks with cron jobs — automate your daily security digest and key rotation
- Build a multi-agent system — understand agent-to-agent trust boundaries when scaling up
A hardened OpenClaw deployment lets you give your agents real capabilities with confidence. Lock down the infrastructure, and you can focus on building agents that actually move the needle.