Dan Levy's Avatar DanLevy.net

Your AI Assistant Gave Me Shell Access

How to secure your local or VPS OpenClaw/Moltbot setup

OpenClaw (previously Clawdbot/Moltbot) gives you a personal AI assistant that works across WhatsApp, Slack, Discord, iMessage, and other channels. But if you put its gateway, node controls, or SSH on the public internet without strong authentication, you’re giving strangers a path toward shell access on your machine.

This guide shows the safest default: keep OpenClaw’s gateway on loopback, expose it only to your tailnet with Tailscale Serve, lock down SSH, and verify from outside that the gateway is not public.

The project’s rapid adoption revealed real security concerns: Shodan scans found 2,847 exposed instances in the first few weeks, and a GitHub security audit issue reported 512 findings in the codebase. Some of that was automated scanner output and some has changed since the January 2026 rename to OpenClaw, so treat the number as a warning sign rather than a precise current vulnerability count. You don’t need to be a security expert—you just need to avoid publishing operator surfaces before you deploy.


What You’re Actually Exposing

Depending on how you installed and exposed it, there are three surfaces worth checking:

Current OpenClaw remote-access docs say the gateway WebSocket binds to loopback by default and recommend keeping it loopback-only unless you intentionally choose a LAN/tailnet/custom bind. That’s good. The risk shows up when you override that default, publish Docker ports, add a reverse proxy, turn on Funnel, or leave SSH open to the world.

The gateway is the big one. It’s the operator surface for your assistant, including tool invocation paths. If it is reachable from the internet and auth is missing, weak, bypassed, or leaked, an attacker may be able to drive the agent or invoke tools with your user’s permissions.

Browser control is almost as sensitive. Current OpenClaw docs recommend running browser control through a paired node host on the browser machine and treating node pairing like operator access. If a gateway can invoke system.run on a paired node, that’s remote code execution on that node, subject to the gateway’s node policy and the node’s own exec approvals.

SSH is SSH. If you’re running with password authentication enabled, brute force attempts are inevitable on a public VPS.


The Tailscale Solution

For OpenClaw, Tailscale gives you remote access without publishing operator services:

  1. Your OpenClaw instance runs on a VPS or local machine
  2. The gateway stays bound to loopback and is reached through Tailscale Serve, or it binds directly to the tailnet IP with explicit auth
  3. You install Tailscale on both the server and your personal devices
  4. You access OpenClaw through its Tailscale IP or MagicDNS name
  5. Everyone else on the internet sees nothing, unless you intentionally enable Funnel or another public proxy

Should You Let OpenClaw Manage Tailscale?

OpenClaw has built-in Tailscale integration that can configure tailscale serve or tailscale funnel for the gateway.

Serve mode keeps things on your tailnet only. The gateway stays bound to 127.0.0.1 while Tailscale handles routing and HTTPS. When gateway.auth.allowTailscale is enabled, OpenClaw can authenticate Control UI/WebSocket traffic using Tailscale identity headers and verify the source with tailscale whois. This is the right mode for most personal deployments.

Funnel mode exposes the gateway publicly through Tailscale’s public endpoint feature. Tailscale’s own docs describe Funnel as routing traffic from the broader internet to a local service. OpenClaw refuses to start Funnel unless gateway auth mode is password, but you’re still choosing public exposure for an operator surface.

OpenClaw’s security documentation is clear that prompt injection and tool access are core risks for a personal assistant. Do not give the agent a path to quietly make itself public. Use Serve deliberately, avoid Funnel unless you truly need public access, and require exec approval for any tailscale command.


Setting Up OpenClaw Securely

Step 1: Install Tailscale

On your VPS or local server:

Terminal window
# Install Tailscale
curl -fsSL https://tailscale.com/install.sh | sh
# Authenticate (opens a browser to log in)
sudo tailscale up
# Get your Tailscale IP
tailscale ip -4
# Output: 100.x.x.x

On your client machine, install Tailscale from the official download page and sign in to the same tailnet.

Now both machines are on the same private network. You can ping your VPS using its Tailscale IP, and it’ll route through the encrypted tunnel.

Step 2: Configure OpenClaw to Use Tailscale

The safest current pattern is: keep the gateway on loopback and expose it to your tailnet with Tailscale Serve.

In OpenClaw config:

{
gateway: {
bind: "loopback",
tailscale: { mode: "serve" },
},
}

Then start the gateway with Serve:

Terminal window
openclaw gateway --tailscale serve

OpenClaw’s docs say this keeps the gateway on 127.0.0.1 while Tailscale provides HTTPS and tailnet routing. You open it at https://<magicdns-name>/, not at your public VPS IP.

If you prefer a direct tailnet bind instead of Serve, use explicit gateway auth:

{
gateway: {
bind: "tailnet",
auth: {
mode: "token",
token: "replace-with-a-long-random-token",
},
},
}

Then connect from another tailnet device:

http://<tailscale-ip>:18789/
ws://<tailscale-ip>:18789

If you’re running in Docker or another container runtime, be extra careful with port publishing. A publish like -p 18789:18789 usually binds on all host interfaces. Prefer loopback plus Tailscale Serve, or bind the host side explicitly to the Tailscale IP after confirming the container still receives traffic:

Terminal window
TAILSCALE_IP=$(tailscale ip -4)
docker run ... -p "$TAILSCALE_IP:18789:18789" ...

After any Docker change, check from outside with nmap and locally with ss. Docker can bypass or reorder host firewall assumptions if you don’t account for it.

Step 3: Lock Down SSH

Even with Tailscale, you should secure SSH properly:

Terminal window
# Keep your current SSH session open while doing this.
# First, from your client machine, confirm you can SSH over Tailscale:
ssh your-user@SERVER_TAILSCALE_IP
# Put hardening in a drop-in file instead of rewriting sshd_config.
sudo tee /etc/ssh/sshd_config.d/99-openclaw-hardening.conf >/dev/null <<'EOF'
PasswordAuthentication no
PermitRootLogin no
KbdInteractiveAuthentication no
EOF
# Validate before reloading. Do not skip this.
sudo sshd -t
sudo systemctl reload ssh || sudo systemctl reload sshd

This disables password-based login and root login. The next step uses UFW to prevent public SSH entirely while still allowing SSH over tailscale0.

Step 4: Firewall Rules

Set up a firewall as a second layer:

Terminal window
# Using UFW (Ubuntu/Debian)
sudo ufw allow in on tailscale0
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw enable
sudo ufw delete allow 22/tcp || true
sudo ufw reload
sudo ufw status verbose

Tailscale’s own Ubuntu hardening guide uses this same shape: allow tailscale0, deny other inbound traffic, then verify that public SSH times out while SSH to the 100.x.y.z address still works. If you run a public website on the same VPS, keep only the public rules you truly need, such as 80/tcp and 443/tcp.


Checking Your Exposure

Check Open Ports from Outside

From a machine that’s NOT on your Tailscale network:

Terminal window
# Check if common public ports are exposed
nmap -p 22,80,443,18789 YOUR_PUBLIC_IP
# Expected output for a secured instance:
# 22/tcp filtered ssh
# 18789/tcp filtered unknown

If 22 or 18789 shows open instead of filtered or closed, you have a problem. If 80 or 443 is open, make sure that is only your intentional public website or Tailscale Funnel endpoint, not the OpenClaw gateway by accident.

Check What’s Listening Locally

On your OpenClaw server:

Terminal window
# Show all listening ports and what they're bound to
sudo ss -tulpn | grep LISTEN
# Look for lines like this (good for Serve):
# tcp LISTEN 0 128 127.0.0.1:18789 *:*
#
# Or this (acceptable for direct tailnet bind with auth):
# tcp LISTEN 0 128 100.x.y.z:18789 *:*
#
# NOT like this (bad):
# tcp LISTEN 0 128 0.0.0.0:18789 *:*

If you see 0.0.0.0 or ::: (IPv6 equivalent), that service is exposed to the world.

Built-in Security Audit

OpenClaw includes a security audit command that checks your configuration against security best practices:

Terminal window
openclaw security audit --deep
openclaw security audit --deep --fix

The audit checks gateway exposure, Tailscale mode, auth settings, channel access, tool policy, plugin inventory, and file permissions. Treat --fix as a useful helper, not a substitute for reading the findings.


What This Doesn’t Solve

Tailscale removes the biggest mistake: public operator exposure. It does not solve everything:

Credential storage: OpenClaw stores session transcripts, OAuth tokens, and API keys on disk. Ensure these have proper file permissions (chmod 600 for files, chmod 700 for private config directories) and aren’t in version control. The built-in audit checks for this.

Plugin sandboxing: Plugins run with your user’s full permissions. Only install plugins from sources you trust, and review what capabilities they request. The audit tool inventories installed plugins.

Device security: If someone compromises your Tailscale account or steals a device on your tailnet, they can access your OpenClaw instance. Enable Tailscale device authorization to require approval for new devices.


Deployment Checklist

Before you consider your OpenClaw/Moltbot instance production-ready:


Resources

Edit on GitHubGitHub