fail2ban test
This commit is contained in:
@@ -28,6 +28,9 @@
|
||||
};
|
||||
network = {
|
||||
enable = true;
|
||||
extraHosts = ''
|
||||
192.168.88.14 vault.cnst.dev
|
||||
'';
|
||||
interfaces = {
|
||||
"eno1" = {
|
||||
allowedTCPPorts = [22 80 443];
|
||||
|
||||
@@ -128,8 +128,8 @@
|
||||
services = {
|
||||
agenix = {
|
||||
enable = true;
|
||||
kima = {
|
||||
enable = false;
|
||||
sobotka = {
|
||||
enable = true;
|
||||
};
|
||||
};
|
||||
blueman = {
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
{
|
||||
{config, ...}: {
|
||||
server = {
|
||||
email = "adam@cnst.dev";
|
||||
domain = "cnst.dev";
|
||||
caddy = {
|
||||
enable = true;
|
||||
};
|
||||
cfFail2ban = {
|
||||
enable = true;
|
||||
apiKeyFile = config.age.secrets.cloudflareFirewallApiKey.path;
|
||||
zoneId = "0027acdfb8bbe010f55b676ad8698dfb";
|
||||
};
|
||||
vaultwarden = {
|
||||
enable = true;
|
||||
};
|
||||
|
||||
@@ -121,6 +121,7 @@
|
||||
imports = [
|
||||
./server
|
||||
./server/caddy
|
||||
./server/cfFail2ban
|
||||
./server/vaultwarden
|
||||
];
|
||||
};
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
109
modules/server/cfFail2ban/default.nix
Normal file
109
modules/server/cfFail2ban/default.nix
Normal 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
|
||||
'';
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -37,7 +37,16 @@ in {
|
||||
}
|
||||
'';
|
||||
|
||||
services.vaultwarden = {
|
||||
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;
|
||||
|
||||
@@ -58,4 +67,5 @@ in {
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
11
secrets/cloudflareFirewallApiKey.age
Normal file
11
secrets/cloudflareFirewallApiKey.age
Normal file
@@ -0,0 +1,11 @@
|
||||
age-encryption.org/v1
|
||||
-> ssh-ed25519 t9iOEg +WyTc7ny6fCCkqYl/y6IpkxOvXqqKEKCgQaFym1ZLSE
|
||||
fWBQRK/iIuPCJ4ktL0Sq9gJR8oI/vczLkDMbZr8IvNI
|
||||
-> ssh-ed25519 KUYMFA BkGncK6jhjVqFPaVAzE9e2d2IXdCdatmDL6qdsD/giQ
|
||||
l06NmVxHODsCOu1vWRoUpZGypsJYjSLOb1tT/lpbAi8
|
||||
-> ssh-ed25519 76RhUQ i3fS2lTIKCclVln0sygcwf1CRW1uDtytLwxgvpL37Co
|
||||
FwSf7i06olqD+XyvAkc6VHHEih70Gks31XYVow5teZU
|
||||
-> ssh-ed25519 Jf8sqw rNEYb8GjN1xn7LSzkx9B570trzum1oT+gB+qNxPA0l4
|
||||
DgX9IPNRImBz9JjSNopiCVyXWAnyFNa88fvw3LMJxBE
|
||||
--- CcmMHcWD0iWgn705hfOLdqo2xpJWIdJ7y+xEHicME6g
|
||||
<EFBFBD><EFBFBD><<3C>v<7F><76><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>0EG<45><47><1C>4<EFBFBD><34><EFBFBD><EFBFBD>ҥ<>%̥<>e<EFBFBD><65>ji<12><><EFBFBD><06><1E>]4yP<79><50>Ӥ,<2C>z<EFBFBD><7A>`#<13><><EFBFBD>l
|
||||
@@ -12,4 +12,5 @@ in {
|
||||
"gcapi.age".publicKeys = [cnst kima];
|
||||
"cloudflare-env.age".publicKeys = [cnst kima usobotka rsobotka];
|
||||
"vaultwarden-env.age".publicKeys = [cnst kima usobotka rsobotka];
|
||||
"cloudflareFirewallApiKey.age".publicKeys = [cnst kima usobotka rsobotka];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user