From c92580bfac773deee3b0cea7a89862d7231c49f9 Mon Sep 17 00:00:00 2001 From: Eric Wolf Date: Thu, 20 Aug 2026 15:19:02 +0200 Subject: [PATCH 1/3] fail2ban: include fixes for systemd socket activation --- pkgs/by-name/fa/fail2ban/package.nix | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pkgs/by-name/fa/fail2ban/package.nix b/pkgs/by-name/fa/fail2ban/package.nix index bbfe641c5581..32a161c8edc4 100644 --- a/pkgs/by-name/fa/fail2ban/package.nix +++ b/pkgs/by-name/fa/fail2ban/package.nix @@ -2,6 +2,7 @@ lib, stdenv, fetchFromGitHub, + fetchpatch, python3, installShellFiles, nixosTests, @@ -57,6 +58,16 @@ python3.pkgs.buildPythonApplication (finalAttrs: { doCheck = false; + patches = [ + # fixes for systemd socket activation - remove next release + (fetchpatch { + url = "https://github.com/fail2ban/fail2ban/commit/403df4a91c8ad8f235a3cb9e17d0cc4d29c2dafd.patch"; + hash = "sha256-HI/9qaB+TMbRu/2NPwV+kVcYK03YNnzcD+iaBOlM20w="; + # causes merge conflicts + excludes = [ "ChangeLog" ]; + }) + ]; + preInstall = '' substituteInPlace setup.py --replace /usr/share/doc/ share/doc/ From a7750865cbbfb105d548b28bc1eb36f064918b94 Mon Sep 17 00:00:00 2001 From: Eric Wolf Date: Thu, 20 Aug 2026 03:57:35 +0200 Subject: [PATCH 2/3] nixos/fail2ban: use systemd socket activation This includes some cleanup to remove redundant entries in `serviceConfig`. --- nixos/doc/manual/release-notes/rl-2611.section.md | 2 ++ nixos/modules/services/security/fail2ban.nix | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/nixos/doc/manual/release-notes/rl-2611.section.md b/nixos/doc/manual/release-notes/rl-2611.section.md index f0269cb2d99a..a04d2a6e65e4 100644 --- a/nixos/doc/manual/release-notes/rl-2611.section.md +++ b/nixos/doc/manual/release-notes/rl-2611.section.md @@ -258,6 +258,8 @@ - `services.gitlab.registry` now uses PostgreSQL as database storage for new installations and supports old installations that use the filesystem as metadata storage. It creates the required PostgreSQL database and user. Users can manually migrate their filesystem based metadata storage. See [GitLab Container Registry Migration to database metadata store](#module-services-gitlab-registry-database-migration). +- `services.fail2ban` now supports systemd socket activation via `fail2ban.socket` + - Enabling [`services.userborn`](#opt-services.userborn.enable) on a system that was previously managed by the default `update-users-groups.pl` script now imports the legacy state from `/var/lib/nixos/` on the first switch. Locked stub entries are added to `/etc/passwd` and `/etc/group` for every name recorded in `uid-map`/`gid-map` that no longer has a live entry, so a previously-used UID/GID cannot be reassigned to a different user. Subordinate id ranges recorded in `auto-subuid-map` are seeded into the subid files as well. If the import fails, userborn does not start and the user database is left untouched. Inspect `journalctl -u userborn-import-legacy.service`, fix or remove the legacy state, and switch again. The import can be skipped entirely with [`services.userborn.importLegacyState`](#opt-services.userborn.importLegacyState)` = false`. - The `newuidmap` and `newgidmap` security wrappers are now installed with `cap_setuid`/`cap_setgid` file capabilities instead of the setuid-root bit, matching shadow's `--with-fcaps` install mode and other major distributions. Rootless containers (podman, docker-rootless, unprivileged user namespaces) are unaffected. The only behavioural change is that mapping host uid 0 via `/etc/subuid` (which NixOS never configures by default) additionally requires `cap_setfcap`; users who explicitly grant uid 0 in a subuid range can restore the previous behaviour with `security.wrappers.newuidmap.capabilities = lib.mkForce "cap_setuid,cap_setfcap+ep";`. diff --git a/nixos/modules/services/security/fail2ban.nix b/nixos/modules/services/security/fail2ban.nix index 87f991a4c7e9..214a9c1da378 100644 --- a/nixos/modules/services/security/fail2ban.nix +++ b/nixos/modules/services/security/fail2ban.nix @@ -400,12 +400,8 @@ in # Security NoNewPrivileges = true; # Directory - RuntimeDirectory = "fail2ban"; RuntimeDirectoryMode = "0750"; - StateDirectory = "fail2ban"; StateDirectoryMode = "0750"; - LogsDirectory = "fail2ban"; - LogsDirectoryMode = "0750"; # Sandboxing ProtectSystem = "strict"; ProtectHome = true; @@ -417,6 +413,10 @@ in ProtectControlGroups = true; }; }; + systemd.sockets.fail2ban.wantedBy = [ + "sockets.target" + "fail2ban.service" + ]; # Defaults for the daemon settings services.fail2ban.daemonSettings.Definition = { From 4ac79832706c1828d27930e4ac275babb4b5f547 Mon Sep 17 00:00:00 2001 From: Eric Wolf Date: Thu, 20 Aug 2026 03:58:18 +0200 Subject: [PATCH 3/3] nixosTests.fail2ban: improve tests - fix ban sometimes resulting in timeouts - test version reporting - explicitly test for connection between server and client - test unbanning - test `fail2ban-regex` executable - test socket activation --- nixos/tests/fail2ban.nix | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/nixos/tests/fail2ban.nix b/nixos/tests/fail2ban.nix index 97e5b4edaeab..934319a3939a 100644 --- a/nixos/tests/fail2ban.nix +++ b/nixos/tests/fail2ban.nix @@ -2,7 +2,7 @@ { name = "fail2ban"; - nodes.machine = _: { + nodes.machine = { ... }: { services.fail2ban = { enable = true; bantime-increment.enable = true; @@ -11,7 +11,7 @@ networking.nftables.enable = true; }; - nodes.client = _: { + nodes.client = { pkgs, ... }: { environment.systemPackages = [ pkgs.sshpass pkgs.netcat @@ -31,16 +31,41 @@ client_addr = "2001:db8:1::1" machine_addr = "2001:db8:1::2" + # Verify that querying the version works + clientVersion = machine.succeed("fail2ban-client -V").rstrip() + t.assertEqual(clientVersion, "${pkgs.fail2ban.version}") + serverVersion = machine.succeed("fail2ban-server -V").rstrip() + t.assertEqual(serverVersion, "${pkgs.fail2ban.version}") + + # Verify that fail2ban-client can communicate with the server + machine.succeed("fail2ban-client ping") + # Verify there is not ban and the port is reachable from the client. machine.succeed(f"test 0 -eq $(fail2ban-client get sshd banned {client_addr})") client.succeed(f"nc -w3 -z {machine_addr} 22") - # Cause authentication failure log entries. - for _ in range(2): - client.fail(f"sshpass -p 'wrongpassword' ssh -o StrictHostKeyChecking=no {machine_addr}") + # Cause authentication failure log entries (detach second command since ban may cause timeout). + client.fail(f"sshpass -p 'wrongpassword' ssh -o StrictHostKeyChecking=no {machine_addr}") + client.execute(f"sshpass -p 'wrongpassword' ssh -o StrictHostKeyChecking=no {machine_addr} >&2 &") # Verify there is a ban and the port is unreachable from the client. machine.wait_until_succeeds(f"test 1 -eq $(fail2ban-client get sshd banned {client_addr})") client.fail(f"nc -w3 -z {machine_addr} 22") + + # Verify that unbanning works + machine.succeed(f"fail2ban-client unban {client_addr}") + client.succeed(f"nc -w3 -z {machine_addr} 22") + + # Verify that fail2ban-regex works + regex = r"^matching log entry: $" + line = "matching log entry: 1.2.3.4" + matches = machine.succeed(f"fail2ban-regex -o matches '{line}' '{regex}'") + t.assertIn(line, matches) + + # Verify that socket activation works + machine.succeed("systemctl stop fail2ban.service") + machine.fail("systemctl --quiet is-active fail2ban.service") + machine.succeed("fail2ban-client ping") + machine.succeed("systemctl --quiet is-active fail2ban.service") ''; }