Skip to content

Instantly share code, notes, and snippets.

@afuggini
Created February 1, 2026 15:59
Show Gist options
  • Select an option

  • Save afuggini/352c1e11aee7861ddc4ee6542585a00f to your computer and use it in GitHub Desktop.

Select an option

Save afuggini/352c1e11aee7861ddc4ee6542585a00f to your computer and use it in GitHub Desktop.
#!/bin/bash
# openclaw-security-check.sh - Verify server security configuration
# Run this periodically to check security status
# Usage: ./openclaw-security-check.sh
echo "=========================================="
echo "OpenClaw Security Status Check"
echo "$(date)"
echo "=========================================="
echo ""
PASS=0
FAIL=0
WARN=0
check_pass() {
echo "✓ $1"
((PASS++))
}
check_fail() {
echo "✗ $1"
((FAIL++))
}
check_warn() {
echo "⚠ $1"
((WARN++))
}
# Check UFW status
if sudo ufw status | grep -q "Status: active"; then
check_pass "Firewall (UFW) is active"
else
check_fail "Firewall (UFW) is NOT active"
fi
# Check Fail2Ban
if sudo systemctl is-active --quiet fail2ban; then
check_pass "Fail2Ban is running"
BANNED=$(sudo fail2ban-client status sshd | grep "Currently banned" | awk '{print $4}')
echo " → Currently banned IPs: $BANNED"
else
check_fail "Fail2Ban is NOT running"
fi
# Check SSH config
if sudo grep -q "^PermitRootLogin no" /etc/ssh/sshd_config.d/99-hardening.conf 2>/dev/null; then
check_pass "SSH root login disabled"
else
check_fail "SSH root login is ENABLED"
fi
if sudo grep -q "^PasswordAuthentication no" /etc/ssh/sshd_config.d/99-hardening.conf 2>/dev/null; then
check_pass "SSH password authentication disabled"
else
check_warn "SSH password authentication is ENABLED"
fi
# Check for security updates
UPDATES=$(apt list --upgradable 2>/dev/null | grep -i security | wc -l)
if [ $UPDATES -eq 0 ]; then
check_pass "No pending security updates"
else
check_warn "$UPDATES security updates available"
fi
# Check Docker running
if sudo systemctl is-active --quiet docker; then
check_pass "Docker is running"
else
check_fail "Docker is NOT running"
fi
# Check disk space
DISK_USAGE=$(df -h / | awk 'NR==2 {print $5}' | sed 's/%//')
if [ $DISK_USAGE -lt 80 ]; then
check_pass "Disk usage: ${DISK_USAGE}%"
else
check_warn "Disk usage: ${DISK_USAGE}% (high)"
fi
# Check for failed login attempts
FAILED_LOGINS=$(sudo grep "Failed password" /var/log/auth.log | wc -l)
if [ $FAILED_LOGINS -lt 10 ]; then
check_pass "Failed login attempts: $FAILED_LOGINS (last 24h)"
else
check_warn "Failed login attempts: $FAILED_LOGINS (last 24h) - potential attack"
fi
# Check listening ports
LISTENING=$(sudo ss -tulpn | grep LISTEN | wc -l)
echo ""
echo "Open ports: $LISTENING"
sudo ss -tulpn | grep LISTEN | awk '{print $5}' | sort -u
echo ""
echo "=========================================="
echo "Security Score"
echo "=========================================="
echo "✓ Passed: $PASS"
echo "⚠ Warnings: $WARN"
echo "✗ Failed: $FAIL"
echo ""
if [ $FAIL -gt 0 ]; then
echo "❌ CRITICAL ISSUES FOUND - Fix immediately!"
exit 1
elif [ $WARN -gt 0 ]; then
echo "⚠️ Warnings found - review recommended"
exit 0
else
echo "✅ All security checks passed"
exit 0
fi
#!/bin/bash
# openclaw-server-hardening.sh - Secure VPS for OpenClaw multi-tenant deployment
# Run this ONCE on a fresh Ubuntu 22.04 LTS VPS as root
# Usage: curl -sSL https://yourgist.com/hardening.sh | sudo bash
set -e
echo "=========================================="
echo "OpenClaw Server Hardening Script"
echo "=========================================="
echo ""
# Check if running as root
if [[ $EUID -ne 0 ]]; then
echo "❌ This script must be run as root (use sudo)"
exit 1
fi
# ==================== 1. UPDATE SYSTEM ====================
echo "==> Step 1/12: Updating system packages..."
apt-get update -qq
apt-get upgrade -y -qq
apt-get autoremove -y -qq
echo "✓ System updated"
# ==================== 2. CREATE NON-ROOT USER ====================
echo ""
echo "==> Step 2/12: Creating non-root user for OpenClaw..."
read -p "Enter username for OpenClaw admin (default: openclaw): " ADMIN_USER
ADMIN_USER=${ADMIN_USER:-openclaw}
if id "$ADMIN_USER" &>/dev/null; then
echo "⚠ User $ADMIN_USER already exists, skipping creation"
else
adduser --disabled-password --gecos "" $ADMIN_USER
usermod -aG sudo $ADMIN_USER
echo "$ADMIN_USER ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/$ADMIN_USER
echo "✓ User $ADMIN_USER created with sudo access"
fi
# Set up SSH key for new user
echo ""
read -p "Do you have an SSH public key to add? (y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
mkdir -p /home/$ADMIN_USER/.ssh
chmod 700 /home/$ADMIN_USER/.ssh
echo "Paste your SSH public key (entire line from id_rsa.pub):"
read SSH_KEY
echo "$SSH_KEY" > /home/$ADMIN_USER/.ssh/authorized_keys
chmod 600 /home/$ADMIN_USER/.ssh/authorized_keys
chown -R $ADMIN_USER:$ADMIN_USER /home/$ADMIN_USER/.ssh
echo "✓ SSH key added for $ADMIN_USER"
else
echo "⚠ Skipping SSH key setup - you'll need password authentication"
fi
# ==================== 3. SECURE SSH ====================
echo ""
echo "==> Step 3/12: Hardening SSH configuration..."
# Backup original config
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup
# SSH Hardening
cat > /etc/ssh/sshd_config.d/99-hardening.conf <<EOF
# Disable root login
PermitRootLogin no
# Disable password authentication (use SSH keys only)
PasswordAuthentication no
PubkeyAuthentication yes
# Disable empty passwords
PermitEmptyPasswords no
# Disable X11 forwarding
X11Forwarding no
# Limit authentication attempts
MaxAuthTries 3
MaxSessions 10
# Use only secure protocols
Protocol 2
# Set login grace time
LoginGraceTime 60
# Only allow specific user
AllowUsers $ADMIN_USER
# Disable unused authentication methods
ChallengeResponseAuthentication no
KerberosAuthentication no
GSSAPIAuthentication no
# Enable strict mode
StrictModes yes
# Set client alive interval (prevent idle disconnects)
ClientAliveInterval 300
ClientAliveCountMax 2
EOF
# Restart SSH (but don't disconnect current session)
systemctl reload sshd
echo "✓ SSH hardened (root login disabled, key-only auth)"
# ==================== 4. CONFIGURE FIREWALL (UFW) ====================
echo ""
echo "==> Step 4/12: Configuring firewall..."
apt-get install -y -qq ufw
# Default policies
ufw --force default deny incoming
ufw --force default allow outgoing
# Allow SSH (critical - don't lock yourself out!)
ufw allow 22/tcp comment 'SSH'
# Allow HTTP/HTTPS for your web platform
ufw allow 80/tcp comment 'HTTP'
ufw allow 443/tcp comment 'HTTPS'
# Allow OpenClaw gateway port range (customer instances)
ufw allow 20000:30000/tcp comment 'OpenClaw Instances'
# Enable UFW
ufw --force enable
echo "✓ Firewall configured and enabled"
ufw status numbered
# ==================== 5. INSTALL FAIL2BAN ====================
echo ""
echo "==> Step 5/12: Installing Fail2Ban (brute force protection)..."
apt-get install -y -qq fail2ban
# Configure Fail2Ban for SSH
cat > /etc/fail2ban/jail.d/sshd.conf <<EOF
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
findtime = 600
EOF
systemctl enable fail2ban
systemctl restart fail2ban
echo "✓ Fail2Ban installed (3 failed attempts = 1hr ban)"
# ==================== 6. DISABLE UNNECESSARY SERVICES ====================
echo ""
echo "==> Step 6/12: Disabling unnecessary services..."
# List of services to disable (if they exist)
SERVICES_TO_DISABLE=(
"avahi-daemon"
"cups"
"isc-dhcp-server"
"isc-dhcp-server6"
"rpcbind"
"rsync"
"nfs-server"
)
for service in "${SERVICES_TO_DISABLE[@]}"; do
if systemctl is-enabled --quiet $service 2>/dev/null; then
systemctl disable $service
systemctl stop $service
echo " ✓ Disabled $service"
fi
done
echo "✓ Unnecessary services disabled"
# ==================== 7. KERNEL HARDENING (SYSCTL) ====================
echo ""
echo "==> Step 7/12: Applying kernel hardening..."
cat > /etc/sysctl.d/99-openclaw-hardening.conf <<EOF
# IP Forwarding (disable unless needed for Docker)
net.ipv4.ip_forward = 1
# Disable IPv6 (unless needed)
net.ipv6.conf.all.disable_ipv6 = 0
net.ipv6.conf.default.disable_ipv6 = 0
# Prevent IP spoofing
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Ignore ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.default.secure_redirects = 0
# Don't send ICMP redirects
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
# Ignore ICMP ping requests (optional - prevents ping flood)
# net.ipv4.icmp_echo_ignore_all = 1
# Log suspicious packets
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
# Ignore broadcast pings
net.ipv4.icmp_echo_ignore_broadcasts = 1
# Protect against SYN flood attacks
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 2048
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 5
# Disable source packet routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
# Increase local port range
net.ipv4.ip_local_port_range = 10000 65535
# Protect against time-wait assassination
net.ipv4.tcp_rfc1337 = 1
# Kernel hardening
kernel.kptr_restrict = 2
kernel.dmesg_restrict = 1
kernel.yama.ptrace_scope = 1
# Limit core dumps
fs.suid_dumpable = 0
EOF
sysctl -p /etc/sysctl.d/99-openclaw-hardening.conf > /dev/null
echo "✓ Kernel hardening applied"
# ==================== 8. INSTALL DOCKER SECURELY ====================
echo ""
echo "==> Step 8/12: Installing Docker with security configurations..."
# Install Docker
apt-get install -y -qq apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update -qq
apt-get install -y -qq docker-ce docker-ce-cli containerd.io docker-compose-plugin
# Add user to docker group
usermod -aG docker $ADMIN_USER
# Docker security configuration
mkdir -p /etc/docker
cat > /etc/docker/daemon.json <<EOF
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"live-restore": true,
"userland-proxy": false,
"no-new-privileges": true,
"icc": false,
"userns-remap": "default"
}
EOF
# Enable user namespace remapping for container isolation
mkdir -p /etc/subuid /etc/subgid
echo "dockremap:231072:65536" >> /etc/subuid
echo "dockremap:231072:65536" >> /etc/subgid
systemctl restart docker
echo "✓ Docker installed with security hardening"
# ==================== 9. SETUP AUTOMATED SECURITY UPDATES ====================
echo ""
echo "==> Step 9/12: Configuring automatic security updates..."
apt-get install -y -qq unattended-upgrades apt-listchanges
cat > /etc/apt/apt.conf.d/50unattended-upgrades <<EOF
Unattended-Upgrade::Allowed-Origins {
"\${distro_id}:\${distro_codename}-security";
};
Unattended-Upgrade::AutoFixInterruptedDpkg "true";
Unattended-Upgrade::MinimalSteps "true";
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Automatic-Reboot "false";
Unattended-Upgrade::Automatic-Reboot-Time "03:00";
EOF
cat > /etc/apt/apt.conf.d/20auto-upgrades <<EOF
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "7";
APT::Periodic::Unattended-Upgrade "1";
EOF
systemctl enable unattended-upgrades
systemctl restart unattended-upgrades
echo "✓ Automatic security updates enabled"
# ==================== 10. SETUP MONITORING & LOGGING ====================
echo ""
echo "==> Step 10/12: Configuring logging and monitoring..."
# Install monitoring tools
apt-get install -y -qq htop iotop nethogs
# Configure log rotation for Docker
cat > /etc/logrotate.d/docker-containers <<EOF
/var/lib/docker/containers/*/*.log {
rotate 7
daily
compress
missingok
delaycompress
copytruncate
}
EOF
# Setup audit logging for sensitive directories
apt-get install -y -qq auditd audispd-plugins
cat > /etc/audit/rules.d/openclaw.rules <<EOF
# Monitor OpenClaw instance directories
-w /root/.openclaw-instances/ -p wa -k openclaw_instances
# Monitor Docker socket
-w /var/run/docker.sock -p wa -k docker_socket
# Monitor system authentication
-w /etc/passwd -p wa -k passwd_changes
-w /etc/group -p wa -k group_changes
-w /etc/shadow -p wa -k shadow_changes
# Monitor sudo
-w /etc/sudoers -p wa -k sudoers_changes
EOF
systemctl restart auditd
echo "✓ Logging and monitoring configured"
# ==================== 11. SETUP INTRUSION DETECTION (OPTIONAL) ====================
echo ""
read -p "Install AIDE (intrusion detection)? This takes time. (y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "==> Step 11/12: Installing AIDE (this may take 10-15 minutes)..."
apt-get install -y -qq aide aide-common
# Initialize AIDE database
echo "⏳ Building AIDE database (this takes time, be patient)..."
aideinit
mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db
# Setup daily AIDE check via cron
cat > /etc/cron.daily/aide-check <<EOF
#!/bin/bash
/usr/bin/aide --check | mail -s "AIDE Report for \$(hostname)" root
EOF
chmod +x /etc/cron.daily/aide-check
echo "✓ AIDE installed (daily integrity checks enabled)"
else
echo "⊘ Skipping AIDE installation"
fi
# ==================== 12. CUSTOMER INSTANCE ISOLATION ====================
echo ""
echo "==> Step 12/12: Setting up customer instance isolation..."
# Create base directory structure
mkdir -p /home/$ADMIN_USER/.openclaw-instances
chown -R $ADMIN_USER:$ADMIN_USER /home/$ADMIN_USER/.openclaw-instances
chmod 750 /home/$ADMIN_USER/.openclaw-instances
# Create iptables rules for container network isolation
cat > /etc/iptables/openclaw-isolation.rules <<EOF
# Allow containers to access internet
-A FORWARD -i docker0 -o eth0 -j ACCEPT
-A FORWARD -i eth0 -o docker0 -m state --state RELATED,ESTABLISHED -j ACCEPT
# Prevent containers from accessing each other (unless on same network)
-A DOCKER-USER -i docker0 -o docker0 -j DROP
# Prevent containers from accessing host services
-A INPUT -i docker0 -j DROP
EOF
echo "✓ Customer isolation rules created"
# ==================== RATE LIMITING FOR API ENDPOINTS ====================
echo ""
echo "Setting up rate limiting for OpenClaw instances..."
# Install nginx for reverse proxy with rate limiting
apt-get install -y -qq nginx
# Disable default site
rm -f /etc/nginx/sites-enabled/default
# Create rate limit config
cat > /etc/nginx/conf.d/rate-limit.conf <<EOF
# Define rate limit zones
limit_req_zone \$binary_remote_addr zone=openclaw_general:10m rate=10r/s;
limit_req_zone \$binary_remote_addr zone=openclaw_api:10m rate=5r/s;
# Connection limits
limit_conn_zone \$binary_remote_addr zone=addr:10m;
EOF
systemctl restart nginx
echo "✓ Nginx installed for rate limiting"
# ==================== GENERATE SECURITY REPORT ====================
echo ""
echo "=========================================="
echo "Security Hardening Complete!"
echo "=========================================="
echo ""
# Create security report
REPORT_FILE="/home/$ADMIN_USER/security-report.txt"
cat > $REPORT_FILE <<EOF
OpenClaw Server Security Report
Generated: $(date)
Hostname: $(hostname)
IP Address: $(curl -s ifconfig.me)
========================================
SECURITY MEASURES APPLIED
========================================
✓ System packages updated
✓ Non-root user created: $ADMIN_USER
✓ SSH hardened (root login disabled, key-only auth)
✓ Firewall configured (UFW):
- SSH: Port 22
- HTTP/HTTPS: Ports 80/443
- OpenClaw: Ports 20000-30000
✓ Fail2Ban installed (brute force protection)
✓ Unnecessary services disabled
✓ Kernel hardening applied
✓ Docker installed with security configs
✓ Automatic security updates enabled
✓ Logging and monitoring configured
✓ Customer instance isolation configured
✓ Rate limiting enabled (Nginx)
========================================
NEXT STEPS
========================================
1. LOGOUT and login as: $ADMIN_USER
ssh $ADMIN_USER@$(curl -s ifconfig.me)
2. VERIFY SSH key authentication works
3. NEVER login as root again
4. TEST firewall rules:
sudo ufw status
5. MONITOR logs:
sudo tail -f /var/log/auth.log
sudo fail2ban-client status sshd
6. SETUP SSL certificates (Let's Encrypt):
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com
7. BACKUP this report file:
$(pwd)/$REPORT_FILE
========================================
SECURITY CHECKLIST
========================================
[ ] Changed default SSH port? (optional)
Edit: /etc/ssh/sshd_config.d/99-hardening.conf
Add: Port 2222
Update UFW: ufw allow 2222/tcp
[ ] Setup monitoring/alerts? (recommended)
Options: UptimeRobot, Datadog, Prometheus
[ ] Configured backups? (critical)
Options: Vultr snapshots, rsnapshot, Backblaze B2
[ ] Setup SSL certificates? (required for production)
Use: Let's Encrypt (free)
[ ] Configured email alerts?
For: Fail2Ban, AIDE, security updates
[ ] Documented recovery procedures?
What to do if server is compromised
[ ] Setup 2FA for admin account? (highly recommended)
Use: Google Authenticator via PAM
========================================
SECURITY MAINTENANCE
========================================
DAILY:
- Check Fail2Ban logs: sudo fail2ban-client status
- Monitor Docker containers: docker ps
WEEKLY:
- Review auth logs: sudo grep -i "failed\|error" /var/log/auth.log
- Check disk space: df -h
- Review active connections: sudo netstat -tupln
MONTHLY:
- Update system: sudo apt update && sudo apt upgrade
- Review UFW rules: sudo ufw status numbered
- Audit Docker images: docker images
- Check for rootkits: sudo rkhunter --check
QUARTERLY:
- Review all user accounts: cat /etc/passwd
- Audit sudo access: sudo cat /etc/sudoers
- Test backup restoration
- Review and rotate SSH keys
========================================
EMERGENCY CONTACTS
========================================
If server is compromised:
1. Disconnect from network: sudo ufw deny out
2. Take snapshot: via Vultr dashboard
3. Analyze logs: /var/log/auth.log, /var/log/syslog
4. Contact Vultr support
5. Restore from clean backup
========================================
USEFUL COMMANDS
========================================
# Check open ports
sudo netstat -tupln
# View firewall status
sudo ufw status verbose
# Check Fail2Ban status
sudo fail2ban-client status sshd
# View recent login attempts
sudo lastlog
# Check for listening services
sudo ss -tulpn
# View Docker container logs
docker logs <container_name>
# Check system resources
htop
# Monitor network traffic
sudo nethogs
# View audit logs
sudo ausearch -k openclaw_instances
========================================
ADDITIONAL HARDENING (OPTIONAL)
========================================
1. Install ClamAV (antivirus):
sudo apt install clamav clamav-daemon
sudo freshclam
sudo clamscan -r /home
2. Setup ModSecurity (WAF):
sudo apt install libapache2-mod-security2
3. Enable AppArmor profiles:
sudo aa-enforce /etc/apparmor.d/*
4. Install Tripwire (file integrity):
sudo apt install tripwire
5. Setup centralized logging:
Forward logs to external service (Papertrail, Loggly)
========================================
EOF
chown $ADMIN_USER:$ADMIN_USER $REPORT_FILE
chmod 600 $REPORT_FILE
echo "✓ Security report generated: $REPORT_FILE"
echo ""
echo "=========================================="
echo "⚠️ IMPORTANT NEXT STEPS"
echo "=========================================="
echo ""
echo "1. SAVE this security report:"
echo " $REPORT_FILE"
echo ""
echo "2. LOGOUT and test SSH key login:"
echo " ssh $ADMIN_USER@$(curl -s ifconfig.me)"
echo ""
echo "3. NEVER use root account again"
echo ""
echo "4. SETUP SSL certificates before going live"
echo ""
echo "=========================================="
echo ""
# Create reminder to disable root password
echo ""
read -p "Disable root password now? (recommended) (y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
passwd -l root
echo "✓ Root password disabled"
else
echo "⚠ Root password still active - disable it manually later:"
echo " sudo passwd -l root"
fi
echo ""
echo "🎉 Server hardening complete!"
echo ""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment