Using Fail2ban on a Docker Host for SSH Containers

I needed to deploy some containers running SSHD on a Docker host (it’s a long story as to why I needed to do this), and I wanted to use the Fail2Ban component alongside to block any brute force attackers. I found quite a few articles/posts of people either trying to do the same thing, or wanting to do the same thing – again all with various reasons as to why it was necessary. It turned out to be a little more fiddly than I was expecting as Fail2Ban when used to monitor SSHD, is expecting to run on the same host as SSHD and look in the auth.log file for a format that it expects.

When you’re trying to run Fail2Ban on a Docker Host to monitor the SSHD component inside containers though, its a little more complex due to the way logs are created by the containers. Rather than the SSHD component in the containers writing to a local log file, they instead output to STDOUT. This is picked up by the container runtime and then logged according to the Docker host configuration. It could be that the Docker host is logging to JSON files, or in my case, logging to the systemd journal.

That meant that there were several specific pieces of configuration I needed to apply to the host Fail2Ban configuration. The first is in the /etc/fail2ban/jail.local file. You need to change the backend to systemd to read from the systemd journal instead of log files.

[DEFAULT]
backend=systemd

You then need to specify the SSHD jail configuration, which I did in the /etc/fail2ban/jail.d/sshd.conf file. There are several non-standard configurations here.

  • The first is specifying the backend as systemd (again it seems).
  • The second is using journalmatch to tell Fail2Ban which filter to apply to the systemd journal (in this case its which container’s logs to monitor).
  • The third is the filter, as Fail2Ban is by default expecting to be reading from an auth.log file for a local SSHD daemon. This means its literally looking for the sshd[XXX] string in the log file. We don’t have that here, as we’re not looking at log output from SSHD, but rather log output of a container. So we need to override the daemon name in the SSHD log filter, and replace it with the container name.
  • The fourth is to use the DOCKER-USER chain to apply the iptables ban so that it affects the Docker containers, rather than the host.
[sshd]
enabled = true
port = ssh
bantime = 86400
maxretry = 3
backend = systemd
journalmatch = CONTAINER_NAME=insertcontainernamehere
filter = sshd[mode=aggressive,__pref="",_daemon=insertcontainernamehere]
chain = DOCKER-USER

This worked well, and I didn’t need to specify a custom regex in the jail configuration file.

~ Mike

3 thoughts on “Using Fail2ban on a Docker Host for SSH Containers

  1. If you get the error ‘iptables: No chain/target/match by that name.’ in your fail2ban.log file, change the fail2ban service to require docker to be started first – otherwise it can’t find the required iptables chains when it starts and you’ll get that error. Edit the ‘/lib/systemd/system/fail2ban.service’ file, and add ‘docker.service’ to the end of the After= line.

  2. On my system, the `_daemon` is not the docker container’s name (e.g. “ssh”), but the docker container’s id (here `15121202fb18`, for example:

    Oct 02 15:33:52 example.com 15121202fb18[638754]: Server listening on 0.0.0.0 port 22.
    Oct 02 15:33:52 example.com 15121202fb18[638754]: Server listening on :: port 22.

    How can I set it to be the container’s name or how can I adjust `_daemon` to match that changing id?

    1. I found a solution that works for me:

      Add `container_name` to every service in the docker composition and add

      “log-driver”: “journald”,
      “log-opts”:
      {
      “tag”: “{{.Name}}”
      }

      to /etc/docker/daemon.json.

      With these settings the container_name shows up in the journald logs.

Leave a comment