I set up fail2ban on my Clawdbot server tonight. Within 60 seconds, it banned its first attacker.
Not a test. An actual bot was actively trying to brute-force SSH while I was configuring the firewall.
Jan 27 02:46:55 sshd: Invalid user oracle from 217.154.38.135Jan 27 02:47:25 sshd: Invalid user main from 217.154.38.135Jan 27 02:47:56 sshd: Invalid user HwHiAiUser from 217.154.38.135Jan 27 02:48:26 sshd: Invalid user guest from 217.154.38.135Jan 27 02:48:57 sshd: Invalid user git from 217.154.38.135
Every 30 seconds. Different usernames. Automated. Relentless.
Then fail2ban kicked in:
2026-01-27 02:50:54 NOTICE [sshd] Ban 217.154.38.135
Done. 30-day ban. If you're running Clawdbot on a VPS, you need this.
Why Clawdbot Servers Are Targets
Clawdbot servers are juicy targets because they typically have:
- Shell access โ The agent can run commands
- API credentials โ Anthropic, OpenAI, messaging platforms
- Personal data โ Session logs, memory files, contacts
- Always-on connectivity โ 24/7 uptime on cloud VPS
An attacker who compromises your Clawdbot server gets access to your AI assistant's full capabilities. They could read your conversations, impersonate you on messaging platforms, or rack up API bills.
Let's fix that.
Step 1: SSH Key Authentication (Critical)
This is the single most important security step. Password authentication is the weakest link. SSH keys are cryptographically secure and impossible to brute-force.
On your local machine, generate a key pair:
ssh-keygen -t ed25519 -C "your_email@example.com"
This creates two files:
~/.ssh/id_ed25519โ Your private key (never share this)~/.ssh/id_ed25519.pubโ Your public key (goes on the server)
Copy your public key to the server:
ssh-copy-id user@your-server-ip
Or manually:
# On your local machinecat ~/.ssh/id_ed25519.pub# Copy the output, then on your server:echo "your-public-key-here" >> ~/.ssh/authorized_keyschmod 600 ~/.ssh/authorized_keys
Test it works:
ssh user@your-server-ip
If you get in without a password prompt, it's working.
Step 2: Disable Password Authentication
Once SSH keys work, disable password authentication entirely:
sudo nano /etc/ssh/sshd_config
Find and change these lines:
PasswordAuthentication noPubkeyAuthentication yesChallengeResponseAuthentication no
Restart SSH:
sudo systemctl restart sshd
Now the only way in is with your private key. Brute-force attacks become completely pointless โ they're trying to guess a password that doesn't exist.
Step 3: Install fail2ban
Even with SSH keys, we want fail2ban as a backup layer to keep logs clean and stop wasted resources.
sudo apt updatesudo apt install -y fail2ban
Create a local config:
sudo tee /etc/fail2ban/jail.local << 'EOF'[sshd]enabled = trueport = sshfilter = sshdlogpath = /var/log/auth.logmaxretry = 3bantime = 2592000findtime = 600EOF
What this does:
- maxretry = 3 โ Three failed attempts = banned
- bantime = 2592000 โ 30-day ban (if you use SSH keys, any failed attempt is definitely an attacker)
- findtime = 600 โ Attempts counted within 10-minute window
Start it:
sudo systemctl enable fail2bansudo systemctl restart fail2ban
Check if it's working:
sudo fail2ban-client status sshd
Step 4: Configure ufw Firewall
# Default: block everything incomingsudo ufw default deny incomingsudo ufw default allow outgoing# Allow only what Clawdbot needssudo ufw allow 22/tcp # SSH (we'll change this later)sudo ufw allow 80/tcp # HTTP (if using webhooks)sudo ufw allow 443/tcp # HTTPS# Enablesudo ufw --force enable
Verify:
sudo ufw status verbose
Step 5: SSH Tarpit with endlessh (The Fun Part)
Here's where it gets interesting. The idea: move real SSH to a non-standard port, put endlessh on port 22. Bots connect to port 22 and get trapped in an infinitely slow banner that sends one random byte every 10 seconds. They never get a login prompt. Meanwhile, real SSH runs unbothered on a different port.
What endlessh Does
endlessh is an SSH tarpit. When a bot connects, it sends an SSH banner infinitely slowly โ one random byte every 10 seconds. The SSH spec allows banners up to 255 characters, but it doesn't specify a minimum speed. Most bots will wait patiently for the full banner, consuming a connection slot and wasting their time.
It's like digital quicksand for SSH bots.
Install endlessh
sudo apt install endlessh
Move Real SSH to Port 2222
On modern Ubuntu, SSH is managed by systemd sockets, so we need to override the socket config:
sudo mkdir -p /etc/systemd/system/ssh.socket.d/sudo tee /etc/systemd/system/ssh.socket.d/override.conf << 'EOF'[Socket]ListenStream=ListenStream=2222EOF
Reload and restart:
sudo systemctl daemon-reloadsudo systemctl restart ssh.socketsudo systemctl restart ssh
Verify SSH is now on 2222:
ss -tlnp | grep :2222
Configure endlessh for Port 22
sudo mkdir -p /etc/endlesshsudo tee /etc/endlessh/config << 'EOF'Port 22Delay 10000MaxLineLength 32MaxClients 4096LogLevel 1EOF
This configures:
- Port 22 โ The standard SSH port (bots will find this)
- Delay 10000 โ Send one byte every 10 seconds
- MaxClients 4096 โ Handle lots of trapped bots
- LogLevel 1 โ Log connections for entertainment
Fix the systemd Service
endlessh might fail with a NAMESPACE error on modern systems. Create a service override:
sudo mkdir -p /etc/systemd/system/endlessh.service.d/sudo tee /etc/systemd/system/endlessh.service.d/override.conf << 'EOF'[Service]PrivateUsers=falseEOF
Start endlessh
sudo systemctl daemon-reloadsudo systemctl enable endlesshsudo systemctl start endlessh
Check it's working:
sudo systemctl status endlesshss -tlnp | grep :22
Update ufw for New SSH Port
# Allow the new SSH portsudo ufw allow 2222/tcp# Remove the old SSH rule (port 22 is now the tarpit)sudo ufw delete allow 22/tcp
Test the Tarpit
From another machine, try connecting to port 22:
ssh user@your-server-ip -p 22
It should just hang there, sending one character every 10 seconds. After a while you'll see a super slow, garbled banner. Press Ctrl+C to escape.
Now try the real SSH:
ssh user@your-server-ip -p 2222
Instant connection with your SSH key.
The Results
Bots waste their time on port 22 while your real SSH runs unbothered on 2222. Your logs stay clean because failed attempts hit the tarpit, not your real SSH service. Watching journalctl -f -u endlessh is surprisingly entertaining โ bots connecting and just... waiting.
How Effective Is It Really?
One researcher ran endlessh for months and collected data on trapped bots. The results are hilarious:
- One bot held 416 concurrent connections open at the same time โ just kept opening new ones without closing the old
- A single IP stayed connected for 690,172 seconds โ that's 8 days straight โ downloading 1.2MB of random garbage thinking it was an SSH banner
- Some connections lasted 12,000+ seconds (3.3 hours) before the bot gave up
- The median trap time was 17 seconds (the "smart" bots with timeouts), but the mean was 119 seconds โ dragged up by the dumb ones that wait forever
The quality of underground scanning software varies immensely. Some bots have a 15-second timeout and move on. Others have no timeout at all โ they'll sit there until the heat death of the universe waiting for a login prompt that never comes.
And the best part: endlessh uses virtually zero resources. It's a single process handling thousands of connections with minimal CPU and memory. The bots are the ones burning resources, not you.
Monitor the Tarpit
Watch bots get trapped in real time:
journalctl -f -u endlessh
Count current trapped connections:
ss -tn | grep :22 | grep ESTAB | wc -l
Step 6: Change SSH Port (Brief)
We already moved SSH to 2222 for the tarpit, but here's the general approach if you just want to change ports without endlessh:
# In /etc/ssh/sshd_configPort 2222
Update firewall:
sudo ufw allow 2222/tcpsudo ufw delete allow 22/tcpsudo systemctl restart sshd
Security through obscurity isn't real security, but it does reduce noise from random scans. Combined with SSH keys and fail2ban, it's effective.
Step 7: Secrets Management with pass
Don't store API keys and passwords in plaintext config files. Use pass โ the standard Unix password manager.
Install it:
sudo apt install pass gnupg
Set it up:
# Generate a GPG key (if you don't have one)gpg --gen-key# Initialize pass with your GPG keypass init "your-email@example.com"
Store secrets:
pass insert api/anthropicpass insert api/openaipass insert gmail/app-password
Retrieve secrets in scripts:
export ANTHROPIC_API_KEY=$(pass show api/anthropic)
Why this matters:
- Secrets are GPG-encrypted at rest
- Even if someone accesses your filesystem, they can't read the passwords without your GPG key
- You can sync your password store via git (safely, since everything is encrypted)
- Works great with Clawdbot โ store your API keys and channel tokens securely
Your ~/.password-store/ directory is encrypted. Your ~/.clawdbot/clawdbot.json with plaintext tokens? Not so much.
Step 8: Clawdbot Security Audit
Clawdbot has a built-in security scanner. Run it:
clawdbot security audit
For a deeper check:
clawdbot security audit --deep
To auto-fix common issues:
clawdbot security audit --fix
What the Audit Checks
- Inbound access โ Can strangers message your bot?
- Tool blast radius โ Could prompt injection lead to shell access?
- Network exposure โ Is your Gateway exposed without auth?
- Browser control โ Is remote browser control secured?
- Disk permissions โ Are credentials and logs protected?
- Plugins โ Are untrusted extensions loaded?
Fix Workspace Permissions
The audit will likely warn about permissions. Fix them:
chmod 700 ~/.clawdbotchmod 600 ~/.clawdbot/clawdbot.json
This ensures only your user can read Clawdbot's config and credentials.
Credential Storage Locations
Know where your secrets live:
- Telegram token: config or
channels.telegram.tokenFile - WhatsApp auth:
~/.clawdbot/credentials/whatsapp/*/creds.json - Pairing allowlists:
~/.clawdbot/credentials/*-allowFrom.json - Session logs:
~/.clawdbot/agents/*/sessions/*.jsonl
All of these should be readable only by your user (not group/world).
Step 9: Channel Security
Lock Down DMs
By default, Clawdbot might accept messages from anyone. Tighten it:
In your config, set DM policies to allowlist:
{"channels": {"telegram": {"dmPolicy": "allowlist","dmAllowFrom": ["your_telegram_id"]}}}
Lock Down Groups
Same for groups โ use allowlists instead of open:
{"groupPolicy": "allowlist","groupAllowFrom": ["allowed_group_id"]}
The security audit will flag open policies.
Step 10: Report Attackers to AbuseIPDB
Blocking attackers locally is good. Getting them blacklisted globally is better.
AbuseIPDB is a community database of abusive IPs. When you report an attacker, every other server using their blocklist benefits. It's collective defense.
Get a Free API Key
Sign up at abuseipdb.com โ the free tier allows 1000 reports/day.
Store it securely:
pass insert abuseipdb/api-key
Report a Banned IP
ABUSEIPDB_KEY=$(pass show abuseipdb/api-key)curl -s https://api.abuseipdb.com/api/v2/report \-H "Key: $ABUSEIPDB_KEY" \-H "Accept: application/json" \--data-urlencode "ip=2.57.122.209" \-d "categories=18,22" \--data-urlencode "comment=SSH brute-force (fail2ban auto-ban)"
Categories 18,22 = brute-force + SSH.
Auto-Report with Clawdbot
I built a Clawdbot skill that auto-reports every new ban to AbuseIPDB. If you're running Clawdbot, install it:
clawdhub install fail2ban-reporter
Or grab it from GitHub:
git clone https://github.com/jestersimpps/clawdbot-fail2ban-reporter.gitsudo bash clawdbot-fail2ban-reporter/scripts/install.sh
After setup, every fail2ban ban automatically reports to AbuseIPDB. Zero effort, maximum community impact.
Step 11: Monitor Ongoing Attacks
Check who's been banned:
sudo fail2ban-client status sshd
Watch live bans:
sudo tail -f /var/log/fail2ban.log | grep Ban
Check recent SSH attempts:
journalctl -u ssh -n 50 | grep -E "Failed|Invalid"
Watch endlessh trap bots in real-time:
sudo journalctl -f -u endlessh
You'll see connections that just hang there. Each one is a bot wasting time instead of bothering your real SSH.
What Attackers Actually Try
From my server logs in just one hour:
- oracle โ Oracle DB default user
- postgres โ PostgreSQL default
- git โ GitLab/Gitea servers
- solana โ Crypto node operators (popular target)
- HwHiAiUser โ Huawei device default
- admin, root โ The classics
- ftpuser โ Legacy FTP
They spray common usernames hoping something sticks. fail2ban stops them after 3 attempts, but now they hit the tarpit first.
The Complete 5-Minute Setup
Copy-paste this entire block to harden a fresh Clawdbot server:
# Install security toolssudo apt update && sudo apt install -y fail2ban ufw endlessh gnupg pass# Generate SSH key (run on your LOCAL machine)ssh-keygen -t ed25519 -C "your_email@example.com"ssh-copy-id user@your-server-ip# Disable password auth (ON THE SERVER)sudo sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_configsudo sed -i 's/^#*PubkeyAuthentication.*/PubkeyAuthentication yes/' /etc/ssh/sshd_config# Move SSH to port 2222 for tarpit setupsudo mkdir -p /etc/systemd/system/ssh.socket.d/sudo tee /etc/systemd/system/ssh.socket.d/override.conf << 'EOF'[Socket]ListenStream=ListenStream=2222EOF# Configure endlessh tarpit on port 22sudo mkdir -p /etc/endlesshsudo tee /etc/endlessh/config << 'EOF'Port 22Delay 10000MaxLineLength 32MaxClients 4096LogLevel 1EOF# Fix endlessh systemd servicesudo mkdir -p /etc/systemd/system/endlessh.service.d/sudo tee /etc/systemd/system/endlessh.service.d/override.conf << 'EOF'[Service]PrivateUsers=falseEOF# Configure fail2bansudo tee /etc/fail2ban/jail.local << 'EOF'[sshd]enabled = trueport = sshfilter = sshdlogpath = /var/log/auth.logmaxretry = 3bantime = 2592000findtime = 600EOF# Configure firewallsudo ufw default deny incomingsudo ufw default allow outgoingsudo ufw allow 2222/tcp # New SSH portsudo ufw allow 80/tcp # HTTPsudo ufw allow 443/tcp # HTTPSsudo ufw --force enable# Start everythingsudo systemctl daemon-reloadsudo systemctl restart ssh.socket sshsudo systemctl enable --now fail2ban endlessh# Fix Clawdbot permissionschmod 700 ~/.clawdbotchmod 600 ~/.clawdbot/clawdbot.json 2>/dev/null# Run Clawdbot security auditclawdbot security audit --fix# Verify everythingecho "=== SSH on port 2222 ==="ss -tlnp | grep :2222echo "=== endlessh tarpit on port 22 ==="ss -tlnp | grep :22echo "=== fail2ban status ==="sudo fail2ban-client status sshdecho "=== ufw status ==="sudo ufw status verboseecho "=== Clawdbot security ==="clawdbot security audit
Important: After running this, connect via SSH on the new port:
ssh user@your-server-ip -p 2222
The Complete Security Stack
After following this guide, your Clawdbot server has:
- SSH key authentication โ No passwords to brute-force
- Password auth disabled โ Even if they guess right, it won't work
- endlessh tarpit โ Bots waste time on port 22 while real SSH hides on 2222
- fail2ban โ 3 attempts โ 30-day ban (backup layer)
- ufw firewall โ Only necessary ports exposed
- pass โ Secrets GPG-encrypted at rest
- Clawdbot pairing โ Strangers can't message your bot
- Proper permissions โ Config and credentials locked down
- AbuseIPDB reporting โ Attackers get blacklisted globally
That's defense in depth. Multiple layers, each one making the next attack harder. The tarpit is the cherry on top โ instead of just blocking attackers, you're wasting their time and resources while keeping your real services hidden.
It's not paranoia when every server with a public IP gets attacked within hours. Five minutes of setup. Sleep better at night.
For more Clawdbot security details, check the official security docs.
Stay Updated
Get notified about new posts on automation, productivity tips, indie hacking, and web3.
No spam, ever. Unsubscribe anytime.


