The scanner still has to knock. I just made sure knocking is a permanent mistake.
Every few months something gets posted on X about a scary new hacking tool that is going to destroy everything.
People panic. Threads get written. Someone calls it terrifying.
Then someone replies with two lines and a shrug.
That reply is correct. It is not clever. It is just correct.
Scanners still have to knock on doors. They cannot skip that part. So if you leave the right door open, their thoroughness becomes your advantage.
Here is the full setup. Tested on production. No fluff.
Why Port 21?
Port 21 is FTP. It has been dead on real production servers for years.
Nobody legitimate is touching it. Not your users. Not your cron jobs. Not your monitoring stack.
So the rule is simple: anything that connects to port 21 is a scanner. Ban it immediately on all ports. Permanently.
Port scanners move fast. They enumerate everything and probe every surface in seconds. The honeypot does not try to be smarter. It just catches the first move before anything else happens.
One probe. One ban. Done.
The Architecture in Plain Terms
Internet → Port 21 → Honeypot (fake FTP) → writes to log
↓
Fail2Ban reads log
↓
Firewall bans IP permanently (all ports)
Two components. One bash script. One Fail2Ban jail.
Step 1: Build the Honeypot
We use netcat to sit on port 21, respond with a fake FTP banner and log whoever showed up.
Create the script
sudo nano /usr/local/bin/honeypot-ftp.sh #!/bin/bash
LOG="/var/log/honeypot-ftp.log"
while true; do
nc -l 21 -w 3 <<< "220 FTP Server Ready" 2>/dev/null
echo "$(date '+%Y-%m-%d %H:%M:%S') HONEYPOT_HIT port=21" >> "$LOG"
done
ncsyntax note: Ubuntu usesnetcat-openbsdwhich takes-l -p 21. CentOS and AlmaLinux shipnmap-ncatwhich uses-l 21without the-pflag. The script above works on both.
sudo chmod +x /usr/local/bin/honeypot-ftp.sh
Wrap it in a systemd service
sudo nano /etc/systemd/system/honeypot-ftp.service [Unit]
Description=FTP Honeypot on Port 21
After=network.target
[Service]
ExecStart=/usr/local/bin/honeypot-ftp.sh
Restart=always
RestartSec=3
User=nobody
[Install]
WantedBy=multi-user.target sudo systemctl daemon-reload
sudo systemctl enable --now honeypot-ftp
sudo systemctl status honeypot-ftp
Runs as nobody. No root exposure.
Step 2: Auto-Ban with Fail2Ban
Fail2Ban watches the log and the moment it sees a HONEYPOT_HIT entry, it fires.
Install
Ubuntu 24.04:
sudo apt update && sudo apt install -y fail2ban netcat-openbsd
CentOS Stream 9 / AlmaLinux / Rocky Linux:
sudo dnf install -y epel-release
sudo dnf install -y fail2ban nmap-ncat
sudo systemctl enable --now firewalld
Create the filter
sudo nano /etc/fail2ban/filter.d/honeypot-ftp.conf [Definition]
failregex = ^%(__prefix_line)s.*HONEYPOT_HIT port=21.*$
ignoreregex =
datepattern = ^%%Y-%%m-%%d %%H:%%M:%%S
Create the jail
Always use .local extension. Never .conf. Package updates silently overwrite .conf files and your config disappears.
sudo nano /etc/fail2ban/jail.d/honeypot-ftp.local [honeypot-ftp]
enabled = true
filter = honeypot-ftp
logpath = /var/log/honeypot-ftp.log
maxretry = 1
bantime = -1
findtime = 3600
# Ubuntu 22.04 (iptables):
# action = iptables-allports[name=honeypot-ftp, protocol=all]
# Ubuntu 24.04+ (nftables - the new default):
action = nftables-allports[name=honeypot-ftp, protocol=all]
# CentOS Stream 9 / AlmaLinux / Rocky Linux (firewalld):
# action = firewallcmd-allports[name=honeypot-ftp, protocol=all]
maxretry = 1 means one strike. bantime = -1 means permanent. Not 24 hours. Not a week. Permanent.
Start Fail2Ban
sudo systemctl enable --now fail2ban
sudo fail2ban-client status honeypot-ftp
Firewall: Keep Port 21 Open
Do not block port 21 at the firewall level. The trap only works if they can reach it.
Ubuntu (UFW):
sudo ufw allow 21/tcp
CentOS / AlmaLinux (firewalld):
sudo firewall-cmd --permanent --add-port=21/tcp
sudo firewall-cmd --reload
Firewall Backend — This Is Where Most Guides Break
Distros changed their default firewall stacks and most copy-pasted guides from 2021 will silently fail on modern servers.
Ubuntu 22.04 uses iptables. Use iptables-allports.
Ubuntu 24.04 and newer switched to nftables by default. Use nftables-allports. The old iptables action will not work properly here.
CentOS Stream 9, AlmaLinux 9 and Rocky Linux 9 use firewalld. Use firewallcmd-allports. Not iptables. Not nftables. Firewalld has its own action and it expects you to use it.
Pick the wrong one and the bans will not apply. You will think it is working. It is not.
Verify It Is Working
# Watch hits in real time
sudo tail -f /var/log/honeypot-ftp.log
# Check who got banned
sudo fail2ban-client status honeypot-ftp
# Unban yourself after testing with your own IP
sudo fail2ban-client set honeypot-ftp unbanip YOUR_IP
Add More Ports
Port 21 is the starting point. Add honeypots on anything else that has no legitimate traffic on your server.
Port 23. Telnet. Nothing real uses this anymore.
Port 3389. RDP. If you are not running Windows servers, any connection here is a red flag.
Port 5900. VNC. Same logic.
Every extra port is another trap. Scanners check all of them. Every check is a permanent ban.
The Point
The tool is not magic. It is a fast scanner with good defaults.
Fast scanners are predictable. They have to check every port. That is how they work. The honeypot uses that against them.
You are not outsmarting anyone. You are just making the first move cost everything.
One touch on port 21. Gone from all ports before the real attack begins.