fail2ban test

This commit is contained in:
2025-07-16 14:07:14 +02:00
parent 26bd1b3487
commit 4116a0e557
10 changed files with 165 additions and 19 deletions

View File

@@ -121,6 +121,7 @@
imports = [
./server
./server/caddy
./server/cfFail2ban
./server/vaultwarden
];
};

View File

@@ -27,6 +27,11 @@ in {
default = {};
description = "Network interface configurations.";
};
extraHosts = mkOption {
type = types.lines;
default = "";
description = "Extra entries for /etc/hosts.";
};
};
};
@@ -45,6 +50,7 @@ in {
enable = true;
inherit (cfg) interfaces;
};
extraHosts = cfg.extraHosts;
};
systemd.services.NetworkManager = {

View File

@@ -57,7 +57,7 @@ in {
})
(mkIf cfg.sobotka.enable {
secrets = {
# Add sobotka specific secrets here
cloudflareFirewallApiKey.file = "${self}/secrets/cloudflareFirewallApiKey.age";
};
})
(mkIf cfg.toothpc.enable {

View File

@@ -0,0 +1,109 @@
# from @notthebee
{
lib,
config,
pkgs,
...
}: let
cfg = config.server.cfFail2ban;
in {
options.server.cfFail2ban = {
enable = lib.mkEnableOption {
description = "Enable cloudflare fail2ban";
};
apiKeyFile = lib.mkOption {
description = "File containing your API key, scoped to Firewall Rules: Edit";
type = lib.types.str;
example = lib.literalExpression ''
Authorization: Bearer Qj06My1wXJEzcW46QCyjFbSMgVtwIGfX63Ki3NOj79o=
'''
'';
};
zoneId = lib.mkOption {
type = lib.types.str;
};
jails = lib.mkOption {
type = lib.types.attrsOf (
lib.types.submodule {
options = {
serviceName = lib.mkOption {
example = "vaultwarden";
type = lib.types.str;
};
failRegex = lib.mkOption {
type = lib.types.str;
example = "Login failed from IP: <HOST>";
};
ignoreRegex = lib.mkOption {
type = lib.types.str;
default = "";
};
maxRetry = lib.mkOption {
type = lib.types.int;
default = 3;
};
};
}
);
};
};
config = lib.mkIf cfg.enable {
services.fail2ban = {
enable = true;
extraPackages = [
pkgs.curl
pkgs.jq
];
jails =
lib.attrsets.mapAttrs (name: value: {
settings = {
bantime = "30d";
findtime = "1h";
enabled = true;
backend = "systemd";
journalmatch = "_SYSTEMD_UNIT=${value.serviceName}.service";
port = "http,https";
filter = "${name}";
maxretry = 3;
action = "cloudflare-token-agenix";
};
})
cfg.jails;
};
environment.etc = lib.attrsets.mergeAttrsList [
(lib.attrsets.mapAttrs' (
name: value: (lib.nameValuePair "fail2ban/filter.d/${name}.conf" {
text = ''
[Definition]
failregex = ${value.failRegex}
ignoreregex = ${value.ignoreRegex}
'';
})
)
cfg.jails)
{
"fail2ban/action.d/cloudflare-token-agenix.conf".text = let
notes = "Fail2Ban on ${config.networking.hostName}";
cfapi = "https://api.cloudflare.com/client/v4/zones/${cfg.zoneId}/firewall/access_rules/rules";
in ''
[Definition]
actionstart =
actionstop =
actioncheck =
actionunban = id=$(curl -s -X GET "${cfapi}" \
-H @${cfg.apiKeyFile} -H "Content-Type: application/json" \
| jq -r '.result[] | select(.notes == "${notes}" and .configuration.target == "ip" and .configuration.value == "<ip>") | .id')
if [ -z "$id" ]; then echo "id for <ip> cannot be found"; exit 0; fi; \
curl -s -X DELETE "${cfapi}/$id" \
-H @${cfg.apiKeyFile} -H "Content-Type: application/json" \
--data '{"cascade": "none"}'
actionban = curl -X POST "${cfapi}" -H @${cfg.apiKeyFile} -H "Content-Type: application/json" --data '{"mode":"block","configuration":{"target":"ip","value":"<ip>"},"notes":"${notes}"}'
[Init]
name = cloudflare-token-agenix
'';
}
];
};
}

View File

@@ -37,24 +37,34 @@ in {
}
'';
services.vaultwarden = {
enable = true;
# environmentFile = config.age.secrets.vaultwarden-env.path;
services = {
cfFail2ban = lib.mkIf config.server.cfFail2ban.enable {
jails = {
vaultwarden = {
serviceName = "vaultwarden";
failRegex = "^.*Username or password is incorrect. Try again. IP: <HOST>. Username: <F-USER>.*</F-USER>.$";
};
};
};
vaultwarden = {
enable = true;
# environmentFile = config.age.secrets.vaultwarden-env.path;
backupDir = "/var/backup/vaultwarden";
backupDir = "/var/backup/vaultwarden";
config = {
DOMAIN = "https://vault.${domain}";
SIGNUPS_ALLOWED = false;
ROCKET_ADDRESS = "127.0.0.1";
ROCKET_PORT = 8222;
config = {
DOMAIN = "https://vault.${domain}";
SIGNUPS_ALLOWED = false;
ROCKET_ADDRESS = "127.0.0.1";
ROCKET_PORT = 8222;
logLevel = "warn";
extendedLogging = true;
useSyslog = true;
invitationsAllowed = false;
showPasswordHint = false;
# IP_HEADER = "CF-Connecting-IP";
logLevel = "warn";
extendedLogging = true;
useSyslog = true;
invitationsAllowed = false;
showPasswordHint = false;
# IP_HEADER = "CF-Connecting-IP";
};
};
};
};