From 438d847d1174b2ae3b432de813de58ad3a7acf78 Mon Sep 17 00:00:00 2001 From: cnst Date: Sun, 14 Sep 2025 08:27:13 +0200 Subject: [PATCH] feat(keepalived): init keepalived for pihole --- hosts/sobotka/server.nix | 4 + hosts/ziggy/server.nix | 4 + modules/default.nix | 1 + modules/server/keepalived/default.nix | 73 ++++++++++++ secrets/keepalived.age | 15 +++ secrets/secrets.nix | 156 ++++++++------------------ 6 files changed, 145 insertions(+), 108 deletions(-) create mode 100644 modules/server/keepalived/default.nix create mode 100644 secrets/keepalived.age diff --git a/hosts/sobotka/server.nix b/hosts/sobotka/server.nix index 9e9e7bda..e3210e09 100644 --- a/hosts/sobotka/server.nix +++ b/hosts/sobotka/server.nix @@ -63,6 +63,10 @@ syncthing = { enable = true; }; + keepalived = { + enable = true; + interface = "enp6s0"; + }; podman = { enable = true; gluetun.enable = true; diff --git a/hosts/ziggy/server.nix b/hosts/ziggy/server.nix index afd474ae..b8836727 100644 --- a/hosts/ziggy/server.nix +++ b/hosts/ziggy/server.nix @@ -48,6 +48,10 @@ fail2ban = { enable = false; }; + keepalived = { + enable = true; + interface = "enu1u1"; + }; podman = { enable = true; gluetun.enable = false; diff --git a/modules/default.nix b/modules/default.nix index 5a2af9a6..96f64506 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -136,6 +136,7 @@ ./server/podman ./server/unbound ./server/uptime-kuma + ./server/keepalived ]; }; settings = { diff --git a/modules/server/keepalived/default.nix b/modules/server/keepalived/default.nix new file mode 100644 index 00000000..5ba0b6c1 --- /dev/null +++ b/modules/server/keepalived/default.nix @@ -0,0 +1,73 @@ +{ + lib, + config, + self, + ... +}: +let + unit = "keepalived"; + cfg = config.server.${unit}; + + hostCfg = + hostname: + if hostname == "sobotka" then + { + ip = "192.168.88.14"; + priority = 20; + } + else if hostname == "ziggy" then + { + ip = "192.168.88.12"; + priority = 10; + } + else + throw "No keepalived config defined for host ${hostname}"; + + _self = hostCfg config.networking.hostName; + + allPeers = [ + "192.168.88.12" + "192.168.88.14" + ]; + + # Remove self from peers + peers = builtins.filter (ip: ip != _self.ip) allPeers; +in +{ + options.server.${unit} = { + enable = lib.mkEnableOption { + description = "Enable ${unit}"; + }; + interface = lib.mkOption { + type = lib.types.str; + example = "eth0"; + description = "The network interface keepalived should bind to."; + }; + }; + + config = lib.mkIf cfg.enable { + age.secrets.keepalived.file = "${self}/secrets/keepalived.age"; + services.keepalived = { + enable = true; + openFirewall = true; + + vrrpInstances.VI = { + interface = cfg.interface; + virtualRouterId = 69; + priority = _self.priority; + unicastPeers = peers; + virtualIps = [ + { + addr = "10.2.1.69/24"; + } + ]; + extraConfig = '' + authentication { + auth_type PASS + auth_pass ${config.age.secrets.keepalived.path} + } + ''; + }; + }; + }; +} diff --git a/secrets/keepalived.age b/secrets/keepalived.age new file mode 100644 index 00000000..7c09b2b5 --- /dev/null +++ b/secrets/keepalived.age @@ -0,0 +1,15 @@ +age-encryption.org/v1 +-> ssh-ed25519 t9iOEg XWSZ9PaQPb0MFf2ZzsCSFnIdMZsiwTkw1w4u644IVSs +11m94aK8vLC3Ua5bcwwt8vF1BxcUBNd2H9IbFz1L5eM +-> ssh-ed25519 KUYMFA 6S7sUrE7ffua0FIqUXArhEAM+l+X4xuiPNryrfeqJng +EJDyvEiAO4Pi6CfblE6wQ/mZV9ZpYpN2O9iLdEKMQis +-> ssh-ed25519 76RhUQ zyX7okInLUL8QrJ/it8jWWpCP/ZhhFplrohKhCaUAAM +oPpWTH74mwbesxTixAzANIuZg35sz18/6hJUcl8h0Nk +-> ssh-ed25519 Jf8sqw NGjWNNF7NDsO9PcIREorsyA8ggNLKYAKCqyDAQGvuxk +9llH7vdlwPLOLqKLZYpPWh8XB2wlRgnquz2uIKyjr7Y +-> ssh-ed25519 AzmhiA wG85fqKHKmzmnj39XSabc2E0U9waeiIf5klJUfF3n3I +3oA0rEQmsUmwigHGAtQG72kJw43B2/UovAzKN+kUarM +-> ssh-ed25519 qWEgFA HgBCuaJ5GcHMXBstvNR5J7AokqgZ+2WOTa5ScYp6PzM +p5+zVg4fe4dAysDHivbSHrgQMArhpXWKHFiHL4RBwLA +--- gJuBfLa4zVMUzQSRat93ImeNwTX3/WI1RyqHjCaCvSY +ˋEqPEi`7OV>hRn~,oes/;FA0=0 \ No newline at end of file diff --git a/secrets/secrets.nix b/secrets/secrets.nix index 530e092d..460e66a5 100644 --- a/secrets/secrets.nix +++ b/secrets/secrets.nix @@ -1,118 +1,58 @@ let + # --- Users --- cnst = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEUub8vbzUn2f39ILhAJ2QeH8xxLSjiyUuo8xvHGx/VB adam@cnst.dev"; kima = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJjoPdpiF8pjKN3ZEHeLEwVxoqwcCdzpVVlZkxJohFdg root@cnix"; + + # --- Hosts: sobotka --- usobotka = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG5ydTeaWcowmNXdDNqIa/lb5l9w5CAzyF2Kg6U5PSSu cnst@sobotka"; rsobotka = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJWLTYWowtpGmGolmkCE7+l9jr5QEnDqRxoezNqAIe+j root@nixos"; + + # --- Hosts: ziggy --- uziggy = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICtL8uBsJ3UL4+scqjEcyXYQOVlKziJk9YJ78YP6jCxq cnst@nixos"; rziggy = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHnca8xg1MZ4Hx5k5SVFSxcPnWc1O6r7w7JGYzX9aQm8 root@nixos"; + + # --- Groups --- + core = [ + cnst + kima + ]; + sobotka = [ + usobotka + rsobotka + ]; + ziggy = [ + uziggy + rziggy + ]; + all = core ++ sobotka ++ ziggy; in { - "cnstssh.age".publicKeys = [ - cnst - kima - ]; - "cnixssh.age".publicKeys = [ - cnst - kima - ]; - "certpem.age".publicKeys = [ - cnst - kima - ]; - "keypem.age".publicKeys = [ - cnst - kima - ]; - "mailpwd.age".publicKeys = [ - cnst - kima - ]; - "gcapi.age".publicKeys = [ - cnst - kima - ]; - "cloudflareEnvironment.age".publicKeys = [ - cnst - kima - usobotka - rsobotka - ]; - "vaultwardenEnvironment.age".publicKeys = [ - cnst - kima - usobotka - rsobotka - ]; - "homepageEnvironment.age".publicKeys = [ - cnst - kima - usobotka - rsobotka - ]; - "cloudflareFirewallApiKey.age".publicKeys = [ - cnst - kima - usobotka - rsobotka - ]; - "vaultwardenCloudflared.age".publicKeys = [ - cnst - kima - usobotka - rsobotka - ]; - "cloudflareDnsApiToken.age".publicKeys = [ - cnst - kima - usobotka - rsobotka - ]; - "cloudflareDnsCredentials.age".publicKeys = [ - cnst - kima - usobotka - rsobotka - ]; - "cloudflareDnsCredentialsZiggy.age".publicKeys = [ - cnst - kima - uziggy - rziggy - ]; - "wgCredentials.age".publicKeys = [ - cnst - kima - usobotka - rsobotka - ]; - "wgSobotkaPrivateKey.age".publicKeys = [ - cnst - kima - usobotka - rsobotka - ]; - "gluetunEnvironment.age".publicKeys = [ - cnst - kima - usobotka - rsobotka - ]; - "pihole.age".publicKeys = [ - cnst - kima - usobotka - rsobotka - ]; - "piholeZiggy.age".publicKeys = [ - cnst - kima - uziggy - rziggy - ]; - "slskd.age".publicKeys = [ - cnst - kima - usobotka - rsobotka - ]; + # Generic + "cnstssh.age".publicKeys = core; + "cnixssh.age".publicKeys = core; + "certpem.age".publicKeys = core; + "keypem.age".publicKeys = core; + "mailpwd.age".publicKeys = core; + "gcapi.age".publicKeys = core; + + # Shared between core + sobotka + "cloudflareEnvironment.age".publicKeys = core ++ sobotka; + "vaultwardenEnvironment.age".publicKeys = core ++ sobotka; + "homepageEnvironment.age".publicKeys = core ++ sobotka; + "cloudflareFirewallApiKey.age".publicKeys = core ++ sobotka; + "vaultwardenCloudflared.age".publicKeys = core ++ sobotka; + "cloudflareDnsApiToken.age".publicKeys = core ++ sobotka; + "cloudflareDnsCredentials.age".publicKeys = core ++ sobotka; + "wgCredentials.age".publicKeys = core ++ sobotka; + "wgSobotkaPrivateKey.age".publicKeys = core ++ sobotka; + "gluetunEnvironment.age".publicKeys = core ++ sobotka; + "pihole.age".publicKeys = core ++ sobotka; + "slskd.age".publicKeys = core ++ sobotka; + + # Ziggy-specific + "cloudflareDnsCredentialsZiggy.age".publicKeys = core ++ ziggy; + "piholeZiggy.age".publicKeys = core ++ ziggy; + + # Both sobotka + ziggy (for HA stuff like keepalived) + "keepalived.age".publicKeys = core ++ sobotka ++ ziggy; }