Skip to content

How I Secured My OpenClaw VPS: A Practical Hardening Guide

The Problem

I set up OpenClaw on my VPS to have my own AI assistant. Everything worked great until I checked my OAuth app permissions and realized something unsettling: my assistant had Mail.Send permission even though I only intended to give it read access.

On Reddit, someone reported the same issue - their AI assistant “has read access to mine [email] though weirdly it did send an email as me.” The assistant had sent an email promising not to do it again. This wasn’t malicious behavior - it was the AI trying to be helpful within the scope of permissions I accidentally gave it.

I realized I had made three critical mistakes:

  1. Running as root - My AI assistant had full system access
  2. Overly permissive OAuth - I granted Mail.Send when I only needed Mail.Read
  3. No logging - I had no visibility into what the AI was actually doing

This post shows how I fixed all three problems and created a proper security setup.

Step 1: Create a Dedicated Non-Root User

The first thing I did was stop running OpenClaw as root. This is the single most important security measure.

Create dedicated user
# Create a non-root user for OpenClaw
sudo useradd -m -s /bin/bash openclaw
sudo usermod -aG docker openclaw # Only if using Docker
# Switch to the user
sudo su - openclaw

Why does this matter? If OpenClaw runs as root and gets compromised (or just makes a mistake), it can:

  • Delete any file on the system
  • Install packages globally
  • Modify system configurations
  • Access all user data

Running as openclaw limits the damage to only what’s in /home/openclaw.

Here’s a simple diagram showing the difference:

┌─────────────────────────────────────────────────────────────┐
│ RUNNING AS ROOT │
├─────────────────────────────────────────────────────────────┤
│ OpenClaw ──► Full System Access ──► /etc, /var, /home/* │
│ ALL USERS │
│ │
│ Risk: ONE MISTAKE = TOTAL SYSTEM COMPROMISE │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ RUNNING AS openclaw USER │
├─────────────────────────────────────────────────────────────┤
│ OpenClaw ──► Limited Access ──► /home/openclaw ONLY │
│ │
│ Risk: ONE MISTAKE = ONLY USER DATA AFFECTED │
└─────────────────────────────────────────────────────────────┘

Step 2: Fix OAuth Scopes

My next problem was overly permissive OAuth scopes. I had blindly accepted default permissions when setting up the Azure AD app registration.

To fix this, I went through my Azure AD app and removed unnecessary permissions:

  1. Navigate to Azure Portal > Azure Active Directory > App registrations
  2. Select the OpenClaw app registration
  3. Go to “API permissions”
  4. Remove Mail.Send - I only need Mail.Read
  5. Grant admin consent for the reduced permission set

The principle here is simple: give the AI the minimum permissions it needs to do its job.

BEFORE:
┌─────────────────────────────────────┐
│ Mail.Read │
│ Mail.Send <-- NOT NEEDED │
│ User.Read.All <-- TOO BROAD │
│ Files.ReadWrite <-- DANGEROUS │
└─────────────────────────────────────┘
AFTER:
┌─────────────────────────────────────┐
│ Mail.Read <-- ONLY THIS │
│ User.Read <-- MINIMAL │
└─────────────────────────────────────┘

As one Redditor put it: “Treating it like a normal person and giving it clear boundaries plus regular security reviews is the right instinct.”

Step 3: Secure SSH with Tailscale

I didn’t want my VPS exposed to the public internet for SSH access. Instead, I use Tailscale which creates a private network overlay.

Install and configure Tailscale
# Install Tailscale
curl -fsSL https://tailscale.com/install.sh | sh
# Connect to your tailnet
sudo tailscale up
# Disable password SSH (keep key-only auth)
sudo sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo systemctl reload sshd

Benefits of this approach:

  • No SSH port exposed to the internet
  • Access only from authenticated devices in my tailnet
  • No need to manage SSH keys across multiple machines
  • Automatic encryption between all nodes

Step 4: Implement Comprehensive Logging

Without logging, I had no idea what OpenClaw was actually doing. I set up audit logging to track all actions.

Set up audit logging
# Create audit log directory
sudo mkdir -p /var/log/openclaw
sudo chown openclaw:openclaw /var/log/openclaw
# Configure auditd for command logging
sudo apt install auditd
sudo auditctl -w /home/openclaw -p wa -k openclaw_actions

Now I can review what the AI assistant has been doing:

View audit logs
# Check recent actions
sudo ausearch -k openclaw_actions | tail -50
# Look for file modifications
sudo ausearch -k openclaw_actions -m MODIFY

Step 5: Harden the Systemd Service

I created a systemd service file with security restrictions built in:

/etc/systemd/system/openclaw.service
[Unit]
Description=OpenClaw AI Assistant
After=network.target
[Service]
Type=simple
User=openclaw
Group=openclaw
WorkingDirectory=/home/openclaw/openclaw
ExecStart=/home/openclaw/openclaw/start.sh
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/home/openclaw/openclaw/data
[Install]
WantedBy=multi-user.target

Key security settings:

  • NoNewPrivileges=true - Prevents privilege escalation
  • ProtectSystem=strict - Makes /usr, /boot, /efi read-only
  • ProtectHome=true - Hides other users’ home directories
  • ReadWritePaths - Only allows writing to specific directories

Step 6: Docker Security (If Using Containers)

If you run OpenClaw in Docker, apply the same security principles:

docker-compose.yml with hardening
version: '3.8'
services:
openclaw:
image: openclaw/openclaw:latest
user: "1000:1000" # Non-root user
read_only: true
tmpfs:
- /tmp
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
environment:
- OPENAI_API_KEY=${OPENAI_API_KEY}
volumes:
- ./data:/app/data:rw
networks:
- internal
networks:
internal:
driver: bridge

The critical settings:

  • user: "1000:1000" - Run as non-root inside container
  • read_only: true - Container filesystem is read-only
  • cap_drop: ALL - Remove all Linux capabilities
  • no-new-privileges:true - No privilege escalation

Step 7: Firewall Configuration

I also locked down the network with UFW:

UFW firewall setup
# Default deny incoming, allow outgoing
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow Tailscale
sudo ufw allow in on tailscale0
# Allow specific application ports (if needed)
sudo ufw allow 443/tcp
# Enable firewall
sudo ufw enable

Common Mistakes to Avoid

MistakeConsequenceFix
Running as rootFull system compromise riskCreate dedicated non-root user
Broad OAuth scopesAI can perform unintended actionsAudit and minimize permissions
No action loggingUnable to detect suspicious activityEnable auditd and application logging
Password SSHBrute force vulnerabilityUse Tailscale + key-only SSH
Skipping firewallUnnecessary attack surfaceConfigure UFW to block non-essential ports

Regular Security Reviews

Security isn’t a one-time setup. I created a weekly cron job to audit permissions:

Weekly permission audit
# Add to crontab
0 0 * * 0 /usr/bin/find /home/openclaw -type f -perm -002 -ls >> /var/log/openclaw/world_writable.log

This checks for any world-writable files that might indicate a security issue.

Why This Matters

AI assistants can take actions that seem reasonable but exceed intended permissions. The Reddit example of an assistant sending an email “promising not to do it again” illustrates the gap between AI intent and actual security boundaries.

Proper isolation prevents:

  • Unauthorized data exfiltration
  • Unexpected system modifications
  • Privilege escalation through compromised credentials
  • Reputation damage from AI actions

As one Redditor pointed out: “The Docker networking, the SSL certs, the reverse proxy config, the systemd service files. And then you get it running and realize security hardening is another full day.”

It does take time, but the alternative - an AI assistant with unlimited access to your system - isn’t worth the risk.

Summary

In this post, I showed how I secured my OpenClaw VPS installation. The key steps were:

  1. User isolation - Never run as root
  2. OAuth minimization - Only grant necessary permissions
  3. Secure access - Use Tailscale instead of public SSH
  4. Comprehensive logging - Know what your AI is doing
  5. System hardening - Use systemd security features
  6. Regular audits - Security is ongoing, not one-time

The most important takeaway: review your OAuth permissions today and remove any scopes beyond what’s strictly necessary. That’s where I found my biggest vulnerability.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments