Bảo mật OpenClaw: phơi gateway ra Internet đúng cách (Caddy + HTTPS + Auth)

Chia sẻ bài viết

Mục lục
Minh hoạ bảo mật OpenClaw gateway qua Caddy HTTPS

Gateway OpenClaw mặc định bind vào 127.0.0.1:18789 vì một lý do rất nghiêm túc: nó có quyền execute code, mở browser, chạy skill, truy cập file system bằng user mà bạn đang đăng nhập. Phơi nguyên port đó ra Internet mà không TLS, không auth, không IP whitelist là cách nhanh nhất để biến VPS của bạn thành botnet node. CrowdStrike từng quét và thấy hơn 42.000 instance OpenClaw bị lộ public, 93% có lỗ hổng bypass auth. Bài này hướng dẫn cách phơi gateway ra Internet đúng chuẩn: đứng sau Caddy, HTTPS tự cấp, Basic Auth, IP whitelist bằng UFW, và fail2ban làm lớp chốt cuối.

Vì sao gateway OpenClaw nguy hiểm hơn một web app thường

OpenClaw không phải dashboard read-only. Nó là agent: nhận message, gọi LLM, rồi LLM được phép gọi skill. Skill chạy như user bash của bạn. Một số skill mặc định trong ClawHub có quyền:

  • Execute shell command (browser, file manager, git).
  • Đọc/ghi vào HOME directory.
  • Gọi outbound network (curl, npm, pip).
  • Kết nối tới các kênh (Zalo, Telegram, Discord, Slack) bằng token bạn đã pair.

Nếu attacker chiếm được endpoint /api/chat, họ có thể gửi prompt khiến agent tự chạy lệnh, đọc ~/.ssh/id_rsa, hoặc dùng kênh đã pair để spam liên hệ của bạn. Khác với một app CRUD bình thường, ở đây surface attack là toàn bộ user space của user chạy gateway.

Bài học rút ra: không bao giờ bind 0.0.0.0 trên gateway OpenClaw. Luôn để 127.0.0.1, rồi cho reverse proxy đứng trước.

⚠️ Lưu ý: Nếu bạn vừa cài OpenClaw bằng openclaw onboard và thấy nó listen 0.0.0.0:18789, đóng port ngay bằng sudo ufw deny 18789 trước khi tiếp tục đọc bài.

Kiến trúc bảo mật khuyến nghị

Mô hình tối thiểu cho một dev tự host trên VPS:

Internet
   |
   v
[Cloudflare DNS] -- không proxy (gray cloud) để Caddy tự lấy cert
   |
   v
[VPS - UFW] cho phép 22, 80, 443 từ IP nhà/4G; chặn 18789
   |
   v
[Caddy :443] terminate TLS, basic_auth, log
   |
   v
[OpenClaw gateway :127.0.0.1:18789]

Caddy gánh hết phần TLS (auto Let's Encrypt), authentication layer 1 (Basic Auth) và logging. Gateway chỉ nghe loopback nên kể cả UFW bị disable cũng không thủng. Đây là defense in depth thật sự, không phải cho có.

Bước 1: cài Caddy và xác nhận DNS

Trên Ubuntu 24.04:

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
  | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \
  | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update && sudo apt install caddy

Trỏ một subdomain (ví dụ claw.example.com) về IP VPS bằng A record, để DNS dạng gray cloud trên Cloudflare (không proxy) để Caddy dùng HTTP-01 challenge mà không bị Cloudflare chặn. Sau khi DNS propagate khoảng 2-5 phút, kiểm tra:

dig +short claw.example.com
# kết quả phải đúng IP VPS

Bước 2: tạo hash mật khẩu cho Basic Auth

Đừng paste plaintext password vào Caddyfile. Caddy có lệnh hash sẵn dùng bcrypt:

caddy hash-password
# nhập password (gõ vào, không hiện)
# nó trả về chuỗi $2a$14$...

Copy chuỗi đó. Đặt username gì cũng được nhưng tránh admin, root, openclaw - bot brute force scan đúng mấy tên này đầu tiên.

Bước 3: viết Caddyfile cho OpenClaw

File /etc/caddy/Caddyfile:

claw.example.com {
    # 1. Bật log để fail2ban đọc được
    log {
        output file /var/log/caddy/claw-access.log {
            roll_size 10mb
            roll_keep 5
        }
        format json
    }

    # 2. Header bảo mật cơ bản
    header {
        Strict-Transport-Security "max-age=31536000;"
        X-Content-Type-Options "nosniff"
        X-Frame-Options "DENY"
        Referrer-Policy "no-referrer"
        -Server
    }

    # 3. Basic auth cho mọi path
    basicauth {
        # username này khác với 'admin' hay tên dễ đoán
        loi-claw $2a$14$YourBcryptHashHereReplaceMe...
    }

    # 4. Reverse proxy lên gateway loopback
    reverse_proxy 127.0.0.1:18789 {
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-For {remote_host}
        # WebSocket cần thiết cho live update của OpenClaw
        transport http {
            versions 1.1 2
        }
    }
}

Reload Caddy: sudo systemctl reload caddy. Caddy sẽ tự xin cert từ Let's Encrypt trong khoảng 30 giây. Test:

curl -I https://claw.example.com
# phải trả 401 Unauthorized (vì chưa gửi creds)

curl -I -u loi-claw:matkhau https://claw.example.com
# phải trả 200 OK hoặc 101 Switching Protocols
💡 Mẹo: Caddy auto-renew cert 30 ngày trước hạn, không cần cron như Certbot. Bạn có thể quên nó tồn tại trong vài năm.

Bước 4: gateway chỉ nghe loopback

Mở file config OpenClaw (thường là ~/.openclaw/config.json hoặc ~/.openclaw/gateway.json):

{
  "gateway": {
    "host": "127.0.0.1",
    "port": 18789,
    "trustedProxies": ["127.0.0.1"]
  }
}

Trường trustedProxies nói với gateway tin X-Forwarded-For chỉ khi đến từ Caddy local. Restart gateway:

openclaw stop
openclaw start
ss -tlnp | grep 18789
# phải hiện 127.0.0.1:18789, KHÔNG được là 0.0.0.0:18789

Bước 5: UFW khóa cứng

Cấu hình firewall đơn giản nhưng hiệu quả:

sudo ufw default deny incoming
sudo ufw default allow outgoing

# SSH (thay 22 nếu bạn đổi port)
sudo ufw allow 22/tcp

# Caddy
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Tuyệt đối không allow 18789 từ ngoài
sudo ufw deny 18789/tcp

sudo ufw enable
sudo ufw status verbose

Muốn thêm lớp nữa, chỉ allow 443 từ IP nhà tĩnh (Viettel FTTH thường cấp IP động, nhưng IP đầu tiên của session khá ổn định trong tuần):

sudo ufw delete allow 443/tcp
sudo ufw allow from 14.161.xxx.xxx to any port 443 proto tcp
sudo ufw allow from 27.65.xxx.0/24 to any port 443 proto tcp

Khi đi cafe hoặc đổi 4G, dùng VPN Tailscale là cách an toàn hơn (sẽ nói cuối bài).

Bước 6: fail2ban đứng sau Caddy

Mặc định Caddy không tích hợp fail2ban, nhưng vì ta đã bật JSON log nên việc parse và ban IP rất dễ. Cài fail2ban:

sudo apt install fail2ban

Tạo filter /etc/fail2ban/filter.d/caddy-auth.conf:

[Definition]
failregex = ^.*"remote_ip":"".*"status":401.*$
ignoreregex =
journalmatch =

Tạo jail /etc/fail2ban/jail.d/caddy-auth.conf:

[caddy-auth]
enabled = true
port = http,https
filter = caddy-auth
logpath = /var/log/caddy/claw-access.log
maxretry = 5
findtime = 600
bantime = 3600
backend = auto

Restart: sudo systemctl restart fail2ban. Bot nào dò Basic Auth sai 5 lần trong 10 phút bị ban 1 tiếng. Theo dõi:

sudo fail2ban-client status caddy-auth
sudo tail -f /var/log/fail2ban.log

Bước 7: audit log của chính OpenClaw

Caddy log request HTTP, nhưng nó không biết gateway đã chạy skill nào. Bật audit log của OpenClaw để biết khi nào agent execute lệnh:

{
  "gateway": {
    "host": "127.0.0.1",
    "port": 18789,
    "audit": {
      "enabled": true,
      "path": "~/.openclaw/audit.log",
      "logSkillExecution": true,
      "logChannelMessages": false
    }
  }
}

Đừng log channel messages nếu bạn pair với Zalo cá nhân (privacy). Nhưng log skill execution thì rất nên - một dòng shell.exec rm -rf ~/projects trong audit.log lúc 3h sáng là cờ đỏ rất rõ.

Bonus: thay Basic Auth bằng Tailscale

Cách an toàn nhất là không phơi ra Internet chút nào. Cài Tailscale lên VPS và lên laptop/điện thoại:

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up

Sau đó sửa Caddyfile chỉ listen trên IP Tailscale:

http://claw {
    bind 100.64.0.1
    reverse_proxy 127.0.0.1:18789
}

Gateway giờ chỉ truy cập được khi máy bạn vào VPN Tailscale. Không cần TLS Let's Encrypt (vì Tailscale tự encrypt). Không cần Basic Auth. Không lo brute force. Đây là setup mình khuyên cho 90% dev tự host - đơn giản và an toàn hơn nhiều so với phơi ra public.

💡 Mẹo: Vẫn muốn vài channel webhook (như Discord, Slack) bắn vào gateway từ public? Tạo route riêng /webhook/* trong Caddy bỏ qua basic auth nhưng require shared secret trong header. Đừng mở toàn bộ.

Checklist trước khi đi ngủ

Mục Kiểm tra
Gateway bind 127.0.0.1 ss -tlnp | grep 18789 không có 0.0.0.0
UFW chặn 18789 sudo ufw status | grep 18789 DENY
HTTPS hoạt động curl -I https://claw.example.com 401
Basic Auth không bypass curl https://claw.example.com không trả body
fail2ban đang theo dõi sudo fail2ban-client status caddy-auth
Audit log đang ghi tail ~/.openclaw/audit.log
Channel token rotate Đổi token Zalo/Telegram sau mỗi 3 tháng

Khi nào nên upgrade VPS

Setup bảo mật trên đây chạy tốt với VPS 50 (4vCPU/4GB) của TND nếu chỉ một mình bạn dùng. Nhưng nếu bạn bật audit log full, chạy thêm 2-3 skill có vision (đọc ảnh) và pair 4-5 channel cùng lúc, RAM dễ chạm 3GB. Lúc đó cân nhắc lên VPS 80 (6vCPU/6GB) để có buffer. RAM ECC trên VPS TND giúp tránh trường hợp bit-flip làm gateway crash giữa đêm - rất khó debug nếu không có ECC.

Cá nhân mình host gateway OpenClaw + n8n + một redis caching cho LLM trên VPS 80 và đủ thoải mái. Đọc thêm bài giới thiệu OpenClaw để self-host VPS nếu bạn vừa mới biết tới project này. Nếu đang phân vân cấu hình nào hợp, tham khảo hướng dẫn chọn VPS cho vibe coder để hình dung workload.

Một số sai lầm bảo mật mình thấy lặp đi lặp lại

Trong các forum và Discord OpenClaw tiếng Việt, đây là pattern lỗi mình thấy 8/10 dev tự host mắc phải. Liệt kê ra để bạn check chéo:

  • Phơi 18789 trực tiếp bằng UFW allow, không qua reverse proxy. Gateway có cơ chế Gateway Token nhưng không có TLS - sniff bằng MITM trên Wi-Fi quán cafe là lộ.
  • Dùng port forwarding router nhà thay vì VPS. IP nhà bị crawl ngay khi join port scanner public list (Shodan, Censys index trong vài giờ).
  • Đặt Basic Auth username là "admin" hoặc "openclaw". Bot brute force biết hai username này đầu tiên - cứ thử thấy ngay.
  • Bật public share Canvas mặc định. URL share có thể leak qua Google index nếu một ngày bạn copy share link vào public document.
  • Không rotate channel token. Token Telegram bot mình thấy có người dùng nguyên 2 năm - một lần leak là agent trở thành bot spam của attacker.
  • Cài skill từ ClawHub không xem source. Snyk từng phát hiện hơn 340 skill độc hại trong marketplace - đọc index.ts trước khi install.
  • Quên đóng debug endpoint /debug. Một số version OpenClaw có endpoint dump full config kể cả API key - chỉ nên enable trong dev.

Test thâm nhập tự làm sau setup

Đừng tin setup của mình - thử attack chính nó. Vài lệnh test cơ bản từ máy ngoài VPS:

# 1. Quét port - chỉ thấy 22, 80, 443
nmap -p- claw.example.com

# 2. Truy cập gateway trực tiếp - phải refused/timeout
curl -v http://claw.example.com:18789

# 3. Brute force basic auth 10 lần - phải bị fail2ban ban
for i in $(seq 1 10); do
  curl -u wrong:wrong https://claw.example.com -o /dev/null -s -w "%{http_code}\n"
done
# Sau 5 lần phải bắt đầu connection refused

# 4. Check HSTS, security header
curl -I https://claw.example.com -u user:pass | grep -i strict-transport

# 5. SSL grade
curl -s https://api.ssllabs.com/api/v3/analyze?host=claw.example.com

Mất 5 phút. Tìm ra một lỗ trong setup ngay sau khi cài là tốt hơn nhiều so với phát hiện 6 tháng sau khi đã bị khai thác.

Tóm lại

Phơi gateway OpenClaw ra Internet không sai, sai là phơi tay không. Quy tắc bất di bất dịch: gateway bind loopback, reverse proxy có TLS đứng trước, Basic Auth hoặc Tailscale, fail2ban chốt cuối, audit log để truy vết. Mất khoảng 30 phút setup, sau đó quên đi. Tốt hơn hẳn việc một sáng đẹp trời tỉnh dậy thấy VPS bị dùng để mine crypto và tài khoản Zalo bị spam bạn bè. Bảo mật không phải tính năng one-time - mỗi lần update gateway, cài skill mới, pair channel mới đều là lúc nên xem lại checklist trên đầu.

Bài viết liên quan

Cần VPS có ECC RAM và NVMe enterprise để chạy OpenClaw gateway 24/7?

TND Cloud VPS từ 639k/tháng, datacenter Việt Nam, latency thấp tới iOS/Android app của OpenClaw, hỗ trợ tiếng Việt 24/7.

Xem bảng giá VPS