Skip to main content
🦞

OpenClaw Security Hardening: A Complete Guide

13 minSecurity

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:

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.

Cookie Preferences

We use essential cookies and analytics to operate and improve the site. Advertising cookies are loaded only after you consent. Privacy Policy