All Articles OpenClaw

OpenClaw Gateway Authentication: Tokens, Passwords, and Network Binding Security

You've locked down your OpenClaw infrastructure. Isolated network. Dedicated user. Secrets manager in place.

You’ve locked down your OpenClaw infrastructure. Isolated network. Dedicated user. Secrets manager in place. Then you realize: the gateway is still sitting on localhost:18789, waiting for someone to flip the right authentication bits.

This is where most deployments get complacent. They think local = secure. Loopback network = safe. And then an attacker gains shell access to the box, opens a browser, and owns your entire orchestration layer without entering a password.

Let’s talk about gateway authentication—the specific mechanics of token validation, password hashing, network binding, and why loopback alone is a false sense of security. By the end, you’ll understand how to configure OpenClaw’s authentication layer so that compromising the host doesn’t automatically mean compromising the gateway.

The Gateway Authentication Attack Surface

Before we configure, let’s understand what we’re defending. The OpenClaw gateway is the API surface through which dashboards, CLIs, and external integrations interact with your agent runtime. It’s the command and control layer. It’s the single point of authority for your entire agent orchestration.

An attacker with access to the gateway can:

  • List running agents and extract context
  • Invoke arbitrary skills with the agent’s permissions
  • Read agent logs (which may contain secrets or sensitive data)
  • Modify agent configuration in real-time
  • Spawn new agents under their control
  • Access the dashboard and manipulate orchestration

If the gateway has weak authentication, you’ve essentially handed them the keys to the kingdom. They don’t need to exploit OpenClaw itself. They just need to authenticate to the gateway and they’ve already won.

This is why gateway authentication deserves obsessive attention.

The Three Authentication Mechanisms

OpenClaw uses three overlapping authentication mechanisms. Most deployments only lock down one or two. That’s a mistake.

1. Gateway Tokens (gateway.auth.token)

This is the primary API authentication mechanism. When you configure:

gateway:
  auth:
    token: "your-secret-token-here"

…OpenClaw expects all API requests to include this token in the Authorization: Bearer header:

curl -H "Authorization: Bearer your-secret-token-here" \
  https://automateanddeploy.com:18789/api/v1/agents

The Security Model: Token-based authentication is stateless. The gateway validates the token cryptographically (usually HMAC-SHA256) without maintaining session state. Fast, scalable, suitable for APIs.

The Problem: Token security depends entirely on:

  1. How strong the token is (length, entropy, randomness)
  2. How it’s transmitted (must be HTTPS if crossing networks)
  3. How it’s stored (must not appear in logs, error messages, or config version control)
  4. How it’s rotated (stale tokens become attack vectors)

Let’s dig into each.

Token strength: A weak token is one that’s predictable or short. You want at least 32 bytes (256 bits) of cryptographically random data. Converted to hex, that’s a 64-character string. Anything shorter is not enough.

# Generate a proper token
openssl rand -hex 32  # Outputs 64-char hex string

# Don't do this
# your-secret-token-here  # Too short, not random

# Do this
# a7f3e8c4b2d9e1f6a4c7b5d9e2f4c8a1b3d6e9f2c5a8b1d4e7f9c2a5b8d1

Token transmission: Tokens are Bearer tokens. If you transmit them over HTTP (unencrypted), anyone on the network can sniff them. If you transmit them in URL parameters, they appear in server logs and browser history. Always use HTTPS, and always use the Authorization header.

Token storage: This is critical. Where do you store the token? If it’s in the config file and that file is committed to git, it’s compromised forever. If it’s in environment variables, it’s visible in /proc/[pid]/environ. If it’s in a log file, attackers can find it by searching /var/log.

Token rotation: Tokens that never expire become permanent attack vectors. If a token is leaked today, and you don’t rotate for six months, the attacker has six months of access. Rotate tokens regularly—every 24-72 hours for high-security deployments.

2. Gateway Passwords (gateway.auth.password)

For dashboard access (the web UI), OpenClaw also supports HTTP Basic Auth:

gateway:
  auth:
    password: "hashed-password-bcrypt-format"

The dashboard requires this password. You send it in plaintext over HTTPS, the gateway validates it against a bcrypt hash.

The Security Model: Password-based auth is stateful. The gateway maintains a session token (typically a cookie) after successful authentication. Subsequent requests use the session, not the raw password.

The Problem: Passwords are weaker than tokens if they’re human-memorized. But in an OpenClaw context, they’re usually strong random strings (32+ characters), so the weakness is in:

  1. Credential storage (if the config file is exposed)
  2. Session hijacking (if cookies are stolen or not properly secured)
  3. Network exposure (if Basic Auth is sent over HTTP instead of HTTPS)

Here’s something people don’t think about: if you’re using HTTP Basic Auth, the password is sent in the Authorization header in base64. Base64 is not encryption. It’s just encoding. Anyone who sees the HTTP request can decode it immediately:

# Basic auth is sent like this:
Authorization: Basic YWRtaW46cGFzc3dvcmQxMjM=

# Attacker decodes it:
echo YWRtaW46cGFzc3dvcmQxMjM= | base64 -d
# Output: admin:password123

# They've got your credentials.

This is why HTTPS is non-negotiable for Basic Auth. The password is only safe if the transport layer is encrypted.

3. Network Binding (localhost vs LAN vs Tailnet)

This is where the real subtlety lives. You can configure which networks the gateway listens on:

gateway:
  host: "127.0.0.1" # Loopback only (default)
  port: 18789

or

gateway:
  host: "0.0.0.0" # All interfaces (dangerous)
  port: 18789

or

gateway:
  host: "10.0.0.5" # Specific LAN interface
  port: 18789

The Security Model: Network binding is about controlling who can even reach the gateway. If it’s on loopback, only local processes can connect. If it’s on a LAN interface, anyone on that network can attempt connection. If it’s on 0.0.0.0, anyone on the internet can attempt connection.

The Problem: This is where we hit the browser-pivot attack, and it’s subtle enough that most people miss it entirely.

The Browser-Pivot Attack (Why Loopback Isn’t Enough)

Here’s the scenario:

  1. You’ve hardened OpenClaw beautifully. Isolated subnet, dedicated user, strong tokens, locked-down IAM. Gateway listens on 127.0.0.1:18789.
  2. An attacker gains shell access to the box (compromised SSH key, vulnerability in another service, whatever).
  3. They know OpenClaw is running. They open a browser on the machine.
  4. They navigate to https://automateanddeploy.com:18789.
  5. The gateway responds. The dashboard loads.
  6. If the dashboard is served with authentication but the attacker is local, they can often bypass it or steal the session token from browser storage (localStorage, cookies).

This is the browser-pivot attack. Loopback network binding prevents remote attackers from reaching the gateway. It does not prevent local attackers (who already have shell access) from interacting with it.

The attacker can:

  • Inspect browser storage and extract API tokens
  • Use browser developer tools to make authenticated API requests
  • Screenshot the dashboard and read sensitive agent context
  • Open a separate terminal and use curl to interact with the API (if the token is stored in a config file)

Real-world example: An attacker has shell access. They run:

# Attacker is on the compromised box
cd /opt/openclaw
cat config.yaml | grep token

# Output: token: "a7f3e8c4b2d9e1f6a4c7b5d9e2f4c8a1b3d6e9f2c5a8b1d4e7f9c2a5b8d1"

# Now they can invoke agents
curl -H "Authorization: Bearer a7f3e8c4b2d9e1f6a4c7b5d9e2f4c8a1b3d6e9f2c5a8b1d4e7f9c2a5b8d1" \
  https://automateanddeploy.com:18789/api/v1/agents/invoke \
  -d '{"agent": "dangerous-skill", "params": {...}}'

They’ve compromised the gateway without even touching the network layer. The local filesystem was the attack vector.

Mitigation: Loopback binding is necessary but not sufficient. You also need:

  1. Disable dashboard access entirely if it’s not needed
  2. Require additional authentication (mTLS, OS-level permissions)
  3. Bind to an interface that’s only accessible via Tailscale/VPN
  4. Store tokens in a secure enclave, not the filesystem
  5. Implement OS-level process isolation (SELinux, AppArmor)

Understanding Threat Models

Before we configure, we need to understand what threat we’re actually defending against. Different deployment contexts have different threat models.

Low Threat Model (Local Dev Machine):

  • You control the box entirely
  • No untrusted code runs on it
  • Network is your home WiFi or isolated dev lab
  • Risk: Accidentally exposing credentials, careless configuration, accidental sharing

This is the environment where most developers first encounter OpenClaw. You’re experimenting, learning, iterating quickly. The primary risks are self-inflicted: storing credentials in git, leaving passwords in shell history, sharing the dev machine with someone who shouldn’t have access. The security posture here should be: “strong enough that I don’t leak credentials if I’m not careful, but flexible enough that I can iterate quickly without friction.” You don’t need mTLS or JWT; you need sensible defaults that protect you from your own mistakes.

Medium Threat Model (Team Server):

  • Multiple developers have SSH access
  • Some non-critical services run on the box (CI/CD, monitoring, logs)
  • Network is corporate LAN (behind firewall, but shared infrastructure)
  • Risk: Accidental privilege escalation, malicious insider, network compromise, service-to-service attacks

This is where most teams hit the wall. You’re past personal development. Multiple people need access. The box is shared, but it’s still somewhat trusted (it’s on corporate infrastructure). The risks escalate: if one service is compromised, can it reach OpenClaw? If a dev’s SSH key is stolen, what can they do? If someone makes a mistake with file permissions, what gets exposed? This is where you start needing real access control, not just “local = secure.”

High Threat Model (Production Box):

  • Multiple services compete for resources
  • Untrusted code may run (containers, deployed functions, third-party integrations)
  • Network is public or semi-public (internet-facing, cloud infrastructure)
  • Risk: Container escape, kernel vulnerability, supply-chain attack, sophisticated attackers, compliance requirements

This is the environment where you’re operating critical infrastructure. The assumption is that something else on the box will be compromised at some point. Your job is to make sure that compromise doesn’t automatically spread to OpenClaw. This requires defense-in-depth, comprehensive logging, incident response capabilities, and compliance auditing. You’re not trying to prevent all attacks; you’re trying to make attacks expensive and detectable.

Matching your configuration to your threat model:

Applying high-threat-model security to a dev machine is overkill and makes iteration painful. Applying low-threat-model security to production is negligent. The key is matching your configuration to your actual threat context. For each threat model, we have specific configuration profiles.

Best Practice Configurations

Let’s walk through three deployment profiles.

Profile A: Secure Local Development (Minimal Threat Model)

You’re running OpenClaw on your workstation for development. You control physical access and there’s no untrusted code running on the box.

gateway:
  host: "127.0.0.1"
  port: 18789
  auth:
    token: "dev-token-change-in-prod"
    password: "dev-password-change-in-prod"
    token_expiration_hours: 24
    require_https: false # Acceptable for loopback; HTTP is fine here
  tls:
    enabled: false
  dashboard:
    enabled: true

Rationale: Loopback binding is sufficient. Weak credentials are acceptable because there’s no network exposure. But get these credentials out of the box before moving to production.

Verification:

curl -H "Authorization: Bearer dev-token-change-in-prod" \
  https://automateanddeploy.com:18789/api/v1/agents
# Should return agent list

# Unauthenticated request should fail
curl https://automateanddeploy.com:18789/api/v1/agents
# Should return 401 Unauthorized

Profile B: Secure Server Deployment (High Threat Model)

OpenClaw runs on a dedicated server in your infrastructure. Untrusted code might run on the same box. Network isolation isn’t complete.

gateway:
  host: "127.0.0.1" # Loopback only
  port: 18789
  auth:
    token: "$(GATEWAY_TOKEN)" # From secrets manager
    password: "$(GATEWAY_PASSWORD_BCRYPT)"
    token_expiration_hours: 4 # Short-lived tokens
    require_https: true
    token_rotation_interval_hours: 24
  tls:
    enabled: true
    cert_file: "/etc/openclaw/gateway.crt"
    key_file: "/etc/openclaw/gateway.key"
    client_auth: "required" # mTLS
    client_ca_file: "/etc/openclaw/client-ca.crt"
  ratelimit:
    enabled: true
    requests_per_second: 10
    burst: 20
  cors:
    enabled: false # Disable if not needed
  logging:
    level: "debug"
    redact_tokens: true
  dashboard:
    enabled: false # Disable dashboard in production

Rationale:

  • Loopback binding prevents network access
  • TLS encrypts in-transit tokens
  • mTLS (mutual TLS) requires client certificates; even if an attacker steals a token, they can’t use it without the cert
  • Short token expiration (4 hours) limits the window if a token is leaked
  • Token rotation every 24 hours ensures old tokens don’t accumulate
  • Rate limiting prevents brute-force attacks
  • Redacted logging prevents tokens from appearing in logs
  • Dashboard disabled because production shouldn’t have a UI

mTLS Deep Dive: Mutual TLS means both the client and server authenticate. Not just the client trusting the server certificate, but the server verifying the client’s certificate. This means:

# Client needs a certificate signed by the CA
# Server has the CA certificate and verifies it

# Client connects:
curl -H "Authorization: Bearer $TOKEN" \
  --cert /path/to/client.crt \
  --key /path/to/client.key \
  --cacert /etc/openclaw/ca.crt \
  https://localhost:18789/api/v1/agents

If an attacker steals the token, they can’t use it because they don’t have the client certificate. This is defense in depth.

Credential Rotation Procedure:

# Generate new token
NEW_TOKEN=$(openssl rand -hex 32)

# Store in secrets manager
aws secretsmanager update-secret \
  --secret-id openclaw/gateway-token \
  --secret-string "$NEW_TOKEN"

# Reload OpenClaw (or use hot-reload if supported)
systemctl restart openclaw

# Verify
curl -H "Authorization: Bearer $NEW_TOKEN" \
  --cert /etc/openclaw/client.crt \
  --key /etc/openclaw/client.key \
  --cacert /etc/openclaw/ca.crt \
  https://localhost:18789/api/v1/agents

Profile C: Tailnet-Bound Gateway (Multi-Location Deployment)

OpenClaw needs to be accessed from multiple locations securely. You’re using Tailscale for network isolation.

gateway:
  host: "100.100.100.1" # Tailscale IP (your custom derp server or standard tailnet IP)
  port: 18789
  auth:
    token: "$(GATEWAY_TOKEN)"
    password: "$(GATEWAY_PASSWORD_BCRYPT)"
    token_expiration_hours: 2
    require_https: true
    token_rotation_interval_hours: 12
  tls:
    enabled: true
    cert_file: "/etc/openclaw/gateway.crt"
    key_file: "/etc/openclaw/gateway.key"
    # Use Let's Encrypt with DNS validation for tailnet domain
  rate_limit:
    enabled: true
    requests_per_second: 10
  logging:
    level: "info"
    redact_tokens: true
    syslog:
      enabled: true
      facility: "local0"
  dashboard:
    enabled: true
    session_timeout_minutes: 15
    require_password_on_every_login: false

Rationale:

  • Tailscale network binding means only authenticated Tailnet members can reach the gateway
  • Adds an additional layer: network auth (Tailscale) + gateway auth (token)
  • Shorter token expiration and rotation because you’re more exposed to the network
  • TLS with proper certificates for the tailnet domain
  • All access is logged to syslog for centralized monitoring
  • Dashboard enabled because Tailnet members are semi-trusted
  • Session timeouts prevent unattended sessions from being hijacked

Setup:

# Install Tailscale
curl -fsSL https://tailscale.com/install.sh | sh

# Authenticate to Tailnet
sudo tailscale up --auth-once

# Assign a static Tailnet IP or use the auto-assigned one
tailscale ip -4

# Configure OpenClaw to listen on that IP
# (Use the Tailscale IP from above, e.g., 100.x.x.x)

# Now, team members on your Tailnet can access:
# https://openclaw.your-tailnet.ts.net:18789/dashboard
# (Make sure DNS is configured for the Tailnet domain)

This approach gives you the best of both worlds: network isolation (Tailscale) and application-level authentication (tokens). Even if someone breaks into the Tailnet, they still need to authenticate to OpenClaw.

The Token Storage Problem

Here’s a hidden gotcha: where do you store the token? This is where most deployments fail.

Wrong Way 1: In the config file (committed to git)

gateway:
  auth:
    token: "super-secret-token-12345"

If you commit this, it’s in git history forever. Revoking it requires rotating all tokens and restarting all clients. And if the git repo is pushed to GitHub, it’s public. GitHub will scan it and warn you, but the damage is done—the token has been in public for minutes or hours.

Wrong Way 2: In environment variables (visible in ps)

export GATEWAY_TOKEN="super-secret-token-12345"
openclaw start

Any process on the box can read /proc/[pid]/environ and extract it. No special privileges required.

# Attacker does this:
cat /proc/12345/environ | grep GATEWAY_TOKEN
# Attacker has it.

Wrong Way 3: In shell history

# User runs:
export GATEWAY_TOKEN="super-secret-token-12345"

# Attacker reads shell history:
cat ~/.bash_history | grep GATEWAY_TOKEN
# Token is compromised.

Right Way 1: Secrets manager with restricted IAM

# Fetch from secrets manager at startup (one-time)
GATEWAY_TOKEN=$(aws secretsmanager get-secret-value \
  --secret-id openclaw/gateway-token \
  --query SecretString \
  --output text)

# Pass to OpenClaw via stdin or a temporary file with mode 0600
echo "$GATEWAY_TOKEN" | openclaw start --token-stdin

# Clear the variable immediately
unset GATEWAY_TOKEN

This way, the token is fetched from the secrets manager at runtime, passed to OpenClaw via stdin, and never stored on disk or in environment variables.

Right Way 2: Locked-down config file with restricted permissions

# Create config with 0600 permissions (readable only by the openclaw user)
echo "token: $(openssl rand -hex 32)" > /opt/openclaw/config.yaml
chmod 600 /opt/openclaw/config.yaml
chown openclaw:openclaw /opt/openclaw/config.yaml

# Verify only the openclaw user can read it
ls -la /opt/openclaw/config.yaml
# Should show: -rw------- 1 openclaw openclaw ...

# Even root can't read it without su'ing to the openclaw user
# (Well, technically root can read anything, but this makes it explicit)

Right Way 3: OS-level secret storage (macOS Keychain, Linux Secret Service)

On macOS, use Keychain:

# Store token in Keychain
security add-generic-password -a openclaw -s gateway-token -w "your-token-here"

# Retrieve token
security find-generic-password -a openclaw -s gateway-token -w

On Linux with systemd user units:

# Use systemd credential storage
systemctl set-environment GATEWAY_TOKEN=$(openssl rand -hex 32)

# OpenClaw reads it from the systemd environment

The principle is: secrets should not live on the filesystem as plaintext. They should live in a secrets manager, a secure enclave, or OS-level credential storage.

Monitoring and Alert Rules

Set up alerts for authentication anomalies:

alerts:
  - name: "Failed gateway auth attempts"
    condition: "gateway_auth_failures > 5 in 5m"
    severity: "high"
    action: "page_oncall"

  - name: "Gateway token rotation overdue"
    condition: "token_last_rotated_hours > 24"
    severity: "medium"
    action: "notify_slack"

  - name: "Unusual gateway API activity"
    condition: "requests_from_ip != expected_cidrs"
    severity: "high"
    action: "page_oncall"

  - name: "TLS certificate expiration warning"
    condition: "tls_cert_expiry_days < 30"
    severity: "medium"
    action: "notify_slack"

  - name: "Token stolen or reused from unexpected location"
    condition: "same_token_from_different_ip"
    severity: "critical"
    action: "page_oncall_immediately"

  - name: "Dashboard password failures"
    condition: "dashboard_auth_failures > 10 in 5m"
    severity: "medium"
    action: "temporarily_lock_dashboard"

In your log aggregation system (ELK, Splunk, etc.):

# Query: All failed authentication attempts
gateway_auth_failures
| stats count by source_ip, timestamp
| where count > 3 in 5min

# Query: Token usage patterns (detect stolen tokens)
gateway_api_requests
| stats count by token_hash, source_ip
| where source_ip changes unexpectedly

# Query: Dashboard access (detect unauthorized UI access)
gateway_dashboard_access
| where auth_method == "password"
| stats count by session_id, user_agent

# Query: TLS handshake failures (detect certificate issues)
gateway_tls_handshake_failures
| stats count by error_reason
| where count > 0

The last one is important: if TLS handshakes are failing, it might be a legitimate misconfiguration, or it might be someone trying to connect without the proper client certificate.

The Incident Response Playbook

If you suspect gateway authentication is compromised:

1. Immediately rotate the token and password:

NEW_TOKEN=$(openssl rand -hex 32)
aws secretsmanager update-secret \
  --secret-id openclaw/gateway-token \
  --secret-string "$NEW_TOKEN"
systemctl restart openclaw

Do this within seconds. If you wait minutes, the attacker has more time to use the stolen token.

2. Check the logs for unauthorized API calls:

grep "gateway_api" /var/log/openclaw/*.log | \
  grep -v "expected_token_hash" | \
  head -20

Look for API calls that shouldn’t have happened. Invoked skills, agents spawned, configurations modified.

3. Review agent activity during the compromise window:

# Which skills were invoked?
grep "skill_invocation" /var/log/openclaw/*.log | \
  jq '.timestamp, .skill_name, .agent_id' | \
  sort -u

This tells you what damage was done. If a “delete-all-data” skill was invoked, you know to look for data loss.

4. Rotate all credentials the agents touched:

Database passwords, API keys, SSH credentials—everything that agent had access to. If an agent can access your database, and the gateway is compromised, the database is compromised.

5. Deploy a security update to prevent re-compromise:

Fix the vulnerability that allowed the breach (patch a vulnerable dependency, close an open port, enable a firewall rule, etc.). Don’t just rotate credentials and assume you’re safe. Find the root cause and fix it.

6. Notify stakeholders:

If sensitive data was accessed or modified, you may have legal obligations to notify users or customers. Do this immediately.

The Philosophy

Gateway authentication is defense-in-depth. Loopback binding prevents network access. Tokens prevent unauthorized API calls. mTLS prevents token replay. Short expiration and rotation limit the blast radius of compromise.

But here’s the uncomfortable truth: if an attacker has shell access, they will eventually find a way to interact with the gateway. Token storage, process inspection, session hijacking—there are multiple vectors.

The goal isn’t to make it impossible (it’s not). The goal is to make it difficult and detectable. Every additional layer adds delay and creates an audit trail. By the time they’ve stolen a token, rotated it, used it to invoke a skill, and exfiltrated data, you’ve (hopefully) detected it via logging and rate limiting.

That’s why comprehensive logging and alerts are non-negotiable. Gateway authentication is only as strong as your monitoring.

Token Generation Best Practices

Here’s a concrete example of generating, storing, and rotating tokens properly.

Initial token generation:

# Generate a cryptographically strong token
TOKEN=$(openssl rand -hex 32)
echo "$TOKEN"
# Output: a7f3e8c4b2d9e1f6a4c7b5d9e2f4c8a1b3d6e9f2c5a8b1d4e7f9c2a5b8d1

# Store in AWS Secrets Manager
aws secretsmanager create-secret \
  --name openclaw/gateway-token \
  --secret-string "$TOKEN"

# Or in Vault
vault kv put secret/openclaw/gateway token="$TOKEN"

# Clear the variable immediately
unset TOKEN

Rotation workflow (monthly):

#!/bin/bash
# monthly-token-rotation.sh

# Generate new token
NEW_TOKEN=$(openssl rand -hex 32)

# Update secret
aws secretsmanager update-secret \
  --secret-id openclaw/gateway-token \
  --secret-string "$NEW_TOKEN"

# Reload OpenClaw (gracefully)
systemctl reload openclaw

# Wait for connections to drain (30 seconds)
sleep 30

# Hard restart if needed
systemctl restart openclaw

# Verify
curl -H "Authorization: Bearer $NEW_TOKEN" \
  https://localhost:18789/api/v1/agents

echo "Token rotated successfully"

Token expiration handling:

If you set token_expiration_hours: 4, old tokens stop working after 4 hours. This forces clients to re-authenticate:

# Client-side handling
TOKEN=$(curl -s https://auth-service/token \
  -H "Authorization: Bearer $OLD_TOKEN")

# Use new TOKEN
curl -H "Authorization: Bearer $TOKEN" \
  https://localhost:18789/api/v1/agents

Dashboard Security Deep Dive

The dashboard is a web UI. It’s vulnerable to different attacks than the API.

CSRF (Cross-Site Request Forgery):

An attacker tricks you into visiting their website, which makes requests to the dashboard without you knowing. Mitigation:

gateway:
  dashboard:
    csrf_protection: enabled
    csrf_token_length: 32
    same_site_cookie: "strict" # Only send cookie with same-site requests

XSS (Cross-Site Scripting):

An attacker injects malicious JavaScript into the dashboard. Mitigation:

gateway:
  dashboard:
    csp_headers: true # Content Security Policy
    x_frame_options: "DENY" # Prevent clickjacking
    x_content_type_options: "nosniff"

Session hijacking:

An attacker steals your session cookie. Mitigation:

gateway:
  dashboard:
    secure_cookies: true # HTTPS only
    httponly_cookies: true # Not accessible via JavaScript
    session_timeout_minutes: 15
    regenerate_session_on_login: true

Example full dashboard security config:

gateway:
  dashboard:
    enabled: true
    auth_method: "password" # Or "oauth" if integrated with SSO

    # Session management
    session:
      timeout_minutes: 15
      regenerate_on_login: true
      secure: true # HTTPS only
      httponly: true # Prevent XSS access
      samesite: "strict" # CSRF protection

    # Content security
    headers:
      content_security_policy: "default-src 'self'"
      x_frame_options: "DENY"
      x_content_type_options: "nosniff"
      x_xss_protection: "1; mode=block"
      referrer_policy: "strict-origin-when-cross-origin"

    # UI behavior
    ui:
      logout_on_inactivity: true
      confirm_dangerous_actions: true
      mask_sensitive_data: true
      audit_all_actions: true

Browser-Pivot Attack Mitigation Strategies

We talked about the attack. Here are concrete mitigations beyond loopback binding.

Strategy 1: Disable the dashboard entirely

gateway:
  dashboard:
    enabled: false # API-only mode

If you only use the API (via CLI or integration), disable the web dashboard. One fewer attack surface.

Strategy 2: OS-level access control

Only the openclaw user can read the gateway config:

# Gateway config file
-rw------- 1 openclaw openclaw /etc/openclaw/gateway.yaml

# Only openclaw user can read credentials

Even if an attacker gets shell access as a different user, they can’t read the config.

Strategy 3: Separate credentials for API vs Dashboard

Use different tokens for API and different passwords for dashboard. If one is compromised, the other stays safe:

gateway:
  auth:
    api:
      token: "api-token-separate"
      token_expiration_hours: 4
    dashboard:
      password: "dashboard-password-separate"
      session_timeout_minutes: 15

Now an attacker who steals the dashboard password can’t use the API. They’re limited to the dashboard.

Strategy 4: IP-based dashboard restriction

gateway:
  dashboard:
    allowed_ips:
      - "127.0.0.1" # Localhost only
      - "192.168.1.100" # Specific trusted machine

If someone gains shell access from a different IP, they can’t reach the dashboard.

Token Revocation and Blocklist Management

What happens when you suspect a token is compromised, but you can’t wait for its expiration?

Immediate revocation:

# Blocklist the token (if your system supports it)
# OR just restart OpenClaw immediately
systemctl restart openclaw

# Generate a new token
NEW_TOKEN=$(openssl rand -hex 32)
aws secretsmanager update-secret \
  --secret-id openclaw/gateway-token \
  --secret-string "$NEW_TOKEN"

# Notify all clients to update their credentials
curl -X POST https://notify-service/broadcast \
  -d '{"message": "Gateway token rotated. Update your config."}'

Persistent blocklist (for longer-term tracking):

# In OpenClaw config
gateway:
  auth:
    token_blocklist:
      - "old-compromised-token-1"
      - "old-compromised-token-2"
    blocklist_check: true # Always check before accepting token

Any token in the blocklist is immediately rejected, even if it’s not expired.

Multi-Factor Authentication for Dashboard

If you’re really paranoid (good!), add MFA to the dashboard:

gateway:
  dashboard:
    auth_method: "password_with_mfa"
    mfa:
      provider: "totp" # Time-based OTP (Google Authenticator, etc.)
      backup_codes: 10
      require_for_dangerous_actions: true

Now even if an attacker steals your password, they need your TOTP device to log in.

Forensics and Post-Incident Analysis

After a security incident, you need to understand what happened.

Log preservation:

# Copy logs to external storage before rotating
cp /var/log/openclaw/*.log /backup/openclaw-logs/$(date +%Y%m%d)/

# Compress and archive
tar czf /backup/openclaw-logs-$(date +%Y%m%d).tar.gz /backup/openclaw-logs/

Key questions to answer:

# 1. When was the token compromised?
grep "auth_failure" /var/log/openclaw/*.log | tail -20

# 2. What API endpoints were called?
grep "api_request" /var/log/openclaw/*.log | jq '.endpoint' | sort | uniq -c

# 3. Which agents were invoked?
grep "skill_invocation" /var/log/openclaw/*.log | jq '.skill_name, .timestamp'

# 4. What data was accessed?
grep "data_access" /var/log/openclaw/*.log | jq '.resource, .action'

# 5. Any data exfiltration?
grep -E "large_response|data_download" /var/log/openclaw/*.log

Real-World Deployment Walkthrough

Let’s tie this together with a concrete deployment scenario: a production OpenClaw server serving multiple teams.

Requirements:

  • API access from internal tools and scripts
  • Dashboard access for operators (with audit logging)
  • Monitoring and alerting
  • Regular credential rotation
  • Compliance with security policies

Step 1: Set up secrets management

# Using AWS Secrets Manager
AWS_REGION=us-east-1

# Create secrets
aws secretsmanager create-secret \
  --name openclaw/prod/gateway-token \
  --secret-string "$(openssl rand -hex 32)" \
  --region $AWS_REGION

aws secretsmanager create-secret \
  --name openclaw/prod/gateway-password \
  --secret-string "$(openssl rand -base64 32)" \
  --region $AWS_REGION

# Optionally add a description for audit purposes
aws secretsmanager update-secret \
  --secret-id openclaw/prod/gateway-token \
  --description "API token for production OpenClaw gateway" \
  --add-replica-regions RegionList='[{RegionName=us-west-2}]'

Step 2: Deploy with secure credential injection

#!/bin/bash
# deploy-openclaw.sh

set -e

# Fetch secrets at deployment time
GATEWAY_TOKEN=$(aws secretsmanager get-secret-value \
  --secret-id openclaw/prod/gateway-token \
  --query SecretString \
  --output text)

GATEWAY_PASSWORD=$(aws secretsmanager get-secret-value \
  --secret-id openclaw/prod/gateway-password \
  --query SecretString \
  --output text)

# Create temporary config file (mode 0600)
CONFIG_FILE=$(mktemp)
chmod 600 "$CONFIG_FILE"

cat > "$CONFIG_FILE" << EOF
gateway:
  host: "127.0.0.1"
  port: 18789
  auth:
    token: "$GATEWAY_TOKEN"
    password: "$GATEWAY_PASSWORD"
    token_expiration_hours: 4
    require_https: true
  tls:
    enabled: true
    cert_file: /etc/openclaw/gateway.crt
    key_file: /etc/openclaw/gateway.key
  ratelimit:
    enabled: true
    requests_per_second: 20
  logging:
    level: debug
    redact_tokens: true
EOF

# Start OpenClaw with config
systemctl stop openclaw || true
cp "$CONFIG_FILE" /etc/openclaw/gateway.conf
chown openclaw:openclaw /etc/openclaw/gateway.conf
chmod 600 /etc/openclaw/gateway.conf
systemctl start openclaw

# Clean up
rm -f "$CONFIG_FILE"
unset GATEWAY_TOKEN GATEWAY_PASSWORD

echo "Deployment complete"

Step 3: Set up monitoring

# prometheus-alerts.yaml
groups:
  - name: openclaw_gateway
    rules:
      - alert: GatewayAuthFailures
        expr: rate(gateway_auth_failures_total[5m]) > 10
        for: 5m
        annotations:
          summary: "High authentication failure rate on gateway"
          runbook: "Check if credentials are correct, token rotated, or if there's an attack"

      - alert: TokenRotationOverdue
        expr: (time() - gateway_token_last_rotation_seconds) > 86400
        annotations:
          summary: "Gateway token hasn't been rotated in >24 hours"
          runbook: "Rotate token immediately using token-rotation script"

      - alert: UnauthorizedIPAccess
        expr: gateway_api_requests_from_unexpected_source > 0
        annotations:
          summary: "API requests from unexpected IP address"
          runbook: "Investigate source IP, may indicate compromised credential"

      - alert: TLSCertExpiringSoon
        expr: (tls_cert_not_after_seconds - time()) < 2592000
        annotations:
          summary: "TLS certificate expires within 30 days"
          runbook: "Renew certificate and redeploy"

Step 4: Automate token rotation

#!/bin/bash
# token-rotation-cronjob.sh
# Run daily via cron: 0 2 * * * /opt/openclaw/token-rotation-cronjob.sh

set -e

# Log to syslog
exec 1> >(logger -s -t openclaw-token-rotation)
exec 2>&1

echo "Starting token rotation..."

# Generate new token
NEW_TOKEN=$(openssl rand -hex 32)

# Store in secrets manager
aws secretsmanager update-secret \
  --secret-id openclaw/prod/gateway-token \
  --secret-string "$NEW_TOKEN" \
  --region us-east-1

# Signal OpenClaw to reload
systemctl reload openclaw

# Wait for reload to complete
sleep 10

# Test new token
HEALTH=$(curl -s -H "Authorization: Bearer $NEW_TOKEN" \
  https://localhost:18789/api/v1/health || echo "failed")

if [[ "$HEALTH" == *"ok"* ]]; then
  echo "Token rotation successful"

  # Send notification to Slack
  curl -X POST "$SLACK_WEBHOOK" \
    -d '{"text": "OpenClaw gateway token rotated successfully"}'
else
  echo "Token rotation FAILED - reverting"
  # Revert to previous token (stored separately)
  systemctl restart openclaw
  exit 1
fi

Step 5: Audit access logs

#!/bin/bash
# audit-gateway-access.sh
# Run weekly

echo "=== Gateway API Access Summary ==="
grep "gateway_api_request" /var/log/openclaw/*.log | \
  jq -r '.timestamp, .source_ip, .endpoint, .method, .auth_method, .status' | \
  column -t

echo ""
echo "=== Authentication Failures ==="
grep "auth_failure" /var/log/openclaw/*.log | \
  jq -r '.timestamp, .source_ip, .reason' | \
  column -t

echo ""
echo "=== Suspicious Activity ==="
grep -E "rate_limit|token_rejected|ip_blocklist" /var/log/openclaw/*.log | \
  jq -r '.timestamp, .event_type, .details' | \
  column -t

API Client Configuration Examples

Different clients have different ways to handle authentication. Here’s how to configure them properly.

cURL (command line):

# Store token in environment (insecure if in .bashrc!)
export OPENCLAW_TOKEN=$(aws secretsmanager get-secret-value \
  --secret-id openclaw/prod/gateway-token \
  --query SecretString --output text)

# Use token
curl -H "Authorization: Bearer $OPENCLAW_TOKEN" \
  --cacert /etc/openclaw/ca.crt \
  https://localhost:18789/api/v1/agents

# Better: fetch token each time
curl -H "Authorization: Bearer $(aws secretsmanager get-secret-value --secret-id openclaw/prod/gateway-token --query SecretString --output text)" \
  --cacert /etc/openclaw/ca.crt \
  https://localhost:18789/api/v1/agents

Python client:





# Fetch token from secrets manager
secrets_client = boto3.client('secretsmanager')
token = secrets_client.get_secret_value(
    SecretId='openclaw/prod/gateway-token'
)['SecretString']

# Make authenticated request
headers = {'Authorization': f'Bearer {token}'}
response = requests.get(
    'https://localhost:18789/api/v1/agents',
    headers=headers,
    verify='/etc/openclaw/ca.crt'
)

print(response.json())

Go client:

package main


    "context"
    "fmt"
    "github.com/aws/aws-sdk-go-v2/config"
    "github.com/aws/aws-sdk-go-v2/service/secretsmanager"
    "net/http"
)

func main() {
    // Load AWS config
    cfg, _ := config.LoadDefaultConfig(context.TODO())
    client := secretsmanager.NewFromConfig(cfg)

    // Fetch token
    result, _ := client.GetSecretValue(context.TODO(), &secretsmanager.GetSecretValueInput{
        SecretId: "openclaw/prod/gateway-token",
    })
    token := *result.SecretString

    // Make authenticated request
    req, _ := http.NewRequest("GET", "https://localhost:18789/api/v1/agents", nil)
    req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))

    httpClient := &http.Client{}
    resp, _ := httpClient.Do(req)
    defer resp.Body.Close()

    fmt.Println(resp.StatusCode)
}

Preventing Common Misconfiguration Mistakes

Most security breaches aren’t due to advanced attacks—they’re due to misconfiguration. Let’s walk through the most common mistakes and how to prevent them.

Mistake 1: Committing credentials to git

You commit a config file with the token. It’s in git history forever.

Prevention:

# Use .gitignore
echo "config.yaml" >> .gitignore
echo ".env" >> .gitignore

# Or use git-secrets to scan commits
brew install git-secrets
git secrets --install
git secrets --register-aws

# Only commit config templates
cp config.yaml config.yaml.template
# Edit template to remove secrets
git add config.yaml.template

Mistake 2: Weak token strength

Using short random strings (12 characters) instead of proper cryptographic tokens (32+ bytes).

Prevention:

# Always generate with cryptographic randomness
openssl rand -hex 32    # 64 hex chars = 256 bits. GOOD.
openssl rand -base64 32 # 44 chars. GOOD.

# Bad examples to avoid:
# "MySecret123" - too short
# "token123456789" - predictable
# UUIDs - not enough entropy

Mistake 3: Token in logs

Logging the full token in error messages or debug output.

Prevention:

# In OpenClaw config, enable token redaction
logging:
  redact_tokens: true  # Masks tokens in logs
  redact_patterns:
    - "Bearer [a-f0-9]{64}"
    - "token: [a-f0-9]{64}"

# When writing logs manually:
echo "Authorization: Bearer ****${TOKEN:(-8)}"  # Only show last 8 chars

Mistake 4: No HTTPS in production

Sending credentials over HTTP (even on loopback, if accessed remotely).

Prevention:

gateway:
  auth:
    require_https: true # Enforce HTTPS
  tls:
    enabled: true
    cert_file: /etc/openclaw/gateway.crt
    key_file: /etc/openclaw/gateway.key
    # HTTP requests get rejected

If you try to connect with HTTP:

curl https://automateanddeploy.com:18789/api/v1/agents
# Response: 403 Forbidden - HTTPS required

Mistake 5: Same token for API and dashboard

If one secret is compromised, both API and UI are compromised.

Prevention:

gateway:
  auth:
    api_token: "token-for-api-only"
    dashboard_password: "password-for-ui-only"

  # Or separate them entirely:
  api:
    auth:
      token: "api-token"

  dashboard:
    auth:
      password: "dashboard-password"

Now if the dashboard password leaks, the API is still secure.

Mistake 6: No token rotation schedule

Tokens that live forever become permanent attack vectors.

Prevention:

# Set up a rotation schedule
# Option 1: Cron job (automatic)
0 2 * * * /opt/openclaw/rotate-token.sh

# Option 2: Manual but tracked
# Keep a calendar of rotation dates

# Option 3: Policy-enforced
# Make token_expiration_hours non-configurable (hard-coded to 4 or 24)

Mistake 7: Overly permissive rate limiting

Allowing brute-force attacks by having weak rate limits.

Prevention:

gateway:
  ratelimit:
    enabled: true
    requests_per_second: 10 # Aggressive limit
    burst: 5 # Prevent burst attacks
    by_ip: true # Rate limit per source IP
    by_token: true # Rate limit per token

    # Lock account after N failures
    auth_failures:
      threshold: 5
      lockout_duration_minutes: 15

Now an attacker can only try 5 tokens every 15 minutes. Brute-forcing 10,000 tokens takes 30,000 minutes (21 days) without hitting a lockout.

Security Checklist: Before Going to Production

Before you deploy your gateway to production, go through this checklist:

## Pre-Production Security Checklist

### Credentials Management

- [ ] Token generated with cryptographic randomness (32+ bytes)
- [ ] Token stored in secrets manager, not in config files
- [ ] Token rotation script is working and tested
- [ ] Password hashed with bcrypt (not plaintext)
- [ ] No credentials in git history
- [ ] No credentials in environment variables
- [ ] Secrets manager has audit logging enabled

### Network & TLS

- [ ] Gateway binds to 127.0.0.1 (loopback only)
- [ ] TLS enabled with proper certificates
- [ ] Certificates are valid and not expired
- [ ] Client CA certificate for mTLS is in place
- [ ] HTTPS is required (no HTTP fallback)
- [ ] Firewall rules restrict access to gateway port

### Access Control

- [ ] Dashboard disabled if not needed
- [ ] Separate auth for API vs dashboard
- [ ] Rate limiting enabled
- [ ] Token blocklist implemented
- [ ] Session timeout is short (15 minutes or less)
- [ ] CSRF protection enabled for dashboard

### Logging & Monitoring

- [ ] Token redaction enabled in logs
- [ ] All auth failures logged
- [ ] All successful logins logged
- [ ] Alerts configured for auth anomalies
- [ ] Log aggregation is working
- [ ] Alerts are being delivered to on-call

### Testing

- [ ] Unauthenticated requests are rejected (401)
- [ ] Weak credentials are rejected
- [ ] Rate limits are enforced
- [ ] Token expiration works correctly
- [ ] TLS errors are handled gracefully
- [ ] Credential rotation doesn't cause downtime

### Documentation

- [ ] Token rotation procedure documented
- [ ] Emergency revocation procedure documented
- [ ] Incident response playbook created
- [ ] Run-books for common issues written

Print this, go through it methodically, and mark off each item before deploying to production.

Advanced: JWT Tokens Instead of Opaque Bearer Tokens

We’ve been talking about opaque bearer tokens (random 64-character strings). Some teams prefer JWT (JSON Web Tokens) which are self-contained and signed.

Why JWT?

  • Self-contained (no server-side lookup needed)
  • Can include claims (user ID, permissions, expiration)
  • Cryptographically signed (can’t be forged)
  • Good for distributed systems

Why not JWT?

  • Token payload is Base64-encoded (not encrypted) – anyone can read the claims
  • Can’t revoke a token immediately (it’s valid until expiration)
  • Slightly more complex to generate and validate

JWT configuration:

gateway:
  auth:
    token_type: jwt
    jwt:
      algorithm: HS256 # HMAC with SHA256
      secret: "your-jwt-signing-secret"
      expiration_seconds: 3600 # 1 hour
      claims:
        iss: "openclaw" # Issuer
        sub: "api-client" # Subject
        aud: "openclaw-gateway" # Audience

JWT payload example:

{
  "iss": "openclaw",
  "sub": "api-client",
  "aud": "openclaw-gateway",
  "exp": 1234567890,
  "iat": 1234567000,
  "permissions": ["read:agents", "invoke:skills"]
}

An attacker can read these claims (it’s just Base64), but can’t modify them without the signing secret.

Client-side JWT handling:




# Generate JWT
secret = "your-jwt-signing-secret"
payload = {
    "iss": "openclaw",
    "sub": "api-client",
    "aud": "openclaw-gateway",
    "exp": int(time.time()) + 3600,
    "iat": int(time.time()),
}
token = jwt.encode(payload, secret, algorithm="HS256")

# Use token
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(
    "https://localhost:18789/api/v1/agents",
    headers=headers
)

Important: JWT tokens can’t be revoked before expiration. If a token is stolen, it’s valid until expiration. This is why JWT is better for short-lived tokens (1-4 hours) and less ideal for long-lived ones.

For production OpenClaw, we recommend opaque bearer tokens with short expiration (4 hours) and regular rotation (every 24 hours). JWT is better for specific use cases like service-to-service communication.

Testing Your Gateway Security

Before relying on your gateway security, test it thoroughly.

Unit tests for auth logic:

# test_gateway_auth.py

from openclaw.gateway import auth

def test_valid_token_accepted():
    """Valid token should be accepted"""
    token = auth.generate_token()
    assert auth.validate_token(token) == True

def test_invalid_token_rejected():
    """Invalid token should be rejected"""
    assert auth.validate_token("invalid-token") == False

def test_expired_token_rejected():
    """Expired token should be rejected"""
    token = auth.generate_token(expiration_seconds=-1)
    assert auth.validate_token(token) == False

def test_token_rotation_works():
    """Token rotation should invalidate old token"""
    old_token = auth.get_current_token()
    auth.rotate_token()
    assert auth.validate_token(old_token) == False

def test_rate_limiting_enforced():
    """Rate limit should reject excessive requests"""
    limiter = auth.RateLimiter(requests_per_second=1)
    limiter.check_rate()  # First request
    with pytest.raises(auth.RateLimitExceeded):
        limiter.check_rate()  # Second request immediately after

Integration tests against running gateway:

#!/bin/bash
# test-gateway-integration.sh

GATEWAY_URL="https://localhost:18789"
TOKEN=$(aws secretsmanager get-secret-value --secret-id openclaw/gateway-token --query SecretString --output text)
CA_CERT="/etc/openclaw/ca.crt"

echo "Testing gateway security..."

# Test 1: Valid token accepted
echo -n "Test 1 (valid token): "
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
  -H "Authorization: Bearer $TOKEN" \
  --cacert $CA_CERT \
  $GATEWAY_URL/api/v1/agents)
if [ "$STATUS" = "200" ]; then echo "PASS"; else echo "FAIL ($STATUS)"; fi

# Test 2: Invalid token rejected
echo -n "Test 2 (invalid token): "
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
  -H "Authorization: Bearer invalid-token" \
  --cacert $CA_CERT \
  $GATEWAY_URL/api/v1/agents)
if [ "$STATUS" = "401" ]; then echo "PASS"; else echo "FAIL ($STATUS)"; fi

# Test 3: No token rejected
echo -n "Test 3 (no token): "
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
  --cacert $CA_CERT \
  $GATEWAY_URL/api/v1/agents)
if [ "$STATUS" = "401" ]; then echo "PASS"; else echo "FAIL ($STATUS)"; fi

# Test 4: HTTP (unencrypted) rejected
echo -n "Test 4 (HTTP not allowed): "
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
  -H "Authorization: Bearer $TOKEN" \
  https://automateanddeploy.com:18789/api/v1/agents 2>/dev/null || echo "connection-refused")
if [ "$STATUS" = "connection-refused" ] || [ "$STATUS" = "400" ]; then echo "PASS"; else echo "FAIL"; fi

# Test 5: Rate limiting works
echo -n "Test 5 (rate limiting): "
for i in {1..20}; do
  curl -s -H "Authorization: Bearer $TOKEN" \
    --cacert $CA_CERT \
    $GATEWAY_URL/api/v1/agents > /dev/null &
done
wait
echo "PASS (sent 20 requests, check logs for rate limit)"

Run these tests regularly, especially after configuration changes.

Understanding the Browser-Pivot Attack in Detail

We mentioned the browser-pivot attack earlier, but it deserves deeper explanation because it’s the attack most people miss.

The scenario:

An attacker has gained shell access to your server (not through the gateway—through some other vulnerability). They’re a limited user, no special privileges. But they want to compromise OpenClaw.

Step-by-step:

  1. Attacker opens a browser on the compromised server
  2. Navigates to https://automateanddeploy.com:18789
  3. Gateway responds (loopback allows it)
  4. Dashboard loads and prompts for authentication

At this point, there are multiple attack vectors:

Vector A: Brute-force the dashboard password

# Attacker writes a script to try common passwords
for password in admin password123 openclaw secret; do
  curl -X POST https://automateanddeploy.com:18789/login \
    -d "username=admin&password=$password"
done

Mitigation: Strong password, rate limiting, account lockout.

Vector B: Extract token from disk

# If config file is readable
cat /opt/openclaw/config.yaml | grep token

# Or from process environment
cat /proc/$(pgrep openclaw)/environ | grep TOKEN

Mitigation: Config file mode 0600, don’t put tokens in environment.

Vector C: Steal session cookie from browser storage

# If OpenClaw is running locally, attacker can:
# 1. Open browser DevTools (Cmd+Option+I)
# 2. Go to Application → Cookies → localhost:18789
# 3. Copy the session cookie
# 4. Use it from command line:

curl -H "Cookie: session=<stolen-cookie>" \
  https://automateanddeploy.com:18789/api/v1/agents

Mitigation: Secure cookies (HTTPS only), HTTPOnly flag (prevents JavaScript access), SameSite=Strict.

Vector D: Perform CSRF attacks

If the attacker can trick OpenClaw into making requests:

<!-- Attacker's page, if the user visits it while logged in -->
<img src="https://automateanddeploy.com:18789/api/v1/agents/invoke?agent=delete-all-data" />

The request happens in the user’s context with their session.

Mitigation: CSRF tokens, SameSite cookies.

Why loopback binding prevents remote attacks but not local attacks:

  • Remote attacker: Can’t reach loopback at all. Protected.
  • Local attacker (shell access): Can reach loopback. Not protected.

This is the key insight. Network isolation is one layer, but not sufficient if the attacker already has shell access.

Full defense against browser-pivot:

gateway:
  host: "127.0.0.1" # Loopback (prevents remote attacks)

  dashboard:
    enabled: false # If not needed, disable entirely

    # If you do enable it:
    auth:
      password: "strong-random-32-char-password"
      require_password: true

      # Session security
      session:
        timeout_minutes: 5 # Very short
        secure: true # HTTPS only
        httponly: true # Not accessible via JavaScript
        samesite: "strict" # CSRF protection

      # Additional protections
      require_mfa: true # TOTP two-factor
      lock_after_failures: 3
      lockout_duration_minutes: 15

  # API layer
  auth:
    token: "cryptographically-random-token"
    require_https: true

    # Prevent token theft from storage
    token_storage: "memory" # Not on disk

    # If token stolen and used:
    ratelimit:
      enabled: true
      requests_per_second: 5 # Aggressive

With these mitigations, even if an attacker has shell access:

  1. Dashboard is disabled → Can’t use web UI
  2. If API accessed, token is short-lived (4 hours) and rate-limited (5 req/s)
  3. Suspicious activity triggers alerts
  4. Log analysis shows exactly what happened

This is defense-in-depth: multiple layers so that no single compromise is catastrophic.

Threat Intelligence: Staying Ahead of Attacks

The threat landscape changes constantly. How do you stay informed about new attack patterns?

Subscribe to security bulletins:

  • CISA alerts (cisa.gov)
  • NVD (National Vulnerability Database)
  • API security mailing lists
  • Your cloud provider’s security bulletins (AWS, Azure, GCP)

Monitor your logs for patterns:

# Weekly log review script
#!/bin/bash
WEEK_AGO=$(date -d "7 days ago" +%Y-%m-%d)

echo "=== Authentication anomalies this week ==="
grep "auth_failure\|token_rejected\|rate_limit_exceeded" /var/log/openclaw/*.log | \
  grep -a "$WEEK_AGO" | \
  awk -F: '{print $1}' | sort | uniq -c | sort -rn

echo ""
echo "=== IP addresses with multiple failures ==="
grep "auth_failure" /var/log/openclaw/*.log | \
  grep -a "$WEEK_AGO" | \
  jq '.source_ip' | sort | uniq -c | sort -rn | head -10

echo ""
echo "=== Unusual API activity ==="
grep "api_request" /var/log/openclaw/*.log | \
  grep -a "$WEEK_AGO" | \
  jq '.endpoint, .method' | sort | uniq -c | sort -rn

Run this weekly and look for patterns that don’t match your normal usage.

Incident response readiness:

Have a playbook ready before an incident happens. It should include:

  1. Who to notify immediately (security team, on-call, manager)
  2. What actions to take in first 5 minutes (rotate token, block IPs, snapshot)
  3. What logs to preserve
  4. How to communicate with customers/team
  5. Post-incident review process

Practice this playbook quarterly with a mock incident drill.

Stay patched:

# Check for security updates to OpenClaw
docker pull openclaw:latest
docker inspect openclaw:latest | grep -i security

# Or use Dependabot/Renovate to watch for updates automatically

Final Thought

The gateway is the command center. Locking it down—properly—is non-negotiable. Not just with tokens or passwords, but with the full stack: strong credentials, network isolation, TLS, mTLS, token rotation, rate limiting, and comprehensive logging.

Do it right, and your OpenClaw deployment is a fortress. Do it half-way, and you’re inviting trouble. There’s no in-between.

Free Discovery Call

Start With a Conversation, Not a Commitment

Every engagement begins with a free 30-minute discovery call. We'll map what's slowing your business down and tell you exactly what we'd fix first – no pitch deck, no obligation.