{"slug": "don-t-trust-the-config-6-tests-that-prove-your-self-hosted-ai-agent-server-is", "title": "Don't trust the config: 6 tests that prove your self-hosted AI agent server is locked down", "summary": "A developer who hardens personal AI agents on VPSes published a six-test checklist for verifying that self-hosted agent servers are actually locked down, arguing that config files can claim one thing while the machine does another. The tests cover external port scanning and loopback-only listening, querying sshd directly for password and root login settings, confirming fail2ban bans after deliberate failed logins, killing the agent's systemd user service to verify auto-restart and lingering, triggering alerts to prove delivery, and checking API-key spend limits since neither OpenClaw nor Hermes ships a hard spend cap. The author reports restart times of 6–7 seconds for the process and 22–42 seconds to first Telegram reply, fail2ban bans landing after 7 and 127 seconds on test VMs, and rollbacks taking 1–3.5 minutes.", "body_md": "*Disclosure: I wrote this with AI assistance and checked every command and number against my own test runs.*\n\nA personal AI agent on a VPS (OpenClaw, Hermes) tends to get \"secured\" by reading config files. The config says one thing; the machine does another. Here are six tests I run that check behavior, not settings. Each takes a few minutes. Replace `AGENT_UNIT`, `YOUR_IP` and `USER` with yours.\n\n`ufw status`\nFrom another machine, not the server itself and not localhost:\n\n```\nnmap -Pn -p- -sS YOUR_IP\nnmap -Pn -sU --top-ports 100 YOUR_IP\n```\n\nExpected: only `22/tcp` open. Run it after the agent is started, since the agent's own ports are open by then. If you see the agent gateway port or a dashboard, fix that before anything else. On the server, also check that the agent listens on loopback only:\n\n```\nsudo ss -tulnp\n```\n\nEverything except sshd should be on loopback: `127.0.0.1` or `::1` for the agent (OpenClaw's gateway is on 18789). Two harmless system entries show up on any Ubuntu: the DNS resolver on `127.0.0.53`/` 127.0.0.54` and the DHCP client on `udp/68`. Open the panel through a tunnel: `ssh -N -L PORT:127.0.0.1:PORT USER@YOUR_IP`.\n\n`grep /etc/ssh/sshd_config` misses drop-ins (cloud images often ship one in `sshd_config.d/` that re-enables password login). Ask the daemon:\n\n```\nsudo sshd -T | grep -Ei 'passwordauthentication|kbdinteractiveauthentication|permitrootlogin'\n```\n\nExpected: `no`, `no`, `no`. Then test it from outside:\n\n```\nssh -o PreferredAuthentications=password -o PubkeyAuthentication=no USER@YOUR_IP\n```\n\nYou should get `Permission denied` without a password prompt.\n\n`systemctl is-active fail2ban` proves a process exists. From a second host, fail a few SSH logins on purpose, then on the server:\n\n```\nsudo fail2ban-client status sshd\n```\n\nThe attacker IP should appear under banned. On my test VMs the ban came after 7 and 127 seconds, depending on timing of retries. Unban yourself afterwards: `sudo fail2ban-client set sshd unbanip IP`.\n\nAgents usually run as a user service, so ask the user's systemd, not the system one (the system one will happily print `MainPID=0`):\n\n```\nsudo systemctl --user -M USER@ show AGENT_UNIT -p MainPID -p Restart\nsudo kill -9 <PID>\n```\n\nThe unit should restart on its own (`Restart=always` or `on-failure`), and the agent should answer in Telegram without you touching it. A user service also needs lingering, or it won't survive a reboot:\n\n```\nloginctl show-user USER -p Linger\n```\n\nI measured 6–7 s for the process and 22–42 s until the first Telegram reply.\n\nAn alert that was never triggered is a hope. Stop the agent and see whether a message reaches your phone within a couple of minutes. Two traps: the alert must go through your own bot, not the bot of whoever set it up; and no alert can fire if the whole VPS dies, so keep an external uptime ping too. Proof of delivery in a script: the Bot API `sendMessage` response has `\"ok\": true` and a `message_id`.\n\nNeither OpenClaw nor Hermes has a built-in hard spend cap. The cap that holds is on the API key. With OpenRouter:\n\n```\ncurl -s https://openrouter.ai/api/v1/key -H \"Authorization: Bearer $KEY\"\n```\n\nLook at `limit` (not null) and `limit_reset` (monthly, if you want a monthly ceiling). Don't paste the key into logs or shell history; read it from a root-only file.\n\nTake a snapshot of the agent state before each update (OpenClaw: `openclaw backup create --verify`; Hermes: `hermes backup`), pin the version, turn off self-update, and once, on a quiet evening, install a deliberately bad version and make sure you can return. My rollback runs took 1–3.5 minutes including checks.\n\nQuestions or checks I missed? I'd like to add them.\n\nAbout the author: I set up and harden personal AI agents on VPSes.", "url": "https://wpnews.pro/news/don-t-trust-the-config-6-tests-that-prove-your-self-hosted-ai-agent-server-is", "canonical_source": "https://dev.to/ownagent/dont-trust-the-config-6-tests-that-prove-your-self-hosted-ai-agent-server-is-locked-down-2k8k", "published_at": "2026-10-22 10:03:15+00:00", "updated_at": "2026-10-22 10:11:43.749523+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "mlops"], "entities": ["OpenClaw", "Hermes", "OpenRouter", "fail2ban", "systemd", "Telegram", "Ubuntu"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/don-t-trust-the-config-6-tests-that-prove-your-self-hosted-ai-agent-server-is", "markdown": "https://wpnews.pro/news/don-t-trust-the-config-6-tests-that-prove-your-self-hosted-ai-agent-server-is.md", "text": "https://wpnews.pro/news/don-t-trust-the-config-6-tests-that-prove-your-self-hosted-ai-agent-server-is.txt", "jsonld": "https://wpnews.pro/news/don-t-trust-the-config-6-tests-that-prove-your-self-hosted-ai-agent-server-is.jsonld"}}