- Add explicit iptables banaction in security.nix for test compatibility - Force IPv4 in all curl requests to prevent IPv4/IPv6 mismatch issues - Fix caddy test: use basic_auth directive (not basicauth) - Override service ports in tests to match direct connections (not via Caddy) - Vaultwarden: override ROCKET_ADDRESS and ROCKET_LOG for external access - Immich: increase VM memory to 4GB for stability - Jellyfin: create placeholder log file and reload fail2ban after startup - Add tests.nix entries for all 6 fail2ban tests All tests now pass: ssh, caddy, gitea, vaultwarden, immich, jellyfin
92 lines
2.6 KiB
Nix
92 lines
2.6 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}:
|
|
let
|
|
testServiceConfigs = {
|
|
zpool_ssds = "";
|
|
zpool_hdds = "";
|
|
};
|
|
|
|
securityModule = import ../modules/security.nix;
|
|
|
|
sshModule =
|
|
{ config, lib, pkgs, ... }:
|
|
{
|
|
imports = [
|
|
(import ../services/ssh.nix {
|
|
inherit config lib pkgs;
|
|
username = "testuser";
|
|
})
|
|
];
|
|
};
|
|
in
|
|
pkgs.testers.runNixOSTest {
|
|
name = "fail2ban-ssh";
|
|
|
|
nodes = {
|
|
server =
|
|
{ config, lib, pkgs, ... }:
|
|
{
|
|
imports = [
|
|
securityModule
|
|
sshModule
|
|
];
|
|
|
|
# Override for testing - enable password auth
|
|
services.openssh.settings.PasswordAuthentication = lib.mkForce true;
|
|
|
|
users.users.testuser = {
|
|
isNormalUser = true;
|
|
password = "correctpassword";
|
|
};
|
|
|
|
networking.firewall.allowedTCPPorts = [ 22 ];
|
|
};
|
|
|
|
client = {
|
|
environment.systemPackages = with pkgs; [ sshpass openssh ];
|
|
};
|
|
};
|
|
|
|
testScript = ''
|
|
import time
|
|
|
|
start_all()
|
|
server.wait_for_unit("sshd.service")
|
|
server.wait_for_unit("fail2ban.service")
|
|
server.wait_for_open_port(22)
|
|
time.sleep(2)
|
|
|
|
with subtest("Verify sshd jail is active"):
|
|
status = server.succeed("fail2ban-client status")
|
|
assert "sshd" in status, f"sshd jail not found in: {status}"
|
|
|
|
with subtest("Generate failed SSH login attempts"):
|
|
# Use -4 to force IPv4, timeout and NumberOfPasswordPrompts=1 to ensure quick failure
|
|
# maxRetry is 3 in our config, so 4 attempts should trigger a ban
|
|
for i in range(4):
|
|
client.execute(
|
|
"timeout 5 sshpass -p 'wrongpassword' ssh -4 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=3 -o NumberOfPasswordPrompts=1 testuser@server echo test 2>/dev/null || true"
|
|
)
|
|
time.sleep(1)
|
|
|
|
with subtest("Verify IP is banned"):
|
|
# Wait for fail2ban to process the logs and apply the ban
|
|
time.sleep(5)
|
|
status = server.succeed("fail2ban-client status sshd")
|
|
print(f"sshd jail status: {status}")
|
|
# Check that at least 1 IP is banned
|
|
import re
|
|
match = re.search(r"Currently banned:\s*(\d+)", status)
|
|
assert match and int(match.group(1)) >= 1, f"Expected at least 1 banned IP, got: {status}"
|
|
|
|
with subtest("Verify banned client cannot connect"):
|
|
# Use -4 to test with same IP that was banned
|
|
exit_code = client.execute("timeout 3 nc -4 -z -w 2 server 22")[0]
|
|
assert exit_code != 0, "Connection should be blocked for banned IP"
|
|
'';
|
|
}
|