Remote IP behavior analysis and ban

Hi there,

First of all I love Caddy and it has been very good so far !

Some (bad) bots regularly scan my public IP address or try to access HTTPS websites with broken SSL versions. Caddy handles them nicely but I wish I could put an end to it and keep my logs beautiful. I am using the ipfilter plugin but it only blocks IPs on existing websites, not for the public IP address for example.

I want to create a plugin with the following ideas:

  • Store bad recurring remote IP addresses in a SQLite database
  • Ban bad recurring remote IP addresses (permanently or temporarily)
  • Evaluate how bad an IP address is depending on:
    • How often does it try to access unlisted SNI (such as with wildcard DNS or public IP)?
    • How often does it try to access unlisted HTTP hostname (such as with wildcard DNS or public IP)?
    • Is it static or dynamic: same pattern of attack?
    • How often does it try to access a website with a broken SSL version?

And I have the following questions:

  1. Does this plugin idea make any sense?
    1. If it does, do you have additional ideas on how to block/evaluate bad IP addresses?
    2. If it does, do you think it would be rather a HTTP Middleware plugin or a Listener Middleware or even an Event Hook plugin?
  2. If it does not make sense, what would you use instead as an alternative to solve this problem?

Thank you !

1 Like

Sounds like fail2ban for Caddy - writing this functionality into Caddy itself could have some merit, fail2ban is *nix-specific and would need some configuring for the kind of logic you’re proposing. Not to mention having some sane defaults preconfigured for this plugin to make it very simple to use is always a bonus.

On the other hand, Caddy doesn’t interact with the firewall, which is the best place to block this kind of stuff; it’d just be rejecting requests.

I’d say there’s definitely a set of users that would utilize this, especially if it’s convenient. Listener middleware would probably suit; no point going through request processing at all if we can figure out a bad actor during protocol negotiation, and I think a listener will have all the information you’d want to assess.

One thing I think might get complicated; generally, directives are limited in scope to the site definition they belong to. Could be worth thinking about whether your plugin affects the entire Caddy instance or only acts on hosts that you specify; if you invoke it multiple times, does the last one overwrite? Do you combine the strictest settings? Can you “exempt” a particular hostname from these checks? etc.

1 Like

First, sorry for the very late answer !

I think it would be nice to directly drop bad IPs (i.e. scanning your public IP if you don’t want to) within Caddy. Fail2ban works but has to read the logs of Caddy so the ban is delayed.

An extra thing would be store the IP addresses in a database which could be used by another program such as fail2ban/iptables to block them at the firewall/kernel level.

I think the plugin should be both global with optional overriding settings for each site definition in the Caddyfile.

I’ll work on this soon hopefully, and will update this thread.

Thanks!

1 Like

I have a change to ipfilters that should be merged soon which allows blacklisting/whitelisting IP addresses by creating files in the filesystem namespace. See Recognize blacklisted IP addrs stored in the file system namespace · Issue #29 · pyed/ipfilter · GitHub. I deliberately did it this way because I had already written an Apache access log monitor daemon which does what you describe. And having the daemon create a file based on the IP address was the cheapest, easiest, way to tell Apache those systems should be forbidden any access.

I don’t think functionality like interacting with iptables is appropriate for a Caddy plugin.

Lastly, switching from Apache+Wordpress to Caddy+Hugo really drove home something I already knew but hadn’t fully internalized. Simply configuring the web server to only accept requests that specify a valid HTTP request Host: header stops 99% of the attacks I see on my web server with no further effort on my part. That is, rather than :80 { use www.example.com:80 {. Obviously that isn’t viable if you’re using virtual hosts. But in my case, a personal web server for blogging, it makes sense to lock it down to the only valid hostname for accessing it.

1 Like

Hi there,

That looks great, I’ll have a deeper look soon. Let me know once it’s merged ! I was about to start working on my plugin, good I didn’t start earlier :slight_smile: I’ll let you know if any extra idea comes to my mind for this issue.

Thanks !

I got around to writing a unit test for my ipfilters enhancement yesterday so it has been merged. See ipfilter/README.md at master · pyed/ipfilter · GitHub. At the moment I’m just manually keeping an eye on my access log and doing touch ~/www/blacklist/$ip_addr when I notice an egregious offender. I’ll probably spend some time this weekend to adapt the monitoring daemon I had been using for Apache+Wordpress to Caddy to automate the process again. That should be fairly easy since I’m using a custom access log format for Caddy that is mostly identical to the one I had in place for Apache. I’ll probably write a blog post at www.skepticism.us when it’s working again and push the code to Github as an example for people like you.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.