Last week I found that my home router was letting the whole internet reach the SMTP proxy I use for email signatures, and that someone had noticed before I did. A NAT rule I wrote months ago forwarded port 25 to the proxy with no restriction on where the traffic came from. The result was spam relayed through my Microsoft 365 tenant with my own domain on it. This is what I did about it over the following two days on a MikroTik RB5009, written up as the checklist I wish I had followed when I set the router up.

Stop the bleeding

The port 25 forward now only accepts connections from Microsoft’s published Exchange Online address ranges, held in an address list, and everything else on that port is logged and dropped.

/ip firewall address-list add list=m365-smtp address=40.92.0.0/15 comment="EOP"
# ... the rest of Microsoft's published ranges

/ip firewall nat set [find comment="smtp-proxy"] src-address-list=m365-smtp
/ip firewall filter add chain=forward protocol=tcp dst-port=25 \
    src-address-list=!m365-smtp action=drop log=yes log-prefix="smtp-drop"

Then I rotated the tenant’s connector credentials and checked the outbound queues. Then I went through every other NAT rule, and that is where the real work was.

The rule I had got backwards

The HTTPS forward to my reverse proxy was meant to accept only Cloudflare, since every public site I run sits behind Cloudflare. I had restricted it with dst-address-list. That matches the destination, which is always my own WAN address, so the rule matched everything. It should have been src-address-list. One word, and the restriction had never done anything. Check every NAT rule for this; it is an easy mistake and it looks right at a glance.

An auto-expiring block list

The core of the hardening is a single address list called wan_block with a two-week timeout. Several triggers feed it, and one early rule in the input and forward chains drops any new connection from an address on it.

  • Trap ports. Connections to ports I never use (telnet, a few common scanner targets) add the source to the list.
  • SMTP attempts from outside the Microsoft ranges.
  • Port scan detection with the built-in psd matcher.
  • Connection floods with connection-limit.
/ip firewall filter
add chain=input in-interface-list=WAN connection-state=new \
    src-address-list=wan_block action=drop
add chain=input in-interface-list=WAN protocol=tcp dst-port=23,2323,445 \
    action=add-src-to-address-list address-list=wan_block \
    address-list-timeout=2w comment="trap ports"
add chain=input in-interface-list=WAN protocol=tcp psd=21,3s,3,1 \
    action=add-src-to-address-list address-list=wan_block address-list-timeout=2w

A never_block list sits above it with my own external addresses, so I cannot lock myself out.

The block list only matches new connections. Existing legitimate sessions are untouched, which matters when a trap fires on a false positive. Within a few hours of enabling it the list held 581 addresses, and torrents, Plex and my remote support tool all carried on working.

Forward chain baseline

Drop invalid, and drop anything new from the WAN that was not explicitly port-forwarded.

add chain=forward connection-state=invalid action=drop
add chain=forward in-interface-list=WAN connection-state=new \
    connection-nat-state=!dstnat action=drop

Other things I tightened

  • Admin interfaces for internal services (such as the DNS filters) are forwarded only from internal networks.
  • The remote-support NAT rules now have dst-address set to the WAN IP. Without it they were catching traffic arriving over the site-to-site VPN and hijacking it.
  • Every router service has an available-from restriction, and TCP SYN cookies are on.

Lessons about logging

My first attempt at a brute-force detector was a scheduler script that scanned the in-memory log for failed logins. It missed slow attempts because the log had already rolled over. Firewall counters and address lists are the right tool; the log is not a database. And do not set log=yes on a per-packet rule to see what is happening. I did, briefly, and sent two million lines a day to syslog.

Do this before you start

Take a full /export and save it off the router. Apply the changes in Safe Mode so a mistake rolls back when the session drops. And work through NAT rules one at a time with the question “where should this traffic come from” rather than “where is it going”. The answer to the second one is always you.