My real SSH port was just as exposed as my fake one
Reading time: 5 minutes
Once the domain and HTTPS were live, I went looking for the next thing to fix and found something dumber than expected: the box that hosts my honeypot had a second, unintentional honeypot running right next to it — my real SSH.
Checking what was actually listening
I'd been assuming the only thing exposed to the world was Cowrie, the fake SSH on port 2222. A quick check said otherwise:
sudo ss -tlnp | grep -E ':22|:2222'
Port 2222 was twistd, the Cowrie process, exactly as expected. But port 22 — the real sshd — was also listening on 0.0.0.0, and a look at the EC2 security group confirmed it: SSH was open to 0.0.0.0/0, same as the honeypot port. Any scanner on the internet had just as much access to my real login as to the fake one. The only difference was that one of them was supposed to be open.
Closing the real door
Fixed it in the security group rather than on the box itself — changed the source on the port 22 rule from 0.0.0.0/0 to "My IP", which auto-fills your current public IP. That's a live IP restriction with no risk of locking myself out, since editing a security group doesn't require SSH access in the first place.
The one tradeoff: my office IP isn't static, so if it changes I'll need to go back and update that rule. Worth it — it's a two-minute fix from a browser, not a config file I could typo my way into a lockout with.
Checked sshd itself while I was in there. Turned out password auth was already disabled — key-only login, set at some point I don't even remember. So even with the port wide open, nobody was ever going to brute-force their way in. The security group fix was about cutting log noise and attack surface, not plugging an active hole. Good to know the floor was already there; better to not be relying on it as the only floor.
Locking /admin behind a second door
The Bludit login at /admin was sitting behind nothing but its own password field. Added HTTP Basic Auth in front of it at the Nginx level, so a browser prompt has to be cleared before the actual login page even loads. One thing that's easy to get wrong: a top-level Nginx location and a regex .php location don't share context by default, so the auth_basic directive has to be duplicated inside a nested location for the PHP handler too, or the rule silently stops applying once the request gets internally rewritten to index.php.
One thing I nearly did wrong: I almost pointed fail2ban at the honeypot port too, out of a vague instinct that "more banning is more secure." That would have defeated the entire point — Cowrie exists to keep attackers talking, not to get rid of them faster. Checked what fail2ban was actually watching, and found one jail, sshd, already running, already correctly scoped to the real port only. It had apparently been there the whole time and I'd just never checked.
What I actually learned
I'd mentally filed "the honeypot is exposed" as the interesting fact about this box and stopped thinking about what else was exposed by default. Every port a security-group wizard opens for you is equally open to everyone, whether you meant it to be a lure or not.
Restricting a security group rule to "my IP" is safer than it sounds, precisely because fixing it wrong just means editing the rule again from a browser — there's no way to lock yourself out of the one thing (the AWS console) you'd need to fix it.
"Appendix: every command from today's hardening pass
sudo ss -tlnp | grep -E ':22|:2222'
sudo grep -i "^Port" /etc/ssh/sshd_config
sudo grep -iE "PasswordAuthentication|PermitRootLogin|PubkeyAuthentication" /etc/ssh/sshd_config
sudo apt install -y apache2-utils
sudo htpasswd -c /etc/nginx/.htpasswd admin
sudo nginx -t
sudo systemctl reload nginx
sudo systemctl status fail2ban
sudo fail2ban-client status
Plus one change made entirely in the AWS console: the EC2 security group's port 22 rule, source changed from 0.0.0.0/0 to my own IP"
Next up: back to the actual honeypot — see what's actually landed in the Cowrie logs and write it up.