diff --git a/.github/README.md b/.github/README.md index f59542a..99247e8 100644 --- a/.github/README.md +++ b/.github/README.md @@ -171,13 +171,14 @@ |๐Ÿ  **treehouse** | NVIDIA DGX Spark | AI Workstation, remote builder, hm-only-reference | |๐Ÿ–ฅ๏ธ **summers** | ASUS Z10PA-D8, 2* Intel Xeon E5-2650 v4, 128GB RAM | Homeserver (microvms), remote builder, data storage | |๐Ÿ–ฅ๏ธ **winters** | ASRock J4105-ITX, 32GB RAM | Homeserver (IoT server in spe) | - |๐Ÿ–ฅ๏ธ **hintbooth** | HUNSN RM02, 8GB RAM | Router | + |๐Ÿ–ฅ๏ธ **hintbooth** | HUNSN RM02, 8GB RAM | Router, DNS Resolver, home NGINX endpoint | |โ˜๏ธ **stoicclub** | Cloud Server: 1 vCPUs, 8GB RAM | Authoritative DNS server | |โ˜๏ธ **liliputsteps** | Cloud Server: 1 vCPUs, 8GB RAM | SSH bastion | |โ˜๏ธ **twothreetunnel**| Cloud Server: 2 vCPUs, 8GB RAM | Service proxy | |โ˜๏ธ **eagleland** | Cloud Server: 2 vCPUs, 8GB RAM | Mailserver | - |โ˜๏ธ **moonside** | Cloud Server: 4 vCPUs, 24GB RAM | Gaming server, syncthing + lightweight services | + |โ˜๏ธ **moonside** | Cloud Server: 4 vCPUs, 24GB RAM | Game servers, syncthing + other lightweight services | |โ˜๏ธ **belchsfactory** | Cloud Server: 4 vCPUs, 24GB RAM | Hydra builder and nix binary cache | + |๐ŸชŸ **chaostheater** | Asus Z97-A, i7-4790k, GTX970, 32GB RAM | Home Game Streaming Server (Windows/AtlasOS, not nix-managed) | |๐Ÿ“ฑ **magicant** | Samsung Galaxy Z Flip 6 | Phone | |๐Ÿ’ฟ **drugstore** | - | NixOS-installer ISO for bootstrapping new hosts | |๐Ÿ’ฟ **brickroad** | - | Kexec tarball for bootstrapping low-memory machines | diff --git a/.sops.yaml b/.sops.yaml index e69d45d..46194ef 100644 --- a/.sops.yaml +++ b/.sops.yaml @@ -19,6 +19,8 @@ keys: - &twothreetunnel age1g7atkxdlt4ymeh7v7aa2yzr2hq2qkvzrc4r49ugttm3n582ymv9qrmpk8d - &winters age1s0vssf9fey2l456hucppzx2x58xep279nsdcglvkqm30sr9ht37s8rvpza - &dgx age1ax5hqk6e2ekgfx5u7pl8ayc3vvhrehyvtvf07llaxhs5azpnny0qpltrns + - &hintbooth-adguardhome age1c2enwel9un28dcs4wg0vcyamx9a4a6g3walkhu8w5lqhmd804paq9d24as + - &hintbooth-nginx age1c2enwel9un28dcs4wg0vcyamx9a4a6g3walkhu8w5lqhmd804paq9d24as creation_rules: - path_regex: secrets/repo/[^/]+\.(yaml|json|env|ini|enc)$ key_groups: @@ -38,6 +40,8 @@ creation_rules: - *pyramid - *moonside - *dgx + - *hintbooth-adguardhome + - *hintbooth-nginx - path_regex: secrets/work/[^/]+\.(yaml|json|env|ini)$ key_groups: @@ -53,6 +57,7 @@ creation_rules: age: - *twothreetunnel - *eagleland + - *hintbooth-nginx - path_regex: hosts/nixos/x86_64-linux/pyramid/secrets/[^/]+\.(yaml|json|env|ini|enc)$ key_groups: @@ -131,6 +136,22 @@ creation_rules: age: - *hintbooth + - path_regex: hosts/nixos/x86_64-linux/hintbooth/secrets/adguardhome/[^/]+\.(yaml|json|env|ini|enc)$ + key_groups: + - pgp: + - *swarsel + age: + - *hintbooth + - *hintbooth-adguardhome + + - path_regex: hosts/nixos/x86_64-linux/hintbooth/secrets/nginx/[^/]+\.(yaml|json|env|ini|enc)$ + key_groups: + - pgp: + - *swarsel + age: + - *hintbooth + - *hintbooth-nginx + - path_regex: hosts/darwin/x86_64-darwin/nbm-imba-166/secrets/[^/]+\.(yaml|json|env|ini|enc)$ key_groups: - pgp: diff --git a/SwarselSystems.org b/SwarselSystems.org index b0d8309..4942346 100644 --- a/SwarselSystems.org +++ b/SwarselSystems.org @@ -884,13 +884,14 @@ Here I give a brief overview over the host machines that I am using. This is hel |๐Ÿ  **treehouse** | NVIDIA DGX Spark | AI Workstation, remote builder, hm-only-reference | |๐Ÿ–ฅ๏ธ **summers** | ASUS Z10PA-D8, 2* Intel Xeon E5-2650 v4, 128GB RAM | Homeserver (microvms), remote builder, data storage | |๐Ÿ–ฅ๏ธ **winters** | ASRock J4105-ITX, 32GB RAM | Homeserver (IoT server in spe) | - |๐Ÿ–ฅ๏ธ **hintbooth** | HUNSN RM02, 8GB RAM | Router | + |๐Ÿ–ฅ๏ธ **hintbooth** | HUNSN RM02, 8GB RAM | Router, DNS Resolver, home NGINX endpoint | |โ˜๏ธ **stoicclub** | Cloud Server: 1 vCPUs, 8GB RAM | Authoritative DNS server | |โ˜๏ธ **liliputsteps** | Cloud Server: 1 vCPUs, 8GB RAM | SSH bastion | |โ˜๏ธ **twothreetunnel**| Cloud Server: 2 vCPUs, 8GB RAM | Service proxy | |โ˜๏ธ **eagleland** | Cloud Server: 2 vCPUs, 8GB RAM | Mailserver | - |โ˜๏ธ **moonside** | Cloud Server: 4 vCPUs, 24GB RAM | Gaming server, syncthing + lightweight services | + |โ˜๏ธ **moonside** | Cloud Server: 4 vCPUs, 24GB RAM | Game servers, syncthing + other lightweight services | |โ˜๏ธ **belchsfactory** | Cloud Server: 4 vCPUs, 24GB RAM | Hydra builder and nix binary cache | + |๐ŸชŸ **chaostheater** | Asus Z97-A, i7-4790k, GTX970, 32GB RAM | Home Game Streaming Server (Windows/AtlasOS, not nix-managed) | |๐Ÿ“ฑ **magicant** | Samsung Galaxy Z Flip 6 | Phone | |๐Ÿ’ฟ **drugstore** | - | NixOS-installer ISO for bootstrapping new hosts | |๐Ÿ’ฟ **brickroad** | - | Kexec tarball for bootstrapping low-memory machines | @@ -1934,191 +1935,252 @@ Another note concerning [[https://flake.parts/][flake-parts]]: inputs.nix-topology.flakeModule ]; - perSystem.topology.modules = [ - ({ config, ... }: - let - inherit (self.outputs) globals; - inherit (config.lib.topology) - mkInternet - mkDevice - mkSwitch - mkRouter - mkConnection - ; - in - { - renderer = "elk"; + perSystem = { system, ... }: + let + inherit (self.outputs) lib; + in + { + topology.modules = [ + ({ config, ... }: + let + globals = self.outputs.globals.${system}; + inherit (config.lib.topology) + mkInternet + mkDevice + mkSwitch + mkRouter + mkConnection + ; + in + { + renderer = "elk"; - networks = { - home-lan = { - name = "Home LAN"; - inherit (globals.networks.home-lan) cidrv4; - }; - fritz-wg = { - name = "Wireguard Tunnel for Fritzbox net access"; - inherit (globals.networks.twothreetunnel-wg) cidrv4; - }; - wg = { - name = "Wireguard Tunnel for proxy access"; - inherit (globals.networks.twothreetunnel-wg) cidrv4; - }; - }; - - nodes = { - internet = mkInternet { - connections = [ - (mkConnection "fritzbox" "dsl") - (mkConnection "moonside" "wan") - (mkConnection "belchsfactory" "wan") - (mkConnection "twothreetunnel" "wan") - (mkConnection "stoicclub" "wan") - (mkConnection "liliputsteps" "wan") - (mkConnection "eagleland" "wan") - (mkConnection "magicant" "wifi") - (mkConnection "toto" "bootstrapper") - (mkConnection "hotel" "demo host") - ]; - }; - - - fritzbox = mkRouter "FRITZ!Box" { - info = "FRITZ!Box 7682"; - image = "${self}/files/topology-images/hunsn.png"; - interfaceGroups = [ - [ - "eth1" - "eth2" - "eth3" - "eth-wan" - "wifi" - ] - [ "dsl" ] - ]; - - connections = { - eth1 = mkConnection "winters" "eth1"; - eth2 = mkConnection "switch-bedroom" "eth1"; - eth3 = mkConnection "switch-livingroom" "eth1"; - eth-wan = mkConnection "hintbooth" "eth6"; - wgPyramid = mkConnection "pyramid" "fritz-wg"; - wgMagicant = mkConnection "magicant" "fritz-wg"; - wifiPyramid = mkConnection "pyramid" "wifi"; - wifiMagicant = mkConnection "magicant" "wifi"; - wifiBakery = mkConnection "bakery" "wifi"; - wifiMachpizza = mkConnection "machpizza" "wifi"; - }; - interfaces = { - eth1 = { - addresses = [ globals.networks.home-lan.hosts.fritzbox.ipv4 ]; - network = "home-lan"; + networks = { + fritz-lan = { + name = "Fritz!Box LAN"; + inherit (globals.networks.home-lan) cidrv4 cidrv6; }; - eth2 = { - addresses = [ globals.networks.home-lan.hosts.fritzbox.ipv4 ]; - network = "home-lan"; + services = { + name = "VLAN: Services"; + inherit (globals.networks.home-lan.vlans.services) cidrv4 cidrv6; }; - eth3 = { - addresses = [ globals.networks.home-lan.hosts.fritzbox.ipv4 ]; - network = "home-lan"; + home = { + name = "VLAN: Home"; + inherit (globals.networks.home-lan.vlans.home) cidrv4 cidrv6; }; - eth-wan = { - addresses = [ globals.networks.home-lan.hosts.fritzbox.ipv4 ]; - network = "home-lan"; + devices = { + name = "VLAN: Devices"; + inherit (globals.networks.home-lan.vlans.devices) cidrv4 cidrv6; }; - wifi = { - addresses = [ globals.networks.home-lan.hosts.fritzbox.ipv4 ]; - virtual = true; - network = "home-lan"; + guests = { + name = "VLAN: Guests"; + inherit (globals.networks.home-lan.vlans.guests) cidrv4 cidrv6; }; fritz-wg = { - addresses = [ globals.networks.fritz-wg.hosts.fritzbox.ipv4 ]; - network = "wg"; - virtual = true; - type = "wireguard"; + name = "WireGuard: Fritz!Box tunnel"; + inherit (globals.networks.fritz-wg) cidrv4 cidrv6; + }; + wgProxy = { + name = "WireGuard: Web proxy tunnel"; + inherit (globals.networks.twothreetunnel-wgProxy) cidrv4 cidrv6; + }; + wgHome = { + name = "WireGuard: Home proxy tunnel"; + inherit (globals.networks.home-wgHome) cidrv4 cidrv6; }; }; - }; - switch-livingroom = mkSwitch "Switch Livingroom" { - info = "TL-SG108"; - image = "${self}/files/topology-images/TL-SG108.png"; - interfaceGroups = [ - [ - "eth1" - "eth2" - "eth3" - "eth4" - "eth5" - "eth6" - "eth7" - "eth8" - ] - ]; - connections = { - eth2 = mkConnection "nswitch" "eth1"; - eth7 = mkConnection "pc" "eth1"; - eth8 = mkConnection "pyramid" "eth1"; + nodes = { + internet = mkInternet { + connections = [ + (mkConnection "fritzbox" "dsl") + (mkConnection "magicant" "wifi") + (mkConnection "liliputsteps" "lan") + (mkConnection "treehouse" "eth1") + (mkConnection "toto" "bootstrapper") + (mkConnection "hotel" "demo host") + ]; + }; + + + fritzbox = mkRouter "FRITZ!Box" { + info = "FRITZ!Box 7682"; + image = "${self}/files/topology-images/Fritz!Box_7682.png"; + interfaceGroups = [ + [ + "eth1" + "eth2" + "eth3" + "eth-wan" + "wifi" + ] + [ "dsl" ] + ]; + + connections = { + eth1 = mkConnection "winters" "eth1"; + eth-wan = mkConnection "hintbooth" "lan"; + }; + interfaces = { + eth1 = { + addresses = [ globals.networks.home-lan.hosts.fritzbox.ipv4 ]; + network = "fritz-lan"; + }; + eth2 = { }; + eth3 = { }; + eth-wan = { + addresses = [ globals.networks.home-lan.hosts.fritzbox.ipv4 ]; + network = "fritz-lan"; + }; + wifi = { + addresses = [ globals.networks.home-lan.hosts.fritzbox.ipv4 ]; + virtual = true; + renderer.hidePhysicalConnections = true; + network = "fritz-lan"; + physicalConnections = [ + (mkConnection "pyramid" "wifi") + (mkConnection "bakery" "wifi") + (mkConnection "machpizza" "wifi") + ]; + }; + fritz-wg = { + addresses = [ globals.networks.fritz-wg.hosts.fritzbox.ipv4 ]; + network = "fritz-wg"; + virtual = true; + renderer.hidePhysicalConnections = true; + type = "wireguard"; + physicalConnections = [ + (mkConnection "pyramid" "fritz-wg") + (mkConnection "magicant" "fritz-wg") + ]; + }; + }; + }; + + switch-livingroom = mkSwitch "Switch Livingroom" { + info = "TL-SG108E"; + image = "${self}/files/topology-images/TL-SG108E.png"; + interfaceGroups = [ + # trunk + [ "eth1" ] + # devices + [ "eth2" ] + # home + [ "eth3" "eth8" ] + # guests + [ "eth4" "eth5" "eth6" "eth7" ] + ]; + interfaces = { + eth2 = { network = lib.mkForce "devices"; }; + eth3 = { network = lib.mkForce "home"; }; + eth7 = { network = lib.mkForce "guests"; }; + eth8 = { network = lib.mkForce "home"; }; + }; + connections = { + eth2 = mkConnection "nswitch" "eth1"; + eth3 = mkConnection "bakery" "eth1"; + eth7 = mkConnection "pc" "eth1"; + eth8 = mkConnection "pyramid" "eth1"; + }; + }; + + switch-bedroom = mkDevice "Switch Bedroom" { + info = "Cisco SG 200-08"; + image = "${self}/files/topology-images/Cisco_SG_200-08.png"; + interfaceGroups = [ + # trunk + [ "eth1" ] + # devices + [ "eth2" ] + # guests + [ "eth3" "eth4" "eth5" "eth6" "eth7" "eth8" ] + ]; + interfaces = { + eth2 = { network = lib.mkForce "devices"; }; + eth3 = { network = lib.mkForce "guests"; }; + }; + connections = { + eth2 = mkConnection "printer" "eth1"; + eth3 = mkConnection "machpizza" "eth1"; + }; + }; + + nswitch = mkDevice "Nintendo Switch" { + info = "Nintendo Switch"; + image = "${self}/files/topology-images/nintendo-switch.png"; + interfaces.eth1 = { }; + }; + + magicant = mkDevice "magicant" { + icon = "${self}/files/topology-images/phone.png"; + info = "Samsung Z Flip 6"; + image = "${self}/files/topology-images/zflip6.png"; + interfaces = { + wifi = { }; + fritz-wg.network = "fritz-wg"; + }; + }; + + machpizza = mkDevice "machpizza" { + info = "MacBook Pro 2016"; + icon = "devices.laptop"; + deviceIcon = "${self}/files/topology-images/mac.png"; + interfaces = { + eth1.network = "guests"; + wifi = { }; + }; + }; + + treehouse = mkDevice "treehouse" { + info = "NVIDIA DGX Spark"; + icon = "${self}/files/topology-images/home-manager.png"; + deviceIcon = "${self}/files/topology-images/dgxos.png"; + interfaces = { + eth1 = { }; + wifi = { }; + }; + services = { + ollama = { + name = "Ollama"; + icon = "${self}/files/topology-images/ollama.png"; + }; + openwebui = { + name = "Open WebUI"; + icon = "${self}/files/topology-images/openwebui.png"; + }; + comfyui = { + name = "Comfy UI"; + icon = "${self}/files/topology-images/comfyui.png"; + }; + }; + }; + + pc = mkDevice "Chaostheater" { + info = "ASUS Z97-A, i7-4790k, GTX970, 32GB RAM"; + icon = "${self}/files/topology-images/windows.png"; + deviceIcon = "${self}/files/topology-images/atlasos.png"; + services = { + sunshine = { + name = "Sunshine"; + icon = "${self}/files/topology-images/sunshine.png"; + }; + }; + interfaces.eth1.network = "guests"; + }; + + printer = mkDevice "Printer" { + info = "DELL C2665dnf"; + image = "${self}/files/topology-images/DELL-C2665dnf.png"; + interfaces.eth1 = { }; + }; + }; - }; - switch-bedroom = mkSwitch "Switch Bedroom" { - info = "TL-SG1005D"; - image = "${self}/files/topology-images/TL-SG1005D.png"; - interfaceGroups = [ - [ - "eth1" - "eth2" - "eth3" - "eth4" - "eth5" - ] - ]; - connections.eth2 = mkConnection "printer" "eth1"; - connections.eth3 = mkConnection "machpizza" "eth1"; - }; - - nswitch = mkDevice "Nintendo Switch" { - info = "Nintendo Switch"; - image = "${self}/files/topology-images/nintendo-switch.png"; - interfaces.eth1 = { }; - }; - - magicant = mkDevice "magicant" { - icon = "${self}/files/topology-images/phone.png"; - info = "Samsung Z Flip 6"; - image = "${self}/files/topology-images/zflip6.png"; - interfaces = { - wifi = { }; - fritz-wg = { }; - }; - }; - - machpizza = mkDevice "machpizza" { - info = "MacBook Pro 2016"; - icon = "${self}/files/topology-images/mac.png"; - interfaces = { - eth1 = { }; - wifi = { }; - }; - }; - - pc = mkDevice "Windows Gaming Server" { - info = "i7-4790k, GTX970, 32GB RAM"; - image = "${self}/files/topology-images/pc.png"; - interfaces.eth1 = { }; - }; - - printer = mkDevice "Printer" { - info = "DELL C2665dnf"; - image = "${self}/files/topology-images/DELL-C2665dnf.png"; - interfaces.eth1 = { }; - }; - - }; - - }) + }) - ]; + ]; + }; } #+end_src @@ -2953,9 +3015,9 @@ My work machine. Built for more security, this is the gold standard of my config topology.self = { interfaces = { - "eth1" = { }; - "wifi" = { }; - "fritz-wg" = { }; + eth1.network = lib.mkForce "home"; + wifi = { }; + fritz-wg.network = "fritz-wg"; }; }; @@ -2994,7 +3056,7 @@ My work machine. Built for more security, this is the gold standard of my config main = { # name = "BOE 0x0BC9 Unknown"; name = "BOE 0x0BC9"; - mode = "2560x1600"; # TEMPLATE + mode = "2560x1600"; scale = "1"; position = "2560,0"; workspace = "15:L"; @@ -3008,10 +3070,10 @@ My work machine. Built for more security, this is the gold standard of my config personal = true; }; - networking.nftables = { - enable = lib.mkForce false; - firewall.enable = lib.mkForce false; - }; + # networking.nftables = { + # enable = lib.mkForce false; + # firewall.enable = lib.mkForce false; + # }; } #+end_src @@ -3179,7 +3241,7 @@ My work machine. Built for more security, this is the gold standard of my config fileSystems = { "/persist".neededForBoot = true; "/home".neededForBoot = true; - "/".neededForBoot = true; + "/".neededForBoot = true; # this is ok because this is not a impermanence host "/var/log".neededForBoot = true; }; } @@ -3216,8 +3278,8 @@ My personal laptop. Closely follows the =pyramid= config, but leaves out some se ]; topology.self.interfaces = { - "eth1" = { }; - "wifi" = { }; + eth1.network = lib.mkForce "home"; + wifi = { }; }; swarselsystems = { @@ -3891,29 +3953,30 @@ This is my main server that I run at home. It handles most tasks that require bi :CUSTOM_ID: h:624b3c6a-6e31-4734-a6ea-7c5b461a3429 :END: #+begin_src nix-ts :tangle hosts/nixos/x86_64-linux/hintbooth/default.nix - { self, config, lib, minimal, confLib, ... }: + { self, config, lib, minimal, confLib, globals, ... }: { imports = [ ./hardware-configuration.nix ./disk-config.nix - "${self}/modules/nixos/optional/systemd-networkd-server.nix" - "${self}/modules/nixos/optional/systemd-networkd-vlan.nix" + "${self}/modules/nixos/optional/systemd-networkd-server-home.nix" + "${self}/modules/nixos/optional/microvm-host.nix" ]; topology.self = { interfaces = { - "eth1" = { }; - "eth2" = { }; - "eth3" = { }; - "eth4" = { }; - "eth5" = { }; - "eth6" = { }; + lan2.physicalConnections = [ { node = "summers"; interface = "eth1";} ]; + lan3.physicalConnections = [ { node = "summers"; interface = "eth2";} ]; + lan4.physicalConnections = [ { node = "switch-bedroom"; interface = "eth1";} ]; + lan5.physicalConnections = [ { node = "switch-livingroom"; interface = "eth1";} ]; }; }; - globals.general.homeProxy = config.node.name; + globals.general = { + homeProxy = config.node.name; + routerServer = config.node.name; + }; swarselsystems = { info = "HUNSN RM02, 8GB RAM"; @@ -3928,12 +3991,16 @@ This is my main server that I run at home. It handles most tasks that require bi swapSize = "8G"; networkKernelModules = [ "igb" ]; withMicroVMs = true; + localVLANs = map (name: "${name}") (builtins.attrNames globals.networks.home-lan.vlans); + initrdVLAN = "home"; server = { wireguard.interfaces = { wgHome = { isServer = true; peers = [ "winters" + "hintbooth-adguardhome" + "hintbooth-nginx" ]; }; }; @@ -3956,6 +4023,7 @@ This is my main server that I run at home. It handles most tasks that require bi guests = lib.mkIf (!minimal && config.swarselsystems.withMicroVMs) ( { } // confLib.mkMicrovm "adguardhome" + // confLib.mkMicrovm "nginx" ); } @@ -4126,8 +4194,8 @@ This is my main server that I run at home. It handles most tasks that require bi :CUSTOM_ID: h:f479a908-8071-4d69-97ea-c03bfd7b88bf :END: -#+begin_src nix-ts :tangle hosts/nixos/x86_64-linux/hintbooth/guests/adguardhome.nix - { self, lib, minimal, ... }: +#+begin_src nix-ts :tangle hosts/nixos/x86_64-linux/hintbooth/guests/adguardhome/default.nix + { self, config, lib, minimal, ... }: { imports = [ "${self}/profiles/nixos/microvm" @@ -4136,8 +4204,24 @@ This is my main server that I run at home. It handles most tasks that require bi swarselsystems = { isMicroVM = true; + isImpermanence = true; + proxyHost = "twothreetunnel"; + server = { + wireguard.interfaces = { + wgHome = { + isClient = true; + serverName = "hintbooth"; + }; + wgProxy = { + isClient = true; + serverName = "twothreetunnel"; + }; + }; + }; }; + globals.general.homeDnsServer = config.node.name; + } // lib.optionalAttrs (!minimal) { microvm = { @@ -4149,6 +4233,79 @@ This is my main server that I run at home. It handles most tasks that require bi microvm = true; }; + swarselmodules.server = { + adguardhome = true; + }; + + } + +#+end_src + +****** Nginx +:PROPERTIES: +:CUSTOM_ID: h:90dc7f71-f9da-49ef-b273-edfab7daaa05 +:END: + +#+begin_src nix-ts :tangle hosts/nixos/x86_64-linux/hintbooth/guests/nginx/default.nix + { self, config, lib, minimal, globals, confLib, ... }: + let + inherit (confLib.static) nginxAccessRules; + in + { + imports = [ + "${self}/profiles/nixos/microvm" + "${self}/modules/nixos" + ]; + + swarselsystems = { + isMicroVM = true; + isImpermanence = true; + proxyHost = config.node.name; + server = { + wireguard.interfaces = { + wgHome = { + isClient = true; + serverName = "hintbooth"; + }; + }; + }; + }; + + globals.general.homeWebProxy = config.node.name; + + } // lib.optionalAttrs (!minimal) { + + microvm = { + mem = 3072 * 1; + vcpu = 1; + }; + + swarselprofiles = { + microvm = true; + }; + + swarselmodules.server = { + nginx = true; + }; + + services.nginx = { + upstreams.fritzbox = { + servers.${globals.networks.home-lan.hosts.fritzbox.ipv4} = { }; + }; + virtualHosts.${globals.services.fritzbox.domain} = { + useACMEHost = globals.domains.main; + forceSSL = true; + acmeRoot = null; + locations."/" = { + proxyPass = "http://fritzbox"; + proxyWebsockets = true; + }; + extraConfig = '' + proxy_ssl_verify off; + '' + nginxAccessRules; + }; + }; + } #+end_src @@ -4654,7 +4811,7 @@ This machine mainly acts as my proxy server to stand before my local machines. postgresql = true; attic = true; garage = true; - hydra = false; + hydra = true; }; } @@ -4829,6 +4986,7 @@ This machine mainly acts as my proxy server to stand before my local machines. ./disk-config.nix "${self}/modules/nixos/optional/systemd-networkd-server.nix" + "${self}/modules/nixos/optional/nix-topology-self.nix" ]; topology.self = { @@ -5026,7 +5184,7 @@ This machine mainly acts as my proxy server to stand before my local machines. :END: #+begin_src nix-ts :tangle hosts/nixos/aarch64-linux/liliputsteps/default.nix - { self, lib, minimal, ... }: + { self, config, lib, minimal, ... }: { imports = [ ./hardware-configuration.nix @@ -5038,6 +5196,16 @@ This machine mainly acts as my proxy server to stand before my local machines. topology.self = { icon = "devices.cloud-server"; + interfaces.ProxyJump = { + virtual = true; + physicalConnections = [ + (config.lib.topology.mkConnection "moonside" "lan") + (config.lib.topology.mkConnection "twothreetunnel" "lan") + (config.lib.topology.mkConnection "belchsfactory" "lan") + (config.lib.topology.mkConnection "stoicclub" "lan") + (config.lib.topology.mkConnection "eagleland" "wan") + ]; + }; }; swarselsystems = { @@ -5245,7 +5413,10 @@ This machine mainly acts as my proxy server to stand before my local machines. icon = "devices.cloud-server"; }; - globals.general.webProxy = config.node.name; + globals.general = { + webProxy = config.node.name; + oauthServer = config.node.name; + }; swarselsystems = { flakePath = "/root/.dotfiles"; @@ -5268,6 +5439,7 @@ This machine mainly acts as my proxy server to stand before my local machines. "winters" "belchsfactory" "eagleland" + "hintbooth-adguardhome" ]; }; }; @@ -6482,6 +6654,7 @@ in (splitPath "services.kanidm.provision.systems.oauth2") (splitPath "sops.secrets") (splitPath "swarselsystems.server.dns") + (splitPath "topology.self.services") ] ++ expandOptions (splitPath "networking.nftables.firewall") [ "zones" "rules" ] ++ expandOptions (splitPath "services.firezone.gateway") [ "enable" "name" "apiUrl" "tokenFile" "package" "logLevel" ] @@ -7384,7 +7557,7 @@ Normally, doing that also resets the lecture that happens on the first use of =s { config, lib, ... }: let mapperTarget = lib.swarselsystems.mkIfElse config.swarselsystems.isCrypted "/dev/mapper/cryptroot" "/dev/disk/by-label/nixos"; - inherit (config.swarselsystems) isImpermanence isCrypted; + inherit (config.swarselsystems) isImpermanence isCrypted isBtrfs; in { options.swarselmodules.impermanence = lib.mkEnableOption "impermanence config"; @@ -7400,7 +7573,7 @@ Normally, doing that also resets the lecture that happens on the first use of =s # So if it doesn't run, the btrfs system effectively acts like a normal system # Taken from https://github.com/NotAShelf/nyx/blob/2a8273ed3f11a4b4ca027a68405d9eb35eba567b/modules/core/common/system/impermanence/default.nix boot.tmp.useTmpfs = lib.mkIf (!isImpermanence) true; - boot.initrd.systemd = lib.mkIf isImpermanence { + boot.initrd.systemd = lib.mkIf (isImpermanence && isBtrfs) { enable = true; services.rollback = { description = "Rollback BTRFS root subvolume to a pristine state"; @@ -9164,6 +9337,9 @@ Auto login for the initial session. #+end_src **** Firezone Client +:PROPERTIES: +:CUSTOM_ID: h:4d018a21-637b-4c7d-b9c9-7f1b95144a07 +:END: #+begin_src nix-ts :tangle modules/nixos/client/firezone-client.nix @@ -9284,6 +9460,8 @@ This is a collection of packages that are useful for server-type hosts that do n sops tmux busybox + ndisc6 + tcpdump swarsel-deploy ] ++ lib.optionals withHomeManager [ swarsel-gens @@ -9484,7 +9662,7 @@ This is a collection of packages that are useful for server-type hosts that do n }; }; config = { - extraConfig = lib.mkIf topmod.config.defaultStapling (lib.mkAfter '' + extraConfig = lib.mkIf topmod.config.defaultStapling (lib.mkBefore '' ssl_stapling on; ssl_stapling_verify on; resolver 1.1.1.1 8.8.8.8 valid=300s; @@ -9756,19 +9934,20 @@ Restricts access to the system by the nix build user as per https://discourse.ni } #+end_src -**** Network settings +**** Network settings (globals.networks population) :PROPERTIES: :CUSTOM_ID: h:0ff3acc5-9ce8-4b22-a2e2-f6f1e69d47a5 :END: Generate hostId using =head -c4 /dev/urandom | od -A none -t x4= +This section is mainly used to populate entries in =globals.networks= with the interfaces defined in the local secrets of the respective host. Also, we expose some convenient values under =globals.hosts= and setup basic networking. + #+begin_src nix-ts :tangle modules/nixos/server/network.nix { lib, config, ... }: let netConfig = config.repo.secrets.local.networking; netPrefix = "${if config.swarselsystems.isCloud then config.node.name else "home"}"; - # netName = "${netPrefix}-${config.swarselsystems.server.localNetwork}"; in { options = { @@ -9794,11 +9973,6 @@ Generate hostId using =head -c4 /dev/urandom | od -A none -t x4= swarselsystems.server.localNetwork = netConfig.localNetwork or ""; - # globals.networks.${netName}.hosts.${config.node.name} = { - # inherit (netConfig.networks.${netConfig.localNetwork}) id; - # mac = netConfig.networks.${netConfig.localNetwork}.mac or null; - # }; - globals.networks = lib.mapAttrs' (netName: _: lib.nameValuePair "${netPrefix}-${netName}" { @@ -9811,7 +9985,8 @@ Generate hostId using =head -c4 /dev/urandom | od -A none -t x4= netConfig.networks; globals.hosts.${config.node.name} = { - inherit (config.repo.secrets.local.networking) defaultGateway4; + defaultGateway4 = netConfig.defaultGateway4 or null; + defaultGateway6 = netConfig.defaultGateway6 or null; wanAddress4 = netConfig.wanAddress4 or null; wanAddress6 = netConfig.wanAddress6 or null; isHome = if (netPrefix == "home") then true else false; @@ -9873,6 +10048,9 @@ I also take some precautions in how I get networking information during stage 1. subnetMask = globals.networks.${config.swarselsystems.server.netConfigName}.subnetMask4; gatewayIp = globals.hosts.${config.node.name}.defaultGateway4; + inherit (globals.general) routerServer; + isRouter = config.node.name == routerServer; + hostKeyPathBase = "/etc/secrets/initrd/ssh_host_ed25519_key"; hostKeyPath = if config.swarselsystems.isImpermanence then @@ -9911,7 +10089,7 @@ I also take some precautions in how I get networking information during stage 1. }; boot = lib.mkIf (!config.swarselsystems.isClient) { - kernelParams = lib.mkIf (!config.swarselsystems.isCloud) [ + kernelParams = lib.mkIf (!config.swarselsystems.isCloud && ((config.swarselsystems.localVLANs == []) || isRouter)) [ "ip=${localIp}::${gatewayIp}:${subnetMask}:${config.networking.hostName}::none" ]; initrd = { @@ -10040,102 +10218,110 @@ In order to define a new wireguard interface, I have to: inherit (cfg) interfaces; ifaceList = builtins.attrValues interfaces; in - { - options = { - swarselmodules.server.${serviceName} = - lib.mkEnableOption "enable ${serviceName} settings"; + { + options = { + swarselmodules.server.${serviceName} = + lib.mkEnableOption "enable ${serviceName} settings"; - swarselsystems.server.wireguard = { - interfaces = let - topConfig = config; - in lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({ config, name, ... }: { - options = { - isServer = lib.mkEnableOption "set this interface as a wireguard server"; - isClient = lib.mkEnableOption "set this interface as a wireguard client"; + swarselsystems.server.wireguard = { + interfaces = let + topConfig = config; + in lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({ config, name, ... }: { + options = { + isServer = lib.mkEnableOption "set this interface as a wireguard server"; + isClient = lib.mkEnableOption "set this interface as a wireguard client"; - serverName = lib.mkOption { - type = lib.types.str; - default = if config.isServer then topConfig.node.name else ""; - description = "Hostname of the WireGuard server this interface connects to (when isClient = true)."; + serverName = lib.mkOption { + type = lib.types.str; + default = if config.isServer then topConfig.node.name else ""; + description = "Hostname of the WireGuard server this interface connects to (when isClient = true)."; + }; + + serverNetConfigPrefix = lib.mkOption { + type = lib.types.str; + default = + let + serverCfg = nodes.${config.serverName}.config; + in + if serverCfg.swarselsystems.isCloud + then serverCfg.node.name + else "home"; + readOnly = true; + description = "Prefix used to look up the server network in globals.networks.\"-wg\"."; + }; + + ifName = lib.mkOption { + type = lib.types.str; + default = name; + description = "Name of the WireGuard interface."; + }; + + port = lib.mkOption { + type = lib.types.int; + default = servicePort; + description = "Port of the WireGuard interface."; + }; + + peers = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = lib.attrNames (lib.filterAttrs (name: _: name != topConfig.node.name) globals.networks."${config.serverNetConfigPrefix}-${config.ifName}".hosts); + description = "WireGuard peer config names of this wireguardinterface."; + }; }; - - serverNetConfigPrefix = lib.mkOption { - type = lib.types.str; - default = - let - serverCfg = nodes.${config.serverName}.config; - in - if serverCfg.swarselsystems.isCloud - then serverCfg.node.name - else "home"; - readOnly = true; - description = "Prefix used to look up the server network in globals.networks.\"-wg\"."; - }; - - ifName = lib.mkOption { - type = lib.types.str; - default = name; - description = "Name of the WireGuard interface."; - }; - - port = lib.mkOption { - type = lib.types.int; - default = servicePort; - description = "Port of the WireGuard interface."; - }; - - peers = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = lib.attrNames (lib.filterAttrs (name: _: name != topConfig.node.name) globals.networks."${config.serverNetConfigPrefix}-${config.ifName}".hosts); - description = "WireGuard peer config names of this wireguardinterface."; - }; - }; - })); - default = { }; - description = "WireGuard interfaces defined on this host."; + })); + default = { }; + description = "WireGuard interfaces defined on this host."; + }; }; }; - }; - config = lib.mkIf config.swarselmodules.server.${serviceName} { + config = lib.mkIf config.swarselmodules.server.${serviceName} { - assertions = lib.concatLists ( - lib.flip lib.mapAttrsToList interfaces ( - ifName: ifCfg: + assertions = lib.concatLists ( + lib.flip lib.mapAttrsToList interfaces ( + ifName: ifCfg: let assertionPrefix = "While evaluating the wireguard network ${ifName}:"; in + [ + { + assertion = ifCfg.isServer || (ifCfg.isClient && ifCfg.serverName != ""); + message = "${assertionPrefix}: This node must either be a server for the wireguard network or a client with serverName set."; + } + { + assertion = lib.stringLength ifName < 16; + message = "${assertionPrefix}: The specified linkName '${ifName}' is too long (must be max 15 characters)."; + } + ] + ) + ); + + topology.self.interfaces = lib.mapAttrs' + (wgName: _: + lib.nameValuePair "${wgName}" { + network = wgName; + } + ) + config.swarselsystems.server.wireguard.interfaces; + + environment.systemPackages = with pkgs; [ + wireguard-tools + ]; + + sops.secrets = + lib.mkMerge ( [ { - assertion = ifCfg.isServer || (ifCfg.isClient && ifCfg.serverName != ""); - message = "${assertionPrefix}: This node must either be a server for the wireguard network or a client with serverName set."; + wireguard-private-key = { + inherit sopsFile; + owner = serviceUser; + group = serviceGroup; + mode = "0600"; + }; } - { - assertion = lib.stringLength ifName < 16; - message = "${assertionPrefix}: The specified linkName '${ifName}' is too long (must be max 15 characters)."; - } - ] - ) - ); - - environment.systemPackages = with pkgs; [ - wireguard-tools - ]; - - sops.secrets = - lib.mkMerge ( - [ - { - wireguard-private-key = { - inherit sopsFile; - owner = serviceUser; - group = serviceGroup; - mode = "0600"; - }; - } - ] ++ (map - (i: + ] ++ (map + (i: let clientSecrets = lib.optionalAttrs i.isClient { @@ -10160,26 +10346,26 @@ In order to define a new wireguard interface, I have to: }) i.peers)); in - clientSecrets // serverSecrets - ) - ifaceList) - ); + clientSecrets // serverSecrets + ) + ifaceList) + ); - networking.firewall = { - checkReversePath = lib.mkIf (lib.any (i: i.isClient) ifaceList) "loose"; - allowedUDPPorts = lib.mkMerge ( - lib.flip lib.mapAttrsToList interfaces ( - _: ifCfg: - lib.optional ifCfg.isServer ifCfg.port - ) - ); - }; - - networking.nftables.firewall = { - zones = lib.mkMerge - ( + networking.firewall = { + checkReversePath = lib.mkIf (lib.any (i: i.isClient) ifaceList) "loose"; + allowedUDPPorts = lib.mkMerge ( lib.flip lib.mapAttrsToList interfaces ( - ifName: ifCfg: + _: ifCfg: + lib.optional ifCfg.isServer ifCfg.port + ) + ); + }; + + networking.nftables.firewall = { + zones = lib.mkMerge + ( + lib.flip lib.mapAttrsToList interfaces ( + ifName: ifCfg: { ${ifName}.interfaces = [ ifName ]; } @@ -10188,30 +10374,30 @@ In order to define a new wireguard interface, I have to: let peerNet = globals.networks."${ifCfg.serverNetConfigPrefix}-${ifName}".hosts.${peer}; in - lib.nameValuePair "${ifName}-node-${peer}" { - parent = ifName; - ipv4Addresses = lib.optional (peerNet.ipv4 != null) peerNet.ipv4; - ipv6Addresses = lib.optional (peerNet.ipv6 != null) peerNet.ipv6; - } + lib.nameValuePair "${ifName}-node-${peer}" { + parent = ifName; + ipv4Addresses = lib.optional (peerNet.ipv4 != null) peerNet.ipv4; + ipv6Addresses = lib.optional (peerNet.ipv6 != null) peerNet.ipv6; + } ) ifCfg.peers) - ) - ); - rules = lib.mkMerge ( - lib.flip lib.mapAttrsToList interfaces ( - ifName: ifCfg: + ) + ); + rules = lib.mkMerge ( + lib.flip lib.mapAttrsToList interfaces ( + ifName: ifCfg: let inherit (config.networking.nftables.firewall) localZoneName; netCfg = globals.networks."${ifCfg.serverNetConfigPrefix}-${ifName}"; in - { - "${ifName}-to-${localZoneName}" = { - inherit (netCfg.firewallRuleForAll) allowedTCPPorts allowedUDPPorts allowedTCPPortRanges allowedUDPPortRanges; - from = [ ifName ]; - to = [ localZoneName ]; - ignoreEmptyRule = true; - }; - } + { + "${ifName}-to-${localZoneName}" = { + inherit (netCfg.firewallRuleForAll) allowedTCPPorts allowedUDPPorts allowedTCPPortRanges allowedUDPPortRanges; + from = [ ifName ]; + to = [ localZoneName ]; + ignoreEmptyRule = true; + }; + } // lib.listToAttrs (map (peer: lib.nameValuePair "${ifName}-node-${peer}-to-${localZoneName}" ( @@ -10224,75 +10410,75 @@ In order to define a new wireguard interface, I have to: ) ) ifCfg.peers) - ) - ); - }; + ) + ); + }; - systemd.network = { - enable = true; + systemd.network = { + enable = true; - networks = lib.mkMerge (map - (i: + networks = lib.mkMerge (map + (i: let inherit (i) ifName; in - { - "50-${ifName}" = { - matchConfig.Name = ifName; - linkConfig = { - MTUBytes = 1408; # TODO: figure out where we lose those 12 bits (8 from pppoe maybe + ???) + { + "50-${ifName}" = { + matchConfig.Name = ifName; + linkConfig = { + MTUBytes = 1408; # TODO: figure out where we lose those 12 bits (8 from pppoe maybe + ???) + }; + + address = [ + globals.networks."${i.serverNetConfigPrefix}-${ifName}".hosts.${config.node.name}.cidrv4 + globals.networks."${i.serverNetConfigPrefix}-${ifName}".hosts.${config.node.name}.cidrv6 + ]; }; + }) + ifaceList); - address = [ - globals.networks."${i.serverNetConfigPrefix}-${ifName}".hosts.${config.node.name}.cidrv4 - globals.networks."${i.serverNetConfigPrefix}-${ifName}".hosts.${config.node.name}.cidrv6 - ]; - }; - }) - ifaceList); - - netdevs = lib.mkMerge (map - (i: + netdevs = lib.mkMerge (map + (i: let inherit (i) ifName; in - { - "50-${ifName}" = { - netdevConfig = { - Kind = "wireguard"; - Name = ifName; - }; + { + "50-${ifName}" = { + netdevConfig = { + Kind = "wireguard"; + Name = ifName; + }; - wireguardConfig = { - ListenPort = lib.mkIf i.isServer servicePort; + wireguardConfig = { + ListenPort = lib.mkIf i.isServer servicePort; - PrivateKeyFile = config.sops.secrets.wireguard-private-key.path; + PrivateKeyFile = config.sops.secrets.wireguard-private-key.path; - RouteTable = lib.mkIf i.isClient "main"; - }; + RouteTable = lib.mkIf i.isClient "main"; + }; - wireguardPeers = - lib.optionals i.isClient [ - { - PublicKey = - builtins.readFile "${self}/secrets/public/wg/${i.serverName}.pub"; + wireguardPeers = + lib.optionals i.isClient [ + { + PublicKey = + builtins.readFile "${self}/secrets/public/wg/${i.serverName}.pub"; - PresharedKeyFile = - config.sops.secrets."wireguard-${i.serverName}-${config.node.name}-${i.ifName}-presharedKey".path; + PresharedKeyFile = + config.sops.secrets."wireguard-${i.serverName}-${config.node.name}-${i.ifName}-presharedKey".path; - Endpoint = - "server.${i.serverName}.${globals.domains.main}:${toString servicePort}"; + Endpoint = + "server.${i.serverName}.${globals.domains.main}:${toString servicePort}"; - PersistentKeepalive = 25; + PersistentKeepalive = 25; - AllowedIPs = - let - wgNetwork = globals.networks."${i.serverNetConfigPrefix}-${i.ifName}"; - in - (lib.optional (wgNetwork.cidrv4 != null) wgNetwork.cidrv4) + AllowedIPs = + let + wgNetwork = globals.networks."${i.serverNetConfigPrefix}-${i.ifName}"; + in + (lib.optional (wgNetwork.cidrv4 != null) wgNetwork.cidrv4) ++ (lib.optional (wgNetwork.cidrv6 != null) wgNetwork.cidrv6); - } - ] + } + ] ++ lib.optionals i.isServer (map (clientName: { PublicKey = @@ -10306,18 +10492,18 @@ In order to define a new wireguard interface, I have to: clientInWgNetwork = globals.networks."${i.serverNetConfigPrefix}-${i.ifName}".hosts.${clientName}; in - (lib.optional (clientInWgNetwork.ipv4 != null) - (lib.net.cidr.make 32 clientInWgNetwork.ipv4)) + (lib.optional (clientInWgNetwork.ipv4 != null) + (lib.net.cidr.make 32 clientInWgNetwork.ipv4)) ++ (lib.optional (clientInWgNetwork.ipv6 != null) (lib.net.cidr.make 128 clientInWgNetwork.ipv6)); }) i.peers); - }; - }) - ifaceList); + }; + }) + ifaceList); + }; }; - }; - } + } #+end_src **** BTRFS @@ -10342,12 +10528,44 @@ In order to define a new wireguard interface, I have to: :CUSTOM_ID: h:b54f2bbb-0088-46b2-957d-fd8234b772c3 :END: -This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hintbooth (Router: HUNSN RM02)]] act as the router for my internal network. This is not a reusable module and highly adapted to its hardware. +This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hintbooth (Router: HUNSN RM02)]] act as the router for my internal network. This is not a reusable module and highly adapted to its hardware. Below is a rough sketch of the functionality: + + - six LAN ports, five of which are bridged + - lan1โ€“lan5 are enslaved into said bridge and behave as a single VLANโ€‘aware switch + - all VLANs are defined under =globals.networks.home-lan.vlans= + - for each VLAN, a routed interface me-${vlanName} is created on the router (NOTE: this interface also serves as the communication link to the local microvms - the respective extra interfaces are defined in [[#h:049fc27e-a28f-4ff0-b5f0-d81401bdd56f][systemd-networkd (server home)]]) + - RA and forwarding are enabled on these me-* interfaces so the router advertises vlanCfg.cidrv6 and routes between VLANs / WAN / WireGuard + - the sixth LAN port is used as the WAN / untrusted uplink to the =Fritz!Box= + - the mapping from MAC addresses to interfaces is defined in =config.repo.secrets.local.networking.networks..mac= (and performed in [[#h:99bf6c0e-2566-4a50-b219-fb6a7d4fb2cd][systemd-networkd (base)]] using =renameInterfacesByMac=) + - connectivity to microvms should not be lost in case there is no cable connected to the router + - this is achieved by connecting the veth interface pair veth-br / veth-int + - veth-br is part of the bridge and carries all VLANs tagged, as if it were another physical switch port + - veth-int stays on the host side and is used as the internal attachment point for microvms / guests + - ConfigureWithoutCarrier and ActivationPolicy = "always-up" are used so that the bridge and veth side stay UP; this however does not guarantee connectivity by itself as the kernel will not route packets if the underlying interface is not up (see also [[#h:049fc27e-a28f-4ff0-b5f0-d81401bdd56f][systemd-networkd (server home)]]) + - nftables firewall is derived from the same VLAN definitions: + - a zone =vlan-*= is created for each VLAN and bound to =me-*=, as well as zones for WAN, WG, and DNS + - all internal =vlan-*= zones are allowed to go to untrusted; NAT is implemented via a custom postrouting chain that masquerades both IPv4 and IPv6 traffic + - any VLAN with internet access is allowed to reach AdGuardHome for DNS (access-adguardhome-dns) + - this is important so that we can make use of the internal nginx instance to prevent bottlenecks over the web proxy + - policy between internal networks: + - the home VLAN is allowed to access the services and devices VLANs + - the services VLAN is allowed to reach selected ports on local (currently wireguard) + - WireGuard peers in wgHome are allowed to talk to each other (wgHome โ†’ wgHome) + - global IPv4/IPv6 forwarding is enabled via boot.kernel.sysctl so this host acts as the main router between all VLANs, the WireGuard network, and the WAN (untrusted) #+begin_src nix-ts :tangle modules/nixos/server/router.nix - { lib, config, globals, ... }: + { lib, config, globals, confLib, ... }: let serviceName = "router"; + bridgeVLANs = lib.mapAttrsToList + (_: vlan: { + VLAN = vlan.id; + }) + globals.networks.home-lan.vlans; + selectVLANs = vlans: map (vlan: { VLAN = globals.networks.home-lan.vlans.${vlan}.id; }) vlans; + lan5VLANs = selectVLANs [ "home" "devices" "guests" ]; + lan4VLANs = selectVLANs [ "home" "services" ]; + inherit (confLib.gen { }) homeDnsServer; in { options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; @@ -10355,13 +10573,27 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin { services.avahi.reflector = true; + topology.self.interfaces = (lib.mapAttrs' + (vlanName: _: + lib.nameValuePair "vlan-${vlanName}" { + network = lib.mkForce vlanName; + } + ) + globals.networks.home-lan.vlans) // (lib.mapAttrs' + (vlanName: _: + lib.nameValuePair "me-${vlanName}" { + network = lib.mkForce vlanName; + } + ) + globals.networks.home-lan.vlans); + networking.nftables = { firewall = { zones = { untrusted.interfaces = [ "lan" ]; wgHome.interfaces = [ "wgHome" ]; - adguardhome.ipv4Addresses = [ globals.networks.home-lan.vlans.services.hosts.hintbooth-adguardhome.ipv4 ]; - adguardhome.ipv6Addresses = [ globals.networks.home-lan.vlans.services.hosts.hintbooth-adguardhome.ipv6 ]; + adguardhome.ipv4Addresses = [ globals.networks.home-lan.vlans.services.hosts.${homeDnsServer}.ipv4 ]; + adguardhome.ipv6Addresses = [ globals.networks.home-lan.vlans.services.hosts.${homeDnsServer}.ipv6 ]; } // lib.flip lib.concatMapAttrs globals.networks.home-lan.vlans ( vlanName: _: { @@ -10371,7 +10603,7 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin rules = { masquerade-internet = { - from = map (name: "vlan-${name}") (builtins.attrNames globals.networks.home-lan.vlans); + from = map (name: "vlan-${name}") (globals.general.internetVLANs); to = [ "untrusted" ]; # masquerade = true; NOTE: custom rule below for ip4 + ip6 late = true; # Only accept after any rejects have been processed @@ -10380,7 +10612,7 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin # Allow access to the AdGuardHome DNS server from any VLAN that has internet access access-adguardhome-dns = { - from = map (name: "vlan-${name}") (builtins.attrNames globals.networks.home-lan.vlans); + from = map (name: "vlan-${name}") (globals.general.internetVLANs); to = [ "adguardhome" ]; verdict = "accept"; }; @@ -10400,7 +10632,7 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin services-to-local = { from = [ "vlan-services" ]; to = [ "local" ]; - allowedUDPPorts = [ 52829 ]; + allowedUDPPorts = [ 52829 547 ]; }; # Forward traffic between wireguard participants @@ -10418,7 +10650,7 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin late = true; rules = lib.forEach - (map (name: "vlan-${name}") (builtins.attrNames globals.networks.home-lan.vlans)) + (map (name: "vlan-${name}") (globals.general.internetVLANs)) ( zone: lib.concatStringsSep " " [ @@ -10440,7 +10672,68 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin systemd.network = { wait-online.anyInterface = true; + + netdevs = { + "10-veth" = { + netdevConfig = { + Kind = "veth"; + Name = "veth-br"; + }; + peerConfig = { + Name = "veth-int"; + }; + }; + "20-br" = { + netdevConfig = { + Kind = "bridge"; + Name = "br"; + }; + bridgeConfig = { + VLANFiltering = true; + }; + }; + }; networks = { + "40-br" = { + matchConfig.Name = "br"; + bridgeConfig = { }; + linkConfig = { + ActivationPolicy = "always-up"; + RequiredForOnline = "no"; + }; + networkConfig = { + ConfigureWithoutCarrier = true; + LinkLocalAddressing = "no"; + }; + }; + "15-veth-br" = { + matchConfig.Name = "veth-br"; + + linkConfig = { + RequiredForOnline = "no"; + }; + + networkConfig = { + Bridge = "br"; + }; + inherit bridgeVLANs; + }; + "15-veth-int" = { + matchConfig.Name = "veth-int"; + + linkConfig = { + ActivationPolicy = "always-up"; + RequiredForOnline = "no"; + }; + + networkConfig = { + ConfigureWithoutCarrier = true; + LinkLocalAddressing = "no"; + }; + + vlan = map (name: "vlan-${name}") (builtins.attrNames globals.networks.home-lan.vlans); + }; + # br "30-lan1" = { matchConfig.MACAddress = config.repo.secrets.local.networking.networks.lan1.mac; linkConfig.RequiredForOnline = "enslaved"; @@ -10448,7 +10741,9 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin Bridge = "br"; ConfigureWithoutCarrier = true; }; + inherit bridgeVLANs; }; + # wifi "30-lan2" = { matchConfig.MACAddress = config.repo.secrets.local.networking.networks.lan2.mac; linkConfig.RequiredForOnline = "enslaved"; @@ -10456,7 +10751,9 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin Bridge = "br"; ConfigureWithoutCarrier = true; }; + inherit bridgeVLANs; }; + # summers "30-lan3" = { matchConfig.MACAddress = config.repo.secrets.local.networking.networks.lan3.mac; linkConfig.RequiredForOnline = "enslaved"; @@ -10464,7 +10761,9 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin Bridge = "br"; ConfigureWithoutCarrier = true; }; + inherit bridgeVLANs; }; + # winters "30-lan4" = { matchConfig.MACAddress = config.repo.secrets.local.networking.networks.lan4.mac; linkConfig.RequiredForOnline = "enslaved"; @@ -10472,7 +10771,9 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin Bridge = "br"; ConfigureWithoutCarrier = true; }; + bridgeVLANs = lan4VLANs; }; + # lr "30-lan5" = { matchConfig.MACAddress = config.repo.secrets.local.networking.networks.lan5.mac; linkConfig.RequiredForOnline = "enslaved"; @@ -10480,10 +10781,37 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin Bridge = "br"; ConfigureWithoutCarrier = true; }; + bridgeVLANs = lan5VLANs; }; - }; - }; + } // lib.flip lib.concatMapAttrs globals.networks.home-lan.vlans ( + vlanName: vlanCfg: { + "40-me-${vlanName}" = lib.mkForce { + address = [ + vlanCfg.hosts.${config.node.name}.cidrv4 + vlanCfg.hosts.${config.node.name}.cidrv6 + ]; + matchConfig.Name = "me-${vlanName}"; + networkConfig = { + IPv4Forwarding = "yes"; + IPv6PrivacyExtensions = "yes"; + IPv6SendRA = true; + IPv6AcceptRA = false; + }; + ipv6Prefixes = [ + { + Prefix = vlanCfg.cidrv6; + } + ]; + ipv6SendRAConfig = { + Managed = true; # set RA M flag -> DHCPv6 for addresses + OtherInformation = true; # optional, for โ€œother infoโ€ via DHCPv6 + }; + linkConfig.RequiredForOnline = "routable"; + }; + } + ); + }; }; } @@ -10559,7 +10887,6 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin virtualHosts = { "${serviceDomain}" = { useACMEHost = globals.domains.main; - forceSSL = true; acmeRoot = null; locations = { @@ -10690,6 +11017,8 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin mpv ]; + topology.self.services.${serviceName}.info = "https://${serviceDomain}"; + users = { groups = { ${serviceGroup} = { @@ -11031,6 +11360,50 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin } #+end_src +**** podman + +#+begin_src nix-ts :tangle modules/nixos/server/podman.nix + { config, lib, ... }: + let + serviceName = "podman"; + in + { + options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; + config = lib.mkIf config.swarselmodules.server.${serviceName} { + + virtualisation = { + podman.enable = true; + oci-containers.backend = "podman"; + }; + + networking.nftables.firewall = lib.mkIf config.networking.nftables.enable { + + zones.podman = { + interfaces = [ "podman0" ]; + }; + + rules = { + podman-to-postgres = lib.mkIf config.services.postgresql.enable { + from = [ "podman" ]; + to = [ "local" ]; + before = [ "drop" ]; + allowedTCPPorts = [ config.services.postgresql.settings.port ]; + }; + + local-to-podman = { + from = [ "local" "wgProxy" "wgHme"]; + to = [ "podman" ]; + before = [ "drop" ]; + verdict = "accept"; + }; + }; + }; + + }; + } +#+end_src + + **** matrix :PROPERTIES: :CUSTOM_ID: h:1e68d84a-8f99-422f-89ac-78f664ac0013 @@ -13184,6 +13557,7 @@ kanidm person credential create-reset-token "radicale.access" = { }; "slink.access" = { }; "opkssh.access" = { }; + "adguardhome.access" = { }; }; inherit (config.repo.secrets.local) persons; @@ -13326,6 +13700,11 @@ kanidm person credential create-reset-token "email" "profile" ]; + "adguardhome.access" = [ + "openid" + "email" + "profile" + ]; }; preferShortUsername = true; claimMaps.groups = { @@ -13336,6 +13715,7 @@ kanidm person credential create-reset-token "firefly.access" = [ "firefly_access" ]; "radicale.access" = [ "radicale_access" ]; "slink.access" = [ "slink_access" ]; + "adguardhome.access" = [ "adguardhome_access" ]; }; }; }; @@ -13387,7 +13767,8 @@ kanidm person credential create-reset-token #+begin_src nix-ts :tangle modules/nixos/server/oauth2-proxy.nix { lib, config, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "oauth2-proxy"; port = 3004; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; + inherit (confLib.gen { name = "oauth2-proxy"; port = 3004; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress proxyAddress4 proxyAddress6; + inherit (confLib.static) isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf homeWebProxy oauthServer nginxAccessRules; kanidmDomain = globals.services.kanidm.domain; mainDomain = globals.domains.main; @@ -13506,10 +13887,6 @@ kanidm person credential create-reset-token }; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { - "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; - }; - sops = { secrets = { "oauth2-cookie-secret" = { inherit sopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; }; @@ -13541,8 +13918,8 @@ kanidm person credential create-reset-token }; }; services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6 isHome; + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; }; }; @@ -13595,32 +13972,42 @@ kanidm person credential create-reset-token }; }; - nodes.${webProxy}.services.nginx = { - upstreams = { - ${serviceName} = { - servers = { - "${serviceAddress}:${builtins.toString servicePort}" = { }; - }; - }; - }; - virtualHosts = { - "${serviceDomain}" = { - useACMEHost = globals.domains.main; - - forceSSL = true; - acmeRoot = null; - locations = { - "/" = { - proxyPass = "http://${serviceName}"; + nodes = + let + genNginx = toAddress: extraConfig: { + upstreams = { + ${serviceName} = { + servers = { + "${toAddress}:${builtins.toString servicePort}" = { }; + }; + }; + }; + virtualHosts = { + "${serviceDomain}" = { + useACMEHost = globals.domains.main; + + forceSSL = true; + acmeRoot = null; + locations = { + "/" = { + proxyPass = "http://${serviceName}"; + }; + }; + extraConfig = '' + proxy_set_header X-Scheme $scheme; + proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri; + '' + lib.optionalString (extraConfig != "") extraConfig; }; }; - extraConfig = '' - proxy_set_header X-Scheme $scheme; - proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri; - ''; }; + in + { + ${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; + }; + ${webProxy}.services.nginx = genNginx serviceAddress ""; + ${homeWebProxy}.services.nginx = genNginx globals.hosts.${oauthServer}.wanAddress4 nginxAccessRules; }; - }; }; } #+end_src @@ -13770,6 +14157,10 @@ kanidm person credential create-reset-token options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { + swarselmodules.server = { + podman = true; + postgresql = true; + }; nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; @@ -14377,6 +14768,10 @@ kanidm person credential create-reset-token }; config = lib.mkIf config.swarselmodules.server.${serviceName} { + swarselmodules.server = { + podman = true; + }; + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -14498,113 +14893,117 @@ Deployment notes: - finally, disable new user registration in web ui #+begin_src nix-ts :tangle modules/nixos/server/slink.nix -{ self, lib, config, dns, globals, confLib, ... }: -let - inherit (confLib.gen { name = "slink"; port = 3000; dir = "/var/lib/slink"; }) servicePort serviceName serviceDomain serviceDir serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; + { self, lib, config, dns, globals, confLib, ... }: + let + inherit (confLib.gen { name = "slink"; port = 3000; dir = "/var/lib/slink"; }) servicePort serviceName serviceDomain serviceDir serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; - containerRev = "sha256:98b9442696f0a8cbc92f0447f54fa4bad227af5dcfd6680545fedab2ed28ddd9"; -in -{ - options = { - swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; - }; - config = lib.mkIf config.swarselmodules.server.${serviceName} { - - nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { - "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; + containerRev = "sha256:98b9442696f0a8cbc92f0447f54fa4bad227af5dcfd6680545fedab2ed28ddd9"; + in + { + options = { + swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; }; + config = lib.mkIf config.swarselmodules.server.${serviceName} { - virtualisation.oci-containers.containers.${serviceName} = { - image = "anirdev/slink@${containerRev}"; - environment = { - "ORIGIN" = "https://${serviceDomain}"; - "TZ" = config.repo.secrets.common.location.timezone; - "STORAGE_PROVIDER" = "local"; - "IMAGE_MAX_SIZE" = "50M"; - "USER_APPROVAL_REQUIRED" = "true"; + swarselmodules.server = { + podman = true; }; - ports = [ "${builtins.toString servicePort}:${builtins.toString servicePort}" ]; - volumes = [ - "${serviceDir}/var/data:/app/var/data" - "${serviceDir}/images:/app/slink/images" + + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; + }; + + virtualisation.oci-containers.containers.${serviceName} = { + image = "anirdev/slink@${containerRev}"; + environment = { + "ORIGIN" = "https://${serviceDomain}"; + "TZ" = config.repo.secrets.common.location.timezone; + "STORAGE_PROVIDER" = "local"; + "IMAGE_MAX_SIZE" = "50M"; + "USER_APPROVAL_REQUIRED" = "true"; + }; + ports = [ "${builtins.toString servicePort}:${builtins.toString servicePort}" ]; + volumes = [ + "${serviceDir}/var/data:/app/var/data" + "${serviceDir}/images:/app/slink/images" + ]; + }; + + systemd.tmpfiles.settings."12-slink" = builtins.listToAttrs ( + map + (path: { + name = "${serviceDir}/${path}"; + value = { + d = { + group = "root"; + user = "root"; + mode = "0750"; + }; + }; + }) [ + "var/data" + "images" + ] + ); + + # networking.firewall.allowedTCPPorts = [ servicePort ]; + + environment.persistence."/persist".directories = lib.mkIf config.swarselsystems.isImpermanence [ + { directory = serviceDir; } ]; - }; - systemd.tmpfiles.settings."12-slink" = builtins.listToAttrs ( - map - (path: { - name = "${serviceDir}/${path}"; - value = { - d = { - group = "root"; - user = "root"; - mode = "0750"; - }; + topology.self.services.${serviceName} = { + name = lib.swarselsystems.toCapitalized serviceName; + info = "https://${serviceDomain}"; + icon = "${self}/files/topology-images/shlink.png"; + }; + + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; }; - }) [ - "var/data" - "images" - ] - ); - - # networking.firewall.allowedTCPPorts = [ servicePort ]; - - environment.persistence."/persist".directories = lib.mkIf config.swarselsystems.isImpermanence [ - { directory = serviceDir; } - ]; - - topology.self.services.${serviceName} = { - name = lib.swarselsystems.toCapitalized serviceName; - info = "https://${serviceDomain}"; - icon = "${self}/files/topology-images/shlink.png"; - }; - - globals = { - networks = { - ${webProxyIf}.hosts = lib.mkIf isProxied { - ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; - }; - ${homeProxyIf}.hosts = lib.mkIf isHome { - ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; - }; - }; - services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6 isHome; - }; - }; - - nodes.${webProxy}.services.nginx = { - upstreams = { - ${serviceName} = { - servers = { - "${serviceAddress}:${builtins.toString servicePort}" = { }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; }; }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; - virtualHosts = { - "${serviceDomain}" = { - useACMEHost = globals.domains.main; - forceSSL = true; - acmeRoot = null; - oauth2.enable = true; - oauth2.allowedGroups = [ "slink_access" ]; - locations = { - "/" = { - proxyPass = "http://${serviceName}"; + nodes.${webProxy}.services.nginx = { + upstreams = { + ${serviceName} = { + servers = { + "${serviceAddress}:${builtins.toString servicePort}" = { }; }; - "/image" = { - proxyPass = "http://${serviceName}"; - setOauth2Headers = false; - bypassAuth = true; + }; + }; + virtualHosts = { + "${serviceDomain}" = { + useACMEHost = globals.domains.main; + + forceSSL = true; + acmeRoot = null; + oauth2.enable = true; + oauth2.allowedGroups = [ "slink_access" ]; + locations = { + "/" = { + proxyPass = "http://${serviceName}"; + }; + "/image" = { + proxyPass = "http://${serviceName}"; + setOauth2Headers = false; + bypassAuth = true; + }; }; }; }; }; }; - }; -} + } #+end_src **** Snipe-IT (currently unused) @@ -14695,7 +15094,7 @@ in :END: #+begin_src nix-ts :tangle modules/nixos/server/homebox.nix - { lib, pkgs, config, globals, dns, confLib, ... }: + { self, lib, pkgs, config, globals, dns, confLib, ... }: let inherit (confLib.gen { name = "homebox"; port = 7745; }) servicePort serviceName serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; in @@ -14707,7 +15106,11 @@ in "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; - topology.self.services.${serviceName}.info = "https://${serviceDomain}"; + topology.self.services.${serviceName} = { + name = "Homebox"; + info = "https://${serviceDomain}"; + icon = "${self}/files/topology-images/${serviceName}.png"; + }; globals = { networks = { @@ -14785,6 +15188,7 @@ in options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { + services.${serviceName} = { enable = true; user = serviceUser; @@ -14829,7 +15233,7 @@ or 2) use classic path addressing =aws s3 cp s3:/// s3:/// s3:/// s3:/// s3:/// s3:/// s3:/// s3:///=64 bytes. + # head -c 64 /dev/urandom | base64 -w 0 > secrets/${secret} + # chmod 600 secrets/${secret} + # fi + # '') + # ); + # loadSecretEnvironment = + # component: + # let + # relevantSecrets = lib.subtractLists (builtins.attrNames cfg.${component}.settings) ( + # builtins.attrNames cfg.settingsSecret + # ); + # in + # lib.concatLines ( + # lib.forEach relevantSecrets ( + # secret: + # ''export ${secret}=$(< ${ + # if cfg.settingsSecret.${secret} == null then + # "secrets/${secret}" + # else + # "\"$CREDENTIALS_DIRECTORY/${secret}\"" + # })'' + # ) + # ); + # in + # { + # script = lib.mkForce '' + # mkdir -p "$TZDATA_DIR" - environment.persistence."/persist".directories = lib.mkIf config.swarselsystems.isImpermanence [ - { directory = serviceDir; mode = "0700"; } - ]; + # # Generate and load secrets + # ${generateSecrets} + # ${loadSecretEnvironment "domain"} - services.firezone = { - server = { - enable = true; - enableLocalDB = true; - - smtp = { - inherit (config.repo.secrets.local.firezone.mail) from username; - host = globals.services.mailserver.domain; - port = 465; - implicitTls = true; - passwordFile = config.sops.secrets.firezone-smtp-password.path; - }; - - provision = { - enable = true; - accounts.main = { - name = "Home"; - relayGroups.relays.name = "Relays"; - gatewayGroups.home.name = "Home"; - actors.admin = { - type = "account_admin_user"; - name = "Admin"; - email = "admin@${globals.domains.main}"; - }; - groups.anyone = { - name = "anyone"; - members = [ - "admin" - ]; - }; - - auth.oidc = - let - client_id = "firezone"; - in - { - name = "Kanidm"; - adapter = "openid_connect"; - adapter_config = { - scope = "openid email profile"; - response_type = "code"; - inherit client_id; - discovery_document_uri = "https://${globals.services.kanidm.domain}/oauth2/openid/${client_id}/.well-known/openid-configuration"; - clientSecretFile = config.sops.secrets.kanidm-firezone-client.path; - }; - }; - - resources = - lib.genAttrs homeDomains - (domain: { - type = "dns"; - name = domain; - address = domain; - gatewayGroups = [ "home" ]; - filters = [ - { protocol = "icmp"; } - { - protocol = "tcp"; - ports = [ - 443 - 80 - ]; - } - { - protocol = "udp"; - ports = [ 443 ]; - } - ]; - }) - // { - "home.vlan-services.v4" = { - type = "cidr"; - name = "home.vlan-services.v4"; - address = globals.networks.home-lan.vlans.services.cidrv4; - gatewayGroups = [ "home" ]; - }; - "home.vlan-services.v6" = { - type = "cidr"; - name = "home.vlan-services.v6"; - address = globals.networks.home-lan.vlans.services.cidrv6; - gatewayGroups = [ "home" ]; - }; - }; - - policies = - { } - // allow "everyone" "home.vlan-services.v4" - // allow "anyone" "home.vlan-services.v4" - // allow "everyone" "home.vlan-services.v6" - // allow "anyone" "home.vlan-services.v6" - // lib.mergeAttrsList (map (domain: allow "everyone" domain) homeDomains) - // lib.mergeAttrsList (map (domain: allow "anyone" domain) homeDomains); - }; - }; - - domain = { - settings.ERLANG_DISTRIBUTION_PORT = domainPort; - package = pkgs.dev.firezone-server-domain; - }; - api = { - externalUrl = "https://${serviceDomain}/api/"; - address = "0.0.0.0"; - port = apiPort; - package = pkgs.dev.firezone-server-api; - }; - web = { - externalUrl = "https://${serviceDomain}/"; - address = "0.0.0.0"; - port = webPort; - package = pkgs.dev.firezone-server-web; - }; - }; - - relay = { - enable = true; - port = relayPort; - inherit (config.node) name; - apiUrl = "wss://${serviceDomain}/api/"; - tokenFile = config.sops.secrets.firezone-relay-token.path; - publicIpv4 = proxyAddress4; - publicIpv6 = proxyAddress6; - openFirewall = lib.mkIf (!isProxied) true; - package = pkgs.dev.firezone-relay; - }; - }; - # systemd.services.firezone-initialize = - # let - # generateSecrets = - # let - # requiredSecrets = lib.filterAttrs (_: v: v == null) cfg.settingsSecret; - # in - # '' - # mkdir -p secrets - # chmod 700 secrets - # '' - # + lib.concatLines ( - # lib.forEach (builtins.attrNames requiredSecrets) (secret: '' - # if [[ ! -e secrets/${secret} ]]; then - # echo "Generating ${secret}" - # # Some secrets like TOKENS_KEY_BASE require a value >=64 bytes. - # head -c 64 /dev/urandom | base64 -w 0 > secrets/${secret} - # chmod 600 secrets/${secret} - # fi - # '') - # ); - # loadSecretEnvironment = - # component: - # let - # relevantSecrets = lib.subtractLists (builtins.attrNames cfg.${component}.settings) ( - # builtins.attrNames cfg.settingsSecret - # ); - # in - # lib.concatLines ( - # lib.forEach relevantSecrets ( - # secret: - # ''export ${secret}=$(< ${ - # if cfg.settingsSecret.${secret} == null then - # "secrets/${secret}" - # else - # "\"$CREDENTIALS_DIRECTORY/${secret}\"" - # })'' - # ) - # ); - # in - # { - # script = lib.mkForce '' - # mkdir -p "$TZDATA_DIR" - - # # Generate and load secrets - # ${generateSecrets} - # ${loadSecretEnvironment "domain"} - - # echo "Running migrations" - # ${lib.getExe cfg.domain.package} eval "Domain.Release.migrate(manual: true)" - # ''; - # }; + # echo "Running migrations" + # ${lib.getExe cfg.domain.package} eval "Domain.Release.migrate(manual: true)" + # ''; + # }; - nodes = { - ${homeProxy} = - let - nodeCfg = nodes.${homeProxy}.config; - nodePkgs = nodes.${homeProxy}.pkgs; - in - { - sops.secrets.firezone-gateway-token = { inherit (nodeCfg.swarselsystems) sopsFile; mode = "0400"; }; - networking.nftables = { - firewall = { - zones.firezone.interfaces = [ "tun-firezone" ]; - rules = { - # masquerade firezone traffic - masquerade-firezone = { - from = [ "firezone" ]; - to = [ "vlan-services" ]; - # masquerade = true; NOTE: custom rule below for ip4 + ip6 - late = true; # Only accept after any rejects have been processed - verdict = "accept"; - }; - # forward firezone traffic - forward-incoming-firezone-traffic = { - from = [ "firezone" ]; - to = [ "vlan-services" ]; - verdict = "accept"; - }; - - # FIXME: is this needed? conntrack should take care of it and we want to masquerade anyway - forward-outgoing-firezone-traffic = { - from = [ "vlan-services" ]; - to = [ "firezone" ]; - verdict = "accept"; - }; - }; - }; - chains.postrouting = { + nodes = { + ${homeProxy} = + let + nodeCfg = nodes.${homeProxy}.config; + nodePkgs = nodes.${homeProxy}.pkgs; + in + { + sops.secrets.firezone-gateway-token = { inherit (nodeCfg.swarselsystems) sopsFile; mode = "0400"; }; + networking.nftables = { + firewall = { + zones.firezone.interfaces = [ "tun-firezone" ]; + rules = { + # masquerade firezone traffic masquerade-firezone = { - after = [ "hook" ]; - late = true; - rules = - lib.forEach - [ - "firezone" + from = [ "firezone" ]; + to = [ "vlan-services" ]; + # masquerade = true; NOTE: custom rule below for ip4 + ip6 + late = true; # Only accept after any rejects have been processed + verdict = "accept"; + }; + # forward firezone traffic + forward-incoming-firezone-traffic = { + from = [ "firezone" ]; + to = [ "vlan-services" ]; + verdict = "accept"; + }; + + # FIXME: is this needed? conntrack should take care of it and we want to masquerade anyway + forward-outgoing-firezone-traffic = { + from = [ "vlan-services" ]; + to = [ "firezone" ]; + verdict = "accept"; + }; + }; + }; + chains.postrouting = { + masquerade-firezone = { + after = [ "hook" ]; + late = true; + rules = + lib.forEach + [ + "firezone" + ] + ( + zone: + lib.concatStringsSep " " [ + "meta protocol { ip, ip6 }" + (lib.head nodeCfg.networking.nftables.firewall.zones.${zone}.ingressExpression) + (lib.head nodeCfg.networking.nftables.firewall.zones.vlan-services.egressExpression) + "masquerade random" ] - ( - zone: - lib.concatStringsSep " " [ - "meta protocol { ip, ip6 }" - (lib.head nodeCfg.networking.nftables.firewall.zones.${zone}.ingressExpression) - (lib.head nodeCfg.networking.nftables.firewall.zones.vlan-services.egressExpression) - "masquerade random" - ] - ); - }; + ); }; }; - - boot.kernel.sysctl = { - "net.core.wmem_max" = 16777216; - "net.core.rmem_max" = 134217728; - }; - services.firezone.gateway = { - enable = true; - logLevel = "trace"; - inherit (nodeCfg.node) name; - apiUrl = "wss://${globals.services.firezone.domain}/api/"; - tokenFile = nodeCfg.sops.secrets.firezone-gateway-token.path; - package = nodePkgs.stable25_05.firezone-gateway; # newer versions of firezone-gateway are not compatible with server package - }; }; - ${idmServer} = - let - nodeCfg = nodes.${idmServer}.config; - accountId = "6b3c6ba7-5240-4684-95ce-f40fdae45096"; - externalId = "08d714e9-1ab9-4133-a39d-00e843a960cc"; - in - { - sops.secrets.kanidm-firezone = { inherit (nodeCfg.swarselsystems) sopsFile; owner = "kanidm"; group = "kanidm"; mode = "0440"; }; - services.kanidm.provision = { - groups."firezone.access" = { }; - systems.oauth2.firezone = { - displayName = "Firezone VPN"; - # NOTE: state: both uuids are runtime values - originUrl = [ - "https://${globals.services.firezone.domain}/${accountId}/sign_in/providers/${externalId}/handle_callback" - "https://${globals.services.firezone.domain}/${accountId}/settings/identity_providers/openid_connect/${externalId}/handle_callback" - ]; - originLanding = "https://${globals.services.firezone.domain}/"; - basicSecretFile = nodeCfg.sops.secrets.kanidm-firezone.path; - preferShortUsername = true; - scopeMaps."firezone.access" = [ - "openid" - "email" - "profile" - ]; - }; + boot.kernel.sysctl = { + "net.core.wmem_max" = 16777216; + "net.core.rmem_max" = 134217728; + }; + services.firezone.gateway = { + enable = true; + # logLevel = "trace"; + inherit (nodeCfg.node) name; + apiUrl = "wss://${globals.services.firezone.domain}/api/"; + tokenFile = nodeCfg.sops.secrets.firezone-gateway-token.path; + package = nodePkgs.stable25_05.firezone-gateway; # newer versions of firezone-gateway are not compatible with server package + }; + + topology.self.services."${serviceName}-gateway" = { + name = lib.swarselsystems.toCapitalized "${serviceName} Gateway"; + icon = "${self}/files/topology-images/${serviceName}.png"; + }; + }; + ${idmServer} = + let + nodeCfg = nodes.${idmServer}.config; + accountId = "6b3c6ba7-5240-4684-95ce-f40fdae45096"; + externalId = "08d714e9-1ab9-4133-a39d-00e843a960cc"; + in + { + sops.secrets.kanidm-firezone = { inherit (nodeCfg.swarselsystems) sopsFile; owner = "kanidm"; group = "kanidm"; mode = "0440"; }; + services.kanidm.provision = { + groups."firezone.access" = { }; + systems.oauth2.firezone = { + displayName = "Firezone VPN"; + # NOTE: state: both uuids are runtime values + originUrl = [ + "https://${globals.services.firezone.domain}/${accountId}/sign_in/providers/${externalId}/handle_callback" + "https://${globals.services.firezone.domain}/${accountId}/settings/identity_providers/openid_connect/${externalId}/handle_callback" + ]; + originLanding = "https://${globals.services.firezone.domain}/"; + basicSecretFile = nodeCfg.sops.secrets.kanidm-firezone.path; + preferShortUsername = true; + scopeMaps."firezone.access" = [ + "openid" + "email" + "profile" + ]; + }; + + }; + }; + ${webProxy} = { + services.nginx = { + upstreams = { + ${serviceName} = { + servers."${serviceAddress}:${builtins.toString webPort}" = { }; + }; + "${serviceName}-api" = { + servers."${serviceAddress}:${builtins.toString apiPort}" = { }; }; }; - ${webProxy} = { - services.nginx = { - upstreams = { - ${serviceName} = { - servers."${serviceAddress}:${builtins.toString webPort}" = { }; + virtualHosts = { + ${serviceDomain} = { + useACMEHost = globals.domains.main; + forceSSL = true; + acmeRoot = null; + locations."/" = { + # The trailing slash is important to strip the location prefix from the request + proxyPass = "http://${serviceName}/"; + proxyWebsockets = true; }; - "${serviceName}-api" = { - servers."${serviceAddress}:${builtins.toString apiPort}" = { }; - }; - }; - virtualHosts = { - ${serviceDomain} = { - useACMEHost = globals.domains.main; - forceSSL = true; - acmeRoot = null; - locations."/" = { - # The trailing slash is important to strip the location prefix from the request - proxyPass = "http://${serviceName}/"; - proxyWebsockets = true; - }; - locations."/api/" = { - # The trailing slash is important to strip the location prefix from the request - proxyPass = "http://${serviceName}-api/"; - proxyWebsockets = true; - }; + locations."/api/" = { + # The trailing slash is important to strip the location prefix from the request + proxyPass = "http://${serviceName}-api/"; + proxyWebsockets = true; }; }; }; }; }; - }; - } + + }; + } +#+end_src +**** Adguardhome +:PROPERTIES: +:CUSTOM_ID: h:ecb66cb8-12b5-44e8-ad6b-7848711e1ffe +:END: + + +#+begin_src nix-ts :tangle modules/nixos/server/adguardhome.nix + { self, inputs, lib, config, globals, dns, confLib, ... }: + let + inherit (confLib.gen { name = "adguardhome"; port = 3000; }) serviceName servicePort serviceAddress serviceDomain proxyAddress4 proxyAddress6; + inherit (confLib.static) isHome isProxied homeProxy homeProxyIf webProxy webProxyIf homeWebProxy dnsServer homeDnsServer homeServiceAddress nginxAccessRules; + + homeServices = lib.attrNames (lib.filterAttrs (_: serviceCfg: serviceCfg.isHome) globals.services); + homeDomains = map (name: globals.services.${name}.domain) homeServices; + in + { + options = { + swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; + }; + config = lib.mkIf config.swarselmodules.server.${serviceName} { + + + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; + }; + + networking.firewall = { + allowedTCPPorts = [ 53 ]; + allowedUDPPorts = [ 53 ]; + }; + + services.adguardhome = { + enable = true; + mutableSettings = false; + host = "0.0.0.0"; + port = servicePort; + settings = { + dns = { + bind_hosts = [ + globals.networks.home-lan.vlans.services.hosts.${homeDnsServer}.ipv4 + globals.networks.home-lan.vlans.services.hosts.${homeDnsServer}.ipv6 + ]; + ratelimit = 300; + upstream_dns = [ + "https://dns.cloudflare.com/dns-query" + "https://dns.google/dns-query" + "https://doh.mullvad.net/dns-query" + ]; + bootstrap_dns = [ + "1.1.1.1" + "2606:4700:4700::1111" + "8.8.8.8" + "2001:4860:4860::8844" + ]; + dhcp.enabled = false; + }; + filtering.rewrites = [ + ] + # Use the local mirror-proxy for some services (not necessary, just for speed) + ++ + map + (domain: { + inherit domain; + # FIXME: change to homeWebProxy once that is setup + answer = globals.networks.home-lan.vlans.services.hosts.${homeWebProxy}.ipv4; + # answer = globals.hosts.${webProxy}.wanAddress4; + }) + homeDomains; + filters = [ + { + name = "AdGuard DNS filter"; + url = "https://adguardteam.github.io/AdGuardSDNSFilter/Filters/filter.txt"; + enabled = true; + } + { + name = "AdAway Default Blocklist"; + url = "https://adaway.org/hosts.txt"; + enabled = true; + } + { + name = "OISD (Big)"; + url = "https://big.oisd.nl"; + enabled = true; + } + ]; + }; + }; + + environment.persistence."/persist".directories = lib.mkIf config.swarselsystems.isImpermanence [ + { + directory = "/var/lib/private/AdGuardHome"; + mode = "0700"; + } + ]; + + nodes = + let + genNginx = toAddress: extraConfig: { + upstreams = { + ${serviceName} = { + servers = { + "${toAddress}:${builtins.toString servicePort}" = { }; + }; + }; + }; + virtualHosts = { + "${serviceDomain}" = { + useACMEHost = globals.domains.main; + forceSSL = true; + acmeRoot = null; + oauth2 = { + enable = true; + allowedGroups = [ "adguardhome_access" ]; + }; + locations = { + "/" = { + proxyPass = "http://${serviceName}"; + proxyWebsockets = true; + }; + }; + extraConfig = lib.mkIf (extraConfig != "") extraConfig; + }; + }; + }; + in + { + ${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; + }; + ${webProxy}.services.nginx = genNginx serviceAddress ""; + ${homeWebProxy}.services.nginx = genNginx homeServiceAddress nginxAccessRules; + }; + }; + } #+end_src *** Darwin :PROPERTIES: @@ -17271,15 +17912,22 @@ Some standard options that should be set for every microvm host. #+begin_src nix-ts :tangle modules/nixos/optional/microvm-host.nix { config, lib, ... }: { - # imports = [ - # inputs.microvm.nixosModules.host - # ]; - config = lib.mkIf (config.guests != { }) { - microvm = { - hypervisor = lib.mkDefault "qemu"; - }; + systemd.tmpfiles.settings."15-microvms" = builtins.listToAttrs ( + map + (path: { + name = "${lib.optionalString config.swarselsystems.isImpermanence "/persist"}/microvms/${path}"; + value = { + d = { + group = "kvm"; + user = "microvm"; + mode = "0750"; + }; + }; + }) (builtins.attrNames config.guests) + ); + }; } #+end_src @@ -17289,41 +17937,111 @@ Some standard options that should be set for every microvm host. :CUSTOM_ID: h:46419b40-c40b-4b55-ac6f-a30169322bd6 :END: -Some standard options that should be set vor every microvm guest. We set the default +Some standard options that should be set for every microvm guest. We set the default #+begin_src nix-ts :tangle modules/nixos/optional/microvm-guest.nix - { self, inputs, ... }: + { self, lib, config, inputs, microVMParent, nodes, ... }: + { + imports = [ + inputs.disko.nixosModules.disko + inputs.home-manager.nixosModules.home-manager + inputs.impermanence.nixosModules.impermanence + inputs.lanzaboote.nixosModules.lanzaboote + inputs.microvm.nixosModules.microvm + inputs.nix-index-database.nixosModules.nix-index + inputs.nix-minecraft.nixosModules.minecraft-servers + inputs.nix-topology.nixosModules.default + inputs.nswitch-rcm-nix.nixosModules.nswitch-rcm + inputs.simple-nixos-mailserver.nixosModules.default + inputs.sops.nixosModules.sops + inputs.stylix.nixosModules.stylix + inputs.swarsel-nix.nixosModules.default + inputs.nixos-nftables-firewall.nixosModules.default + + (inputs.nixos-extra-modules + "/modules/interface-naming.nix") + + "${self}/modules/shared/meta.nix" + ]; + + config = { + _module.args.dns = inputs.dns; + + nix.settings.experimental-features = [ + "nix-command" + "flakes" + ]; + systemd.services."systemd-networkd".environment.SYSTEMD_LOG_LEVEL = "debug"; + # NOTE: this is needed, we dont import sevrer network module for microvms + globals.hosts.${config.node.name}.isHome = true; + + fileSystems."/persist".neededForBoot = lib.mkForce true; + + systemd.network.networks."10-vlan-services" = { + dhcpV6Config = { + WithoutRA = "solicit"; + # duid-en is nice in principle, but I already have MAC info anyways for reservations + DUIDType = "link-layer"; + }; + # networkConfig = { + # IPv6PrivacyExtensions = "no"; + # IPv6AcceptRA = false; + # }; + ipv6AcceptRAConfig = { + DHCPv6Client = "always"; + }; + }; + + microvm = { + shares = [ + { + tag = "persist"; + source = "${lib.optionalString nodes.${microVMParent}.config.swarselsystems.isImpermanence "/persist"}/microvms/${config.networking.hostName}"; + mountPoint = "/persist"; + proto = "virtiofs"; + } + ]; + # mount the writeable overlay so that we can use nix shells inside the microvm + volumes = [ + { + image = "/tmp/nix-store-overlay-${config.networking.hostName}.img"; + autoCreate = true; + mountPoint = config.microvm.writableStoreOverlay; + size = 1024; + } + ]; + }; + }; + } + +#+end_src + +**** systemd-networkd (base) +:PROPERTIES: +:CUSTOM_ID: h:99bf6c0e-2566-4a50-b219-fb6a7d4fb2cd +:END: + +This set of options enables the network of the system to be managed by =systemd-networkd=: + - =networking.useNetworkd= has the effect that options from =networking.*= are not performed using network scripts but rather using =systemd-networkd=. + - =systemd.network.enable= enables the actual management of networks using the =systemd-networkd= interface. + +#+begin_src nix-ts :tangle modules/nixos/optional/systemd-networkd-base.nix + { lib, config, ... }: { - imports = [ - inputs.disko.nixosModules.disko - inputs.home-manager.nixosModules.home-manager - inputs.impermanence.nixosModules.impermanence - inputs.lanzaboote.nixosModules.lanzaboote - inputs.microvm.nixosModules.host - inputs.microvm.nixosModules.microvm - inputs.nix-index-database.nixosModules.nix-index - inputs.nix-minecraft.nixosModules.minecraft-servers - inputs.nix-topology.nixosModules.default - inputs.nswitch-rcm-nix.nixosModules.nswitch-rcm - inputs.simple-nixos-mailserver.nixosModules.default - inputs.sops.nixosModules.sops - inputs.stylix.nixosModules.stylix - inputs.swarsel-nix.nixosModules.default - inputs.nixos-nftables-firewall.nixosModules.default - - (inputs.nixos-extra-modules + "/modules/interface-naming.nix") - - "${self}/modules/shared/meta.nix" - ]; - - config = { - system.stateVersion = "23.05"; + networking = { + useDHCP = lib.mkForce false; + useNetworkd = true; + dhcpcd.enable = lib.mkIf (!config.swarselsystems.isMicroVM) false; + renameInterfacesByMac = lib.mkIf (!config.swarselsystems.isMicroVM) (lib.mapAttrs (_: v: if (v ? mac) then v.mac else "") ( + config.repo.secrets.local.networking.networks or { } + )); }; + + systemd.network.enable = true; } #+end_src -**** systemd-networkd (server) +**** systemd-networkd (server base) :PROPERTIES: :CUSTOM_ID: h:12370671-7892-4a74-a804-84f871acde06 :END: @@ -17331,31 +18049,33 @@ Some standard options that should be set vor every microvm guest. We set the def Some standard options that should be set vor every microvm guest. We set the default #+begin_src nix-ts :tangle modules/nixos/optional/systemd-networkd-server.nix - { lib, config, globals, ... }: + { self, lib, config, globals, ... }: + let + inherit (config.swarselsystems) isCrypted localVLANs; + inherit (globals.general) routerServer; + + isRouter = config.node.name == routerServer; + ifName = config.swarselsystems.server.localNetwork; + in { - networking = { - useDHCP = lib.mkForce false; - useNetworkd = true; - dhcpcd.enable = false; - renameInterfacesByMac = lib.mapAttrs (_: v: if (v ? mac) then v.mac else "") ( - config.repo.secrets.local.networking.networks or { } - ); - }; - boot.initrd.systemd.network = { + imports = [ + "${self}/modules/nixos/optional/systemd-networkd-base.nix" + ]; + + boot.initrd.systemd.network = lib.mkIf (isCrypted && ((localVLANs == [ ]) || isRouter)) { enable = true; - networks."10-${config.swarselsystems.server.localNetwork}" = config.systemd.network.networks."10-${config.swarselsystems.server.localNetwork}"; + networks."10-${ifName}" = config.systemd.network.networks."10-${ifName}"; }; systemd = { network = { - enable = true; wait-online.enable = false; networks = let netConfig = config.repo.secrets.local.networking; in { - "10-${config.swarselsystems.server.localNetwork}" = { + "10-${ifName}" = lib.mkIf (isRouter || (localVLANs == [ ])) { address = [ "${globals.networks.${config.swarselsystems.server.netConfigName}.hosts.${config.node.name}.cidrv4}" "${globals.networks.${config.swarselsystems.server.netConfigName}.hosts.${config.node.name}.cidrv6}" @@ -17384,136 +18104,158 @@ Some standard options that should be set vor every microvm guest. We set the def #+end_src -**** systemd-networkd (vlans/microvms) +**** TODO systemd-networkd (server home) :PROPERTIES: -:CUSTOM_ID: h:9e1b68cb-f482-417d-8be8-48bfbf3a0d99 +:CUSTOM_ID: h:049fc27e-a28f-4ff0-b5f0-d81401bdd56f :END: -This sets up the networking framework that is needed for a server that hosts microvms. +This sets up the networking framework that is needed for a server that manages its VLAN interfaces using =systemd-networkd=. A host that is not both in the home network and using VLANs should rather be using [[#h:12370671-7892-4a74-a804-84f871acde06][systemd-networkd (server base)]] only. -The general idea is as follows: -- A host has =n= physical interfaces, which bind to the =br= bridge. Also bound to the bridge is the =veth= interfaces that the vlans are applied to. This makes it so that the macvlan interfaces still get an IP even if the physical interfaces have no carrier. -- For each VLAN defined in globals we create a VLAN here - we also create a macvlan interface for the hosts which is bound to the respective VLAN interface; also binding to that VLAN interface are the macvtap devices that are being created by the microvm module. - - normally, a guest using macvtap is not reachable by the host unless using a switch that supports hairpin-mode. However, consumers of the same VLAN can still communicate, which is realized using the macvlan interface. +We will differentiate between a host that uses microvms versus a host that is not using them. + +For a host with microvms the general idea is as follows: +- For each local VLAN we create a VLAN here - we also create a macvlan interface for the hosts which is bound to the respective VLAN interface; also binding to that VLAN interface are the macvtap devices that are being created by the microvm module. + - normally, a guest using macvtap is not reachable by the host unless using a switch that supports hairpin-mode. However, consumers of the same VLAN can still communicate, which is realized using the macvlan =me-*= interface. - even then, the kernel will only route requests when the underlying interface is up. In the case that no physical ports are used, this means that the bridge interface would effectively not work (even when administratively set to UP using =activationPolicy=) - the aforementioned =veth= takes care of that problem. + - this is really only a consideration for the [[#h:b54f2bbb-0088-46b2-957d-fd8234b772c3][Router]] (because if the interface to the router is missing on the hosts, there will be no connectivity anyways) and is hence implemented there -#+begin_src nix-ts :tangle modules/nixos/optional/systemd-networkd-vlan.nix - { lib, config, globals, ... }: - { +The principle is the same for a host without microvms, but we do not need the local =me-*= interfaces and can ignore =macvtap= config. - systemd.network = { - wait-online.anyInterface = true; - netdevs = { - "10-veth" = { - netdevConfig = { - Kind = "veth"; - Name = "veth-br"; - }; - peerConfig = { - Name = "veth-int"; - }; - }; - "20-br" = { - netdevConfig = { - Kind = "bridge"; - Name = "br"; - }; - }; - } // lib.flip lib.concatMapAttrs globals.networks.home-lan.vlans ( - vlanName: vlanCfg: { - "30-vlan-${vlanName}" = { - netdevConfig = { - Kind = "vlan"; - Name = "vlan-${vlanName}"; +A VLAN can also be used as the initrd network - this is however disabled for the router host. For that host, we need to connect from the =FritzBox!= side in case we need to reboot it (TODO: fix interface naming lan/wan which blocks this) + + +#+begin_src nix-ts :tangle modules/nixos/optional/systemd-networkd-server-home.nix + { self,lib, config, globals, ... }: + let + inherit (globals.general) routerServer; + inherit (config.swarselsystems) withMicroVMs isCrypted initrdVLAN; + + isRouter = config.node.name == routerServer; + localVLANsList = config.swarselsystems.localVLANs; + localVLANs = lib.genAttrs localVLANsList (x: globals.networks.home-lan.vlans.${x}); + in + { + imports = [ + "${self}/modules/nixos/optional/systemd-networkd-server.nix" + ]; + config = { + assertions = [ + { + assertion = ((localVLANsList != []) && (initrdVLAN != null)) || (localVLANsList == []) || (!isCrypted); + message = "This host uses VLANs and disk encryption, thus a VLAN must be specified for initrd or disk encryption must be removed."; + } + ]; + + boot.initrd = lib.mkIf (isCrypted && (localVLANsList != []) && (!isRouter)) { + availableKernelModules = [ "8021q" ]; + systemd.network = { + enable = true; + netdevs."30-vlan-${initrdVLAN}" = { + netdevConfig = { + Kind = "vlan"; + Name = "vlan-${initrdVLAN}"; + }; + vlanConfig.Id = globals.networks.home-lan.vlans.${initrdVLAN}.id; }; - vlanConfig.Id = vlanCfg.id; - }; - "40-me-${vlanName}" = { - netdevConfig = { - Name = "me-${vlanName}"; - Kind = "macvlan"; + networks = { + "10-lan" = { + matchConfig.Name = "lan"; + # This interface should only be used from attached vlans. + # So don't acquire a link local address and only wait for + # this interface to gain a carrier. + networkConfig.LinkLocalAddressing = "no"; + linkConfig.RequiredForOnline = "carrier"; + vlan = [ "vlan-${initrdVLAN}" ]; + }; + "30-vlan-${initrdVLAN}" = { + address = [ + globals.networks.home-lan.vlans.${initrdVLAN}.hosts.${config.node.name}.cidrv4 + globals.networks.home-lan.vlans.${initrdVLAN}.hosts.${config.node.name}.cidrv6 + ]; + matchConfig.Name = "vlan-${initrdVLAN}"; + networkConfig = { + IPv6PrivacyExtensions = "yes"; + }; + linkConfig.RequiredForOnline = "routable"; + }; }; - extraConfig = '' - [MACVLAN] - Mode=bridge - ''; - }; - } - ); - networks = { - "40-br" = { - matchConfig.Name = "br"; - bridgeConfig = { }; - linkConfig = { - ActivationPolicy = "always-up"; - RequiredForOnline = "no"; - }; - networkConfig = { - ConfigureWithoutCarrier = true; - LinkLocalAddressing = "no"; }; }; - "15-veth-br" = { - matchConfig.Name = "veth-br"; - linkConfig = { - RequiredForOnline = "no"; - }; - - networkConfig = { - Bridge = "br"; - }; - }; - "15-veth-int" = { - matchConfig.Name = "veth-int"; - - linkConfig = { - ActivationPolicy = "always-up"; - RequiredForOnline = "no"; - }; - - networkConfig = { - ConfigureWithoutCarrier = true; - LinkLocalAddressing = "no"; - }; - - vlan = map (name: "vlan-${name}") (builtins.attrNames globals.networks.home-lan.vlans); - }; - "90-macvtap-ignore" = { - matchConfig.Kind = "macvtap"; - linkConfig.ActivationPolicy = "manual"; - linkConfig.Unmanaged = "yes"; - }; - } // lib.flip lib.concatMapAttrs globals.networks.home-lan.vlans ( - vlanName: vlanCfg: { - "30-vlan-${vlanName}" = { - matchConfig.Name = "vlan-${vlanName}"; - networkConfig.LinkLocalAddressing = "no"; - networkConfig.MACVLAN = "me-${vlanName}"; - linkConfig.RequiredForOnline = "no"; - }; - "40-me-${vlanName}" = { - address = [ - vlanCfg.hosts.${config.node.name}.cidrv4 - vlanCfg.hosts.${config.node.name}.cidrv6 - ]; - matchConfig.Name = "me-${vlanName}"; - networkConfig = { - IPv4Forwarding = "yes"; - IPv6PrivacyExtensions = "yes"; - IPv6SendRA = true; - IPv6AcceptRA = false; + systemd.network = { + netdevs = lib.flip lib.concatMapAttrs localVLANs ( + vlanName: vlanCfg: { + "30-vlan-${vlanName}" = { + netdevConfig = { + Kind = "vlan"; + Name = "vlan-${vlanName}"; + }; + vlanConfig.Id = vlanCfg.id; + }; + # Create a MACVTAP for ourselves too, so that we can communicate with + # our guests on the same interface. + "40-me-${vlanName}" = lib.mkIf withMicroVMs { + netdevConfig = { + Name = "me-${vlanName}"; + Kind = "macvlan"; + }; + extraConfig = '' + [MACVLAN] + Mode=bridge + ''; + }; + } + ); + networks = { + "10-lan" = lib.mkIf (!isRouter) { + matchConfig.Name = "lan"; + # This interface should only be used from attached vlans. + # So don't acquire a link local address and only wait for + # this interface to gain a carrier. + networkConfig.LinkLocalAddressing = "no"; + linkConfig.RequiredForOnline = "carrier"; + vlan = (map (name: "vlan-${name}") (builtins.attrNames localVLANs)); }; - ipv6Prefixes = [ - { Prefix = vlanCfg.cidrv6; } - ]; - linkConfig.RequiredForOnline = "routable"; - }; - } - ); - }; + # Remaining macvtap interfaces should not be touched. + "90-macvtap-ignore" = lib.mkIf withMicroVMs { + matchConfig.Kind = "macvtap"; + linkConfig.ActivationPolicy = "manual"; + linkConfig.Unmanaged = "yes"; + }; + } + // lib.flip lib.concatMapAttrs localVLANs ( + vlanName: vlanCfg: + let + me = { + address = [ + vlanCfg.hosts.${config.node.name}.cidrv4 + vlanCfg.hosts.${config.node.name}.cidrv6 + ]; + gateway = lib.optionals (vlanName == "services") [ vlanCfg.hosts.${routerServer}.ipv4 vlanCfg.hosts.${routerServer}.ipv6 ]; + matchConfig.Name = "${if withMicroVMs then "me" else "vlan"}-${vlanName}"; + networkConfig.IPv6PrivacyExtensions = "yes"; + linkConfig.RequiredForOnline = "routable"; + }; - } + in + { + "30-vlan-${vlanName}" = if (!withMicroVMs) then me else { + matchConfig.Name = "vlan-${vlanName}"; + # This interface should only be used from attached macvlans. + # So don't acquire a link local address and only wait for + # this interface to gain a carrier. + networkConfig.LinkLocalAddressing = "no"; + networkConfig.MACVLAN = "me-${vlanName}"; + linkConfig.RequiredForOnline = if isRouter then "no" else "carrier"; + }; + "40-me-${vlanName}" = lib.mkIf withMicroVMs (lib.mkDefault me); + } + ); + }; + + }; + + } #+end_src @@ -17525,16 +18267,28 @@ The general idea is as follows: Hold standard options for nix-topology per config #+begin_src nix-ts :tangle modules/nixos/optional/nix-topology-self.nix - { lib, config, globals, ... }: + { lib, config, globals, confLib, ... }: + let + inherit (confLib.static) webProxy; + in { topology.self = { icon = lib.mkIf config.swarselsystems.isCloud "devices.cloud-server"; - interfaces.wan = lib.mkIf config.swarselsystems.isCloud { }; - interfaces.wg = lib.mkIf (config.swarselsystems.server.wireguard.isClient || config.swarselsystems.server.wireguard.isServer) { - addresses = [ globals.networks.twothreetunnel-wg.hosts.${config.node.name}.ipv4 ]; - renderer.hidePhysicalConnections = true; - virtual = true; - type = "wireguard"; + interfaces = { + wan = lib.mkIf (config.swarselsystems.isCloud && config.swarselsystems.server.localNetwork == "wan") { }; + lan = lib.mkIf (config.swarselsystems.isCloud && config.swarselsystems.server.localNetwork == "lan") { }; + wgProxy = lib.mkIf (config.swarselsystems.server.wireguard ? wgHome) { + addresses = [ globals.networks."${webProxy}-wg.hosts".${config.node.name}.ipv4 ]; + renderer.hidePhysicalConnections = true; + virtual = true; + type = "wireguard"; + }; + wgHome = lib.mkIf (config.swarselsystems.server.wireguard ? wgHome) { + addresses = [ globals.networks.home-wgHome.hosts.${config.node.name}.ipv4 ]; + renderer.hidePhysicalConnections = true; + virtual = true; + type = "wireguard"; + }; }; }; } @@ -21676,6 +22430,9 @@ Sets up a systemd user service for anki that does not stall the shutdown process #+end_src ***** firezone service for tray +:PROPERTIES: +:CUSTOM_ID: h:2690d49b-2b25-4204-b349-36e3efe2462a +:END: #+begin_src nix-ts :tangle modules/home/common/firezone-tray.nix { lib, config, pkgs, ... }: @@ -24344,6 +25101,18 @@ TODO: check which of these can be replaced but builtin functions. type = lib.types.str; default = ""; }; + # @ future me: dont put this under server prefix + # home-manager would then try to import all swarselsystems.server.* options + localVLANs = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + }; + # @ future me: dont put this under server prefix + # home-manager would then try to import all swarselsystems.server.* options + initrdVLAN = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + }; mainUser = lib.mkOption { type = lib.types.str; default = "swarsel"; @@ -24696,29 +25465,29 @@ In short, the options defined here are passed to the modules systems using =_mod :END: #+begin_src nix-ts :noweb yes :tangle modules/shared/config-lib.nix { self, config, lib, globals, inputs, outputs, minimal, nixosConfig ? null, ... }: + let + domainDefault = service: config.repo.secrets.common.services.domains.${service}; + proxyDefault = config.swarselsystems.proxyHost; + + addressDefault = + if + config.swarselsystems.proxyHost != config.node.name + then + if + config.swarselsystems.server.wireguard.interfaces.wgProxy.isClient + then + globals.networks."${config.swarselsystems.server.wireguard.interfaces.wgProxy.serverNetConfigPrefix}-wgProxy".hosts.${config.node.name}.ipv4 + else + globals.networks.${config.swarselsystems.server.netConfigName}.hosts.${config.node.name}.ipv4 + else + "localhost"; + in { _module.args = { confLib = rec { - - addressDefault = - if - config.swarselsystems.proxyHost != config.node.name - then - if - config.swarselsystems.server.wireguard.interfaces.wgProxy.isClient - then - globals.networks."${config.swarselsystems.server.wireguard.interfaces.wgProxy.serverNetConfigPrefix}-wgProxy".hosts.${config.node.name}.ipv4 - else - globals.networks.${config.swarselsystems.server.netConfigName}.hosts.${config.node.name}.ipv4 - else - "localhost"; - - domainDefault = service: config.repo.secrets.common.services.domains.${service}; - proxyDefault = config.swarselsystems.proxyHost; - getConfig = if nixosConfig == null then config else nixosConfig; - gen = { name, user ? name, group ? name, dir ? null, port ? null, domain ? (domainDefault name), address ? addressDefault, proxy ? proxyDefault }: rec { + gen = { name ? "n/a", user ? name, group ? name, dir ? null, port ? null, domain ? (domainDefault name), address ? addressDefault, proxy ? proxyDefault }: rec { servicePort = port; serviceName = name; specificServiceName = "${name}-${config.node.name}"; @@ -24733,44 +25502,66 @@ In short, the options defined here are passed to the modules systems using =_mod proxyAddress4 = globals.hosts.${proxy}.wanAddress4 or null; proxyAddress6 = globals.hosts.${proxy}.wanAddress6 or null; inherit (globals.hosts.${config.node.name}) isHome; - inherit (globals.general) homeProxy webProxy dnsServer idmServer; + inherit (globals.general) homeProxy webProxy dnsServer homeDnsServer homeWebProxy idmServer; webProxyIf = "${webProxy}-wgProxy"; homeProxyIf = "home-wgHome"; isProxied = config.node.name != webProxy; }; - mkMicrovm = if config.swarselsystems.withMicroVMs then (guestName: { - ${guestName} = { - backend = "microvm"; - autostart = true; - modules = [ - (config.node.configDir + /guests/${guestName}.nix) - { - node.secretsDir = config.node.configDir + /secrets/${guestName}; - node.configDir = config.node.configDir + /guests/${guestName}; - networking.nftables.firewall = { - zones.untrusted.interfaces = lib.mkIf ( - lib.length config.guests.${guestName}.networking.links == 1 - ) config.guests.${guestName}.networking.links; + static = rec { + inherit (globals.hosts.${config.node.name}) isHome; + inherit (globals.general) homeProxy webProxy dnsServer homeDnsServer homeWebProxy idmServer oauthServer; + webProxyIf = "${webProxy}-wgProxy"; + homeProxyIf = "home-wgHome"; + isProxied = config.node.name != webProxy; + nginxAccessRules = '' + allow ${globals.networks.home-lan.vlans.home.cidrv4}; + allow ${globals.networks.home-lan.vlans.home.cidrv6}; + allow ${globals.networks.home-lan.vlans.services.hosts.${homeProxy}.ipv4}; + allow ${globals.networks.home-lan.vlans.services.hosts.${homeProxy}.ipv6}; + deny all; + ''; + homeServiceAddress = lib.optionalString (config.swarselsystems.server.wireguard.interfaces ? wgHome) globals.networks."${config.swarselsystems.server.wireguard.interfaces.wgHome.serverNetConfigPrefix}-wgHome".hosts.${config.node.name}.ipv4; + }; + + mkMicrovm = + if config.swarselsystems.withMicroVMs then + (guestName: { + ${guestName} = { + backend = "microvm"; + autostart = true; + modules = [ + (config.node.configDir + /guests/${guestName}/default.nix) + { + node.secretsDir = config.node.configDir + /secrets/${guestName}; + node.configDir = config.node.configDir + /guests/${guestName}; + networking.nftables.firewall = { + zones.untrusted.interfaces = lib.mkIf + ( + lib.length config.guests.${guestName}.networking.links == 1 + ) + config.guests.${guestName}.networking.links; + }; + } + "${self}/modules/nixos/optional/microvm-guest.nix" + "${self}/modules/nixos/optional/systemd-networkd-base.nix" + ]; + microvm = { + system = config.node.arch; + baseMac = config.repo.secrets.local.networking.networks.lan.mac; + interfaces.vlan-services = { }; }; - } - "${self}/modules/nixos/optional/microvm-guest.nix" - ]; - microvm = { - system = config.node.arch; - baseMac = config.repo.secrets.local.networking.networks.lan.mac; - interfaces.vlan-services = { }; - }; - extraSpecialArgs = { - inherit (outputs) nodes; - inherit (inputs.self.pkgs.${config.node.arch}) lib; - inherit inputs outputs minimal; - inherit (inputs) self; - withHomeManager = false; - globals = outputs.globals.${config.node.arch}; - }; - }; - }) else (_: {_ = {};}); + extraSpecialArgs = { + inherit (outputs) nodes; + inherit (inputs.self.pkgs.${config.node.arch}) lib; + inherit inputs outputs minimal; + inherit (inputs) self; + withHomeManager = false; + microVMParent = config.node.name; + globals = outputs.globals.${config.node.arch}; + }; + }; + }) else (_: { _ = { }; }); }; }; @@ -27233,6 +28024,8 @@ Modules that need to be loaded on the NixOS level. Note that these will not be a general = lib.mkDefault true; packages = lib.mkDefault true; ssh = lib.mkDefault true; + wireguard = lib.mkDefault true; + dns-home = lib.mkDefault true; }; }; }; @@ -27531,7 +28324,7 @@ This holds modules that are to be used on most hosts. These are also the most im #+end_src -* Emacse +* Emacs :PROPERTIES: :CUSTOM_ID: h:ed4cd05c-0879-41c6-bc39-3f1246a96f04 :END: @@ -29652,7 +30445,7 @@ It supports all functions that I normally need. Note that getting completions fo (use-package nix-ts-mode :after lsp-mode - :mode "\\.nix\\'" + :mode ("\\.nix\\'" . "\\.nix\\.enc\\'") :ensure t :hook (nix-ts-mode . lsp-deferred) ;; So that envrc mode will work diff --git a/files/emacs/init.el b/files/emacs/init.el index 26d3439..52ffc4e 100644 --- a/files/emacs/init.el +++ b/files/emacs/init.el @@ -1117,7 +1117,7 @@ create a new one." (use-package nix-ts-mode :after lsp-mode - :mode "\\.nix\\'" + :mode ("\\.nix\\'" . "\\.nix\\.enc\\'") :ensure t :hook (nix-ts-mode . lsp-deferred) ;; So that envrc mode will work diff --git a/files/topology-images/Cisco_SG_200-08.png b/files/topology-images/Cisco_SG_200-08.png new file mode 100644 index 0000000..216fbfc Binary files /dev/null and b/files/topology-images/Cisco_SG_200-08.png differ diff --git a/files/topology-images/Fritz!Box_7682.png b/files/topology-images/Fritz!Box_7682.png new file mode 100644 index 0000000..84ed885 Binary files /dev/null and b/files/topology-images/Fritz!Box_7682.png differ diff --git a/files/topology-images/TL-SG108E.png b/files/topology-images/TL-SG108E.png new file mode 100644 index 0000000..6d3d57d Binary files /dev/null and b/files/topology-images/TL-SG108E.png differ diff --git a/files/topology-images/atlasos.png b/files/topology-images/atlasos.png new file mode 100644 index 0000000..c7c28a4 Binary files /dev/null and b/files/topology-images/atlasos.png differ diff --git a/files/topology-images/clamav.png b/files/topology-images/clamav.png new file mode 100644 index 0000000..8832dc8 Binary files /dev/null and b/files/topology-images/clamav.png differ diff --git a/files/topology-images/comfyui.png b/files/topology-images/comfyui.png new file mode 100644 index 0000000..88f1191 Binary files /dev/null and b/files/topology-images/comfyui.png differ diff --git a/files/topology-images/dgxos.png b/files/topology-images/dgxos.png new file mode 100644 index 0000000..d593fc3 Binary files /dev/null and b/files/topology-images/dgxos.png differ diff --git a/files/topology-images/dovecot.png b/files/topology-images/dovecot.png new file mode 100644 index 0000000..16e35fa Binary files /dev/null and b/files/topology-images/dovecot.png differ diff --git a/files/topology-images/firezone.png b/files/topology-images/firezone.png new file mode 100644 index 0000000..fbbbf67 Binary files /dev/null and b/files/topology-images/firezone.png differ diff --git a/files/topology-images/garage.png b/files/topology-images/garage.png new file mode 100644 index 0000000..50306cf Binary files /dev/null and b/files/topology-images/garage.png differ diff --git a/files/topology-images/home-manager.png b/files/topology-images/home-manager.png new file mode 100644 index 0000000..8ff9258 Binary files /dev/null and b/files/topology-images/home-manager.png differ diff --git a/files/topology-images/homebox.png b/files/topology-images/homebox.png new file mode 100644 index 0000000..c4728f9 Binary files /dev/null and b/files/topology-images/homebox.png differ diff --git a/files/topology-images/kea.png b/files/topology-images/kea.png new file mode 100644 index 0000000..9e68bcd Binary files /dev/null and b/files/topology-images/kea.png differ diff --git a/files/topology-images/minecraft.png b/files/topology-images/minecraft.png new file mode 100644 index 0000000..a9690b9 Binary files /dev/null and b/files/topology-images/minecraft.png differ diff --git a/files/topology-images/nintendo-switch.png b/files/topology-images/nintendo-switch.png index 21f8de3..b42d542 100644 Binary files a/files/topology-images/nintendo-switch.png and b/files/topology-images/nintendo-switch.png differ diff --git a/files/topology-images/nsd.png b/files/topology-images/nsd.png new file mode 100644 index 0000000..4a76c8f Binary files /dev/null and b/files/topology-images/nsd.png differ diff --git a/files/topology-images/ollama.png b/files/topology-images/ollama.png new file mode 100644 index 0000000..8c2d452 Binary files /dev/null and b/files/topology-images/ollama.png differ diff --git a/files/topology-images/openwebui.png b/files/topology-images/openwebui.png new file mode 100644 index 0000000..a158087 Binary files /dev/null and b/files/topology-images/openwebui.png differ diff --git a/files/topology-images/postfix.png b/files/topology-images/postfix.png new file mode 100644 index 0000000..c809b0f Binary files /dev/null and b/files/topology-images/postfix.png differ diff --git a/files/topology-images/roundcube.png b/files/topology-images/roundcube.png new file mode 100644 index 0000000..014dc92 Binary files /dev/null and b/files/topology-images/roundcube.png differ diff --git a/files/topology-images/rspamd.png b/files/topology-images/rspamd.png new file mode 100644 index 0000000..8bf4c12 Binary files /dev/null and b/files/topology-images/rspamd.png differ diff --git a/files/topology-images/sunshine.png b/files/topology-images/sunshine.png new file mode 100644 index 0000000..14c9b79 Binary files /dev/null and b/files/topology-images/sunshine.png differ diff --git a/files/topology-images/tp_link_tl-sg108e.jpg b/files/topology-images/tp_link_tl-sg108e.jpg new file mode 100644 index 0000000..ea8826c Binary files /dev/null and b/files/topology-images/tp_link_tl-sg108e.jpg differ diff --git a/files/topology-images/windows.png b/files/topology-images/windows.png new file mode 100644 index 0000000..c16d05f Binary files /dev/null and b/files/topology-images/windows.png differ diff --git a/flake.lock b/flake.lock index 44acb12..5731952 100644 --- a/flake.lock +++ b/flake.lock @@ -203,11 +203,11 @@ ] }, "locked": { - "lastModified": 1728330715, - "narHash": "sha256-xRJ2nPOXb//u1jaBnDP56M7v5ldavjbtR6lfGqSvcKg=", + "lastModified": 1764011051, + "narHash": "sha256-M7SZyPZiqZUR/EiiBJnmyUbOi5oE/03tCeFrTiUZchI=", "owner": "numtide", "repo": "devshell", - "rev": "dd6b80932022cea34a019e2bb32f6fa9e494dfef", + "rev": "17ed8d9744ebe70424659b0ef74ad6d41fc87071", "type": "github" }, "original": { @@ -426,11 +426,11 @@ "flake-compat_3": { "flake": false, "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "lastModified": 1761588595, + "narHash": "sha256-XKUZz9zewJNUj46b4AJdiRZJAvSZ0Dqj2BNfXvFlJC4=", "owner": "edolstra", "repo": "flake-compat", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "rev": "f387cd2afec9419c8ee37694406ca490c3f34ee5", "type": "github" }, "original": { @@ -688,11 +688,11 @@ "systems": "systems_3" }, "locked": { - "lastModified": 1726560853, - "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", "owner": "numtide", "repo": "flake-utils", - "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", "type": "github" }, "original": { @@ -1369,11 +1369,11 @@ "pre-commit-hooks": "pre-commit-hooks" }, "locked": { - "lastModified": 1762088663, - "narHash": "sha256-rpCvFan9Dji1Vw4HfVqYdfWesz5sKZE3uSgYR9gRreA=", + "lastModified": 1767198021, + "narHash": "sha256-O/7ZAy0OczYEy7zl+EegeekvRqb3JPh0btyBKtRvbVw=", "owner": "oddlama", "repo": "nix-topology", - "rev": "c15f569794a0f1a437850d0ac81675bcf23ca6cb", + "rev": "0c052d902678b592b957eac2c250e4030fe70ebc", "type": "github" }, "original": { @@ -1862,11 +1862,11 @@ }, "nixpkgs_12": { "locked": { - "lastModified": 1730531603, - "narHash": "sha256-Dqg6si5CqIzm87sp57j5nTaeBbWhHFaVyG7V6L8k3lY=", + "lastModified": 1766651565, + "narHash": "sha256-QEhk0eXgyIqTpJ/ehZKg9IKS7EtlWxF3N7DXy42zPfU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "7ffd9ae656aec493492b44d0ddfb28e79a1ea25d", + "rev": "3e2499d5539c16d0d173ba53552a4ff8547f4539", "type": "github" }, "original": { @@ -2574,18 +2574,14 @@ "nixpkgs": [ "nix-topology", "nixpkgs" - ], - "nixpkgs-stable": [ - "nix-topology", - "nixpkgs" ] }, "locked": { - "lastModified": 1730797577, - "narHash": "sha256-SrID5yVpyUfknUTGWgYkTyvdr9J1LxUym4om3SVGPkg=", + "lastModified": 1765911976, + "narHash": "sha256-t3T/xm8zstHRLx+pIHxVpQTiySbKqcQbK+r+01XVKc0=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "1864030ed24a2b8b4e4d386a5eeaf0c5369e50a9", + "rev": "b68b780b69702a090c8bb1b973bab13756cc7a27", "type": "github" }, "original": { diff --git a/hosts/nixos/aarch64-linux/belchsfactory/default.nix b/hosts/nixos/aarch64-linux/belchsfactory/default.nix index f024b3b..f44891b 100644 --- a/hosts/nixos/aarch64-linux/belchsfactory/default.nix +++ b/hosts/nixos/aarch64-linux/belchsfactory/default.nix @@ -61,7 +61,7 @@ postgresql = true; attic = true; garage = true; - hydra = false; + hydra = true; }; } diff --git a/hosts/nixos/aarch64-linux/liliputsteps/default.nix b/hosts/nixos/aarch64-linux/liliputsteps/default.nix index 4a4ead2..28083e9 100644 --- a/hosts/nixos/aarch64-linux/liliputsteps/default.nix +++ b/hosts/nixos/aarch64-linux/liliputsteps/default.nix @@ -1,4 +1,4 @@ -{ self, lib, minimal, ... }: +{ self, config, lib, minimal, ... }: { imports = [ ./hardware-configuration.nix @@ -10,6 +10,16 @@ topology.self = { icon = "devices.cloud-server"; + interfaces.ProxyJump = { + virtual = true; + physicalConnections = [ + (config.lib.topology.mkConnection "moonside" "lan") + (config.lib.topology.mkConnection "twothreetunnel" "lan") + (config.lib.topology.mkConnection "belchsfactory" "lan") + (config.lib.topology.mkConnection "stoicclub" "lan") + (config.lib.topology.mkConnection "eagleland" "wan") + ]; + }; }; swarselsystems = { diff --git a/hosts/nixos/aarch64-linux/stoicclub/default.nix b/hosts/nixos/aarch64-linux/stoicclub/default.nix index 01b40d4..38128a9 100644 --- a/hosts/nixos/aarch64-linux/stoicclub/default.nix +++ b/hosts/nixos/aarch64-linux/stoicclub/default.nix @@ -5,6 +5,7 @@ ./disk-config.nix "${self}/modules/nixos/optional/systemd-networkd-server.nix" + "${self}/modules/nixos/optional/nix-topology-self.nix" ]; topology.self = { diff --git a/hosts/nixos/aarch64-linux/twothreetunnel/default.nix b/hosts/nixos/aarch64-linux/twothreetunnel/default.nix index 0b33db2..2b16886 100644 --- a/hosts/nixos/aarch64-linux/twothreetunnel/default.nix +++ b/hosts/nixos/aarch64-linux/twothreetunnel/default.nix @@ -12,7 +12,10 @@ icon = "devices.cloud-server"; }; - globals.general.webProxy = config.node.name; + globals.general = { + webProxy = config.node.name; + oauthServer = config.node.name; + }; swarselsystems = { flakePath = "/root/.dotfiles"; @@ -35,6 +38,7 @@ "winters" "belchsfactory" "eagleland" + "hintbooth-adguardhome" ]; }; }; diff --git a/hosts/nixos/x86_64-linux/bakery/default.nix b/hosts/nixos/x86_64-linux/bakery/default.nix index fb971d6..f11512a 100644 --- a/hosts/nixos/x86_64-linux/bakery/default.nix +++ b/hosts/nixos/x86_64-linux/bakery/default.nix @@ -17,8 +17,8 @@ in ]; topology.self.interfaces = { - "eth1" = { }; - "wifi" = { }; + eth1.network = lib.mkForce "home"; + wifi = { }; }; swarselsystems = { diff --git a/hosts/nixos/x86_64-linux/hintbooth/default.nix b/hosts/nixos/x86_64-linux/hintbooth/default.nix index 3e3e344..648be45 100644 --- a/hosts/nixos/x86_64-linux/hintbooth/default.nix +++ b/hosts/nixos/x86_64-linux/hintbooth/default.nix @@ -1,26 +1,27 @@ -{ self, config, lib, minimal, confLib, ... }: +{ self, config, lib, minimal, confLib, globals, ... }: { imports = [ ./hardware-configuration.nix ./disk-config.nix - "${self}/modules/nixos/optional/systemd-networkd-server.nix" - "${self}/modules/nixos/optional/systemd-networkd-vlan.nix" + "${self}/modules/nixos/optional/systemd-networkd-server-home.nix" + "${self}/modules/nixos/optional/microvm-host.nix" ]; topology.self = { interfaces = { - "eth1" = { }; - "eth2" = { }; - "eth3" = { }; - "eth4" = { }; - "eth5" = { }; - "eth6" = { }; + lan2.physicalConnections = [{ node = "summers"; interface = "eth1"; }]; + lan3.physicalConnections = [{ node = "summers"; interface = "eth2"; }]; + lan4.physicalConnections = [{ node = "switch-bedroom"; interface = "eth1"; }]; + lan5.physicalConnections = [{ node = "switch-livingroom"; interface = "eth1"; }]; }; }; - globals.general.homeProxy = config.node.name; + globals.general = { + homeProxy = config.node.name; + routerServer = config.node.name; + }; swarselsystems = { info = "HUNSN RM02, 8GB RAM"; @@ -35,12 +36,16 @@ swapSize = "8G"; networkKernelModules = [ "igb" ]; withMicroVMs = true; + localVLANs = map (name: "${name}") (builtins.attrNames globals.networks.home-lan.vlans); + initrdVLAN = "home"; server = { wireguard.interfaces = { wgHome = { isServer = true; peers = [ "winters" + "hintbooth-adguardhome" + "hintbooth-nginx" ]; }; }; @@ -63,6 +68,7 @@ guests = lib.mkIf (!minimal && config.swarselsystems.withMicroVMs) ( { } // confLib.mkMicrovm "adguardhome" + // confLib.mkMicrovm "nginx" ); } diff --git a/hosts/nixos/x86_64-linux/hintbooth/guests/adguardhome.nix b/hosts/nixos/x86_64-linux/hintbooth/guests/adguardhome.nix deleted file mode 100644 index 2a0d511..0000000 --- a/hosts/nixos/x86_64-linux/hintbooth/guests/adguardhome.nix +++ /dev/null @@ -1,23 +0,0 @@ -{ self, lib, minimal, ... }: -{ - imports = [ - "${self}/profiles/nixos/microvm" - "${self}/modules/nixos" - ]; - - swarselsystems = { - isMicroVM = true; - }; - -} // lib.optionalAttrs (!minimal) { - - microvm = { - mem = 1024 * 1; - vcpu = 1; - }; - - swarselprofiles = { - microvm = true; - }; - -} diff --git a/hosts/nixos/x86_64-linux/hintbooth/guests/adguardhome/default.nix b/hosts/nixos/x86_64-linux/hintbooth/guests/adguardhome/default.nix new file mode 100644 index 0000000..b10f730 --- /dev/null +++ b/hosts/nixos/x86_64-linux/hintbooth/guests/adguardhome/default.nix @@ -0,0 +1,43 @@ +{ self, config, lib, minimal, ... }: +{ + imports = [ + "${self}/profiles/nixos/microvm" + "${self}/modules/nixos" + ]; + + swarselsystems = { + isMicroVM = true; + isImpermanence = true; + proxyHost = "twothreetunnel"; + server = { + wireguard.interfaces = { + wgHome = { + isClient = true; + serverName = "hintbooth"; + }; + wgProxy = { + isClient = true; + serverName = "twothreetunnel"; + }; + }; + }; + }; + + globals.general.homeDnsServer = config.node.name; + +} // lib.optionalAttrs (!minimal) { + + microvm = { + mem = 1024 * 1; + vcpu = 1; + }; + + swarselprofiles = { + microvm = true; + }; + + swarselmodules.server = { + adguardhome = true; + }; + +} diff --git a/hosts/nixos/x86_64-linux/hintbooth/guests/nginx/default.nix b/hosts/nixos/x86_64-linux/hintbooth/guests/nginx/default.nix new file mode 100644 index 0000000..39656a1 --- /dev/null +++ b/hosts/nixos/x86_64-linux/hintbooth/guests/nginx/default.nix @@ -0,0 +1,60 @@ +{ self, config, lib, minimal, globals, confLib, ... }: +let + inherit (confLib.static) nginxAccessRules; +in +{ + imports = [ + "${self}/profiles/nixos/microvm" + "${self}/modules/nixos" + ]; + + swarselsystems = { + isMicroVM = true; + isImpermanence = true; + proxyHost = config.node.name; + server = { + wireguard.interfaces = { + wgHome = { + isClient = true; + serverName = "hintbooth"; + }; + }; + }; + }; + + globals.general.homeWebProxy = config.node.name; + +} // lib.optionalAttrs (!minimal) { + + microvm = { + mem = 3072 * 1; + vcpu = 1; + }; + + swarselprofiles = { + microvm = true; + }; + + swarselmodules.server = { + nginx = true; + }; + + services.nginx = { + upstreams.fritzbox = { + servers.${globals.networks.home-lan.hosts.fritzbox.ipv4} = { }; + }; + virtualHosts.${globals.services.fritzbox.domain} = { + useACMEHost = globals.domains.main; + forceSSL = true; + acmeRoot = null; + locations."/" = { + proxyPass = "http://fritzbox"; + proxyWebsockets = true; + }; + extraConfig = '' + proxy_ssl_verify off; + '' + nginxAccessRules; + }; + }; + +} diff --git a/hosts/nixos/x86_64-linux/hintbooth/secrets/adguardhome/secrets.yaml b/hosts/nixos/x86_64-linux/hintbooth/secrets/adguardhome/secrets.yaml new file mode 100644 index 0000000..ff54541 --- /dev/null +++ b/hosts/nixos/x86_64-linux/hintbooth/secrets/adguardhome/secrets.yaml @@ -0,0 +1,57 @@ +wireguard-private-key: ENC[AES256_GCM,data:5RdR6CvGBwaklSgiP0kmz/ShroIa1By7ZqgxKrnSGjHRyrzaeWGTuJmqKJM=,iv:D5UmcQkbRs8WVQUA8XpFCwLy8+O4+RoJLWOkHj0H7ss=,tag:feSuK9jW+wLeygqhKHycDw==,type:str] +sops: + age: + - recipient: age1wmx8y2hs83j2u5srdnfxljrzxm8jtxl6fr0mq7xf2ldxyglpzf2qq89rpx + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBBMEM4alliWlBCT3VsbVA5 + OGt5bmQvZW1TaUNkbWtFdzVGNDNpY0hBOVhzCm84TldYNHBrU01HMlBkbGNwZFAw + WVk0T3FycVRHUUNtM1pTYkQ4Qmw3RTgKLS0tIE9LUlNEVjJHOGVIK1RSMmRXUDF6 + QlRKY1hRVzNTVXhESUd3OElXL2pBZXcKDWYoOzi2b4qeIbCVCfTj0lTW+OfbnsXB + 8MugCHu7+b+ju0v/lUP66jDW9/2AH4PzHtCNHjsafyzr2qnW8HlOzA== + -----END AGE ENCRYPTED FILE----- + - recipient: age1c2enwel9un28dcs4wg0vcyamx9a4a6g3walkhu8w5lqhmd804paq9d24as + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJRWJXR2tYdEd4cTZsSi9l + Tm1pSC9pek5BakpEMlkwVTcrMlBuVzlXWUVrCmlnV0xJc25nL0twK3VCZ3FRK2x2 + RW52Q1NxWUhTUGY0NnQ0WEhLMWxIcFUKLS0tIG83eVM0KzdLQ004aDRKNTYvdmVZ + d3ZOSStBMFpSU2ZjNWhFRkREQWlUdmcKggVvLy1mLYGf8084RQtlipS4+z4dfPsN + HZfid0srwYnezlQ5qOY8/HrDLWHEyuZ4xFZVi4n0k49qBpNwJdmvyQ== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2026-01-02T04:14:03Z" + mac: ENC[AES256_GCM,data:aA+oIq31QBla9hOpApaMeP7MFl/hI0kDjC1QyPkmexXuMB2pQJ6bBEmazreX2m2TPtHv1rtVUak7F6TbA+97IFb9EQFuAREi1Ca0xjz2eGVFQKu94qkS/FNemXTAkEZxC9LQ1TRqNXXNITehKUeIN65epuNbWqo+iOW0OHEXm/w=,iv:1NKL2PZBUDyHEIiB2ZpvTdCh9ZO+r8bPyJo+EO1PBmQ=,tag:5W9owm1Z+7O1CGVmH1afUw==,type:str] + pgp: + - created_at: "2026-01-02T21:12:51Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hQIMAwDh3VI7VctTARAAmvkQ9V14f0BT/bNdFVZtTlY4yVon37CX32SZPUcHV7o8 + Dya0sZd9tuVATSv79TnybscuNx95fkoZJwujBfAadexn2zY8zl1oEWEHx7p+8/mE + W8JbQAjbcbX9sNQYXc8kYJylBThmgNN/HXK7CGtgDFr9xnGzDBnDm/M31P1HwYBm + IdIQgFGErEt1K3xvw28Lk3tPuZLK3Y+H2Yna7RRF6K1blGJUvEnL6yFdA10/eFW7 + 8066mO26F2l5xFuktK0nNeniLHKa5VVYp8iM+JMhX38l0wiIi8pGyxo3uAjNpa0w + IfpCneEBe/yyaUPcWMjXmUG5LJe3kWUup8cSzvu01Z3W159/QsflxIMkIsklqhim + B2zuPdAlYsjjS/05DIHInN2IIB/rjADkQvXji1XYLhWJj4jxDeck/UIc6Q22TED+ + autlbl8d/5sqyO5ghPpShF/s0vMTqUfpXZrDrbuyDFqCfwi0ahP03bUsv20ZEz6u + zG3K5HuXHh7ATSppwuMbcv7vcjF1tkbo6XhWZDv0rY0DFWqiYhnxWwlFlGLxf4zX + g6r7Ca/E/YXG/eOET6M9DxwHjj0D7u/ryAkCktqPL9w8oNGarZQ/xMx0+ocI3byc + Zvzlmd63BtgaGNSxH3stK29KN3ED8cDkG/JzAxCATWiUBBkqW/ga4sGZqtLlSO+F + AgwDC9FRLmchgYQBD/9JbFZie25PO2CyELlUWm5SmJcugT9SK/mIA2fe1PlA+Gnf + 5z9iXraMSQchz4R1IoiixDhubwKeKp/auqhlOPvo58Lsi6iDR/WaLWabD+hcyAb1 + ck/f/PUzTLhlLcfu18VPfXVzfnky3dX8P5aS0WMLAQblj2RaaiHxnPqf49kXSn3q + VSJ0pr0nEsPuWtoCkHUAwAJ8X5GPXN2OD4YbHsNaA9h2vrJAxNd5+HNsvg8JtI88 + X/uMM7cWcaXcmNZOz166HUIPcJ5cabJ48Sv8sDfMPOcTiJkMiESBnRYTwdUcp08m + nGipSrUeW3pVOC1bGyukZb6sF84pTtCpqS+kOSfKFlxFFdAEcpzFIPuOMeo2dbKj + GSGPDemZFC2yFq883yk9/mZbgjOUsqrj0ZP3rCD5ZHpfUM5IxGQ+mKaOucTXYmif + lrTPMYnAc7pHxKZ87BgiKBYrfRAZvorLYKv8zG8YagAUw8iCtc68YUUdvLW9haQf + rwWCU1z+sszYSac7I57gfqICQhMUbs1n9S2Cn0C0xo4q2Lu36ysip4rEVGg6TmUu + znXYu+3orodw2TwC0tGxXHYKwmlr7EGnBCbdVKpDoCbV6cYkDYoPUFg0alqIPd5r + KCkee9MaCLLX7IdBrbLf1lkHGwSAs81GfZRMLBauM7/hn+hMUeIJnMbtJnVIB9Je + AdT2nSH06+POnjvxa2t0dUasnG/6ISBRSk6FgBBZ+pdVlrvaB4javgWGpiAWCUu6 + b2CMZF3HullmLj+wwAKlsZsIOXGICN5GeQxLHYF8Kx7Doj68Owu/zGM5MS+7XQ== + =wYdb + -----END PGP MESSAGE----- + fp: 4BE7925262289B476DBBC17B76FD3810215AE097 + unencrypted_suffix: _unencrypted + version: 3.11.0 diff --git a/hosts/nixos/x86_64-linux/hintbooth/secrets/nginx/secrets.yaml b/hosts/nixos/x86_64-linux/hintbooth/secrets/nginx/secrets.yaml new file mode 100644 index 0000000..cc13dad --- /dev/null +++ b/hosts/nixos/x86_64-linux/hintbooth/secrets/nginx/secrets.yaml @@ -0,0 +1,57 @@ +wireguard-private-key: ENC[AES256_GCM,data:3T0ZoPAs/OIkhdZlH171d9d2Ycxtp4WfI92pTBI3vRw7BVvEgQZKu5DCvbA=,iv:gsczaGwcI3JocOazMIEsgHFruEKDPxOTUQzx+rdCaio=,tag:/Sw7QsZ4fV+BMWdfcUevBA==,type:str] +sops: + age: + - recipient: age1wmx8y2hs83j2u5srdnfxljrzxm8jtxl6fr0mq7xf2ldxyglpzf2qq89rpx + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBBMEM4alliWlBCT3VsbVA5 + OGt5bmQvZW1TaUNkbWtFdzVGNDNpY0hBOVhzCm84TldYNHBrU01HMlBkbGNwZFAw + WVk0T3FycVRHUUNtM1pTYkQ4Qmw3RTgKLS0tIE9LUlNEVjJHOGVIK1RSMmRXUDF6 + QlRKY1hRVzNTVXhESUd3OElXL2pBZXcKDWYoOzi2b4qeIbCVCfTj0lTW+OfbnsXB + 8MugCHu7+b+ju0v/lUP66jDW9/2AH4PzHtCNHjsafyzr2qnW8HlOzA== + -----END AGE ENCRYPTED FILE----- + - recipient: age1c2enwel9un28dcs4wg0vcyamx9a4a6g3walkhu8w5lqhmd804paq9d24as + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJRWJXR2tYdEd4cTZsSi9l + Tm1pSC9pek5BakpEMlkwVTcrMlBuVzlXWUVrCmlnV0xJc25nL0twK3VCZ3FRK2x2 + RW52Q1NxWUhTUGY0NnQ0WEhLMWxIcFUKLS0tIG83eVM0KzdLQ004aDRKNTYvdmVZ + d3ZOSStBMFpSU2ZjNWhFRkREQWlUdmcKggVvLy1mLYGf8084RQtlipS4+z4dfPsN + HZfid0srwYnezlQ5qOY8/HrDLWHEyuZ4xFZVi4n0k49qBpNwJdmvyQ== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2026-01-03T05:23:18Z" + mac: ENC[AES256_GCM,data:u9N7GzLPDW7cHT4mkUAC9Diq1RdV5iSwcz/fqzXQKRmic09eVydAgyk2g6NbJ+4tBbAjIfeUch8Bhf5eG0sGzeDkb1qWAMEnP8EPmQ64OdRyN2SxJgxkc8KFGxkrGz9slS2ozWth6q/tKBSsOYbo8WDlCqXhmYp+zBxvYFR30Mg=,iv:HC1e2i0E7dV9/au+A0kHd+UXDhw3xf7RbTpwJI+hjpY=,tag:dPCDh9qalNtbHIhs//cBpg==,type:str] + pgp: + - created_at: "2026-01-02T21:12:51Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hQIMAwDh3VI7VctTARAAmvkQ9V14f0BT/bNdFVZtTlY4yVon37CX32SZPUcHV7o8 + Dya0sZd9tuVATSv79TnybscuNx95fkoZJwujBfAadexn2zY8zl1oEWEHx7p+8/mE + W8JbQAjbcbX9sNQYXc8kYJylBThmgNN/HXK7CGtgDFr9xnGzDBnDm/M31P1HwYBm + IdIQgFGErEt1K3xvw28Lk3tPuZLK3Y+H2Yna7RRF6K1blGJUvEnL6yFdA10/eFW7 + 8066mO26F2l5xFuktK0nNeniLHKa5VVYp8iM+JMhX38l0wiIi8pGyxo3uAjNpa0w + IfpCneEBe/yyaUPcWMjXmUG5LJe3kWUup8cSzvu01Z3W159/QsflxIMkIsklqhim + B2zuPdAlYsjjS/05DIHInN2IIB/rjADkQvXji1XYLhWJj4jxDeck/UIc6Q22TED+ + autlbl8d/5sqyO5ghPpShF/s0vMTqUfpXZrDrbuyDFqCfwi0ahP03bUsv20ZEz6u + zG3K5HuXHh7ATSppwuMbcv7vcjF1tkbo6XhWZDv0rY0DFWqiYhnxWwlFlGLxf4zX + g6r7Ca/E/YXG/eOET6M9DxwHjj0D7u/ryAkCktqPL9w8oNGarZQ/xMx0+ocI3byc + Zvzlmd63BtgaGNSxH3stK29KN3ED8cDkG/JzAxCATWiUBBkqW/ga4sGZqtLlSO+F + AgwDC9FRLmchgYQBD/9JbFZie25PO2CyELlUWm5SmJcugT9SK/mIA2fe1PlA+Gnf + 5z9iXraMSQchz4R1IoiixDhubwKeKp/auqhlOPvo58Lsi6iDR/WaLWabD+hcyAb1 + ck/f/PUzTLhlLcfu18VPfXVzfnky3dX8P5aS0WMLAQblj2RaaiHxnPqf49kXSn3q + VSJ0pr0nEsPuWtoCkHUAwAJ8X5GPXN2OD4YbHsNaA9h2vrJAxNd5+HNsvg8JtI88 + X/uMM7cWcaXcmNZOz166HUIPcJ5cabJ48Sv8sDfMPOcTiJkMiESBnRYTwdUcp08m + nGipSrUeW3pVOC1bGyukZb6sF84pTtCpqS+kOSfKFlxFFdAEcpzFIPuOMeo2dbKj + GSGPDemZFC2yFq883yk9/mZbgjOUsqrj0ZP3rCD5ZHpfUM5IxGQ+mKaOucTXYmif + lrTPMYnAc7pHxKZ87BgiKBYrfRAZvorLYKv8zG8YagAUw8iCtc68YUUdvLW9haQf + rwWCU1z+sszYSac7I57gfqICQhMUbs1n9S2Cn0C0xo4q2Lu36ysip4rEVGg6TmUu + znXYu+3orodw2TwC0tGxXHYKwmlr7EGnBCbdVKpDoCbV6cYkDYoPUFg0alqIPd5r + KCkee9MaCLLX7IdBrbLf1lkHGwSAs81GfZRMLBauM7/hn+hMUeIJnMbtJnVIB9Je + AdT2nSH06+POnjvxa2t0dUasnG/6ISBRSk6FgBBZ+pdVlrvaB4javgWGpiAWCUu6 + b2CMZF3HullmLj+wwAKlsZsIOXGICN5GeQxLHYF8Kx7Doj68Owu/zGM5MS+7XQ== + =wYdb + -----END PGP MESSAGE----- + fp: 4BE7925262289B476DBBC17B76FD3810215AE097 + unencrypted_suffix: _unencrypted + version: 3.11.0 diff --git a/hosts/nixos/x86_64-linux/pyramid/default.nix b/hosts/nixos/x86_64-linux/pyramid/default.nix index 5f662f8..54b80c5 100644 --- a/hosts/nixos/x86_64-linux/pyramid/default.nix +++ b/hosts/nixos/x86_64-linux/pyramid/default.nix @@ -23,9 +23,9 @@ in topology.self = { interfaces = { - "eth1" = { }; - "wifi" = { }; - "fritz-wg" = { }; + eth1.network = lib.mkForce "home"; + wifi = { }; + fritz-wg.network = "fritz-wg"; }; }; @@ -64,7 +64,7 @@ in main = { # name = "BOE 0x0BC9 Unknown"; name = "BOE 0x0BC9"; - mode = "2560x1600"; # TEMPLATE + mode = "2560x1600"; scale = "1"; position = "2560,0"; workspace = "15:L"; @@ -78,8 +78,8 @@ in personal = true; }; - networking.nftables = { - enable = lib.mkForce false; - firewall.enable = lib.mkForce false; - }; + # networking.nftables = { + # enable = lib.mkForce false; + # firewall.enable = lib.mkForce false; + # }; } diff --git a/hosts/nixos/x86_64-linux/pyramid/disk-config.nix b/hosts/nixos/x86_64-linux/pyramid/disk-config.nix index 6feb4c8..a3e2361 100644 --- a/hosts/nixos/x86_64-linux/pyramid/disk-config.nix +++ b/hosts/nixos/x86_64-linux/pyramid/disk-config.nix @@ -75,7 +75,7 @@ fileSystems = { "/persist".neededForBoot = true; "/home".neededForBoot = true; - "/".neededForBoot = true; + "/".neededForBoot = true; # this is ok because this is not a impermanence host "/var/log".neededForBoot = true; }; } diff --git a/hosts/nixos/x86_64-linux/winters/secrets/pii.nix.enc b/hosts/nixos/x86_64-linux/winters/secrets/pii.nix.enc index 8256be6..dc6fd8a 100644 --- a/hosts/nixos/x86_64-linux/winters/secrets/pii.nix.enc +++ b/hosts/nixos/x86_64-linux/winters/secrets/pii.nix.enc @@ -1,5 +1,5 @@ { - "data": "ENC[AES256_GCM,data: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,iv:Wc2rOI9aWgchjFGCl5R8d6E6GmYEKCIkIAZlUIlbE/4=,tag:3p301gA9WrBjwcpXs68ayg==,type:str]", + "data": "ENC[AES256_GCM,data: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,iv:kkH/Hy/0PNzkVdTfYTgKBAN6nYslP0OFIndsmORZVEg=,tag:j/fMiT9DCog0CHnM74MNMw==,type:str]", "sops": { "age": [ { @@ -7,8 +7,8 @@ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA3UFZTaXFNdjF2UmRFd3VL\nY2pZZ3ZaRkhZSjdVUjIraHV5ZlNaNGtwM3k0CkZ4OVRFcmR3MFBDcmdsbWFId3Iy\nVzQyUGI1eG44d3JFL2NvZEg4NnduT2cKLS0tIEdhOEZETk9nRTlVbmJ5UW9GalVx\nS00yaUpJZVFVNThFei8yRzJYejRkYk0Kf6Z8WnG8phRtFIUWIPys3PW0OImhAcF+\nUFLuL4Qr7zWaeItCRieYCs1yBn7KbUJHZNkJcvnkYW50NYvlEa8wBw==\n-----END AGE ENCRYPTED FILE-----\n" } ], - "lastmodified": "2025-12-31T12:22:14Z", - "mac": "ENC[AES256_GCM,data:H17OWQwkZaugzTuMM8kBPZLYZs/poaLJt8osoY/gzC2CMpWXUtWpwgJ83CO7GkiPrPN2SZtEiaADP3PvZZqVcV5rDJNhdELmdvZfB14RQDUD9rYfnIX5uuzMMII+kguTkk+Zd1IRWv+MN9y4cdhys0lYJ7Nw1RyEMP9Bxd1zvcA=,iv:01Yode7T/2pUP1dFLyIUoDIfeWRWKf1Qq7pHvUDKQJQ=,tag:Ldxq5cRB2iexLhIrBfqGVQ==,type:str]", + "lastmodified": "2026-01-02T22:52:45Z", + "mac": "ENC[AES256_GCM,data:p/m76sd+5HhD+tz7oSnoSzVRCnB1czTUTF90LSyLQuL6aVyTpVZp+p6/CnYc/fG+L/8wBUsLrwwajl22S2+MZAqvQFoYQwY/AiFb10wZNK2fzPEURW3P+QYzaf62nb4G3GlckjAcGxGyeGcU4TnL1qZEDgp/KcdZpsUwvVQvV/U=,iv:k7m4dOr13gczZTGlz7uHIQB/uFPEQJX19uHuLB1fupg=,tag:mzpbLMV5aun7IOvPIJv0ng==,type:str]", "pgp": [ { "created_at": "2025-12-02T14:59:33Z", diff --git a/modules/nixos/common/impermanence.nix b/modules/nixos/common/impermanence.nix index e111c86..2e9b437 100644 --- a/modules/nixos/common/impermanence.nix +++ b/modules/nixos/common/impermanence.nix @@ -1,7 +1,7 @@ { config, lib, ... }: let mapperTarget = lib.swarselsystems.mkIfElse config.swarselsystems.isCrypted "/dev/mapper/cryptroot" "/dev/disk/by-label/nixos"; - inherit (config.swarselsystems) isImpermanence isCrypted; + inherit (config.swarselsystems) isImpermanence isCrypted isBtrfs; in { options.swarselmodules.impermanence = lib.mkEnableOption "impermanence config"; @@ -17,7 +17,7 @@ in # So if it doesn't run, the btrfs system effectively acts like a normal system # Taken from https://github.com/NotAShelf/nyx/blob/2a8273ed3f11a4b4ca027a68405d9eb35eba567b/modules/core/common/system/impermanence/default.nix boot.tmp.useTmpfs = lib.mkIf (!isImpermanence) true; - boot.initrd.systemd = lib.mkIf isImpermanence { + boot.initrd.systemd = lib.mkIf (isImpermanence && isBtrfs) { enable = true; services.rollback = { description = "Rollback BTRFS root subvolume to a pristine state"; diff --git a/modules/nixos/common/nodes.nix b/modules/nixos/common/nodes.nix index 236354d..daa270c 100644 --- a/modules/nixos/common/nodes.nix +++ b/modules/nixos/common/nodes.nix @@ -33,6 +33,7 @@ let (splitPath "services.kanidm.provision.systems.oauth2") (splitPath "sops.secrets") (splitPath "swarselsystems.server.dns") + (splitPath "topology.self.services") ] ++ expandOptions (splitPath "networking.nftables.firewall") [ "zones" "rules" ] ++ expandOptions (splitPath "services.firezone.gateway") [ "enable" "name" "apiUrl" "tokenFile" "package" "logLevel" ] diff --git a/modules/nixos/optional/microvm-guest.nix b/modules/nixos/optional/microvm-guest.nix index adf069f..21c29ce 100644 --- a/modules/nixos/optional/microvm-guest.nix +++ b/modules/nixos/optional/microvm-guest.nix @@ -1,11 +1,10 @@ -{ self, inputs, ... }: +{ self, lib, config, inputs, microVMParent, nodes, ... }: { imports = [ inputs.disko.nixosModules.disko inputs.home-manager.nixosModules.home-manager inputs.impermanence.nixosModules.impermanence inputs.lanzaboote.nixosModules.lanzaboote - inputs.microvm.nixosModules.host inputs.microvm.nixosModules.microvm inputs.nix-index-database.nixosModules.nix-index inputs.nix-minecraft.nixosModules.minecraft-servers @@ -23,6 +22,51 @@ ]; config = { - system.stateVersion = "23.05"; + _module.args.dns = inputs.dns; + + nix.settings.experimental-features = [ + "nix-command" + "flakes" + ]; + systemd.services."systemd-networkd".environment.SYSTEMD_LOG_LEVEL = "debug"; + # NOTE: this is needed, we dont import sevrer network module for microvms + globals.hosts.${config.node.name}.isHome = true; + + fileSystems."/persist".neededForBoot = lib.mkForce true; + + systemd.network.networks."10-vlan-services" = { + dhcpV6Config = { + WithoutRA = "solicit"; + # duid-en is nice in principle, but I already have MAC info anyways for reservations + DUIDType = "link-layer"; + }; + # networkConfig = { + # IPv6PrivacyExtensions = "no"; + # IPv6AcceptRA = false; + # }; + ipv6AcceptRAConfig = { + DHCPv6Client = "always"; + }; + }; + + microvm = { + shares = [ + { + tag = "persist"; + source = "${lib.optionalString nodes.${microVMParent}.config.swarselsystems.isImpermanence "/persist"}/microvms/${config.networking.hostName}"; + mountPoint = "/persist"; + proto = "virtiofs"; + } + ]; + # mount the writeable overlay so that we can use nix shells inside the microvm + volumes = [ + { + image = "/tmp/nix-store-overlay-${config.networking.hostName}.img"; + autoCreate = true; + mountPoint = config.microvm.writableStoreOverlay; + size = 1024; + } + ]; + }; }; } diff --git a/modules/nixos/optional/microvm-host.nix b/modules/nixos/optional/microvm-host.nix index 2948824..073353c 100644 --- a/modules/nixos/optional/microvm-host.nix +++ b/modules/nixos/optional/microvm-host.nix @@ -1,13 +1,21 @@ { config, lib, ... }: { - # imports = [ - # inputs.microvm.nixosModules.host - # ]; - config = lib.mkIf (config.guests != { }) { - microvm = { - hypervisor = lib.mkDefault "qemu"; - }; + systemd.tmpfiles.settings."15-microvms" = builtins.listToAttrs ( + map + (path: { + name = "${lib.optionalString config.swarselsystems.isImpermanence "/persist"}/microvms/${path}"; + value = { + d = { + group = "kvm"; + user = "microvm"; + mode = "0750"; + }; + }; + }) + (builtins.attrNames config.guests) + ); + }; } diff --git a/modules/nixos/optional/nix-topology-self.nix b/modules/nixos/optional/nix-topology-self.nix index 66299f0..e713893 100644 --- a/modules/nixos/optional/nix-topology-self.nix +++ b/modules/nixos/optional/nix-topology-self.nix @@ -1,13 +1,25 @@ -{ lib, config, globals, ... }: +{ lib, config, globals, confLib, ... }: +let + inherit (confLib.static) webProxy; +in { topology.self = { icon = lib.mkIf config.swarselsystems.isCloud "devices.cloud-server"; - interfaces.wan = lib.mkIf config.swarselsystems.isCloud { }; - interfaces.wg = lib.mkIf (config.swarselsystems.server.wireguard.isClient || config.swarselsystems.server.wireguard.isServer) { - addresses = [ globals.networks.twothreetunnel-wg.hosts.${config.node.name}.ipv4 ]; - renderer.hidePhysicalConnections = true; - virtual = true; - type = "wireguard"; + interfaces = { + wan = lib.mkIf (config.swarselsystems.isCloud && config.swarselsystems.server.localNetwork == "wan") { }; + lan = lib.mkIf (config.swarselsystems.isCloud && config.swarselsystems.server.localNetwork == "lan") { }; + wgProxy = lib.mkIf (config.swarselsystems.server.wireguard ? wgHome) { + addresses = [ globals.networks."${webProxy}-wg.hosts".${config.node.name}.ipv4 ]; + renderer.hidePhysicalConnections = true; + virtual = true; + type = "wireguard"; + }; + wgHome = lib.mkIf (config.swarselsystems.server.wireguard ? wgHome) { + addresses = [ globals.networks.home-wgHome.hosts.${config.node.name}.ipv4 ]; + renderer.hidePhysicalConnections = true; + virtual = true; + type = "wireguard"; + }; }; }; } diff --git a/modules/nixos/optional/systemd-networkd-base.nix b/modules/nixos/optional/systemd-networkd-base.nix new file mode 100644 index 0000000..238a1ff --- /dev/null +++ b/modules/nixos/optional/systemd-networkd-base.nix @@ -0,0 +1,13 @@ +{ lib, config, ... }: +{ + networking = { + useDHCP = lib.mkForce false; + useNetworkd = true; + dhcpcd.enable = lib.mkIf (!config.swarselsystems.isMicroVM) false; + renameInterfacesByMac = lib.mkIf (!config.swarselsystems.isMicroVM) (lib.mapAttrs (_: v: if (v ? mac) then v.mac else "") ( + config.repo.secrets.local.networking.networks or { } + )); + }; + + systemd.network.enable = true; +} diff --git a/modules/nixos/optional/systemd-networkd-server-home.nix b/modules/nixos/optional/systemd-networkd-server-home.nix new file mode 100644 index 0000000..9b6ef50 --- /dev/null +++ b/modules/nixos/optional/systemd-networkd-server-home.nix @@ -0,0 +1,131 @@ +{ self, lib, config, globals, ... }: +let + inherit (globals.general) routerServer; + inherit (config.swarselsystems) withMicroVMs isCrypted initrdVLAN; + + isRouter = config.node.name == routerServer; + localVLANsList = config.swarselsystems.localVLANs; + localVLANs = lib.genAttrs localVLANsList (x: globals.networks.home-lan.vlans.${x}); +in +{ + imports = [ + "${self}/modules/nixos/optional/systemd-networkd-server.nix" + ]; + config = { + assertions = [ + { + assertion = ((localVLANsList != [ ]) && (initrdVLAN != null)) || (localVLANsList == [ ]) || (!isCrypted); + message = "This host uses VLANs and disk encryption, thus a VLAN must be specified for initrd or disk encryption must be removed."; + } + ]; + + boot.initrd = lib.mkIf (isCrypted && (localVLANsList != [ ]) && (!isRouter)) { + availableKernelModules = [ "8021q" ]; + systemd.network = { + enable = true; + netdevs."30-vlan-${initrdVLAN}" = { + netdevConfig = { + Kind = "vlan"; + Name = "vlan-${initrdVLAN}"; + }; + vlanConfig.Id = globals.networks.home-lan.vlans.${initrdVLAN}.id; + }; + networks = { + "10-lan" = { + matchConfig.Name = "lan"; + # This interface should only be used from attached vlans. + # So don't acquire a link local address and only wait for + # this interface to gain a carrier. + networkConfig.LinkLocalAddressing = "no"; + linkConfig.RequiredForOnline = "carrier"; + vlan = [ "vlan-${initrdVLAN}" ]; + }; + "30-vlan-${initrdVLAN}" = { + address = [ + globals.networks.home-lan.vlans.${initrdVLAN}.hosts.${config.node.name}.cidrv4 + globals.networks.home-lan.vlans.${initrdVLAN}.hosts.${config.node.name}.cidrv6 + ]; + matchConfig.Name = "vlan-${initrdVLAN}"; + networkConfig = { + IPv6PrivacyExtensions = "yes"; + }; + linkConfig.RequiredForOnline = "routable"; + }; + }; + }; + }; + + systemd.network = { + netdevs = lib.flip lib.concatMapAttrs localVLANs ( + vlanName: vlanCfg: { + "30-vlan-${vlanName}" = { + netdevConfig = { + Kind = "vlan"; + Name = "vlan-${vlanName}"; + }; + vlanConfig.Id = vlanCfg.id; + }; + # Create a MACVTAP for ourselves too, so that we can communicate with + # our guests on the same interface. + "40-me-${vlanName}" = lib.mkIf withMicroVMs { + netdevConfig = { + Name = "me-${vlanName}"; + Kind = "macvlan"; + }; + extraConfig = '' + [MACVLAN] + Mode=bridge + ''; + }; + } + ); + networks = { + "10-lan" = lib.mkIf (!isRouter) { + matchConfig.Name = "lan"; + # This interface should only be used from attached vlans. + # So don't acquire a link local address and only wait for + # this interface to gain a carrier. + networkConfig.LinkLocalAddressing = "no"; + linkConfig.RequiredForOnline = "carrier"; + vlan = (map (name: "vlan-${name}") (builtins.attrNames localVLANs)); + }; + # Remaining macvtap interfaces should not be touched. + "90-macvtap-ignore" = lib.mkIf withMicroVMs { + matchConfig.Kind = "macvtap"; + linkConfig.ActivationPolicy = "manual"; + linkConfig.Unmanaged = "yes"; + }; + } + // lib.flip lib.concatMapAttrs localVLANs ( + vlanName: vlanCfg: + let + me = { + address = [ + vlanCfg.hosts.${config.node.name}.cidrv4 + vlanCfg.hosts.${config.node.name}.cidrv6 + ]; + gateway = lib.optionals (vlanName == "services") [ vlanCfg.hosts.${routerServer}.ipv4 vlanCfg.hosts.${routerServer}.ipv6 ]; + matchConfig.Name = "${if withMicroVMs then "me" else "vlan"}-${vlanName}"; + networkConfig.IPv6PrivacyExtensions = "yes"; + linkConfig.RequiredForOnline = "routable"; + }; + + in + { + "30-vlan-${vlanName}" = if (!withMicroVMs) then me else { + matchConfig.Name = "vlan-${vlanName}"; + # This interface should only be used from attached macvlans. + # So don't acquire a link local address and only wait for + # this interface to gain a carrier. + networkConfig.LinkLocalAddressing = "no"; + networkConfig.MACVLAN = "me-${vlanName}"; + linkConfig.RequiredForOnline = if isRouter then "no" else "carrier"; + }; + "40-me-${vlanName}" = lib.mkIf withMicroVMs (lib.mkDefault me); + } + ); + }; + + }; + +} diff --git a/modules/nixos/optional/systemd-networkd-server.nix b/modules/nixos/optional/systemd-networkd-server.nix index 99019b2..76b2bf3 100644 --- a/modules/nixos/optional/systemd-networkd-server.nix +++ b/modules/nixos/optional/systemd-networkd-server.nix @@ -1,28 +1,30 @@ -{ lib, config, globals, ... }: +{ self, lib, config, globals, ... }: +let + inherit (config.swarselsystems) isCrypted localVLANs; + inherit (globals.general) routerServer; + + isRouter = config.node.name == routerServer; + ifName = config.swarselsystems.server.localNetwork; +in { - networking = { - useDHCP = lib.mkForce false; - useNetworkd = true; - dhcpcd.enable = false; - renameInterfacesByMac = lib.mapAttrs (_: v: if (v ? mac) then v.mac else "") ( - config.repo.secrets.local.networking.networks or { } - ); - }; - boot.initrd.systemd.network = { + imports = [ + "${self}/modules/nixos/optional/systemd-networkd-base.nix" + ]; + + boot.initrd.systemd.network = lib.mkIf (isCrypted && ((localVLANs == [ ]) || isRouter)) { enable = true; - networks."10-${config.swarselsystems.server.localNetwork}" = config.systemd.network.networks."10-${config.swarselsystems.server.localNetwork}"; + networks."10-${ifName}" = config.systemd.network.networks."10-${ifName}"; }; systemd = { network = { - enable = true; wait-online.enable = false; networks = let netConfig = config.repo.secrets.local.networking; in { - "10-${config.swarselsystems.server.localNetwork}" = { + "10-${ifName}" = lib.mkIf (isRouter || (localVLANs == [ ])) { address = [ "${globals.networks.${config.swarselsystems.server.netConfigName}.hosts.${config.node.name}.cidrv4}" "${globals.networks.${config.swarselsystems.server.netConfigName}.hosts.${config.node.name}.cidrv6}" diff --git a/modules/nixos/optional/systemd-networkd-vlan.nix b/modules/nixos/optional/systemd-networkd-vlan.nix deleted file mode 100644 index 2a3470e..0000000 --- a/modules/nixos/optional/systemd-networkd-vlan.nix +++ /dev/null @@ -1,116 +0,0 @@ -{ lib, config, globals, ... }: -{ - - systemd.network = { - wait-online.anyInterface = true; - netdevs = { - "10-veth" = { - netdevConfig = { - Kind = "veth"; - Name = "veth-br"; - }; - peerConfig = { - Name = "veth-int"; - }; - }; - "20-br" = { - netdevConfig = { - Kind = "bridge"; - Name = "br"; - }; - }; - } // lib.flip lib.concatMapAttrs globals.networks.home-lan.vlans ( - vlanName: vlanCfg: { - "30-vlan-${vlanName}" = { - netdevConfig = { - Kind = "vlan"; - Name = "vlan-${vlanName}"; - }; - vlanConfig.Id = vlanCfg.id; - }; - "40-me-${vlanName}" = { - netdevConfig = { - Name = "me-${vlanName}"; - Kind = "macvlan"; - }; - extraConfig = '' - [MACVLAN] - Mode=bridge - ''; - }; - } - ); - networks = { - "40-br" = { - matchConfig.Name = "br"; - bridgeConfig = { }; - linkConfig = { - ActivationPolicy = "always-up"; - RequiredForOnline = "no"; - }; - networkConfig = { - ConfigureWithoutCarrier = true; - LinkLocalAddressing = "no"; - }; - }; - "15-veth-br" = { - matchConfig.Name = "veth-br"; - - linkConfig = { - RequiredForOnline = "no"; - }; - - networkConfig = { - Bridge = "br"; - }; - }; - "15-veth-int" = { - matchConfig.Name = "veth-int"; - - linkConfig = { - ActivationPolicy = "always-up"; - RequiredForOnline = "no"; - }; - - networkConfig = { - ConfigureWithoutCarrier = true; - LinkLocalAddressing = "no"; - }; - - vlan = map (name: "vlan-${name}") (builtins.attrNames globals.networks.home-lan.vlans); - }; - "90-macvtap-ignore" = { - matchConfig.Kind = "macvtap"; - linkConfig.ActivationPolicy = "manual"; - linkConfig.Unmanaged = "yes"; - }; - } // lib.flip lib.concatMapAttrs globals.networks.home-lan.vlans ( - vlanName: vlanCfg: { - "30-vlan-${vlanName}" = { - matchConfig.Name = "vlan-${vlanName}"; - networkConfig.LinkLocalAddressing = "no"; - networkConfig.MACVLAN = "me-${vlanName}"; - linkConfig.RequiredForOnline = "no"; - }; - "40-me-${vlanName}" = { - address = [ - vlanCfg.hosts.${config.node.name}.cidrv4 - vlanCfg.hosts.${config.node.name}.cidrv6 - ]; - matchConfig.Name = "me-${vlanName}"; - networkConfig = { - IPv4Forwarding = "yes"; - IPv6PrivacyExtensions = "yes"; - IPv6SendRA = true; - IPv6AcceptRA = false; - }; - ipv6Prefixes = [ - { Prefix = vlanCfg.cidrv6; } - ]; - linkConfig.RequiredForOnline = "routable"; - }; - } - ); - }; - -} diff --git a/modules/nixos/server/adguardhome.nix b/modules/nixos/server/adguardhome.nix new file mode 100644 index 0000000..f5b6262 --- /dev/null +++ b/modules/nixos/server/adguardhome.nix @@ -0,0 +1,138 @@ +{ self, inputs, lib, config, globals, dns, confLib, ... }: +let + inherit (confLib.gen { name = "adguardhome"; port = 3000; }) serviceName servicePort serviceAddress serviceDomain proxyAddress4 proxyAddress6; + inherit (confLib.static) isHome isProxied homeProxy homeProxyIf webProxy webProxyIf homeWebProxy dnsServer homeDnsServer homeServiceAddress nginxAccessRules; + + homeServices = lib.attrNames (lib.filterAttrs (_: serviceCfg: serviceCfg.isHome) globals.services); + homeDomains = map (name: globals.services.${name}.domain) homeServices; +in +{ + options = { + swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; + }; + config = lib.mkIf config.swarselmodules.server.${serviceName} { + + + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; + }; + + networking.firewall = { + allowedTCPPorts = [ 53 ]; + allowedUDPPorts = [ 53 ]; + }; + + services.adguardhome = { + enable = true; + mutableSettings = false; + host = "0.0.0.0"; + port = servicePort; + settings = { + dns = { + bind_hosts = [ + globals.networks.home-lan.vlans.services.hosts.${homeDnsServer}.ipv4 + globals.networks.home-lan.vlans.services.hosts.${homeDnsServer}.ipv6 + ]; + ratelimit = 300; + upstream_dns = [ + "https://dns.cloudflare.com/dns-query" + "https://dns.google/dns-query" + "https://doh.mullvad.net/dns-query" + ]; + bootstrap_dns = [ + "1.1.1.1" + "2606:4700:4700::1111" + "8.8.8.8" + "2001:4860:4860::8844" + ]; + dhcp.enabled = false; + }; + filtering.rewrites = [ + ] + # Use the local mirror-proxy for some services (not necessary, just for speed) + ++ + map + (domain: { + inherit domain; + # FIXME: change to homeWebProxy once that is setup + answer = globals.networks.home-lan.vlans.services.hosts.${homeWebProxy}.ipv4; + # answer = globals.hosts.${webProxy}.wanAddress4; + }) + homeDomains; + filters = [ + { + name = "AdGuard DNS filter"; + url = "https://adguardteam.github.io/AdGuardSDNSFilter/Filters/filter.txt"; + enabled = true; + } + { + name = "AdAway Default Blocklist"; + url = "https://adaway.org/hosts.txt"; + enabled = true; + } + { + name = "OISD (Big)"; + url = "https://big.oisd.nl"; + enabled = true; + } + ]; + }; + }; + + environment.persistence."/persist".directories = lib.mkIf config.swarselsystems.isImpermanence [ + { + directory = "/var/lib/private/AdGuardHome"; + mode = "0700"; + } + ]; + + nodes = + let + genNginx = toAddress: extraConfig: { + upstreams = { + ${serviceName} = { + servers = { + "${toAddress}:${builtins.toString servicePort}" = { }; + }; + }; + }; + virtualHosts = { + "${serviceDomain}" = { + useACMEHost = globals.domains.main; + forceSSL = true; + acmeRoot = null; + oauth2 = { + enable = true; + allowedGroups = [ "adguardhome_access" ]; + }; + locations = { + "/" = { + proxyPass = "http://${serviceName}"; + proxyWebsockets = true; + }; + }; + extraConfig = lib.mkIf (extraConfig != "") extraConfig; + }; + }; + }; + in + { + ${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; + }; + ${webProxy}.services.nginx = genNginx serviceAddress ""; + ${homeWebProxy}.services.nginx = genNginx homeServiceAddress nginxAccessRules; + }; + }; +} diff --git a/modules/nixos/server/attic.nix b/modules/nixos/server/attic.nix index e278ed6..3aeb11e 100644 --- a/modules/nixos/server/attic.nix +++ b/modules/nixos/server/attic.nix @@ -14,6 +14,12 @@ in "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; + topology.self.services.${serviceName} = { + name = lib.swarselsystems.toCapitalized serviceName; + info = "https://${serviceDomain}"; + # attic does not have a logo + }; + globals = { networks = { ${webProxyIf}.hosts = lib.mkIf isProxied { diff --git a/modules/nixos/server/disk-encrypt.nix b/modules/nixos/server/disk-encrypt.nix index 9c0da25..943077c 100644 --- a/modules/nixos/server/disk-encrypt.nix +++ b/modules/nixos/server/disk-encrypt.nix @@ -4,6 +4,9 @@ let subnetMask = globals.networks.${config.swarselsystems.server.netConfigName}.subnetMask4; gatewayIp = globals.hosts.${config.node.name}.defaultGateway4; + inherit (globals.general) routerServer; + isRouter = config.node.name == routerServer; + hostKeyPathBase = "/etc/secrets/initrd/ssh_host_ed25519_key"; hostKeyPath = if config.swarselsystems.isImpermanence then @@ -42,7 +45,7 @@ in }; boot = lib.mkIf (!config.swarselsystems.isClient) { - kernelParams = lib.mkIf (!config.swarselsystems.isCloud) [ + kernelParams = lib.mkIf (!config.swarselsystems.isCloud && ((config.swarselsystems.localVLANs == [ ]) || isRouter)) [ "ip=${localIp}::${gatewayIp}:${subnetMask}:${config.networking.hostName}::none" ]; initrd = { diff --git a/modules/nixos/server/dns-home.nix b/modules/nixos/server/dns-home.nix new file mode 100644 index 0000000..abb3f5a --- /dev/null +++ b/modules/nixos/server/dns-home.nix @@ -0,0 +1,16 @@ +{ lib, config, globals, confLib, ... }: +let + inherit (confLib.gen { name = "dns-home"; }) serviceName homeProxy; +in +{ + options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; + config = lib.mkIf (config.swarselmodules.server.${serviceName}) { + + networking.hosts = { + ${globals.networks.home-lan.vlans.services.hosts.${homeProxy}.ipv4} = [ "server.${homeProxy}.${globals.domains.main}" ]; + ${globals.networks.home-lan.vlans.services.hosts.${homeProxy}.ipv6} = [ "server.${homeProxy}.${globals.domains.main}" ]; + }; + + }; + +} diff --git a/modules/nixos/server/firezone.nix b/modules/nixos/server/firezone.nix index eb0f546..26addfc 100644 --- a/modules/nixos/server/firezone.nix +++ b/modules/nixos/server/firezone.nix @@ -1,4 +1,4 @@ -{ lib, pkgs, config, globals, confLib, dns, nodes, ... }: +{ self, lib, pkgs, config, globals, confLib, dns, nodes, ... }: let inherit (confLib.gen { name = "firezone"; dir = "/var/lib/private/firezone"; }) serviceName serviceDir serviceAddress serviceDomain proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy homeProxyIf webProxyIf idmServer dnsServer; inherit (config.swarselsystems) sopsFile; @@ -60,6 +60,12 @@ in }; }; + topology.self.services.${serviceName} = { + name = lib.swarselsystems.toCapitalized serviceName; + info = "https://${serviceDomain}"; + icon = "${self}/files/topology-images/${serviceName}.png"; + }; + sops = { secrets = { kanidm-firezone-client = { inherit sopsFile; mode = "0400"; }; @@ -314,12 +320,17 @@ in }; services.firezone.gateway = { enable = true; - logLevel = "trace"; + # logLevel = "trace"; inherit (nodeCfg.node) name; apiUrl = "wss://${globals.services.firezone.domain}/api/"; tokenFile = nodeCfg.sops.secrets.firezone-gateway-token.path; package = nodePkgs.stable25_05.firezone-gateway; # newer versions of firezone-gateway are not compatible with server package }; + + topology.self.services."${serviceName}-gateway" = { + name = lib.swarselsystems.toCapitalized "${serviceName} Gateway"; + icon = "${self}/files/topology-images/${serviceName}.png"; + }; }; ${idmServer} = let diff --git a/modules/nixos/server/garage.nix b/modules/nixos/server/garage.nix index d180487..de46446 100644 --- a/modules/nixos/server/garage.nix +++ b/modules/nixos/server/garage.nix @@ -1,5 +1,5 @@ # inspired by https://github.com/atropos112/nixos/blob/7fef652006a1c939f4caf9c8a0cb0892d9cdfe21/modules/garage.nix -{ lib, pkgs, config, globals, dns, confLib, ... }: +{ self, lib, pkgs, config, globals, dns, confLib, ... }: let inherit (confLib.gen { name = "garage"; @@ -81,6 +81,12 @@ in "*.${subDomain}-web" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; + topology.self.services.${serviceName} = { + name = lib.swarselsystems.toCapitalized serviceName; + info = "https://${serviceDomain}"; + icon = "${self}/files/topology-images/${serviceName}.png"; + }; + sops = { secrets.garage-admin-token = { inherit sopsFile; }; secrets.garage-rpc-secret = { inherit sopsFile; }; diff --git a/modules/nixos/server/homebox.nix b/modules/nixos/server/homebox.nix index 5dba937..05fd201 100644 --- a/modules/nixos/server/homebox.nix +++ b/modules/nixos/server/homebox.nix @@ -1,4 +1,4 @@ -{ lib, pkgs, config, globals, dns, confLib, ... }: +{ self, lib, pkgs, config, globals, dns, confLib, ... }: let inherit (confLib.gen { name = "homebox"; port = 7745; }) servicePort serviceName serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; in @@ -10,7 +10,11 @@ in "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; - topology.self.services.${serviceName}.info = "https://${serviceDomain}"; + topology.self.services.${serviceName} = { + name = "Homebox"; + info = "https://${serviceDomain}"; + icon = "${self}/files/topology-images/${serviceName}.png"; + }; globals = { networks = { diff --git a/modules/nixos/server/hydra.nix b/modules/nixos/server/hydra.nix index 9227117..9798ff8 100644 --- a/modules/nixos/server/hydra.nix +++ b/modules/nixos/server/hydra.nix @@ -13,6 +13,8 @@ in "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; + topology.self.services.${serviceName}.info = "https://${serviceDomain}"; + globals = { networks = { ${webProxyIf}.hosts = lib.mkIf isProxied { diff --git a/modules/nixos/server/kanidm.nix b/modules/nixos/server/kanidm.nix index 339da48..da583f7 100644 --- a/modules/nixos/server/kanidm.nix +++ b/modules/nixos/server/kanidm.nix @@ -228,6 +228,7 @@ in "radicale.access" = { }; "slink.access" = { }; "opkssh.access" = { }; + "adguardhome.access" = { }; }; inherit (config.repo.secrets.local) persons; @@ -370,6 +371,11 @@ in "email" "profile" ]; + "adguardhome.access" = [ + "openid" + "email" + "profile" + ]; }; preferShortUsername = true; claimMaps.groups = { @@ -380,6 +386,7 @@ in "firefly.access" = [ "firefly_access" ]; "radicale.access" = [ "radicale_access" ]; "slink.access" = [ "slink_access" ]; + "adguardhome.access" = [ "adguardhome_access" ]; }; }; }; diff --git a/modules/nixos/server/kavita.nix b/modules/nixos/server/kavita.nix index 717266d..8a44cdc 100644 --- a/modules/nixos/server/kavita.nix +++ b/modules/nixos/server/kavita.nix @@ -62,7 +62,6 @@ in virtualHosts = { "${serviceDomain}" = { useACMEHost = globals.domains.main; - forceSSL = true; acmeRoot = null; locations = { diff --git a/modules/nixos/server/kea.nix b/modules/nixos/server/kea.nix index 01074fa..b9686f6 100644 --- a/modules/nixos/server/kea.nix +++ b/modules/nixos/server/kea.nix @@ -1,6 +1,75 @@ -{ lib, config, globals, confLib, ... }: +{ self, lib, config, globals, confLib, ... }: let - inherit (confLib.gen { name = "kea"; dir = "/var/lib/private/kea"; }) serviceName serviceDir; + inherit (confLib.gen { name = "kea"; dir = "/var/lib/private/kea"; }) serviceName serviceDir homeDnsServer; + dhcpX = intX: + let + x = builtins.toString intX; + in + { + enable = true; + settings = { + reservations-out-of-pool = true; + lease-database = { + name = "/var/lib/kea/dhcp${x}.leases"; + persist = true; + type = "memfile"; + }; + valid-lifetime = 86400; + renew-timer = 3600; + interfaces-config = { + interfaces = map (name: "me-${name}") (builtins.attrNames globals.networks.home-lan.vlans); + service-sockets-max-retries = -1; + }; + "subnet${x}" = lib.flip lib.mapAttrsToList globals.networks.home-lan.vlans ( + vlanName: vlanCfg: { + inherit (vlanCfg) id; + interface = "me-${vlanName}"; + subnet = vlanCfg."cidrv${x}"; + rapid-commit = lib.mkIf (intX == 6) true; + pools = [ + { + pool = "${lib.net.cidr.host 20 vlanCfg."cidrv${x}"} - ${lib.net.cidr.host (-6) vlanCfg."cidrv${x}"}"; + } + ]; + pd-pools = lib.mkIf (intX == 6) [ + { + prefix = builtins.replaceStrings [ "::" ] [ ":0:0:100::" ] (lib.head (lib.splitString "/" vlanCfg.cidrv6)); + prefix-len = 56; + delegated-len = 64; + } + ]; + option-data = + lib.optional (intX == 4) + { + name = "routers"; + data = vlanCfg.hosts.hintbooth."ipv${x}"; + } + # Advertise DNS server for VLANS that have internet access + ++ + lib.optional + (lib.elem vlanName globals.general.internetVLANs) + { + name = if (intX == 4) then "domain-name-servers" else "dns-servers"; + data = globals.networks.home-lan.vlans.services.hosts.${homeDnsServer}."ipv${x}"; + }; + reservations = lib.concatLists ( + lib.forEach (builtins.attrValues vlanCfg.hosts) ( + hostCfg: + lib.optional (hostCfg.mac != null) { + hw-address = lib.mkIf (intX == 4) hostCfg.mac; + duid = lib.mkIf (intX == 6) "00:03:00:01:${hostCfg.mac}"; # 00:03 = duid type 3; 00:01 = ethernet + ip-address = lib.mkIf (intX == 4) hostCfg."ipv${x}"; + ip-addresses = lib.mkIf (intX == 6) [ hostCfg."ipv${x}" ]; + prefixes = lib.mkIf (intX == 6) [ + "${builtins.replaceStrings ["::"] [":0:0:${builtins.toString (256 + hostCfg.id)}::"] (lib.head (lib.splitString "/" vlanCfg.cidrv6))}/64" + ]; + } + ) + ); + } + ); + }; + }; in { options = { @@ -12,65 +81,18 @@ in { directory = serviceDir; mode = "0700"; } ]; - services.kea.dhcp4 = { - enable = true; - settings = { - lease-database = { - name = "/var/lib/kea/dhcp4.leases"; - persist = true; - type = "memfile"; - }; - valid-lifetime = 86400; - renew-timer = 3600; - interfaces-config = { - # XXX: BUG: why does this bind other macvtaps? - interfaces = map (name: "me-${name}") (builtins.attrNames globals.networks.home-lan.vlans); - service-sockets-max-retries = -1; - }; - subnet4 = lib.flip lib.mapAttrsToList globals.networks.home-lan.vlans ( - vlanName: vlanCfg: { - inherit (vlanCfg) id; - interface = "me-${vlanName}"; - subnet = vlanCfg.cidrv4; - pools = [ - { - pool = "${lib.net.cidr.host 20 vlanCfg.cidrv4} - ${lib.net.cidr.host (-6) vlanCfg.cidrv4}"; - } - ]; - option-data = - [ - { - name = "routers"; - data = vlanCfg.hosts.hintbooth.ipv4; # FIXME: how to advertise v6 address also? - } - ]; - # Advertise DNS server for VLANS that have internet access - # ++ - # lib.optional - # (lib.elem vlanName [ - # "services" - # "home" - # "devices" - # "guests" - # ]) - # { - # name = "domain-name-servers"; - # data = globals.networks.home-lan.vlans.services.hosts.hintbooth-adguardhome.ipv4; - # }; - reservations = lib.concatLists ( - lib.forEach (builtins.attrValues vlanCfg.hosts) ( - hostCfg: - lib.optional (hostCfg.mac != null) { - hw-address = hostCfg.mac; - ip-address = hostCfg.ipv4; - } - ) - ); - } - ); + topology = { + extractors.kea.enable = false; + self.services.${serviceName} = { + name = lib.swarselsystems.toCapitalized serviceName; + icon = "${self}/files/topology-images/${serviceName}.png"; }; }; + services.kea = { + dhcp4 = dhcpX 4; + dhcp6 = dhcpX 6; + }; }; } diff --git a/modules/nixos/server/koillection.nix b/modules/nixos/server/koillection.nix index 951812e..0cef41b 100644 --- a/modules/nixos/server/koillection.nix +++ b/modules/nixos/server/koillection.nix @@ -13,6 +13,10 @@ in options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { + swarselmodules.server = { + podman = true; + postgresql = true; + }; nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; diff --git a/modules/nixos/server/mailserver.nix b/modules/nixos/server/mailserver.nix index 8d08b9f..24df0c8 100644 --- a/modules/nixos/server/mailserver.nix +++ b/modules/nixos/server/mailserver.nix @@ -1,4 +1,4 @@ -{ lib, config, globals, dns, confLib, ... }: +{ self, lib, config, globals, dns, confLib, ... }: let inherit (config.swarselsystems) sopsFile; inherit (confLib.gen { name = "mailserver"; dir = "/var/lib/dovecot"; user = "virtualMail"; group = "virtualMail"; port = 443; }) serviceName serviceDir servicePort serviceUser serviceGroup serviceAddress serviceDomain proxyAddress4 proxyAddress6 isHome webProxy dnsServer; @@ -32,6 +32,16 @@ in }; }; + topology.self.services = lib.listToAttrs (map + (service: + lib.nameValuePair "${service}" { + name = lib.swarselsystems.toCapitalized service; + info = lib.mkIf (service == "postfix" || service == "roundcube") (if service == "postfix" then "https://${serviceDomain}" else "https://${roundcubeDomain}"); + icon = "${self}/files/topology-images/${service}.png"; + } + ) + [ "postfix" "dovecot" "rspamd" "clamav" "roundcube" ]); + sops.secrets = { user1-hashed-pw = { inherit sopsFile; owner = serviceUser; }; user2-hashed-pw = { inherit sopsFile; owner = serviceUser; }; diff --git a/modules/nixos/server/minecraft/default.nix b/modules/nixos/server/minecraft/default.nix index d333d0a..d54e031 100644 --- a/modules/nixos/server/minecraft/default.nix +++ b/modules/nixos/server/minecraft/default.nix @@ -1,4 +1,4 @@ -{ lib, config, pkgs, globals, dns, confLib, ... }: +{ self, lib, config, pkgs, globals, dns, confLib, ... }: let inherit (confLib.gen { name = "minecraft"; port = 25565; dir = "/opt/minecraft"; proxy = config.node.name; }) serviceName servicePort serviceDir serviceDomain proxyAddress4 proxyAddress6 isHome dnsServer; inherit (config.swarselsystems) mainUser; @@ -12,7 +12,11 @@ in "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; - topology.self.services.${serviceName}.info = "https://${serviceDomain}"; + topology.self.services.${serviceName} = { + name = "Minecraft"; + info = "https://${serviceDomain}"; + icon = "${self}/files/topology-images/${serviceName}.png"; + }; globals.services.${serviceName} = { domain = serviceDomain; diff --git a/modules/nixos/server/navidrome.nix b/modules/nixos/server/navidrome.nix index 9bf5fc8..d825653 100644 --- a/modules/nixos/server/navidrome.nix +++ b/modules/nixos/server/navidrome.nix @@ -16,6 +16,8 @@ in mpv ]; + topology.self.services.${serviceName}.info = "https://${serviceDomain}"; + users = { groups = { ${serviceGroup} = { diff --git a/modules/nixos/server/network.nix b/modules/nixos/server/network.nix index 9172d54..064a509 100644 --- a/modules/nixos/server/network.nix +++ b/modules/nixos/server/network.nix @@ -2,7 +2,6 @@ let netConfig = config.repo.secrets.local.networking; netPrefix = "${if config.swarselsystems.isCloud then config.node.name else "home"}"; - # netName = "${netPrefix}-${config.swarselsystems.server.localNetwork}"; in { options = { @@ -28,11 +27,6 @@ in swarselsystems.server.localNetwork = netConfig.localNetwork or ""; - # globals.networks.${netName}.hosts.${config.node.name} = { - # inherit (netConfig.networks.${netConfig.localNetwork}) id; - # mac = netConfig.networks.${netConfig.localNetwork}.mac or null; - # }; - globals.networks = lib.mapAttrs' (netName: _: lib.nameValuePair "${netPrefix}-${netName}" { @@ -45,7 +39,8 @@ in netConfig.networks; globals.hosts.${config.node.name} = { - inherit (config.repo.secrets.local.networking) defaultGateway4; + defaultGateway4 = netConfig.defaultGateway4 or null; + defaultGateway6 = netConfig.defaultGateway6 or null; wanAddress4 = netConfig.wanAddress4 or null; wanAddress6 = netConfig.wanAddress6 or null; isHome = if (netPrefix == "home") then true else false; diff --git a/modules/nixos/server/nftables.nix b/modules/nixos/server/nftables.nix index b31407f..6ded12c 100644 --- a/modules/nixos/server/nftables.nix +++ b/modules/nixos/server/nftables.nix @@ -40,6 +40,7 @@ in nnf-drop.enable = true; nnf-loopback.enable = true; nnf-ssh.enable = true; + nnf-dhcpv6.enable = true; }; rules.untrusted-to-local = { diff --git a/modules/nixos/server/nginx.nix b/modules/nixos/server/nginx.nix index 604f509..4f57d92 100644 --- a/modules/nixos/server/nginx.nix +++ b/modules/nixos/server/nginx.nix @@ -67,7 +67,7 @@ in }; }; config = { - extraConfig = lib.mkIf topmod.config.defaultStapling (lib.mkAfter '' + extraConfig = lib.mkIf topmod.config.defaultStapling (lib.mkBefore '' ssl_stapling on; ssl_stapling_verify on; resolver 1.1.1.1 8.8.8.8 valid=300s; diff --git a/modules/nixos/server/nsd/default.nix b/modules/nixos/server/nsd/default.nix index 6e79fad..84efeca 100644 --- a/modules/nixos/server/nsd/default.nix +++ b/modules/nixos/server/nsd/default.nix @@ -1,4 +1,4 @@ -{ lib, config, globals, dns, confLib, ... }: +{ self, lib, config, globals, dns, confLib, ... }: let inherit (confLib.gen { name = "nsd"; port = 53; }) serviceName servicePort proxyAddress4 proxyAddress6; inherit (config.swarselsystems) sopsFile; @@ -34,6 +34,11 @@ in }; }; + topology.self.services.${serviceName} = { + name = lib.toUpper serviceName; + icon = "${self}/files/topology-images/${serviceName}.png"; + }; + services.nsd = { enable = true; keys = { diff --git a/modules/nixos/server/nsd/site1.nix b/modules/nixos/server/nsd/site1.nix index 901774c..8c155c7 100644 --- a/modules/nixos/server/nsd/site1.nix +++ b/modules/nixos/server/nsd/site1.nix @@ -3,7 +3,7 @@ with dns.lib.combinators; { SOA = { nameServer = "soa"; adminEmail = "admin@${globals.domains.main}"; # this option is not parsed as domain (we cannot just write "admin") - serial = 2025122401; # update this on changes for secondary dns + serial = 2026010201; # update this on changes for secondary dns }; useOrigin = false; diff --git a/modules/nixos/server/oauth2-proxy.nix b/modules/nixos/server/oauth2-proxy.nix index 2d85ba0..83e06b3 100644 --- a/modules/nixos/server/oauth2-proxy.nix +++ b/modules/nixos/server/oauth2-proxy.nix @@ -1,6 +1,7 @@ { lib, config, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "oauth2-proxy"; port = 3004; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; + inherit (confLib.gen { name = "oauth2-proxy"; port = 3004; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress proxyAddress4 proxyAddress6; + inherit (confLib.static) isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf homeWebProxy oauthServer nginxAccessRules; kanidmDomain = globals.services.kanidm.domain; mainDomain = globals.domains.main; @@ -119,10 +120,6 @@ in }; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { - "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; - }; - sops = { secrets = { "oauth2-cookie-secret" = { inherit sopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; }; @@ -208,31 +205,41 @@ in }; }; - nodes.${webProxy}.services.nginx = { - upstreams = { - ${serviceName} = { - servers = { - "${serviceAddress}:${builtins.toString servicePort}" = { }; - }; - }; - }; - virtualHosts = { - "${serviceDomain}" = { - useACMEHost = globals.domains.main; - - forceSSL = true; - acmeRoot = null; - locations = { - "/" = { - proxyPass = "http://${serviceName}"; + nodes = + let + genNginx = toAddress: extraConfig: { + upstreams = { + ${serviceName} = { + servers = { + "${toAddress}:${builtins.toString servicePort}" = { }; + }; + }; + }; + virtualHosts = { + "${serviceDomain}" = { + useACMEHost = globals.domains.main; + + forceSSL = true; + acmeRoot = null; + locations = { + "/" = { + proxyPass = "http://${serviceName}"; + }; + }; + extraConfig = '' + proxy_set_header X-Scheme $scheme; + proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri; + '' + lib.optionalString (extraConfig != "") extraConfig; }; }; - extraConfig = '' - proxy_set_header X-Scheme $scheme; - proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri; - ''; }; + in + { + ${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; + }; + ${webProxy}.services.nginx = genNginx serviceAddress ""; + ${homeWebProxy}.services.nginx = genNginx globals.hosts.${oauthServer}.wanAddress4 nginxAccessRules; }; - }; }; } diff --git a/modules/nixos/server/opkssh.nix b/modules/nixos/server/opkssh.nix index 1cc01bc..178e3d7 100644 --- a/modules/nixos/server/opkssh.nix +++ b/modules/nixos/server/opkssh.nix @@ -11,6 +11,7 @@ in options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { + services.${serviceName} = { enable = true; user = serviceUser; diff --git a/modules/nixos/server/packages.nix b/modules/nixos/server/packages.nix index af6702a..6b954eb 100644 --- a/modules/nixos/server/packages.nix +++ b/modules/nixos/server/packages.nix @@ -13,6 +13,8 @@ sops tmux busybox + ndisc6 + tcpdump swarsel-deploy ] ++ lib.optionals withHomeManager [ swarsel-gens diff --git a/modules/nixos/server/podman.nix b/modules/nixos/server/podman.nix new file mode 100644 index 0000000..80bdc22 --- /dev/null +++ b/modules/nixos/server/podman.nix @@ -0,0 +1,38 @@ +{ config, lib, ... }: +let + serviceName = "podman"; +in +{ + options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; + config = lib.mkIf config.swarselmodules.server.${serviceName} { + + virtualisation = { + podman.enable = true; + oci-containers.backend = "podman"; + }; + + networking.nftables.firewall = lib.mkIf config.networking.nftables.enable { + + zones.podman = { + interfaces = [ "podman0" ]; + }; + + rules = { + podman-to-postgres = lib.mkIf config.services.postgresql.enable { + from = [ "podman" ]; + to = [ "local" ]; + before = [ "drop" ]; + allowedTCPPorts = [ config.services.postgresql.settings.port ]; + }; + + local-to-podman = { + from = [ "local" "wgProxy" "wgHme" ]; + to = [ "podman" ]; + before = [ "drop" ]; + verdict = "accept"; + }; + }; + }; + + }; +} diff --git a/modules/nixos/server/router.nix b/modules/nixos/server/router.nix index 51311be..6dc482f 100644 --- a/modules/nixos/server/router.nix +++ b/modules/nixos/server/router.nix @@ -1,6 +1,15 @@ -{ lib, config, globals, ... }: +{ lib, config, globals, confLib, ... }: let serviceName = "router"; + bridgeVLANs = lib.mapAttrsToList + (_: vlan: { + VLAN = vlan.id; + }) + globals.networks.home-lan.vlans; + selectVLANs = vlans: map (vlan: { VLAN = globals.networks.home-lan.vlans.${vlan}.id; }) vlans; + lan5VLANs = selectVLANs [ "home" "devices" "guests" ]; + lan4VLANs = selectVLANs [ "home" "services" ]; + inherit (confLib.gen { }) homeDnsServer; in { options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; @@ -8,13 +17,27 @@ in { services.avahi.reflector = true; + topology.self.interfaces = (lib.mapAttrs' + (vlanName: _: + lib.nameValuePair "vlan-${vlanName}" { + network = lib.mkForce vlanName; + } + ) + globals.networks.home-lan.vlans) // (lib.mapAttrs' + (vlanName: _: + lib.nameValuePair "me-${vlanName}" { + network = lib.mkForce vlanName; + } + ) + globals.networks.home-lan.vlans); + networking.nftables = { firewall = { zones = { untrusted.interfaces = [ "lan" ]; wgHome.interfaces = [ "wgHome" ]; - adguardhome.ipv4Addresses = [ globals.networks.home-lan.vlans.services.hosts.hintbooth-adguardhome.ipv4 ]; - adguardhome.ipv6Addresses = [ globals.networks.home-lan.vlans.services.hosts.hintbooth-adguardhome.ipv6 ]; + adguardhome.ipv4Addresses = [ globals.networks.home-lan.vlans.services.hosts.${homeDnsServer}.ipv4 ]; + adguardhome.ipv6Addresses = [ globals.networks.home-lan.vlans.services.hosts.${homeDnsServer}.ipv6 ]; } // lib.flip lib.concatMapAttrs globals.networks.home-lan.vlans ( vlanName: _: { @@ -24,7 +47,7 @@ in rules = { masquerade-internet = { - from = map (name: "vlan-${name}") (builtins.attrNames globals.networks.home-lan.vlans); + from = map (name: "vlan-${name}") (globals.general.internetVLANs); to = [ "untrusted" ]; # masquerade = true; NOTE: custom rule below for ip4 + ip6 late = true; # Only accept after any rejects have been processed @@ -33,7 +56,7 @@ in # Allow access to the AdGuardHome DNS server from any VLAN that has internet access access-adguardhome-dns = { - from = map (name: "vlan-${name}") (builtins.attrNames globals.networks.home-lan.vlans); + from = map (name: "vlan-${name}") (globals.general.internetVLANs); to = [ "adguardhome" ]; verdict = "accept"; }; @@ -53,7 +76,7 @@ in services-to-local = { from = [ "vlan-services" ]; to = [ "local" ]; - allowedUDPPorts = [ 52829 ]; + allowedUDPPorts = [ 52829 547 ]; }; # Forward traffic between wireguard participants @@ -71,7 +94,7 @@ in late = true; rules = lib.forEach - (map (name: "vlan-${name}") (builtins.attrNames globals.networks.home-lan.vlans)) + (map (name: "vlan-${name}") (globals.general.internetVLANs)) ( zone: lib.concatStringsSep " " [ @@ -93,7 +116,68 @@ in systemd.network = { wait-online.anyInterface = true; + + netdevs = { + "10-veth" = { + netdevConfig = { + Kind = "veth"; + Name = "veth-br"; + }; + peerConfig = { + Name = "veth-int"; + }; + }; + "20-br" = { + netdevConfig = { + Kind = "bridge"; + Name = "br"; + }; + bridgeConfig = { + VLANFiltering = true; + }; + }; + }; networks = { + "40-br" = { + matchConfig.Name = "br"; + bridgeConfig = { }; + linkConfig = { + ActivationPolicy = "always-up"; + RequiredForOnline = "no"; + }; + networkConfig = { + ConfigureWithoutCarrier = true; + LinkLocalAddressing = "no"; + }; + }; + "15-veth-br" = { + matchConfig.Name = "veth-br"; + + linkConfig = { + RequiredForOnline = "no"; + }; + + networkConfig = { + Bridge = "br"; + }; + inherit bridgeVLANs; + }; + "15-veth-int" = { + matchConfig.Name = "veth-int"; + + linkConfig = { + ActivationPolicy = "always-up"; + RequiredForOnline = "no"; + }; + + networkConfig = { + ConfigureWithoutCarrier = true; + LinkLocalAddressing = "no"; + }; + + vlan = map (name: "vlan-${name}") (builtins.attrNames globals.networks.home-lan.vlans); + }; + # br "30-lan1" = { matchConfig.MACAddress = config.repo.secrets.local.networking.networks.lan1.mac; linkConfig.RequiredForOnline = "enslaved"; @@ -101,7 +185,9 @@ in Bridge = "br"; ConfigureWithoutCarrier = true; }; + inherit bridgeVLANs; }; + # wifi "30-lan2" = { matchConfig.MACAddress = config.repo.secrets.local.networking.networks.lan2.mac; linkConfig.RequiredForOnline = "enslaved"; @@ -109,7 +195,9 @@ in Bridge = "br"; ConfigureWithoutCarrier = true; }; + inherit bridgeVLANs; }; + # summers "30-lan3" = { matchConfig.MACAddress = config.repo.secrets.local.networking.networks.lan3.mac; linkConfig.RequiredForOnline = "enslaved"; @@ -117,7 +205,9 @@ in Bridge = "br"; ConfigureWithoutCarrier = true; }; + inherit bridgeVLANs; }; + # winters "30-lan4" = { matchConfig.MACAddress = config.repo.secrets.local.networking.networks.lan4.mac; linkConfig.RequiredForOnline = "enslaved"; @@ -125,7 +215,9 @@ in Bridge = "br"; ConfigureWithoutCarrier = true; }; + bridgeVLANs = lan4VLANs; }; + # lr "30-lan5" = { matchConfig.MACAddress = config.repo.secrets.local.networking.networks.lan5.mac; linkConfig.RequiredForOnline = "enslaved"; @@ -133,10 +225,37 @@ in Bridge = "br"; ConfigureWithoutCarrier = true; }; + bridgeVLANs = lan5VLANs; }; - }; - }; + } // lib.flip lib.concatMapAttrs globals.networks.home-lan.vlans ( + vlanName: vlanCfg: { + "40-me-${vlanName}" = lib.mkForce { + address = [ + vlanCfg.hosts.${config.node.name}.cidrv4 + vlanCfg.hosts.${config.node.name}.cidrv6 + ]; + matchConfig.Name = "me-${vlanName}"; + networkConfig = { + IPv4Forwarding = "yes"; + IPv6PrivacyExtensions = "yes"; + IPv6SendRA = true; + IPv6AcceptRA = false; + }; + ipv6Prefixes = [ + { + Prefix = vlanCfg.cidrv6; + } + ]; + ipv6SendRAConfig = { + Managed = true; # set RA M flag -> DHCPv6 for addresses + OtherInformation = true; # optional, for โ€œother infoโ€ via DHCPv6 + }; + linkConfig.RequiredForOnline = "routable"; + }; + } + ); + }; }; } diff --git a/modules/nixos/server/shlink.nix b/modules/nixos/server/shlink.nix index 023b831..00853db 100644 --- a/modules/nixos/server/shlink.nix +++ b/modules/nixos/server/shlink.nix @@ -12,6 +12,10 @@ in }; config = lib.mkIf config.swarselmodules.server.${serviceName} { + swarselmodules.server = { + podman = true; + }; + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; diff --git a/modules/nixos/server/slink.nix b/modules/nixos/server/slink.nix index 8e71aae..8ca9509 100644 --- a/modules/nixos/server/slink.nix +++ b/modules/nixos/server/slink.nix @@ -10,6 +10,10 @@ in }; config = lib.mkIf config.swarselmodules.server.${serviceName} { + swarselmodules.server = { + podman = true; + }; + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; diff --git a/modules/nixos/server/wireguard.nix b/modules/nixos/server/wireguard.nix index 98d9fdf..dcfa71d 100644 --- a/modules/nixos/server/wireguard.nix +++ b/modules/nixos/server/wireguard.nix @@ -95,6 +95,14 @@ in ) ); + topology.self.interfaces = lib.mapAttrs' + (wgName: _: + lib.nameValuePair "${wgName}" { + network = wgName; + } + ) + config.swarselsystems.server.wireguard.interfaces; + environment.systemPackages = with pkgs; [ wireguard-tools ]; diff --git a/modules/shared/config-lib.nix b/modules/shared/config-lib.nix index 1bc7e2a..eac2370 100644 --- a/modules/shared/config-lib.nix +++ b/modules/shared/config-lib.nix @@ -1,27 +1,27 @@ { self, config, lib, globals, inputs, outputs, minimal, nixosConfig ? null, ... }: +let + domainDefault = service: config.repo.secrets.common.services.domains.${service}; + proxyDefault = config.swarselsystems.proxyHost; + + addressDefault = + if + config.swarselsystems.proxyHost != config.node.name + then + if + config.swarselsystems.server.wireguard.interfaces.wgProxy.isClient + then + globals.networks."${config.swarselsystems.server.wireguard.interfaces.wgProxy.serverNetConfigPrefix}-wgProxy".hosts.${config.node.name}.ipv4 + else + globals.networks.${config.swarselsystems.server.netConfigName}.hosts.${config.node.name}.ipv4 + else + "localhost"; +in { _module.args = { confLib = rec { - - addressDefault = - if - config.swarselsystems.proxyHost != config.node.name - then - if - config.swarselsystems.server.wireguard.interfaces.wgProxy.isClient - then - globals.networks."${config.swarselsystems.server.wireguard.interfaces.wgProxy.serverNetConfigPrefix}-wgProxy".hosts.${config.node.name}.ipv4 - else - globals.networks.${config.swarselsystems.server.netConfigName}.hosts.${config.node.name}.ipv4 - else - "localhost"; - - domainDefault = service: config.repo.secrets.common.services.domains.${service}; - proxyDefault = config.swarselsystems.proxyHost; - getConfig = if nixosConfig == null then config else nixosConfig; - gen = { name, user ? name, group ? name, dir ? null, port ? null, domain ? (domainDefault name), address ? addressDefault, proxy ? proxyDefault }: rec { + gen = { name ? "n/a", user ? name, group ? name, dir ? null, port ? null, domain ? (domainDefault name), address ? addressDefault, proxy ? proxyDefault }: rec { servicePort = port; serviceName = name; specificServiceName = "${name}-${config.node.name}"; @@ -36,12 +36,28 @@ proxyAddress4 = globals.hosts.${proxy}.wanAddress4 or null; proxyAddress6 = globals.hosts.${proxy}.wanAddress6 or null; inherit (globals.hosts.${config.node.name}) isHome; - inherit (globals.general) homeProxy webProxy dnsServer idmServer; + inherit (globals.general) homeProxy webProxy dnsServer homeDnsServer homeWebProxy idmServer; webProxyIf = "${webProxy}-wgProxy"; homeProxyIf = "home-wgHome"; isProxied = config.node.name != webProxy; }; + static = rec { + inherit (globals.hosts.${config.node.name}) isHome; + inherit (globals.general) homeProxy webProxy dnsServer homeDnsServer homeWebProxy idmServer oauthServer; + webProxyIf = "${webProxy}-wgProxy"; + homeProxyIf = "home-wgHome"; + isProxied = config.node.name != webProxy; + nginxAccessRules = '' + allow ${globals.networks.home-lan.vlans.home.cidrv4}; + allow ${globals.networks.home-lan.vlans.home.cidrv6}; + allow ${globals.networks.home-lan.vlans.services.hosts.${homeProxy}.ipv4}; + allow ${globals.networks.home-lan.vlans.services.hosts.${homeProxy}.ipv6}; + deny all; + ''; + homeServiceAddress = lib.optionalString (config.swarselsystems.server.wireguard.interfaces ? wgHome) globals.networks."${config.swarselsystems.server.wireguard.interfaces.wgHome.serverNetConfigPrefix}-wgHome".hosts.${config.node.name}.ipv4; + }; + mkMicrovm = if config.swarselsystems.withMicroVMs then (guestName: { @@ -49,7 +65,7 @@ backend = "microvm"; autostart = true; modules = [ - (config.node.configDir + /guests/${guestName}.nix) + (config.node.configDir + /guests/${guestName}/default.nix) { node.secretsDir = config.node.configDir + /secrets/${guestName}; node.configDir = config.node.configDir + /guests/${guestName}; @@ -62,6 +78,7 @@ }; } "${self}/modules/nixos/optional/microvm-guest.nix" + "${self}/modules/nixos/optional/systemd-networkd-base.nix" ]; microvm = { system = config.node.arch; @@ -74,6 +91,7 @@ inherit inputs outputs minimal; inherit (inputs) self; withHomeManager = false; + microVMParent = config.node.name; globals = outputs.globals.${config.node.arch}; }; }; diff --git a/modules/shared/options.nix b/modules/shared/options.nix index 5d3f169..28f2e2e 100644 --- a/modules/shared/options.nix +++ b/modules/shared/options.nix @@ -37,6 +37,18 @@ type = lib.types.str; default = ""; }; + # @ future me: dont put this under server prefix + # home-manager would then try to import all swarselsystems.server.* options + localVLANs = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + }; + # @ future me: dont put this under server prefix + # home-manager would then try to import all swarselsystems.server.* options + initrdVLAN = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + }; mainUser = lib.mkOption { type = lib.types.str; default = "swarsel"; diff --git a/nix/topology.nix b/nix/topology.nix index 72f92e8..dc22d6f 100644 --- a/nix/topology.nix +++ b/nix/topology.nix @@ -4,189 +4,250 @@ inputs.nix-topology.flakeModule ]; - perSystem.topology.modules = [ - ({ config, ... }: - let - inherit (self.outputs) globals; - inherit (config.lib.topology) - mkInternet - mkDevice - mkSwitch - mkRouter - mkConnection - ; - in - { - renderer = "elk"; + perSystem = { system, ... }: + let + inherit (self.outputs) lib; + in + { + topology.modules = [ + ({ config, ... }: + let + globals = self.outputs.globals.${system}; + inherit (config.lib.topology) + mkInternet + mkDevice + mkSwitch + mkRouter + mkConnection + ; + in + { + renderer = "elk"; - networks = { - home-lan = { - name = "Home LAN"; - inherit (globals.networks.home-lan) cidrv4; - }; - fritz-wg = { - name = "Wireguard Tunnel for Fritzbox net access"; - inherit (globals.networks.twothreetunnel-wg) cidrv4; - }; - wg = { - name = "Wireguard Tunnel for proxy access"; - inherit (globals.networks.twothreetunnel-wg) cidrv4; - }; - }; - - nodes = { - internet = mkInternet { - connections = [ - (mkConnection "fritzbox" "dsl") - (mkConnection "moonside" "wan") - (mkConnection "belchsfactory" "wan") - (mkConnection "twothreetunnel" "wan") - (mkConnection "stoicclub" "wan") - (mkConnection "liliputsteps" "wan") - (mkConnection "eagleland" "wan") - (mkConnection "magicant" "wifi") - (mkConnection "toto" "bootstrapper") - (mkConnection "hotel" "demo host") - ]; - }; - - - fritzbox = mkRouter "FRITZ!Box" { - info = "FRITZ!Box 7682"; - image = "${self}/files/topology-images/hunsn.png"; - interfaceGroups = [ - [ - "eth1" - "eth2" - "eth3" - "eth-wan" - "wifi" - ] - [ "dsl" ] - ]; - - connections = { - eth1 = mkConnection "winters" "eth1"; - eth2 = mkConnection "switch-bedroom" "eth1"; - eth3 = mkConnection "switch-livingroom" "eth1"; - eth-wan = mkConnection "hintbooth" "eth6"; - wgPyramid = mkConnection "pyramid" "fritz-wg"; - wgMagicant = mkConnection "magicant" "fritz-wg"; - wifiPyramid = mkConnection "pyramid" "wifi"; - wifiMagicant = mkConnection "magicant" "wifi"; - wifiBakery = mkConnection "bakery" "wifi"; - wifiMachpizza = mkConnection "machpizza" "wifi"; - }; - interfaces = { - eth1 = { - addresses = [ globals.networks.home-lan.hosts.fritzbox.ipv4 ]; - network = "home-lan"; + networks = { + fritz-lan = { + name = "Fritz!Box LAN"; + inherit (globals.networks.home-lan) cidrv4 cidrv6; }; - eth2 = { - addresses = [ globals.networks.home-lan.hosts.fritzbox.ipv4 ]; - network = "home-lan"; + services = { + name = "VLAN: Services"; + inherit (globals.networks.home-lan.vlans.services) cidrv4 cidrv6; }; - eth3 = { - addresses = [ globals.networks.home-lan.hosts.fritzbox.ipv4 ]; - network = "home-lan"; + home = { + name = "VLAN: Home"; + inherit (globals.networks.home-lan.vlans.home) cidrv4 cidrv6; }; - eth-wan = { - addresses = [ globals.networks.home-lan.hosts.fritzbox.ipv4 ]; - network = "home-lan"; + devices = { + name = "VLAN: Devices"; + inherit (globals.networks.home-lan.vlans.devices) cidrv4 cidrv6; }; - wifi = { - addresses = [ globals.networks.home-lan.hosts.fritzbox.ipv4 ]; - virtual = true; - network = "home-lan"; + guests = { + name = "VLAN: Guests"; + inherit (globals.networks.home-lan.vlans.guests) cidrv4 cidrv6; }; fritz-wg = { - addresses = [ globals.networks.fritz-wg.hosts.fritzbox.ipv4 ]; - network = "wg"; - virtual = true; - type = "wireguard"; + name = "WireGuard: Fritz!Box tunnel"; + inherit (globals.networks.fritz-wg) cidrv4 cidrv6; + }; + wgProxy = { + name = "WireGuard: Web proxy tunnel"; + inherit (globals.networks.twothreetunnel-wgProxy) cidrv4 cidrv6; + }; + wgHome = { + name = "WireGuard: Home proxy tunnel"; + inherit (globals.networks.home-wgHome) cidrv4 cidrv6; }; }; - }; - switch-livingroom = mkSwitch "Switch Livingroom" { - info = "TL-SG108"; - image = "${self}/files/topology-images/TL-SG108.png"; - interfaceGroups = [ - [ - "eth1" - "eth2" - "eth3" - "eth4" - "eth5" - "eth6" - "eth7" - "eth8" - ] - ]; - connections = { - eth2 = mkConnection "nswitch" "eth1"; - eth7 = mkConnection "pc" "eth1"; - eth8 = mkConnection "pyramid" "eth1"; + nodes = { + internet = mkInternet { + connections = [ + (mkConnection "fritzbox" "dsl") + (mkConnection "magicant" "wifi") + (mkConnection "liliputsteps" "lan") + (mkConnection "treehouse" "eth1") + (mkConnection "toto" "bootstrapper") + (mkConnection "hotel" "demo host") + ]; + }; + + + fritzbox = mkRouter "FRITZ!Box" { + info = "FRITZ!Box 7682"; + image = "${self}/files/topology-images/Fritz!Box_7682.png"; + interfaceGroups = [ + [ + "eth1" + "eth2" + "eth3" + "eth-wan" + "wifi" + ] + [ "dsl" ] + ]; + + connections = { + eth1 = mkConnection "winters" "eth1"; + eth-wan = mkConnection "hintbooth" "lan"; + }; + interfaces = { + eth1 = { + addresses = [ globals.networks.home-lan.hosts.fritzbox.ipv4 ]; + network = "fritz-lan"; + }; + eth2 = { }; + eth3 = { }; + eth-wan = { + addresses = [ globals.networks.home-lan.hosts.fritzbox.ipv4 ]; + network = "fritz-lan"; + }; + wifi = { + addresses = [ globals.networks.home-lan.hosts.fritzbox.ipv4 ]; + virtual = true; + renderer.hidePhysicalConnections = true; + network = "fritz-lan"; + physicalConnections = [ + (mkConnection "pyramid" "wifi") + (mkConnection "bakery" "wifi") + (mkConnection "machpizza" "wifi") + ]; + }; + fritz-wg = { + addresses = [ globals.networks.fritz-wg.hosts.fritzbox.ipv4 ]; + network = "fritz-wg"; + virtual = true; + renderer.hidePhysicalConnections = true; + type = "wireguard"; + physicalConnections = [ + (mkConnection "pyramid" "fritz-wg") + (mkConnection "magicant" "fritz-wg") + ]; + }; + }; + }; + + switch-livingroom = mkSwitch "Switch Livingroom" { + info = "TL-SG108E"; + image = "${self}/files/topology-images/TL-SG108E.png"; + interfaceGroups = [ + # trunk + [ "eth1" ] + # devices + [ "eth2" ] + # home + [ "eth3" "eth8" ] + # guests + [ "eth4" "eth5" "eth6" "eth7" ] + ]; + interfaces = { + eth2 = { network = lib.mkForce "devices"; }; + eth3 = { network = lib.mkForce "home"; }; + eth7 = { network = lib.mkForce "guests"; }; + eth8 = { network = lib.mkForce "home"; }; + }; + connections = { + eth2 = mkConnection "nswitch" "eth1"; + eth3 = mkConnection "bakery" "eth1"; + eth7 = mkConnection "pc" "eth1"; + eth8 = mkConnection "pyramid" "eth1"; + }; + }; + + switch-bedroom = mkDevice "Switch Bedroom" { + info = "Cisco SG 200-08"; + image = "${self}/files/topology-images/Cisco_SG_200-08.png"; + interfaceGroups = [ + # trunk + [ "eth1" ] + # devices + [ "eth2" ] + # guests + [ "eth3" "eth4" "eth5" "eth6" "eth7" "eth8" ] + ]; + interfaces = { + eth2 = { network = lib.mkForce "devices"; }; + eth3 = { network = lib.mkForce "guests"; }; + }; + connections = { + eth2 = mkConnection "printer" "eth1"; + eth3 = mkConnection "machpizza" "eth1"; + }; + }; + + nswitch = mkDevice "Nintendo Switch" { + info = "Nintendo Switch"; + image = "${self}/files/topology-images/nintendo-switch.png"; + interfaces.eth1 = { }; + }; + + magicant = mkDevice "magicant" { + icon = "${self}/files/topology-images/phone.png"; + info = "Samsung Z Flip 6"; + image = "${self}/files/topology-images/zflip6.png"; + interfaces = { + wifi = { }; + fritz-wg.network = "fritz-wg"; + }; + }; + + machpizza = mkDevice "machpizza" { + info = "MacBook Pro 2016"; + icon = "devices.laptop"; + deviceIcon = "${self}/files/topology-images/mac.png"; + interfaces = { + eth1.network = "guests"; + wifi = { }; + }; + }; + + treehouse = mkDevice "treehouse" { + info = "NVIDIA DGX Spark"; + icon = "${self}/files/topology-images/home-manager.png"; + deviceIcon = "${self}/files/topology-images/dgxos.png"; + interfaces = { + eth1 = { }; + wifi = { }; + }; + services = { + ollama = { + name = "Ollama"; + icon = "${self}/files/topology-images/ollama.png"; + }; + openwebui = { + name = "Open WebUI"; + icon = "${self}/files/topology-images/openwebui.png"; + }; + comfyui = { + name = "Comfy UI"; + icon = "${self}/files/topology-images/comfyui.png"; + }; + }; + }; + + pc = mkDevice "Chaostheater" { + info = "ASUS Z97-A, i7-4790k, GTX970, 32GB RAM"; + icon = "${self}/files/topology-images/windows.png"; + deviceIcon = "${self}/files/topology-images/atlasos.png"; + services = { + sunshine = { + name = "Sunshine"; + icon = "${self}/files/topology-images/sunshine.png"; + }; + }; + interfaces.eth1.network = "guests"; + }; + + printer = mkDevice "Printer" { + info = "DELL C2665dnf"; + image = "${self}/files/topology-images/DELL-C2665dnf.png"; + interfaces.eth1 = { }; + }; + }; - }; - switch-bedroom = mkSwitch "Switch Bedroom" { - info = "TL-SG1005D"; - image = "${self}/files/topology-images/TL-SG1005D.png"; - interfaceGroups = [ - [ - "eth1" - "eth2" - "eth3" - "eth4" - "eth5" - ] - ]; - connections.eth2 = mkConnection "printer" "eth1"; - connections.eth3 = mkConnection "machpizza" "eth1"; - }; - - nswitch = mkDevice "Nintendo Switch" { - info = "Nintendo Switch"; - image = "${self}/files/topology-images/nintendo-switch.png"; - interfaces.eth1 = { }; - }; - - magicant = mkDevice "magicant" { - icon = "${self}/files/topology-images/phone.png"; - info = "Samsung Z Flip 6"; - image = "${self}/files/topology-images/zflip6.png"; - interfaces = { - wifi = { }; - fritz-wg = { }; - }; - }; - - machpizza = mkDevice "machpizza" { - info = "MacBook Pro 2016"; - icon = "${self}/files/topology-images/mac.png"; - interfaces = { - eth1 = { }; - wifi = { }; - }; - }; - - pc = mkDevice "Windows Gaming Server" { - info = "i7-4790k, GTX970, 32GB RAM"; - image = "${self}/files/topology-images/pc.png"; - interfaces.eth1 = { }; - }; - - printer = mkDevice "Printer" { - info = "DELL C2665dnf"; - image = "${self}/files/topology-images/DELL-C2665dnf.png"; - interfaces.eth1 = { }; - }; - - }; - - }) + }) - ]; + ]; + }; } diff --git a/profiles/nixos/microvm/default.nix b/profiles/nixos/microvm/default.nix index b00d5a1..689bbbf 100644 --- a/profiles/nixos/microvm/default.nix +++ b/profiles/nixos/microvm/default.nix @@ -20,6 +20,8 @@ general = lib.mkDefault true; packages = lib.mkDefault true; ssh = lib.mkDefault true; + wireguard = lib.mkDefault true; + dns-home = lib.mkDefault true; }; }; }; diff --git a/secrets/public/wg/hintbooth-adguardhome.pub b/secrets/public/wg/hintbooth-adguardhome.pub new file mode 100644 index 0000000..f917a4a --- /dev/null +++ b/secrets/public/wg/hintbooth-adguardhome.pub @@ -0,0 +1 @@ +emVomIbxGS95tDiwPpUbsRTAur/TZ+BRy7+4737Sv1Q= diff --git a/secrets/public/wg/hintbooth-nginx.pub b/secrets/public/wg/hintbooth-nginx.pub new file mode 100644 index 0000000..4f23f14 --- /dev/null +++ b/secrets/public/wg/hintbooth-nginx.pub @@ -0,0 +1 @@ +G4NXSwhwU5a2K1q3oAwmnOtBlCj15J2aqh6zIJJtggE= diff --git a/secrets/repo/certs.yaml b/secrets/repo/certs.yaml index 50999c1..cec5da9 100644 --- a/secrets/repo/certs.yaml +++ b/secrets/repo/certs.yaml @@ -8,143 +8,161 @@ sops: - recipient: age1s0vssf9fey2l456hucppzx2x58xep279nsdcglvkqm30sr9ht37s8rvpza enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBpZW1LTVFtaGVraTFDRVI2 - dkNRV0tpdUg1YmdiL2hMVUlodzFJM1hHUkJBClRHaGRMTDNlbVpDS1A5dzBpc2Yv - ancxU3lSRTJNMFBjeFNoSEF1Rys0YVUKLS0tIHVpb2g4bnN4NHJTdHJrSDdKVFZY - aGNOMDgvakoxcWJ3bEVyeFZOQU94UXcK/mjqz9Ys7ZEm2+9Y+mLrQVqoC5g5ag85 - Xp9Fo7gmsgc9lZwgxRPLzIBeWcaN17NIaMRDMxTz5cdDCRi5JP9FiQ== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBFdDZiY1BsaHhLYy93L2Ju + TkJHRUdTU0VFKy9jd1p2ZmJrU092Z09yMTNvCnpzbXFIZ1VMVk1QVzN6YXFmckQ0 + U0hFam53Z3V5U2xYNjkwU1UxNmI4ZncKLS0tIGk1MC9EV2hnMFN6aFRtekt0enQy + RWxXVmhJU3NVbHdlS1Rsd05sSkZiY1UKaK5bSDPhQlVTryAYr/9mIgmXDzVp2KWF + M4FQURHk6kvSIVjHNfRyMX0IVtCFZMSmVpuPUP46J/5kzdN59Jn2Bw== + -----END AGE ENCRYPTED FILE----- + - recipient: age18cgqlely56hgmhscllkmafwpjdk6dwep6ej3vkk97dzemp8jtuksqrrjjl + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBLRElrSnhrM0ZVQ043K1Nk + VTNlWDVDNW5veDNPM0RuOWZTZ3IwQ2pBdmpFCktEZFNvUTRrVVRBdFU5alhJSTE0 + RVlqRkl5MUEzZjhUVWwrWTFWMk8yWG8KLS0tIHJKZXJrNXZiRjNiZEh6VEhHWnZT + dlI3RTl2MW43YVdJMVlDaUoxSDV3YTAKBCjLqctIpPeTYsRxhj0/7DzR0q1cGe2d + w0B8DmH56XP7vq+nLh6+imWFLsbEOS0lRPHRRBEiimEkYljO5ZkoZg== -----END AGE ENCRYPTED FILE----- - recipient: age1g7atkxdlt4ymeh7v7aa2yzr2hq2qkvzrc4r49ugttm3n582ymv9qrmpk8d enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBXMXRHK1Nud0VEMUh5OFRZ - Z1ljcmZyRG9yRFFwdWtTcEVXc3NEOVVId2xjCnVIb3cvSG9oODJvd0NGd1BIb3A4 - TmJ4SGdmd1FDWCs2c0YvZXkxWWFidG8KLS0tIHIzUlloTUhxY29pQ081akFGSmRO - Y25iUkw4bU1sUzZlUHEyWDB3d3hlZDAK4auJRQylMD3vf/PeMdImqRrx86sT2vdM - lbmSBNqry2CueSgvFp2z4q+NOE4F5z/rlc0GZnUvdzBTLZ83C1BYoQ== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA3TEREZ0lVR2Q5aEN1S2sv + dVhURlhhWTJBdEdhbHlXZGVQa2ZwZkhFd2lnClM1QUJrMW1GU3FuVTFwUkZxR0xD + YS9kVFVRdGZKMVVRTk5sNm9nRVV1YkkKLS0tIExyKzlJUHhUdEVhSGJEUUs1YlQz + Yk5WMERLb1MyVTRvM3hkVVFNU3ZtajAKqH9sLSNTacfRj4c/FeeOyCITdz9zwgqm + e52OOUzI9nmq3zbhzd7b2nWHlJ2d/vmFN0u4oEpLodQPqBy/cvuoLQ== -----END AGE ENCRYPTED FILE----- - recipient: age1ly2endyt0y9xyddj6yuj4nw6fa3ltvzlvew4cr4lzs6dv8dkavpqadmyxx enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAyZFpCaWNlSGdWTG9EanBC - MlVKZ1FCMHpzMkNJUTA3QzZ3aUtCbmFIemxjCktVZ0V3L25NNGxLaWRVbTZCWjBV - NXJKSzczYUd1ajJ6U0VsTmRpQW5SalEKLS0tIER1Y3VCTzJ5TjZ5dHMxSG8yaHM3 - OGFWc240c0lSYXEzWlp6eGtIcHpnUlkK+7wia8nrB982IdbW8zXg0p4Zf/3XE+Rn - u0Nsa7cbeOpB+stTV/i/2ayY86+zkN8UQXJnlv+AVf3Oe/3xaKsZ1A== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBEbmRtN1FXaFYwUTBsbEhr + T0tGcVkvdmhjOWZMLzZ3UUpoQ0V6Y2luRlNrCnU0RThMcXlabWQraHB0ZjNldFRW + bUtRNGgwNDZhNjF6MzlQM2V5RmVRRnMKLS0tIDB0ZUpPZzJFMndSZXJNQWEzbjZQ + YVJxb3NLNEJkNXlzR0Q2MUNhZ0NGeTQKC204L2g3b/ER0RtnTaGtuZSukTawgiC0 + 94UolrcApg2tAUDJR9AqJ0iAAu8KSkcy77mQIs1t1d5lwejwsOBUMQ== -----END AGE ENCRYPTED FILE----- - recipient: age15klj4t7gpfp69472mne4ue62pp6m4e04dmjyw7yf30qtqd3vl3uqjmcyxm enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzUzNPMUE0M3Uzc2JqTUhk - ejVmS2FpSHN2YitaRHNMNm9abHdTSUF3QVdRCnlQcW9aMFh5MHRHNjNrWXFrRTlF - TXNBN3FEY3dPMHhNbEQ0eGdacGNvbkUKLS0tIEtoWlFqblROSndGOEZwdEN2cXcr - c0IzdGY3T3E4Vmk1SzkydEVLbng3MG8K2pF8UY+89cI0dh7xgdrvl7fn8f05jSpM - MFcVBU9ySITPb3lcGtOKVcyyh8OaTusdZWmzbnl/dKr7kWjg/M8l/g== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjbmZRNDYwVjhacnZzTm1J + MG5FWVd5TytPNWhDVTR6Z2dxdEYwbEU5VVFNCkVpdmMxTmR2dHBKNnZNTmF4c3hs + eUozU2VkKzlKMjhWaDJXQUx3ZXdHV1UKLS0tIEtNb25NU204dFovM2xvMEpDYUJX + QWZLNDQvZjZjaDZiN3JLcWswMkhtcnMK0kkTPKMgKNiOOgen2BQANozKY0npxINI + ZhKkf/eQsPD5kUbD1gLshfeOS+GOcDJSjrYigneJo11yEhNVF7juDQ== -----END AGE ENCRYPTED FILE----- - recipient: age1k73gy5em3js9zklnnkzp5hme9k04lny32fgahmzddknjw5c295asdyr4x6 enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBQZmdsMEpVanFKdEhaNUlp - Tk5KYllvRWRhQXNBN2VzMGxHSVA2aVhhclZFCkdGWWhLakN4dkVVMnhMYkk2ZmdF - TDhtOGlpWlF3QlBKWVJkejhwUERZczQKLS0tIGMvdWpXV3pJaytsY2kvbXlsY3Rl - Wm56NTVKcmdESG43NGhhWk0xbHZFeU0KUwZW24hBFmC3PR5iP4e5eMt94I4xHgo6 - qB2ceOd4c86h16+XiJvhiYbPQdvuKcHoMSdmt2ZbHstzeuOn1ZdClA== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0VVBQaVluOExJSnBxaWtn + N2RXb0UzaFgvTmpXcENKaTkzb05Vb3hMeHpnCjRVSmhTZ01sVy9ZRnZrK1FTa0Ey + TnJmYlVaNGFLRmZUcnFXYkswQnQ5emMKLS0tIGNuMlBmYnNzNXczYTljbUY3bUtk + TkFMSlZzTjVjMkVUaVcyVXI3cXc0K28KUDg9+qZqrbUk+D8TEG2p5tu6v8HgGfHK + MGkbN/+3wmitC2T1HEdA18ULGrmd1SFN6qGIfYOkR6dkhETIs+w/jQ== -----END AGE ENCRYPTED FILE----- - recipient: age1cmzh82q8k59yzceuuy2epmqu22g7m84gqvq056mhgehwpmvjadfsc3glc8 enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBEZjEvNlMzQXYvZG9QaXdY - SDFqSDUzVE1xNE1sM0l5RXNJZllEL3c2Y3dzCjFMTEp0eFNRTndWWExYS1phSjlu - MGluSVZPOExnd3ErN2VZVTJIb2l4WjQKLS0tIHd2SEwwaXIvRXhEVFd5OC9vQ3ph - azdrTklmaWNYR3RuaitKbHk5WVhsb0UKgpHMKCR0/LzLWUgH3aasF9tETrapgsoB - oNXyxH7ry8jE+WYw4p+oOgSpNVuq9ae2Hot36u3hTjgXuub9kF2IhA== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHSjhleGt2Y0JTU3ZRWnda + US9ScWpPYTk2Qlhjdlh6RS9JR0lzTVc1djMwCm8zZnJ2a1prOW9Vc3h4RjJXNE5G + d0IwYSttaC9iVGZaejBmK1J6SXc0aTgKLS0tIGZvbnJTZ29xcDJGbzkraytKb2tY + OUIxMHJmbTlhei9nSEpQeUtIWjJLSGsKwLTnTxIqkumhWoVbt7eKTU03upmZYvF1 + S3a4mS/FZAU/9PgtHeY7LF4a0wwnHBAOxTwKcj8lYPWQzfPNSBFEBQ== -----END AGE ENCRYPTED FILE----- - recipient: age1wmx8y2hs83j2u5srdnfxljrzxm8jtxl6fr0mq7xf2ldxyglpzf2qq89rpx enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAyNFFWendCOFptUU15a1kz - TTNmSTUySkFrN2ZNUmZzNDVTWG96SDdwNENNCmx0TmdnTnVRYWo5ZHJQK093bG1X - TWk3Sm41RVFUY2FPVUN4TldjaEs1RUEKLS0tIFF3Vm9sY1BIeUxteFgwd0xaVXRt - SkJ6WmlobXF6Y01semFHUnJUK0wzZWcKMwhhIBUZxXRsaemaaJ7zFXJ8CH3Gw6N8 - nNZUTPb7eg7yvHIiST7EqO57qhpHuxqK/OCFBOIPYs/wjdO806ubAA== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjMmF1VkdhUHIzYW5sdlo0 + UnpRUjNieVREc09ONWtqQTdWQU82R2g2S1RzClowWVpMZTEwM1czdW0zWmdiMGd3 + V1djaDBpRFZHU1BZbmRhUDk2RndmaGMKLS0tIG9oQmlKMW5TVE1lQ2ZEMnU1c0pI + dlRVNFR5ZDhJcitoaXdmdFBHenM2NEkKHrWek+5xtVdwVaLF7FuhdJJCdZxvg3ib + JKIN6/IKg6v8wWbts+oJZmH0+ibv3dPPCNll0U0toYGBkbJVUUiMbw== -----END AGE ENCRYPTED FILE----- - recipient: age1mjgw3nxlnqdj04mgjz3wn7fj2nl2nxla4p2r2fn4nkvayfgp09pqllxzyh enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSByUXA3T0Z6eVpGQVZheWFF - N2NOV2NjZFdlTUwySE16NlBuU2pPRlRFV25NCnlxOENwa3lKMlZHcXhhMkd1dHFH - bXAyQXErVlFvSnNvekxkWFpsNW1rc3cKLS0tIDRSS0lVT3BYNXZUcWk5dTNMc2Qw - ZjBKOW5XVTJiMFhpdm5qeUY0VjBqT2MK83ZdreVtLQdV5zPPjRpcDVKPtU2heVME - yhXwR5VmKKpSHnu9uYSzIjyWoLQA7uLD6GcFkUEcUuRifd0tYMJTuA== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBXQ0FYTnBILzF1TENDVFcx + Wi9pZUg1eGNiTCtGdUFrRERBVGxxR05jQWkwClBLK0FBd201ZzlRVC9aQUIwUzlz + Yy9Sc2dpQ2l3Ty9ST3JrdU54VFN2UlEKLS0tIG5ldXY1R1k0dGlsRXhPYlpLS2Yv + dFBoM0RGQXVLdnhxVnJ0V1FpWmFkTUUKCHv8KQYB2QdcTdCB3Wig7YTRKt1ZiqkA + MT5A0z0rizax0YZLGJ7QJlWkT/EmX0EsV62cvjzXeUkE2FKpYffiNQ== -----END AGE ENCRYPTED FILE----- - recipient: age16vzhcvz8tyxj8e0f47fy0z4p3dsg0ak4vl52ut3l07a0tz465cxslmhevl enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBTZWl1dGFGajFZNzhJZkUr - Nm5hc0d2WG9QRDkraWYySVpiT2twakpqa2pVCjhDUk94LzFZalkyaUdIVE5YMDJL - V3NLRHUvaWcwbEpXcFNOVTlhMkg2UVEKLS0tIGxrYk9wVWFFd2V5ZDNycVA5VXUx - Y2wzNGFGMTJXUUpPbGxuMU5nWlFvU28KTmP0byJFneTPHUJ6e+ScSiKypMrz7TNV - FHZ78vmNIzcstLKgXwK23hyEIYTtgrLOP37B768fAM8aHiqGQORs+Q== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1VVlxd3RrRDFnN0Q5SGgx + OGlDV3lyYmRQTjBsZ1kydlI4UFNVMUNZYmdRCmh0dzdEUmFDWmU0VjZHZHhhN2ZX + MW04LzB1ZHc4K1dCbGVBK1NMRDJJbDAKLS0tIDEzclhsWWhIRlUrU1ZjV2Q2dExF + cW42NFdBa1JKZTRpb2Z3aVpNVDJaWFkKynL65X4AjGw7PDrFZw+J34KajCl/TfZ7 + fA1c8fyngnt42FuKVoSHiIrEUCfFEsf37NbV5WQFF61V0bO83EX5qQ== -----END AGE ENCRYPTED FILE----- - recipient: age15cx90pnp54xp5gxlt02yn9j2pz968wp3l5ukdkx55xuecp34e5pszjku4m enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBSakpNTmczZGNWUmlxODlS - eURFTUcxLzZtSU5RT1J0cE56TXdPb3NsRlJnCm1pZVBGNFJrd0w4cFhFMkxzRmYw - Q2thUU1DVlhBc1Z2d1EvVEJvKzVQOG8KLS0tIDk4MUJ1Q1dIM1lkdC9KbjgrNG1n - YjF2TzltV2NsTVNRM3pRZVByS001R0EK6d+Hwl4dQB79n5Baq8kQKUqYaifbp7PH - YUZmR8xwiJP87oFmD2lETgJQyWDvGBk+nM5glAjIqO6PEhJBvviVHw== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1U2d3WXpqbkNQUnhvNmxT + NE1hUDFSZXBTOFlQMFYxT0F2VkdzK1MvRmlnCmkvc21GMTJjNmlXNWFxS0JsQzBL + MUVoWkdMR1hOd09MZUF4bUpubi9CM00KLS0tIDdCVkxXenIwN3F0YW1COEJxMTFn + a3VvbEZUUHNJcEhUMllud2tYQU1RQUkKDG5dUpTAKHdGrnD1U2JWWv2Ue/LShVwt + XRgdjTwmXtvf2s9sIetX2rLiJxcLkpRz8z+AjK5c3GWxF6ZVPuuGvA== -----END AGE ENCRYPTED FILE----- - recipient: age18quey88vge7xytclg2nuq4ncme86dg04lxwczqxczmdchnjg3p0saehsnh enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBRVVYweEtGQSt2Qk9Ic2pJ - QnJlQ21wVzJTZGNrby82dU41bDRkWTFvandnCmtYSUpFRkNSZ2FBNFhpSURQVXFt - ekhBaHRlc2tSS0lXdFdRdXR5SEd2MzQKLS0tIEZTeFhqZjgzZDVwN2NxVVluMVlE - ZkhzSFkrS2NMK1diL2RwbnNzTzNQaWMK7/Q16epHfSDQ4q29DN0WpLKCWh2rnLJC - cKR+gQKXUNzt/7Osh6eWuCVWMfc9p03ruvlxrJ416Am4k66kbAmrQA== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjQThNQVJyR1BVOElNNldl + ZmphdkE4ZVpaaHE2N2tPdGpxMFFaVHUzZkg4CmlsSmtRdnVMSDhTK1p3S05Kamhi + THRVMUE2d1NDc3FIdUpXQXoxa00xRk0KLS0tIEhhUnBIQnJQUnZKU21vTHk5WFVt + cUhkVFRNMHFkdVFMQTdWWUwyVGZxZU0K4Dn0V7ulWdSOnsFSaFTBdfOz6RD0R3Ba + MOM2I0afFAcbQI2rzdySbGzy7yJeuA1puBbrHDMOkVCK2soYm3Gerg== -----END AGE ENCRYPTED FILE----- - recipient: age1ax5hqk6e2ekgfx5u7pl8ayc3vvhrehyvtvf07llaxhs5azpnny0qpltrns enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB5OXhuNk1VcFhpWWJYMHBN - U2pjWXJNbFpUZUdBbWFxOHRZY3N5bmxwdUJvClpTc3ZiTmpyZW8vS2UyNGh6N0l6 - UHBnTk9lSTEyOGFvSnRKUE5rN0lrRDgKLS0tIHRsOXJiR3RwVlRXSzlVRG1PRWxu - Zk5EVlJUekwxYWpuVkgxQ2oxcGI3K0UKmA+Ae0Ja6CfkGL3yhpGsmHVdZLPz2/Sc - TYeUhIwKRJql7Rg4Yu0aG//2gK0HKLnSOq/nJtSIJ+8P87OKTYg8iQ== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjOVpRQVJBVWFnaFR6SnBo + UXdrTk4yMW95QStMOGFqV0paSHp2Ly9Ga3pNCk1PSnFOWFUramM5OUxLU3BibG1C + blJtc3dNTWlBek5POXRMbUZDbG1WelkKLS0tIGZkVEErZ0twTFV6dWhVanUrdlpY + VkJDQ2Vmc3FPTEJKbWcrQkRwYnN2TG8KO245mM0A94pLL4/EHi6/eeh4rak02IuR + n8rBcJ/U4qOD+1QNW+mHbGiNNSHLtGHuVrM5uKaruRJDmxzNWQjmXA== + -----END AGE ENCRYPTED FILE----- + - recipient: age1c2enwel9un28dcs4wg0vcyamx9a4a6g3walkhu8w5lqhmd804paq9d24as + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkUXdnZ1FLenQ3SjNwN3c1 + aTREVTBhNEIrN1NWWUJ3SlRrQmppSnVTc1JJCnFydzZoNEdxUVdDUEQ4UkUyUTYv + aXAvZmxXT1ZkSmpCWUpWeWJZYXFGcmMKLS0tIDdySzROS3dxSk1id2FKbHZ4UERG + ZzZzYjVjVCtMdVpoM0tzY2w4Mm9aSXcKr88HZkBxwuRzDtb/I8D7uopzjglZQsKD + oEd1uz/uYhDvy58MIKz9nnTMKyPUOE+uICZbjZ25ZsdkcDdCWNhyig== -----END AGE ENCRYPTED FILE----- lastmodified: "2025-11-19T14:09:27Z" mac: ENC[AES256_GCM,data:tZ6QzVPivueZiC9Qfb3KNZAv02QatgHRNnlM+Y0iV4BZkYoBjxeDojutizvAMwUarnubUdk5I6m2OZK1mvVDZKXyI6zALX4JMeT2xYQWRHYzHpOygLhhGwTFVhV+0C4jN+eJFF2cNf9lu7NuZI9ylZSOY8I3YKUl+l0l3CkXUl4=,iv:JSGOUq+j9T/NXspn70dfu0J4ISV6vVFZUe/Z1CirrJk=,tag:Hm9N55f9qMc056nSTR1piw==,type:str] pgp: - - created_at: "2025-12-15T21:53:33Z" + - created_at: "2026-01-02T21:17:29Z" enc: |- -----BEGIN PGP MESSAGE----- - hQIMAwDh3VI7VctTAQ//T3Ld00/QPCKpVc4bonVGR1XRF8POkqr1eGc9locjDs+x - MZFYAUApLXsM2A7W6hPa+sgffHTPmyjg85xC3XC+Z9aWCnHAh0tFjU0OdBaMOaK1 - ha3nX3FmmC4QEuUs4elA0/CwfRG/16/bRTV+ys1jESM8AnQt9RhsBtDqJ42wNLFW - zlM96X+7j3DGyQAkgFV4qW4uL2tpPgnTgpvhjotW4Fl0xsBzf8E09IvmDGAV7sbt - JQcSAUxZilKjVlwutz2AnAOl3fLaaBuNIYySr0r6pc4uv9W9g9z3NdxkKLrdAN4w - UJglMIRai7Pv4DA9QdC6khJUswngfrk5GWuRTkqNSMkVOi5pl9y+Tdw9qKuSVFoE - GGmMzdmbFd7XOAYwIawAZqWAqlZ3mJaxAwSwut1Gls0u6BBQh4wjYJW6yknS8qJg - Gb92+Ly4r3/Kt+iOxpL9Uz6JUWtk4DFRCAdiwGdie6tnoB6VI01SVzkw2aDj7deN - kG7AFx1kofX+kUvi6gKK6kN0cZuwCOlyUE7OUSWSKWCmVzqUiDMy2WUdw+x5arqc - zjstPzBWW/hLr5aY8vHpPONoM32KWc6ewKnxeVt8kjCh64IHOJQ4c6ilgghqECUd - DN8AnpCXWeP09LkOu5rNX9miuLAEIGEYXZoP4bfVHKxP6jsx9jQdEBD0G21RaFOF - AgwDC9FRLmchgYQBD/9lPhj+gc929oxlP7cEanqOpcb3lHpS6d6wOF1PXoFn48P/ - JMchtMpNfQZ1kDW5CcdoG31zeTzhjpUlYVuZHhvhBuT+pEshjZOsw7vY0NAdScFE - /45MrKsRKeHEkhKcke6w2BNW+o+202ZIjSZv+1QvAQYt0wGfI9EzYclE9CV9zcrm - 3M5exSXVOYgt6REwEVKb0OaoAAgNrlRpJRj8CjrJ5g1vN21wG+anqhaxYoWFdsXm - LMKY9eyAAiFm01RU7cSHfl1Qy1op3KzpbQyabQ6zIbq7BS/ouo4jGyMyHt3DPT1e - EY7dkNFcZh5sIIZEmKWjFbjIXT/urVWdKJPfkdC/QLsCvm37V5sDs8JB9JFivumA - LcNgFVmoCdmM8K2f98q3R6K38VsNH+AgcJA87RKGnHMw3NVnHthR6x4T6+cknK09 - e0iV6wdazNN5/1Bmu1Y/JrnaGyIAuyoom0Nrv+OTVgjjenftw+J4GsWIpicSYHHc - mN8TcFqqrZ9QjHQS9BMXFNLC+m1HGpP4Mi8J7haEh6cIURFVkmZRDl+SUwnqX+BN - XtHs2q0Ht6jDY1K93StrXTFqNTasG31MUEcs8og/KkUhT4LGRIrAJoR70jPojV8c - TQsT/bLFgYVvKaKPRoI6SbJyhQeoJMnEoDFAZdlB78KBMcRSAHZt16j6f6bke9Jc - AYQEzirwmSjViuIEnu57k96w1UHJB+rjjgP62C88A0ENr97UbCacIC8EemFN0xPP - UDubuNbq7O9XAfoXbKy8jqeciZVo40AF8YN3HZuRnrppgik5zzs4o3yBv5k= - =1hR6 + hQIMAwDh3VI7VctTAQ//cFd10y5cI5pCudkNrwJjjCvaJmP33pteHYJrzH951JFt + FmO5NVPxFkyZIApN2LJ+neqDhfOHWoAqb+2GWeAnKQwqWWD3mAMzTFbZ/GnfNTpJ + tdGh/PtTawnjl5olwv/TSDj4DJSH9B/r5KJG+2oKvDNYHQ06K6qVoZTtlI9wA8nR + TLZlqlHlLNrYBBKNH8hUcdwCbd9wlBZGY/dhprTUlKb+W7i5LszQJuKdmxUj4njq + QVjxMG9AHKn+SU4awAOyEI0uzTdKApem1REaJE/d+quaR7KLCjZIpQ3n4SsbBUfJ + yAsBv57JAlxbaG5xuMZ/kjWQaGiufUK0J9IBfEKAyHZM//iFl+CAPNzPrQDPI4wy + 6HRL2r9EvUwZb6vJYskd8Od57nT4/0+zV8bSzh18JkEc9UKvMn7d4bx7AnKU+rsP + cDeIC5JTJjygaiodIdYAYS81FZZGj+t04hrAzkiPZcM/+3JdHNkuxV4jKbD6ljzr + xdCaJUXSmGrK4uznLFoIBz4A35UHSwSsa8s3RUL1Knem59cGSMVqfz6nJPQ/0sOT + tI+8VI5HCdJ0vlNw669opzgGGiaIKcBKWfrXsxYkww9ekWxbjWaBcFLKm3g7p/mh + KzFRmQwcOXG6nQ2ROUnL18Jh1u+iiXvzc17yK/6xuh3lLi+wvfRXMb3/ePG4j86F + AgwDC9FRLmchgYQBD/918RvwuPqcGLYgUJLJr/ycnAG5EzoSA7qqBvItgoJfzbah + oCnnVRwAyvvvYYyqWrpIxc9X5w8vD5CeLDWEX4slm6AXUbBFK9i+tesmXC6D7du/ + Sb/DHYH1Fa168nZ/oK2YieVQto6VS0WK37WqT6thybyEpYZu898tE9mY9riKlE+B + JHKjGailLl4KeaWbZQgLNP9KKi1wwOC/Ae43SS3XqFWu0zx/l4RHn/KvNNVcYyc5 + UuuBHhFem12f14xVy2bwjELTkQfImlBFmPmbN2sqOIG8maFzdwUVfVMcAR2Ceq9Z + pTOL2q7NCZ/JlwZ25ficJ1Og4Wnk3oTQwZswoN+nApIF7Td1QxmDLnGB8xmrnLpn + 7A1PCo9IR3MdOatqN2xvotV1WotYTz3lcqpHfsYpscEBuyozxVw++1c2+QpZtmBQ + C3MQoqUDXBwvku5BfVG97XFy1QDCCTelUPdNCvIkXoJg9AUugPYRokCcI2bTybFY + unL0sirc8+/mgEQTwZgJXDhtvYTpps0pj5PFwdDHRDgrW/CzSkLz05HIKl+NKDmc + +aIxFLAQiFBiqgJneb5q3GK7uZq0YVIb86MD5sqSNT2Utz1KMzhL56sTjPEtn73E + zfwMX/EpCkpshsfbG1hLZeKPsYZBAo14Jbv/IfWHX0OXp6EVWrLM4FT8sEEwWtJc + AeNU3uXSpOW5xlKv6bpNSl0A9C8XYK28FU8JaENq85Q5VZGLf4JP2auFXQ2PWbpo + 9MmhPhMNk4a5aDh+AqaB7yXCFvqzsGEJu8Xqe3gAN8Jn9ThMvUW+nwm56A0= + =arGb -----END PGP MESSAGE----- fp: 4BE7925262289B476DBBC17B76FD3810215AE097 unencrypted_suffix: _unencrypted diff --git a/secrets/repo/common.yaml b/secrets/repo/common.yaml index c32b307..3f054ac 100644 --- a/secrets/repo/common.yaml +++ b/secrets/repo/common.yaml @@ -41,143 +41,161 @@ sops: - recipient: age1s0vssf9fey2l456hucppzx2x58xep279nsdcglvkqm30sr9ht37s8rvpza enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB1UEUyZDNVQ3U0NklpU1Bs - VndoTnRBMW5ESUlDWi83TUdGdXpMeDdRam1rClh2SU4zRmFscWtSWFMvYnlySXAw - S0hjcVZZN1MwTm1kZ2xUcVhxQ0wwdHMKLS0tIGE5djdQZkl6NFdJMUJPVWh2ZEJW - Y1BMbDgwaDliN2tLdjBYOU5TaG9uaDAK0bHyEDpXoM1hZlhw9uJVOpCG8FnfONyh - cZKAl0ykQ5dZ6pQo8K/4jtOf08GesZIRenyCxIE/lNZBvFg6OgJBJw== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvcTNvUVVSZ2R1N1FKWWw5 + dGJHc2swRXN6SmdvZ09zTGhYa1VqWnp3L0VzClZvYmhYTjdnVytBZFI3c2JqdlhI + M3NiNVFQTXkzZ09ESkUzamRleU02WFEKLS0tIElxaENIb0V6RElZeVZFZWRIRzlH + UE1mM0VEOExFNm9OaUV1NFh4cEc1alUKX6niNF5QxQ9ub+grPkUqeLw+gjBcwV2A + Y7zeR9eYAABCkDh789luQd37LXP2QdD90hiaDGMQChVMpmcIjdP34w== + -----END AGE ENCRYPTED FILE----- + - recipient: age18cgqlely56hgmhscllkmafwpjdk6dwep6ej3vkk97dzemp8jtuksqrrjjl + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBOS3g4dEhVYlBGMXNzcGN6 + ZldoUVRtY3FsNVNpcXI2cTk4T3RzNUQ2cW5NCmFmUXRXb3REaXZTVk5MemhOaUNk + bjYyYThFSm1ZZ2JRcDF4WnFxSndxL0kKLS0tIGJXZEsxRS9LcVRrd01xYzM5QVQx + MW9TYkhMRUdXZVVBeVkvRUFQWElhMW8KIhz1sFGRNkhVyLRZjA3IyYInRbNhN/Qq + 5OWHJj/iS05xunkPNoWfxphRtHRudljgDXpt2UwYgWoTm1cAnoIj5w== -----END AGE ENCRYPTED FILE----- - recipient: age1g7atkxdlt4ymeh7v7aa2yzr2hq2qkvzrc4r49ugttm3n582ymv9qrmpk8d enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBpcXFsVzNBRFlsQUxiREZI - dG1UVEE2c0NzSjk3bEM1UWlXMU5wakFZTFRJCm5VQUs4alk0TlYwNmRaOUNFOHdW - dC9CTVRuODFnMmpxRGY4L2l5azU3MTQKLS0tIE00Rm1BSDh3NnZRZ1BJbzVLL0t3 - TzM0TExsbUVXRUhKMDVMS3B1VzYwZGsKDD5JXe6ySfCrgiVXN8NjI75z2nr6HuhD - cTx0S3m3KD+uyw9bVVdUkRTCFaLwD05080PN46uB532HsZAMLLSFQQ== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBXbkc3aGtMcXRiTm82Nllp + aHRJdFVvcXp6ZEorNmVmUmIySzdRWW8ydWlnCm9oYWZhTStJQmNSL21hVFFyOFpL + aUFmQk0wbzVwTWRtSEtkQ3RFc3hOUFkKLS0tIHo4YzU0aHhFVmZCTGZWclNpakdn + eVVDYUNXUk5BM2M4MFJjVlY5NzJKd0EKt/vaHVeTowmbc8MfQwHqxnbolf0t5Th/ + wBQHe+MUBg9lCr+TqDi7+TaRYa93V0oiBVQ1uDLweV5olpOi2ttc3g== -----END AGE ENCRYPTED FILE----- - recipient: age1ly2endyt0y9xyddj6yuj4nw6fa3ltvzlvew4cr4lzs6dv8dkavpqadmyxx enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJTFYzakJ4cVJoMnhTUDFx - SG82QWFMWW9La2lOcmdPNlpaWjNpNk11M0hZCmp6TXI5Q2NLa1ZieVdlbllhRUtB - cU1EVkZxa053NFJkZUs0bE00MnpsZGsKLS0tIC85UzhzQnNKUlMrdDB1SDJQZDUx - d2h1MEJHcjdxTXEySTlLNFlnSDJoSXcK2kWhF7v9mm3EIQoJ4aQIT8I/INKB3lSf - xEscNVOqeOGnfdrOIYHBWhWIsBNRsgfJMIxFY8TVxmDNGj6r4GqvHA== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBDUVQwYXNiVTE3dENRV2N1 + Z3AwcDk0YkRFZkRjZW9KZHFlYVNaWHljMUNjCnJmN2pzaXV6RFk4NmVFdSthYVcx + TVpTclp3NnlBakNaYUQwMmg3VVM1SDQKLS0tIFF2enEwWXAwYVVTeWxNY0kwRWdC + bVJuaEdsOERBKyszcHRYUFpUcm5zeDgKISCkCqLSCUwSM7XqrY+JMW9OE9q5sfbB + EXVLqqjqBICQ50vtnGywG70qu3WXzpuVy0NK+zA/3sgVpXuU6/Cplw== -----END AGE ENCRYPTED FILE----- - recipient: age15klj4t7gpfp69472mne4ue62pp6m4e04dmjyw7yf30qtqd3vl3uqjmcyxm enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBSVWU3YlNtby9ZVWNWZ0R1 - NWV1VUo2V2hJSmJYVlJmVkloMzVxbVFobVdnCmxJV3ZvUzBrYmFLYW5NNWVWdDl0 - TDkzZmY0a0FHQTVmOUwwajhtaWRqNjQKLS0tIEJFUE9Oc0IwTXpJUFFjUWxXTFBW - NmpVbzRDbzVGOW5iVFBQYU9FdFJtUU0K1NAuCYjrYUZVLajR3h7Le1p7ZDcfGYpr - kctaY2S80mPxN2Dv1Y81S0G84ptmi7NXTCghDrPhkdrnzFd+egeWvA== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArYnQzTm5nMHUrTForWTlq + akhabVhFRlVxQmZFS3RId1RBU3FTYVl0NUJNCmRGUlp3QzR2RFVNU2Q4aGlud1BN + bWlzWnNUQnJlelVsS3dhWTdyTGgxUWsKLS0tIG9sRE00QVozUzJ5eEJtekRQWmVr + YlM2WXdQTFJRMTlZQ2xlTzMzbXFFd0UKZBiT6x4zfpKQctOCUFmBQHzWZ2rFIIKh + Lx2oteWtHrGABf+wc45ypz6CBEJjw7moBf7ZCupOOL+jIJpe3UriqA== -----END AGE ENCRYPTED FILE----- - recipient: age1k73gy5em3js9zklnnkzp5hme9k04lny32fgahmzddknjw5c295asdyr4x6 enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkdHJCREhwRWdqVXVYa1o0 - dEdRNjhxd1VxWWI5b3dQQUVacXdmNUxWSFZrCjFFdEJHNmFGeVhyZVdSeW5BQXNO - L1I5VkdBK1FEZFQyblVtOWJOYzl5U1UKLS0tIE54NENJRUt5bzNvN0NIVVhlNEQ5 - YXFPeDBQZUJsL0IyNFVpVFB4NUdtZ28K4LIamLUU4zn9MY7NphIjfCMSWlwLkF1c - 8zqOyNS5tpjCsdZ47IInoPSxtlKEq/97PV/nUzw1BTH10Rxx3ENCGg== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBFcHZqNFJWNGFOUUNkZTlN + L0IzcU9qR09BcGQ5ZGxGMmcxaWx4SHZDakdvCnZpc0N4QzNzTU1XWWhZYk10QjVN + dHEwMS9xOGRBSlFKQi82eE84aGMwbTgKLS0tIHNjNUFCaFdEM3lmTlhNUDJHQXlM + ZVhBQUNoYlMwQ056V1ZCRUdrdG5ESWsKO8a6+mhNR2jzTHuKROp0QbFFLJD0TN/e + 5zKySP6bHrlcK9fguXWnOqshWuxPdECNmE32L0pm9maL7G2T4FhvzA== -----END AGE ENCRYPTED FILE----- - recipient: age1cmzh82q8k59yzceuuy2epmqu22g7m84gqvq056mhgehwpmvjadfsc3glc8 enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB1djk4SEFBUXF2U1FOQjZ2 - eTEvaWdMcTFzY0ZBTk1XUGE0UGUzNVMyYmlZClUwRU9KeGc0cVZGSTdDaERQSWhL - bHhwa3RtUWZXSWFxYmF5UWtLK0JabDAKLS0tIFR1eHFPVGNtRDJFMnNoaVJnUHlJ - d2k4TjF0a0tkbFRvVGR3ZS9XcndpcXMKpqZXI/APjHSqR+j4/k4UcQfdL7toFGHJ - 9vRdsWWYGae3JABNpFGC8xCpvEtdPyMwEQw0JpjkRz3n0KKSa0XL3Q== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGWDdyVm51cFZ1SjNVQU5L + UUFqbEhkV2puWEJxVmJkb1prOGNnZ1pRZG44CkZpOEZ5dUdjN0NYWXZSK1lLMERG + M3Rwb3FLa0crQmx1YkFZTnZlVGF6Z2MKLS0tIHpmd2RyZC9ZR2lIQUszZlNQbUJu + bVNkMTI3ZXpDY01KamRFb0FmY1B2dWcK1RRtVtIwzgckwxX5YEQWdL+BHUdEAD57 + 9Y/lLGzhGHwRA9lYaN8q+cpMDjhuIDiDqSZV7N16EZcOjZ0MIjZonw== -----END AGE ENCRYPTED FILE----- - recipient: age1wmx8y2hs83j2u5srdnfxljrzxm8jtxl6fr0mq7xf2ldxyglpzf2qq89rpx enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvSVR1SzFxSFphazlaM3Jv - a3V5Um5hMS9SU3FHTXJmQW1ncWh1VTdXNEhVClI5NmowcDdzSWVsTzRWSzN6L2JC - R3A1NElHdmFFU0psejNlWkNidTlWVUUKLS0tIEZWMjM0b25YSXRCS3U5aXdTN05W - eFUyYzZydWdvTjBTV2JnK3dQam5Ya1kKBhrX+2CX9ZJdymOY5yC2thfUY3/N2TB2 - LqUctgXpqxk67bupTN/wkfSkYV+//FdSdVtLiopnXHC5k0QIVizCSQ== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzaXZTNU4vNTArazNxZnBz + ZGNrWXJ6Y0JSaGY1UzhjdzhIdVVsNjlQUERZCm5RV0Ftams3US9WVzA0eHdOTkpX + RVYwS3MvUXZNOFFUMG9VKzh3aWk5YjQKLS0tIHZhb2ZSYndLcGJIK0xURTRBR2NQ + OVN2ZURGSWxCNkViK3NFSVRqdUo3aWMK4tjIx7paJg7bTgTwjnXjbpHE+Vvf3YbF + C9Ekp1Kw0k+THfTgnjwyNWuaLv+8VoLqt2k10H+oALjLHxQKpPSmCw== -----END AGE ENCRYPTED FILE----- - recipient: age1mjgw3nxlnqdj04mgjz3wn7fj2nl2nxla4p2r2fn4nkvayfgp09pqllxzyh enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA2bW5hblpKNGlYRkwvVXpz - ZDlNdkNjS1ZPSGZUbXdnZHgrTE1XaWw4Z0FnCkNEZ1JFRDA5eVJEWTVGdVdwdXNI - Z1JXUnhXOXdTV0RoZnNYNnAxREZvV1kKLS0tIDB0dlM5ZVNzRlVzRDU5QjZDcllU - VFVjc3pZdHphT1dwYnV6NTFtTmViSTgK7a6/HWAV8Bxf3d062LFXB1QyTqvfLFPB - ZrTV01HMKzleFY3Fv9HLjJNGt/l54ZVdq+Ea3sp9UUfw+BKMpoqrrA== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB1ZmV3WUc3SGR3OU8wb1RX + aGVLUnJWdU5BTE1uSy9YSUhVUHIrMEhDVVVrCmhNYW9yd0I4RnBaSlJRRzR0R2NS + R2JkSENzQ2N4ZW5Xdmh2cW9iZHVvNW8KLS0tIDA4cTI2YitUMFlVL3ZlVFJlT1du + eXh3ZkJyRk1oOUlmM1dwdGUraXZsU1EKCVvFlj0X0Q79116nQ+8Ybsjk1HBE55vh + /j5g7ggeLdCcoU7qEevHvBukbGa84xmiwJxa13mumI7Yi4383P8Twg== -----END AGE ENCRYPTED FILE----- - recipient: age16vzhcvz8tyxj8e0f47fy0z4p3dsg0ak4vl52ut3l07a0tz465cxslmhevl enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0M2dYWVBZWVFNQ3JnWXVn - YVNnYWFzdGxXeVhLQ0tBdzdaYzhaN0p6cDIwClRXNUNtYTRiNXFCZnZ6WGQ5WUtJ - VzBRV211YzZ2VzNkTTJtblNFZ1hiUVUKLS0tIFVhVEpCRHZOTzVsUmlxT1VXZWZx - bi9pWnhsY0V4TjIzN1hqeVhkb3FsNTAKcngM/MK1uLuw2i9AOxPnscD+OLopKIuv - B9U7mSE5zRyYFgXK8Y+s8r3CBq9NUFlKFv8Rl9wmD7EMa1DfZ5GF7g== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBMZkptNVRuYWZMWmZkdXMz + VDR2OEpOM2E2S2tZOTNPaXQ3c0taYVBZVm5rCnNqWnNHNEt2SlphdmhZZ2hGS0JE + RXlDQ281cEtjblNZeVBvWkVUaW5lc00KLS0tIGRLKytBRWNvUmlJZ2RzZHZWdFRy + K0VWbzdiOTRvbllSb1dIemVLNnZiWjQK9StbdLSPG2h98Dao5ZG3qvhvD6iSg3XK + MhgksJ/YfuWo0aaLMZ++1jjGD3O5DE3QbGrBA5R2RcInvUi7jHWBtQ== -----END AGE ENCRYPTED FILE----- - recipient: age15cx90pnp54xp5gxlt02yn9j2pz968wp3l5ukdkx55xuecp34e5pszjku4m enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1Q1lSdld6Q2kxMjJ1WTlB - M1I5MTRpMkNiRjJJOUVKRnlJcnZsd0UwbzBjCmlsMlFvMnp6UitRcjR3NCtDZ2Jz - TjgxSmwwRHFMQjlrTUFTSVBpNU93cXcKLS0tIGxxMXU4aURVbjFpSGlLS2JYNVdF - MnhxTURKWWdzWCtaMS9lTjM3YUdNSDQKhNFCq2mRvWMAsCUXrpiW69ti5LfBilp+ - 8B+mk86cFxhpaZtQLOthSBhhzTTdScCKDTywEVV6K51x1DGHqrYfbA== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAyMjZLa3J2dUlDNnJmcUtq + VndpSWxGREhCY21xRlpNb0F6dVhvZXV5YkNNCmJaVFFRdGY4NEdUcTZ4ckhmZldM + TmU5TDRwTG5QMGVHN3BwOWZuTFpQWlUKLS0tIEg5TjVjOUpaa1dabTlzM3REMmtz + ZUVWRGFsbDNWRTBaVUpnZlZLYW1GT2sKInrMXDj0MLNb4r2hwZEsw9HxpVE/eThv + TYyiPbaNkzr0EqDcOQQ/3MEw0Q2XPaMDdCG18u7kYlGGwdYNcWRgmw== -----END AGE ENCRYPTED FILE----- - recipient: age18quey88vge7xytclg2nuq4ncme86dg04lxwczqxczmdchnjg3p0saehsnh enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBtR2NWOW45T0VSeWdPSHBM - OHJWRUUyaW5qbWx4ZytGOHJuQk5rRjlIRjBVClFJSm1kSWdGZ2xwMFE2TVRkSG9W - Z0FRS281UU5oSDkzMHkxOXhIMnpzcUkKLS0tIDNNTUhMR2Z1UVcrOFhRWWRYdUFF - T2d4dnJxTnBueW1LWHl3VnJMTm5abEEKUpUtH/deey97QuKWMFxabVSrpzzfaoSw - pR7nianRBaD8b8HsC8V7x98CenFBK0jVQAkx9VGMgqIGDsTaHiHPhQ== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB0UVBvdXMzRVRuc0N4NktF + MzdnVExNWDNGanc3cGc0NkNoajJROFR2MUM0ClIvQnk1RmJCWXJZcjk4aHRYUTkv + cUN4NHJaSWxHckw0NTV4WWxKWHZHelEKLS0tIEs1em8xamQ4YnVpTzRZMDg0UkIz + b2N2RDRYemVDSzZESVhUOVR6MUEydG8KVLj9kVMaZeKDIQiVQF9lu80wA0m8CuMD + IBCwg5sHoz7fAK8vHpUPKh2XooD2BL9q0pPZCc3sCUjQvLgW1ZK1YQ== -----END AGE ENCRYPTED FILE----- - recipient: age1ax5hqk6e2ekgfx5u7pl8ayc3vvhrehyvtvf07llaxhs5azpnny0qpltrns enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA2Zk9wN2hRTzJYVnZ6RUwx - WFVtdzF2NUM1QUxFRjdYNHVOWFErK0dOM0NZClZpc0JQZzBNc0lFd2IyZDAremZQ - aWxmK1ZtOEVZRFF5ajZ2QkFmK1J1bWcKLS0tIElpNHh4eDZZaFNWczl4MG9ZdjJ5 - L0gzWDFia2Jha0lDaGZaSWEwOWFmb1EKJqqjxODIgVeiMKtV6361sjYQa559pKCG - 1pKczlzXxL1FliBQoZZGq55NR4azWYEl/yV5tee1dtUohJW0pAyScA== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2YWtZNlJBMUVna2tmODdo + TC82U0ZtMFV5L3Q4NktxZ1N5cklyR0ZUZ1R3ClNzSEhEVmg5WEpROTZIQzQ1dm92 + UGpxcHNzUytVYUM5dFJDcVVzVzNuWU0KLS0tIGtYaVNBTHNYNVZQTnZUN3BZSEh5 + eFQvUWx2Y3JPa2VCem55TkR3M3U2M00K5TSdJyIS1yCV8GIYZgWlSC/y2r5+hquL + RLougeJFaSXiZ9AOzsnC9jGzzBVvlsf5RNH+fYsK9oHuTH3Kv/s8Jg== + -----END AGE ENCRYPTED FILE----- + - recipient: age1c2enwel9un28dcs4wg0vcyamx9a4a6g3walkhu8w5lqhmd804paq9d24as + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwWWRpTkFsWVZGbkY4dmli + VzRQaWt0UFM0L1BkV3dTSjMxbm4xMFhCT3hVCjNXbllqNDl4cXhZaDJVd1gxQ2No + d2tLL2VSY3N1T3E2WldHdDlwNXBycE0KLS0tIGd0YzUrZzF5cjlxVDBwWCtiZHNp + RVFnZWs5T1pxN3VHRWdnbjR4UFFIcGsKUnEeBhX9K2tUNd48XOvpb0n2OoeUPyq2 + N+LBxwPYoSxoiZAW0vDg4mNcJwALYDXA5ew0fSZj1nP2LvaROV84Jw== -----END AGE ENCRYPTED FILE----- lastmodified: "2025-12-23T01:11:36Z" mac: ENC[AES256_GCM,data:e0WoFBQSR5q3GOQ+GMJGBd4lNBAMqlnVjtUq3snxrdvcytb9YvKnoYQH+GjbdGIiqrND8pOVnZt34AjkR8YfpWe+VrkP3Vj/3l+1GjF1XIHbzBNKOQHdYPSVsH2NZwftcAdphbStf3GTlb+b+cpTn4a9Y4pTNGVoOaOA1tBr8bM=,iv:sPXktitTNMkBhHr6E/QRZCVKrgyED9/o9hiivbObACI=,tag:tTNr4UEf92UrtI0Jvi5o3g==,type:str] pgp: - - created_at: "2025-12-15T21:53:36Z" + - created_at: "2026-01-02T21:17:35Z" enc: |- -----BEGIN PGP MESSAGE----- - hQIMAwDh3VI7VctTAQ/+J73641lle7VHi7015hM2tzDaDhtLfoPl7R7jz2dOSmFx - FXfzu0kHK6Mg44WC5pcWvIHWJA9D0bKlMO5pxPO9vrG6tNU6C24WuibaLYbYWQDV - 7Gxbgi1SPEhLZY4BOU4nObRqR7GfDyHMVASKzzO5Rp15t4vxKko5VyPYPkX96XrG - OHcxeHe6BYBKCE6beee2KPtVfWtWxuKmK57inzeSnCGNVAXI/6RX7GMcCa1Y865C - QWvSmO8h6rEvCtj6heNdj39p1Iz1MckqAepsaTw6hpU9yiLkkiGzimZaeOJjU2V7 - hOfqmawpPWwGic7pcKE1kcbv9e7ogtcIOd1up1fWNn9jZ3wErwUiFYfT2oKfqlFB - rkO5IrDy6zvfF8diKmHM7/NiXwUa6h32uHxnLv0AcK8nOKaCW4ZcWOwmbu7gcZIb - 3Kl9teuaJ5FEV/7giz3lf8Fd5ljyGOUE3t1TWjJxzI0ovBjFMy8l5DEWhoNejfqI - cB2AbQqXuuOvdMTC+qaftpE+XXiIHniwQ3IzkUzhUE5p//xF2pk8SZkQ2iDn7Bb0 - 10p8TD8aLM7AuHd0fpkZPxpAyHCrN++i4wjsv8qdq8obmZ/BzsY4eUJnvcLlfaER - zMUuoT2JhbSLaFRgxgGrm/ez022X4pFmF5ONbbTrAEMxwcmvp9xGwy/krV43qX2F - AgwDC9FRLmchgYQBD/9PgG+2n2k5sUCLh266s4cHKbpVZdOnJ2uSiImyCtW95H2T - Uy0EpafcYyxEs/Hzz73UKaGDz9TwP2cAEVjQ4YumvrKq38SthyFXUAsiI8skXkic - LSEgEWzD0hmvW167mg1PptQyiPsgfAOBw6X6QdOFWJ3nadf8+2AV55JvHuQ9SxC4 - ejOsrVCQL7rMhNTXP2TSfm+ZFM9U6Qbv/xSXKPRBiqEsN/DZXD8BXTUY7KBpazOI - Ttjapr7DcI1ptMK/AIG1eJQAOpgRSsy6z+INpGsvbUIaAJDn8XFEt7kSMXGmQxDJ - oGtwHH1oDXK+ws/F3bn1FPZZlBVbq7fpSoEBBqnAd2jrGibEWEZ2zRBDSROalqCS - fA72h/1ZRUfx8iFBRd6Mu2PJAQ/oecZV+dcIlTD7B3v30HyAZTtVC2Mtdmxqi03U - TFTfhaeCNlficx1R1VtgAABlAMBeGerb6AuBVf9vziRkXiWcP/9RdYuTEJskHjZb - VXCQbEKck0YQHOds6QTu/+QlLZUoZF1LXVKF1iy+khc16oKYhRBUL58eJTOpYbtE - PUrayC6PXE0tKg1Ghe/yVsW9rLWmu7FMdAvM44/PjyI66zAwzKEu6STVahIrpxje - qKd1jugtsmR5kNK5CsORvval1iA6/UhU9fGdV9Zz1t6eyrMNatWk9yOO6uIq3NJe - AVqzW1bH3U8Ys+C7M/FHBskdSPnwgvANKb5hswadBkkRGep01HIJK5QdUj0W9CQ8 - obHQ1j0Rlzs70r6cxpruM9ddmGqP0/yRGncXHowrnf0ZnTzZr+Dhs+whA0V3Zg== - =jCXG + hQIMAwDh3VI7VctTARAAzjG3sJ25PaNOD6VowSNYaaK4DCY6PU2LGCCpJWzDXnwo + irJZUtGOiqeZeu/8BdWmQSwBiI9k/JuCt131rwXCrtj0+Yj0MRKTK/xc8NoDnW5t + R0CpQnWJ8yc1RRAMhx9l26HXH44786rhVt5/4ZCvq2mje6Ha0yK0vmjO6P+WR8kA + AUCuVUtQCtN5UP0yecdRHl6MvZe1qPd8tv3kLittJ2c48LD02CL3g0ZeATtCgiGN + 5h9cBfDQnFcBh5EuMxn3AoGQjIJollCS5fRNNi1VUTp4uyAqYnyRjnORNI6vRk2k + hju5W5ThidbYQlJlwO6hZM7e/sW/XM8RWi6x4TO1VN8MuXavKnIbzlOKmJZlLbb/ + yp0ueIzKuJpEz0nQZh0DFCD/D8yoTAfgtXgzzZKuUgErhkCQEXe/xKPDFvSKkWqA + AnVl6RwyTl5110Ea3535K3vycYR0EAxapEqzEZ0Ez/o28wR/1hgMrTWE9f++6HwT + wZ7EiPTs45yzZYJ0ONE4s9u9HNIvUY4UX3ncIhvTIWMyASfS2dbDu5fqxg5e+Szk + 9QcDL0bzLnGCmR4w6fKUl2mkupTMsi6sfRFMzGlabK6FizUnpwNQHd2LAxL7rNF6 + JiD6LxVQfY/TfKqQdKa93Ctf2k/tkdW2aw8JKZh02V9PqXUax0N4uR/TxOjKa/GF + AgwDC9FRLmchgYQBEADJTfHgtKmwdG4s+Mbml+VD3f0tDJnbOXBfISFp4+s/6Wf+ + kILyeL7V4yllzJWqUZ97IH4BScUBSbvcg45IvuzgfTrBTx6ZSa3OQYo+2KS76Wsv + uQkItpGdsVSrLYDseH4cOWev9D5zscU+pEdT6VKXP47UHbJPdxQ2IU0oncrMatgm + rBh+PfB+XeJRX9mKzsL9qVWuc5k9SVcsXLKSCM2IJszfRy/vsSJcAphReHxXHSuO + XYGVkSYLGgzA1by0wjZ4Gi3y8MVaUEm8rXYVnbaVRnx5bntEfj1pTjmqStH0EE07 + vkOooLLDFaP6o4c58jvRTwXBWKlBbgWqoSBgm08OPKQpFFpM+14GrLqGv5ECti6W + 299pjfyWU99ILNG6RdQiS5MLA01z4wWYXRlxyNVXOHlspVaacX8cE3/uGOslURwH + ZbDgmpyAr3Ss5nj7s8plnxpYIRdXNCgPJ8qZtEVSuQkHbkHMKUJ8gycQNRcB8wZI + H4EtOkeG6mLt7WJlWa4T2VF1AEfQXZoewkaWCK7JekY+DPyyK+b3JT6qArAMvGMl + apVaDegSptYYUteO67yHwP83GNAGwDY+o1/ECZhG4ucGkXYpS6MCBkyCeV++gy3B + qotF5Hr5xSOK8jzFm7k4UUiBiQmv8IDh6UUOOXAzejxn61ZYPdA+aEIBiSoueNJe + AffTd0ldG9HGm016X1uYrN63xNCRA6kVUWP+2c86CTGX2GKHb4YgtULBrVRW8hUR + KoofPhcaEVIUvrnMrPDIrp25Uh4DRakUMQUwsOjfliLmm03/07IJziTMWTZkYQ== + =qR6M -----END PGP MESSAGE----- fp: 4BE7925262289B476DBBC17B76FD3810215AE097 unencrypted_suffix: _unencrypted diff --git a/secrets/repo/globals.nix.enc b/secrets/repo/globals.nix.enc index bcead07..c95426f 100644 --- a/secrets/repo/globals.nix.enc +++ b/secrets/repo/globals.nix.enc @@ -1,62 +1,70 @@ { - "data": "ENC[AES256_GCM,data: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,iv:Fep8PNrxh18J4n8gYLbmJ2+Jn7kD/4KCLs11GDDfshM=,tag:UwkE59mcIslEcrqNQ0qCTw==,type:str]", + "data": "ENC[AES256_GCM,data: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,iv:O0b7UxLyCOGRu8N3dXjDtHuD/O6vHO3FWnsFT6LyjFU=,tag:qysiXpaiHGvhtjYwS8gjQA==,type:str]", "sops": { "age": [ { "recipient": "age1s0vssf9fey2l456hucppzx2x58xep279nsdcglvkqm30sr9ht37s8rvpza", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBBaUxQZ2ExRmVJSWFnZFEv\nQ1A5VXNXZjZ6SHlLcnJtMWtSbTJyWm1CdFJFCnRBS3I5YUZkL2NyT2ZjL2M1ZmxT\nbWpoRHVCbVRwS2NqcHdOdkRQNkhzcXMKLS0tIFlzbERHVGJLL1hYczRBbllZaDc1\nVTF3dzM2TjlsY3pmV3ZDS3lGM2tsbE0KYOKd7D1jm5fgrdC/GFlZtxhnfsN5m0AB\n5gOWisPQYezayKs12Ver0t2reLV0vr63BY+mdi1qmTa4lMCzC1cqLw==\n-----END AGE ENCRYPTED FILE-----\n" + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1YWl2aUhsVUZXVGZHUnhm\nK1hSUVl3ZW1MeGZGWDR3cGNrS2E1WnBCY3drCkNKcWcrZHk1UVZuWkdDRGoxRnhP\nVE8vYzRSSUpOSmRsYUZRaXFDbWVXT28KLS0tIFVLSFRPd3hKTEdtMmkwRFBWdVZ5\nd0hyYXVmYmFubjdHN255b0d6c0F1RlUKHlNhWypoeqRe9wuF5jGh2omhGTvnTtt6\nWRWnEw1i3wa1HFJn8S81oppvGrjmnGYPgaXcHjb4OHxt+9RKQYro6A==\n-----END AGE ENCRYPTED FILE-----\n" + }, + { + "recipient": "age18cgqlely56hgmhscllkmafwpjdk6dwep6ej3vkk97dzemp8jtuksqrrjjl", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA2WE9XZDZJV2tMeGhBanNH\nL2hLUVIwVk54OGFJSE1DMW5BMGk4aGYvTVZJCldKb000N1dFQVVxSFVoblpHSm5C\nM0dpUTREdlAvbkxaS2pLUXdtNjdYOEkKLS0tIHR6djdTMld6dTJSS3ZiRmpVZWtI\nWU1rT3hHWG5rdHU3eXJRTzlWcnV3RnMKI9jX76xcdENMjh4Be4gIzc0qedHUQtk+\nB7ORW+d2Wf9PdlTt3LZazjzw3xu8A+02IBsqwzJJjBIedDnRd/oUqA==\n-----END AGE ENCRYPTED FILE-----\n" }, { "recipient": "age1g7atkxdlt4ymeh7v7aa2yzr2hq2qkvzrc4r49ugttm3n582ymv9qrmpk8d", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2WEMzWTMxSTdsUUVNYmNH\nRUkyQVpRWnQ3N3ZFN2dleVJNRmp3VENWaHpFClAxSGtqRi9GUUdwMWgrbzJLVkxW\nM3loNU1rNTFiUm1tMkpWQVV2b0RZZG8KLS0tIHNuRGtZMEdqaTh6VDMyZU5udm1G\nQUphdzNjU3NQTXBvQVpVeWczcFNtczgKjDN9tdNkTD1PES0CdB3aclsuQr58EGZC\n2dOpw8XOOZ6pCoPeaoLdZKv/prtSCdfwW4NKcKKWc9NRd79qg63WGw==\n-----END AGE ENCRYPTED FILE-----\n" + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBESU5DcG1XV3hwb3VmZHdk\nblg3cHYyVVJBWGcrb1lJVEdnNGFzWWtPWUY0CklOZHU2ZWNoYm9ESHhJakl3THQw\nMk1XS0N5K1d1MGdwaEZ2Y1gvZDJqNEUKLS0tIHFvUXVNWHNPZGpTUVBnWWhTampG\nc0tSTC8zUVBuQjN0VjlNRkswZVFJaGsKSZ99ix3CP3IQ0m0GYVML7fa3R0gD5N7z\na1xwlW7e1bSEwMAUyPy5+E0alvVdC1nP95LtDyyPMkSXuiNJLbV07A==\n-----END AGE ENCRYPTED FILE-----\n" }, { "recipient": "age1ly2endyt0y9xyddj6yuj4nw6fa3ltvzlvew4cr4lzs6dv8dkavpqadmyxx", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBFcGg1L0w2czJCVit4bE9I\nRWs1UXBKREYwaGVNS2gxYUZTUnc4M1lXSVZZCjg0OGw2dk5BUjl0cFBBWjJwUkRU\nMmxYZWtqRWZKQnhVWHArTTJaVlpLV2MKLS0tIDZpWFRYSWF3TThSSEY4MEM0ZHg2\nNVVpSEpSS3d3YlloVHZYTHhhQkwva28KHcZko+q7mRJk+eDTSVyk3wYG8VLfnUJs\nH8uOoFaIwH5sf3K+1/LvUEGp7rEgbqHCTgRXrylNB2jQKq5bYkYlsw==\n-----END AGE ENCRYPTED FILE-----\n" + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBBVlA4WUszaWQ3OUc5RXBM\nU0VQWVVKeThtcU9SVTcvdi9VVGtGRDdqWTNzCkRZTXd1a045ZXZWUnFUaXUyNGFM\nU3lodFRtQjVudlFHVS9WMXFVbGo2bk0KLS0tIGVKZVZEQ0x1ZnI4cHhOK2k0elYz\ncFkwMWRHejQ1eURORnpaekNReUg5U1kKKghYvqmOysmJ12ZXjeUomoqc0O334Hcv\nnulvcj1NSwhVLKUZ4JalNrSNo/4xFcdyi7hn2d9DlkkU9KJ9OcWBOQ==\n-----END AGE ENCRYPTED FILE-----\n" }, { "recipient": "age15klj4t7gpfp69472mne4ue62pp6m4e04dmjyw7yf30qtqd3vl3uqjmcyxm", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBFOHRxVzI5RllzUzI4MnNC\nN1IrOFRGdTQ1VExBYUozNDNXUTNpa3dPUTJzCjFvaTRON3hJd09zOVprZjExLzMr\nYTNCWklKR1Q2UVUvNndNc1RyS0ZPazAKLS0tIFVuZ0dYOWxaMjFYajAwRSs2UzBv\nUDJGcHUxSmZCem1Sc3BmNFV5WXVWTUUKkiqZwgj6iCmAGW9VGRVU+wuRuauDaUjF\nVorMFhPjU/Ukb5PjaGzTH2LxsfW3Z3vH1S3iQM3xSRgRagW2ut3VHA==\n-----END AGE ENCRYPTED FILE-----\n" + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBaRmc4YlduNzlaK3VXQVFt\nRXdHckU2TlRLUHltSTRxV0Y0UnBJTkFwdm1BCktBWWIyMnZYZnFvWEFRaFd2d1Vs\nc2VVbkY3WlZhRXN4c1FJTnl5dFBTR2MKLS0tIGFKbmdPL200Y01mMzZObEFqZElq\nTkpVb3AvVWswdkNEUWR2V3dXMHpMZDAKFUAb6ZxAycB4PbjUgePEWmUpbWRomU+A\ntpFpU30ZeWSicsRQRbzm340ZmtAhVk0Ud1M2N3J0TcIpre3CghZ9lQ==\n-----END AGE ENCRYPTED FILE-----\n" }, { "recipient": "age1k73gy5em3js9zklnnkzp5hme9k04lny32fgahmzddknjw5c295asdyr4x6", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBtdUlnZDlvaHVkN2ZDYVBq\nVWVSNXhHNVV5OGNUUm9RV0VsQ1pHNld5VUhZCkloaVBWc09jTnhLc0pqbzdmRW8z\nbnM4QXRnSXBoNDdvVGs2cVFYcWZ6cU0KLS0tIFZLUlIxVjBKdVBJaDBGMGVJT1ND\nVktFRHQzNnlZSE1BRW1wVFkzWlZ4NTQKo2CcfWKQ7wdiqfmaSokCCpf4CZnaeTBX\n/jb0saoV6Hp0n92mi/60eZIX95wWwGe4NAAisFFD+UQGWSCukm18Mw==\n-----END AGE ENCRYPTED FILE-----\n" + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA5ZU1vVUMyOHBXTm5iNUZz\ndWI2TVZpQnhkdVc4aUlIWmpwbUtwRXlDTkFJCmY3bVZ2ZmlWVEJjVy9oeEN1b01k\nR3pTUUlFTG54c0dyWUlQQzN5TlJFL0kKLS0tIDZGTGlHL1FoWC96dUdJS1I0UkpX\nVWowM1dmVnhYcEJXaEQxTitPQmsrbkUK7R+HqB6ohYcS1rQDfIJ5lHHcqeYcpRTT\nT23fTqjGlDmVUzDUdUISZeaVtTsaAwtq14YWdi1UEzwbGBdUQGmb/g==\n-----END AGE ENCRYPTED FILE-----\n" }, { "recipient": "age1cmzh82q8k59yzceuuy2epmqu22g7m84gqvq056mhgehwpmvjadfsc3glc8", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBja0dhd1VzMVBBcFBTd3dw\nYlN4QUlvREhtcnpLTjFQVGNQV244T2JnVlNzCnlwZXI2cWZhVUZpTzVveERaZ2R2\nR0Y3RVNrUnNMNDM4WlB0YWF2bWxzQUkKLS0tIGFEemp6emloejlneE42UEY4Qitm\nK3ZBdFlVM0dIM2wxWk5JZCtqTUhWVG8K/q+YNSZT+HsqYTdxyB2FrrVCpSywDhkf\n796xAJjeXRX4E2ETN2QvHQ6EGG2tZFkQzVcWGX1ozugi8ZZe1mJP1Q==\n-----END AGE ENCRYPTED FILE-----\n" + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAxL2pFNG1JTzNQUzN3bDJI\nTi8wU0grcWhQcmp3SzZUYzdqZmc0TGsrNDNnCktwZTBYZEg1dnZsYXhPU3NtL05I\nWHZtQ1Q4YUdYay91RzJaQXVRWW51bTAKLS0tIHNDZ0UxWkRVQlE0L3JjaTZmZzJP\nQStKaGFISEJEVTdQSVdIOVMzNjdhWVUKbMH8jkFLlkvbl1e9mndT9civieCuH/z2\nweqBunzI9ANMqQvBH0HxWymUz6kc4Rg9HpChcLx5be2M1URl1VUTiw==\n-----END AGE ENCRYPTED FILE-----\n" }, { "recipient": "age1wmx8y2hs83j2u5srdnfxljrzxm8jtxl6fr0mq7xf2ldxyglpzf2qq89rpx", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBUMzdIVHRFbUFrLzlISVQw\nbG02Ukp5R1dlNVJaZmFpZmRSNVVpd1pDb0Q0CnVpL2JrMHRyOGF2bmVqRjNCc3cw\ncXZyNmxsZ2tFOHBjdGcvYnplcDVKKzgKLS0tIGoyNHI1bVdOcTR2MXhWZGhSTzI0\nSE1SdGVFRDVac3pLSE1TQ09BaE94aHMKMN9YNj4wbAdlR4VGoCPYVrEtQeFcFZh+\nWfhl5ra2R1yB+JNkxgf0jDKJUlEjlW/DKl/FOJ57g7lpNhPfUrMGkg==\n-----END AGE ENCRYPTED FILE-----\n" + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJd21EYyszUU4xdnkxZVRT\neU5FMXVQcEFtM1VWcVBHK2lVOUdkaHk3aG5zCldkWWx5SHVzVWtKRE1LUUR6ZDRF\nc3Z6K0QyYWRiekh0R1pvSTR5UGpLR3cKLS0tIHhGWFppR3cwTEVTUUUwc2dsWUZz\nRVdETzJ2V25LUTU2a3AzWFFZUC9GTEUKrF9QLJEVxxMG0iJULun2QKmd1ATQUGlu\n8nnEWHOpuI+tZS4VM7bjs43fFsN/D1dwA4LFlVfYBNfH0EpVs/CjQw==\n-----END AGE ENCRYPTED FILE-----\n" }, { "recipient": "age1mjgw3nxlnqdj04mgjz3wn7fj2nl2nxla4p2r2fn4nkvayfgp09pqllxzyh", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZM1A1VVBKTE5IOXlqRk5T\nQ3RJOXkzdFpkUmIyRXdRL1ZsSjhiSGxldWpNClNJRC9sbGJ6SVlFVUdWcXI0WVhV\ncWJTckxteGhkRFFqZDNSL2lIMXJONXcKLS0tIHBkajc3dEFQZUNEQ0ErTXlTZUFr\nRUJ4SGNuTWJIQWNNMFlpc055Z1NxVk0KPt1CoTXsvKs+XytsIgkjqxM2fQLJmC10\nTS5PQ3NgZhy5rT7ejnQUQeb2KDS/uN6dCNrWHK6R/OLR3wKzzASuGw==\n-----END AGE ENCRYPTED FILE-----\n" + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA5anZvRXRFRDRoTFFlL2Rz\nYU1UMWlmZVgrMGpqNC9oK1VhQ3lDbUNRVW1rClJhNVp4NlczWEdWWUdDa3dqSUhn\nLzQ5TTJyZjFrOUtXTTdUSytBb1pCcVkKLS0tIHp5NCs3cCtlNnlFUHN0ZkdNaWVs\nM1hpVlI3ZTVFSUFsdFBVQnM1MlJOUzQKLjYXNFjBkCo/+Sy+/3gzWPXRhAxtvae7\nJGGNUk9AoTPM/VSiKEJL3F+X+z3QvoiMjq/+uLU2hKMlYOH570X1Rw==\n-----END AGE ENCRYPTED FILE-----\n" }, { "recipient": "age16vzhcvz8tyxj8e0f47fy0z4p3dsg0ak4vl52ut3l07a0tz465cxslmhevl", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA3WnhRTjU4R1R1MFJPRTNi\nN09LM3dMdlJlQkN2WnBmQWJPQVg4OHl1M0dRCnhhZVkrNTF3NUVvYUFGN2tmSHRT\neC9yc2dOUzFkTEV2NkJubElhNm55MlUKLS0tIHJZRnBxRWdBbWVUOUdJVWdwY00y\nOCtqMk4ySjlTZ2UyeTZMRHJwcktPemMKdOZleZmklSOQTo4fnCzjm4fC/KReWTzn\nJaS6FzbAWc/SwNIs+c/UHfCAWTQkX5xma9uzLOdr0/3cuu00+k73dw==\n-----END AGE ENCRYPTED FILE-----\n" + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvcXVLanhqYVMxaTJ1NGx2\nNEFvdHRFWmxiS2dMUCs2S2N1OVV6N2IzRHhrCnB4TWZUQ1BqWHVTMVBFeDlSL0tU\nYmd4L29UV3ZVUGk3Snp3V3ZpYmlSdlEKLS0tIFVnYnY1WWJ2dHlWT2o3ZVV1aDlB\neXN5THRUNi9QR2pVUC9IclFBa2gwV1kKghCqpelZh84rvtwH7BjPWma4h+0axaUR\nntG9Bpoya3JzVFgyuae/ljmXf4WQvB+OBhh88GiHwXqX4SpH6udvrA==\n-----END AGE ENCRYPTED FILE-----\n" }, { "recipient": "age15cx90pnp54xp5gxlt02yn9j2pz968wp3l5ukdkx55xuecp34e5pszjku4m", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBEWWNOdjJPNzQvRFdRc2xL\nT1FHM2tpV0tDSUFJdVJyTXk3cXZKWm5Cd1NFCi94dEVOT1NyRDhlSHZGRVpmMkZS\naWJLdzJMaGkzTnFicG8yRmJCdzltR3cKLS0tIEg2ZGU0SWFTQkR6aUdzc01VOEtS\nckhkM09FWnBqdkNVZng0ekVvWHdFNW8K+dutcLgF/Oq35B33/vxAiePxCLlcvC6H\nH/doZRDyor+93hWpC0nXJxJiPu3Pc0oYznDK/gRWYTnynBu2GP0adw==\n-----END AGE ENCRYPTED FILE-----\n" + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB3NW1lRFFYZjFOUTFkQTF3\nTWpqdGx1Y0lncjFxemNJNmpqN240VjJtWFN3CkhGdnNOVGF4aGdlL3k0RThOaWN3\nU1gvdUQxY2k2YXg3VTFwVTlFdWRSYUEKLS0tIHQyOVJOcjZselNtM2ZRWjZpQlJm\nZ3ozRjdmeWtHNWpESXpCZ3VtK1Nrc28KM0Hjz2wjsefnqAXqcqhE/kCJVXR5r4CD\n4VnG1bl4MV5dcaWooG9RBTgU1eSnoa1CQeH1SBS18ueA6/kRF+RuYg==\n-----END AGE ENCRYPTED FILE-----\n" }, { "recipient": "age18quey88vge7xytclg2nuq4ncme86dg04lxwczqxczmdchnjg3p0saehsnh", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzdGp0dkxpNDJpSHcvRURV\nMmQxcHJiWHhhNWlkd01lMVpCZ0greUZoeUQ4Cm9iQ1NUSHVIN012Vy9lWmpRdzJ5\nNmFwT0lxYWJyNHFDRlhEbEJDOGFYZTgKLS0tIEJGUXM4dlMwRGhoNW5JRGpSZElC\nK1kyb0V4MHBaNFRGZGg1dk12MFNrNGsKKMAWhhYHwWe+wXweyZNOldNKZbN93n4A\ntgNSFIpgCXYHLEryxby1nOZfyUVFNrCFdgN3DQQNy9KzUrCBuVb/tQ==\n-----END AGE ENCRYPTED FILE-----\n" + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBLNHJtSGhZTkR3ZG4wY2NE\nRHozK2cxR3pkZUl5TXREVFRma2ZJN3kzdmo0ClVLb0xXZ1pLVGJ6eGlsWmI1QzNo\nWnRNMTd5N2NYY29wQlZNOUtvMHRVNEUKLS0tIGU4QU9Fd2Y3aEJYT2JvMFdhRFpj\nRmRqRno4RVMzQ1ZzRDRMVDdFQXpiMEEK2QJ0gKd3QOKIVWYfFMbDCwmhPyBuFXG0\niqyCcJIyut/XzoqMTIuyyNWk+74ABdwj+vFPuq0nxnJUwiwnDrT+lw==\n-----END AGE ENCRYPTED FILE-----\n" }, { "recipient": "age1ax5hqk6e2ekgfx5u7pl8ayc3vvhrehyvtvf07llaxhs5azpnny0qpltrns", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvbTFqR3RScWxnTFR3dlhv\nNUNEVGRkUDB4L3J6ckJIMWZCWk44RjdpVmxRCkhzZTBuSGduanBmQWsvNW5XMWQv\nYVZmNS9FVzN3a05kUVBheDljYUUrcHcKLS0tIGxPN1dWVkg4NUpnZGJ6VWFJWFVZ\nYnNvRG94MmFxYnlDQ0JyeDNFQldzdlkKsp+nYSR6Lxq8b3/dpMO7uTbNnO0Bva7w\nb9s908PLaZEN1jywEoba3yq743vuEHCKQWFIfDtsRcbNR6Yr4d2eGw==\n-----END AGE ENCRYPTED FILE-----\n" + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBBSTVmQUJ4THA1Ui8yMGxM\nN1dlbUJYSGxycy9WUTE2eSttSVYvR0RIL1VRCmpKbHBFcVBld2lKb1lqdFE0TkJ6\nS1ZCM1BYS0RLNUwzcEcwM01aOVV5V1UKLS0tIE9BNitEelVidDdzZE9QbE1Qb2hk\nWVY2emx0Q3BLbkNhdWJEYys0a2ZyZnMKqGe5ckj9cK1LnbtrOfPJPdhJCIBm2lOt\naq8nl+REJ0xFR2Pl4SMhIbqDgo5oV349OYQfRUBMhVE9XUUa5TsEAg==\n-----END AGE ENCRYPTED FILE-----\n" + }, + { + "recipient": "age1c2enwel9un28dcs4wg0vcyamx9a4a6g3walkhu8w5lqhmd804paq9d24as", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwS2paRXpLVk5yQzV3Zjly\nYkd5Q3NTU2xCaUlvZFpPQVVxUm5BZldpWXlNCmd2QzVFR0NuZjgxb0R5VDFmNHhN\nQnFhRWs3NVhBY2dlOUhmMmZYVytaOGsKLS0tIDUyTi9BK3l3RGhLWWxJbVZNZUV5\nWS9pTUdsUTJ6YklRNWhWbG9KVllZSTQKUsrU53r2xbnheBj9aEc2V2ZwKICFsU+5\nVXxUdwwmb6WTE53kK/yV0zsrWTDocb2/4e7qBUqnMb9Wgk2VHvhe4g==\n-----END AGE ENCRYPTED FILE-----\n" } ], - "lastmodified": "2025-12-24T23:33:32Z", - "mac": "ENC[AES256_GCM,data:mrwZUGeMtgAqSd2v387rGaf0bHZDC1IjIUlFPzB3Tj7CHE9Li4vAp7f9Bvd6v62eNcuwrnW8UoXju63fdDLK7l3EczdWsA/EJrvxIr29x+NByzfbUtShexg24mwsN7HMBnhyvil1CF4PMN4EmxxOn05cXOsAqln38IEPY6n6+SM=,iv:7PeUcjVA5u1KXsBZv+UrVQaCqEjXqY6wnELDEeHyQ9c=,tag:A+bETVg1tlHZlAm0rJ9Z8A==,type:str]", + "lastmodified": "2026-01-03T23:44:12Z", + "mac": "ENC[AES256_GCM,data:7+fRKPv0JkwHiWZOvPsp+gYHaDN2Dfdrm9cWLY378cA9xqcLpim1TvxeG3UaQQuFV6KSBiU7jBlZSOUUToXSG9mBL/pcLsErBsnqg1Wph7mK0JLzKUxPXJQIlRzZh0KmG1GCSHGQBqVQjVgyw4qUfmUdq4QZ5k5tWcQK3yCpuQA=,iv:1Sb3CkP9ad26vQjCpRLv1wXFtEHZL01EObPzstwY4C4=,tag:k1EC+zR/j/HZ1sejHFXv9A==,type:str]", "pgp": [ { - "created_at": "2025-12-15T21:53:38Z", - "enc": "-----BEGIN PGP MESSAGE-----\n\nhQIMAwDh3VI7VctTAQ/8CWW8rCTmkuQS8SdFyDVBOu1xj4lQZn8hUVDVBfExU7EB\n3c+cO8DmvVBMCzwiwD4dgkektoQ6UqInEL/beYKc4MPUUZaXLHDXysbL5Z1iAHBZ\nFPsxgE7BmPManamCewqrcBYFeHTGVzSeU6FqZDkpNkzAkjyktq4Lj16U6JTzJhjV\nw+IiU7Lg+MBym7POPYqqq8EVGtNKWNPA3T9i+bYXirwPjyNbRjxghe76I8O4zWsQ\n5vnQZhkkwCaavBusNdg0Dffl7UkFDhFCjXB/Jgfwsb2JpTxjvWgBwsfb2qT7EnPA\nOvdi+wM3nyhvQM1yMhAoAXTjlJRr/coEw48LhvcX+u+TUr1nyrTktEzcuEFVW3z8\nq+TCLT9c87GonIJOIoGaE3oUHiyVtsYeMGBpIVAW2H9eA61LtOIj0/NtO8YDGdAr\nnO2G+LxEIr1ZEstNmKaiJKkrfmgYG2fl3zu0bZS4Sm9xuwOsLvGxc6e5Bd6Q9Aw8\nOllbWoeF23j5NLwi2JXDJ8zUxby+u/Q5AmxoyuFsCgMzKQG6F7VCkDoVx5gyetMl\nQNfrjElPOv0e0wVtHzf7wqkey1ulI25P6FD4ZoCaLYLU31eTA6d1vRCX0GxWs0Tf\nDiXZIVRvONk/2naH5rN6Qz7fT+Ub5g1ZhRB0Z9NK60GXnYZhQ6FnuRyWpv5eeRyF\nAgwDC9FRLmchgYQBD/wIIRbPDxsE1uJh1/PXlRPGu9G6WBl3FuiuqbimzHFKDApZ\nzw4qVwiZeVYmxOC3ZokA23Oe+hIDSgjIeVt94XvuentxmblOwEL+Mr6qpwQEPYvE\nh9nBgGWpwJzVfZcrtwVIoJcv9NmkYPMspKj3cgCCYZmbr3PbwNaaAmfn0h/FjGcS\n0pjEGlJAr6wODsxZBJ7mim8LOzXj2Ep+5ivqT2GwuIAeR6ahabClanMzoq/ayHKk\nAtUKBja+UZwRKFh0GSir9jmICDNrrQIqTE0d0MFQicSnjrHus+mHF1hMJkm6Zw35\nypm9zEbnSnVx0DFifSTth/69h63i7H3+CW7EqkfZ/zWpmPa2D6wg+SmhLnsWpclz\nJPDQOwiiBgFloJ+4BDM3vscI8KHhWcrRTuwD8RwUB/5BLw1WWfQ+6yOK9lWDtvaL\ntibk/iYz+fM6y8VQEHwF1S1EcdHW8VlcE/xPTS4xOukShbJmMSW+0Srhm5swRa9q\nty+txwUcU3w+IiLpxp34GoaxK3qz3OWpF5yAG6dMqZ5q/6u9PcuDuMVkMW26gj31\nxryWCoemlrp6DW3Gfmp6EeyVUNVbiJdXmU4jnCjWViQei3AtQ062yES8b/HDUyFg\nVmPfcTN0hpWPBxQGWGDTvDC/LzK9DeHLVUGhCoUlvBwS/zDbgAoo7xmUe7IqkdJe\nAT0zalGWoZdsebqWwp0txgrsgn17VzILFHgvmgU7NgjN8cTwiQ5odqAEfkzZQ/nx\niEfVFYe8fmzQVrCcDsCcxGBOavPTFQG5HbY5ssDSur3QfqKW7PgkPztPl2IkDA==\n=r685\n-----END PGP MESSAGE-----", + "created_at": "2026-01-02T21:17:44Z", + "enc": "-----BEGIN PGP MESSAGE-----\n\nhQIMAwDh3VI7VctTAQ/9H7LfgDWyE3F7SrLQaGdx8rNwWy8olBcJC2O7C8usiBUH\nCqKNUUAfXaOqsNoZahWES2KNyTZgARGW/7lsQU3ISrYD/gxn45Pf8HyVXag2qbKQ\nSmYii8HXvrST+XHU7aErAZb293ByBSBvlg9JfQK8lNTKccvIKLLHTI8Xo/EkjUtl\nvfRocIPzesBnOBxTwvuWvnI+nvcuvael3UoKK9V9HaFBrS3i9U/z2EROaBOY+LI8\nCNgSaUNEGqeYh7/g3hSMzLWicM+3YS5MnlVDJIaERz08XvjgAfnqlmZPINZGCKpg\nUY2fBJKkG2lUsx4dfs2XcoIayygA9IIN4kPztI8jDL+KTh2he2iyfDEzTGqZ+Yfr\nbWzoUNxtCT5FluVYLsAUYBJB2TZhQJeNHpV+dOLzs0zTRbzh0f6dQQEs6u1W4ZVe\nRN8UwO+e35Bcn5sJ48y6143BCaJGsNwKg8wqmhOejUV6F8di9nHWasXgaxZKPbnz\nxIyt8E+PvWIYYQHadUQ0SIL5TMWU4csyoDvRbpQxYPVMl/Fi6v1khW7Dw3thhOPD\nXAygAQ3SDr2bzB4ZlWAozx9D4b2jgPk1qMHMHmypf4vdTqxKVs9UhzLRLK4IYDKB\n91drnFsAPhOzgx24CPEZANZEln/IlRmIFPReVCT+8sAvqDU+1CROSvR+OLqwfPyF\nAgwDC9FRLmchgYQBD/0dgT3VStJ6BJH29XYsfQsQcHSGgYan3Ecy6g02d3h4Md1r\npJN4s2THrlHe4LVLIyZ5xdetrxSvU6uz5TM2jAXL6cYLEFyr7FqrHNWHDXcbEiE4\nRgtVxV0P0c69skkVnBSObvc9drD/m7jbAy/9dsTs/1Xij1eAgvHlcItadAQVKCYO\nd7E5jFJfVmtQQ0VWqC6Y1i4tb1J/UKRGUG0T2oP9RkXPdCctVMr8J5DwNIw87p2Y\n2GF87yDM/D7mo6Gk00aeOYFPej9qTQHNo0e1xCw30cGOqlGQpcr+3Fc6hdt+ww8e\nPHVbR7ZA/OifnbBk+Wi1qqIlO4+FdxTHpOK8lZP1A/B2XtJsrmWGLPTHHw2iiIR4\nahPpsBtxOyGZXB9Qr/kTX+uNUQOtFXVbxi0AgnQ3W+UkJlpSd5BhxeeN/IqC0Xsd\nvzhv8A8evy6dEwa4eoDPdECBfHfcXQX7Cin4WFef1Vr/6r/mL4OvY+yeMRmZ0J+O\naEPeP5zX3u9YBZyjay3daLCTS2Jqoh55Vp1e94Hzi2CpZexj+tvph92CHAf0yJz/\nIbJfO1VXSMBnpRDzzM+lSGEv3RNk5OXre09j3kThuscGqXKH4+48qbhYBLr9TkCB\nVwxwYijDEAst0XOky+RXZxtR1muAi/hA07H1jZK0lMJYv26T3SL3pDSheMrxGtJe\nATYtKOMookgjDuzsQAthoIL+bV3Lx5yFJjF+phwL5vqTLWa0xj2IBUSnmXnUgCa7\niRLsMPrLG7qvX34Mr7HrOFRXWQjy8SdmU7PHgUyBAyK8I0JFl+mzF8x+fbRYow==\n=gdhs\n-----END PGP MESSAGE-----", "fp": "4BE7925262289B476DBBC17B76FD3810215AE097" } ], diff --git a/secrets/repo/pii.nix.enc b/secrets/repo/pii.nix.enc index 4cd6a2a..c54f933 100644 --- a/secrets/repo/pii.nix.enc +++ b/secrets/repo/pii.nix.enc @@ -1,62 +1,70 @@ { - "data": "ENC[AES256_GCM,data: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,iv:4ZbxVxZHzxn2eSqP8h8eV53IV9J0avHBg1R6/TgAmtY=,tag:SqndgxyiwbD89LSrZBMnuQ==,type:str]", + "data": "ENC[AES256_GCM,data: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,iv:CHxvIxUy9RNlieC17BWfSKFqz7wHvC9epHnBnaHZFyI=,tag:TSxK0lMVpDTo768ZWbJy9g==,type:str]", "sops": { "age": [ { "recipient": "age1s0vssf9fey2l456hucppzx2x58xep279nsdcglvkqm30sr9ht37s8rvpza", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzTXhjVDNLUW5sT2ZsbXhx\nRENRREhqeVp0KzBIVUE0RGo4eW5HZ3dRV2pNClY2N01ac2VscjRiOUdtNVBnSFg4\neFdSekl2UlZZekVFMXdVRVBnbXI4TGcKLS0tIG84RXQ3bXVxTmxXQm54akhxNG9I\nSzZxejlwMjVVRkFVOEFKYWJ4RFNOQVkKwUtbMRKa0CJYCa4TzHwMisw/mBCGroZn\nGnK489S1W833OkLtLApjsFowpoHpaizPMcrASTTkc7h+OtQ3yJlMig==\n-----END AGE ENCRYPTED FILE-----\n" + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBOU3VqNFJpVEFBN2lwWlNS\naExNbUpMMWQ2Zm5kWklxR0dMOUZUVWNhYWxrClFBRkVFUkhaSVlTamxhRTc5d09H\nWW9wVzdxTkZjME5pbVVmUnBXakpaK0EKLS0tIHFIY2JwTDB2REdqWHRSQkFoaGE2\nN3FWbCtpUm1SSFg0b3BEN1ZwUEhINzAKFjA6KD3PK7BZRYkHLSs58ffMHrCwhvy/\nrF0zA2UKhIBN1LvYlyDdO2yZSOCK+0iwEuvz++VhDm1UnR0hDNu8Tw==\n-----END AGE ENCRYPTED FILE-----\n" + }, + { + "recipient": "age18cgqlely56hgmhscllkmafwpjdk6dwep6ej3vkk97dzemp8jtuksqrrjjl", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBoa2F5ejliVW80blE4NUwy\nTUdCamtkZ1hPMk9hQ1I0d3hYV2o2R0p5NVNBCmQ5WGZLS0ZnYzRUTk5rc0JKbEQ2\nTEM4V1hNdEhzWFFJbWNuSUpVN3ZTT0UKLS0tIEU0TGlNYVJZV1dCQkRHM0phYmRH\nUVg1bi9IQllUQWpkYTA1bGRyMWVIRVEKPiZdGYR0bxk2ldQ/4Ce1RcXyZACcCjD3\n6oDBe1DT7mPHN1jPertICLYW8Q5/LBRX63AJxZIi+epZMGszI4m48Q==\n-----END AGE ENCRYPTED FILE-----\n" }, { "recipient": "age1g7atkxdlt4ymeh7v7aa2yzr2hq2qkvzrc4r49ugttm3n582ymv9qrmpk8d", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzcGFhUWMzdlpQZnpxejBK\nUzFKcXFsRHIwdUJQdmEraDVJNGJuQ3RqUFZrCkZNVDNIZE9XSkxXV2UvaFB0cmQz\nUSsvUi9VRm5VSVN0NklqOHZhdkZySjgKLS0tIGFYREFxOXJrOFBCa1pua3oyVE9r\nS0l5QThvZXkwZ1Q5dytjNG4yaklpbHMKLVslGe5lj+ED2496KEcb8ccWW5YqF1qG\nSJGwZf6xRl8cLsbCE8qfC9zlZ9DDc63U1bgLGgqkEvYnmZ76BX6GXQ==\n-----END AGE ENCRYPTED FILE-----\n" + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1T29EeXY5cDA1em1oWjhn\nYXRuci83ekU2d21ZMFc1R2tPT3c5RHNXazFNCmFYVWNwYXk2Q1pTQW1pVWpPNkV3\nb0UwSk9BVUJMMTFKV0xxQUgrNVNmYUkKLS0tIDNpcW55MTF6a29nd1d4WHEwNTZh\nWDBNcHF4S1hwVzFTaTVycHUyMnNFaHMKCJDevUKtn46qMNpNjTIctBVMCLs5lH5Z\nfU2rzvwIBxwn16NCOAInRxQEOASYITYo7FKuMD7W+3eQf0vAj22LRQ==\n-----END AGE ENCRYPTED FILE-----\n" }, { "recipient": "age1ly2endyt0y9xyddj6yuj4nw6fa3ltvzlvew4cr4lzs6dv8dkavpqadmyxx", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnWE81MW9LQllDQTZ0OGh5\nRDdCK0RNMERJTFQ0RlpWYjV4RkU4ci9waWhzCkx2MEh5UmkrdUVWUmxGN3J6c0RQ\nYWpUdGE3T2dWNmMzV2x4MENYYmgwZkkKLS0tIGlra0RtYlhydHJCeUxFekZLdTcv\nbk1wOGszc2xQcVhUM2ZzNDBqQ1NzQ0UKftq1Kcxi+GHGf5SNswuprffd2SA6kKKl\nH+I/btSl+Sl9GTmTu3HTkAg4+Bux6uJtBAGqWyTOfRWV79Z5bQBNwg==\n-----END AGE ENCRYPTED FILE-----\n" + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlZUhxUDdETlZCRnNJbldO\nU0NiV0hRVmtNYW5TWjdvOThyS1RSaExaWmdzCjJ3S1lXVmlFMjIyUEdad2xDWmlM\neFJOSnJSc2ZIMEJuK2NLVmdXSzRvcG8KLS0tIHlmZTNra0pzVVA5QThhN1ZaY0lO\naTN2S0VSTVgwSzBVZEN0em53STE3Q2sKlaQp9T/T7ZRY8FLB7yksMOwIfRWs/s7G\nPk3JnpuptFehhI/FeYAh+0fznBuTCKjqXF9X96a6RIFIC44/nKlfxQ==\n-----END AGE ENCRYPTED FILE-----\n" }, { "recipient": "age15klj4t7gpfp69472mne4ue62pp6m4e04dmjyw7yf30qtqd3vl3uqjmcyxm", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0alBqQjFTa1FHOEU3VkZu\nMjc1R2diZzJyNHNLTi9MU1czdy9EVHpGR1F3CmdUanRteFRPL0R6Rk83U0diQk1F\nTVJXWHBMbWE4R3pob1pMdGtoV3NMWDgKLS0tIEZYZ3cwRFRJdERUeHYrQit4WGl6\nSTd2cmNHNkFHSy9DVjF5bWp2SVBTd1UKwx/D3B8UWQHcifsOxCDkpFsi4KVG82t+\n+8Yw1X4ls8h76Xvgvx9F0QbmCw7DXX4cP1xYlqIQBf0N+vMo9shBtg==\n-----END AGE ENCRYPTED FILE-----\n" + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzdVo5cUswRm1yS1BWU20v\nN1RlMkRXR1dhcFQwdXZhUVNWbkVrb2RrVmxFClZVVDJ1ektCT3NmYlk1WTBXVGUy\nWllkOTdlZ24wMHdKSmUzdmFMd0F6SEkKLS0tIGd3WGdueEpWTkNxQnZrbXdXTzhB\ncXVrNk43NlA2OWE1cnpqWmJKVktsNFkKwyaRDz53nk8nljSnqN+CXY7J1f2LmJls\nXfbOZfFH7Ayoelv0OCoWCm1ZfD5QLnTBoCZGCUw21r8YuXzu5+maoA==\n-----END AGE ENCRYPTED FILE-----\n" }, { "recipient": "age1k73gy5em3js9zklnnkzp5hme9k04lny32fgahmzddknjw5c295asdyr4x6", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvT0d6Z2dtbm9vN0RweHVS\ncWhDb3VMWEFmQ3pGbVJzYU1hM1dJK3d5V0Q0ClBrVG43ZHhWQTZPVHY1MVRDb3F2\naE9lMzdWM28yT0ZLZWZielduM3pzZE0KLS0tIG84eWpZNktCTExSL2tNclVJMmhi\nVDV1MHp2VWpUNmZDbDEzaXhkVUQ5TzgK7Tdlsyya9kRwIc6oG3FBywJ9SWAvvKy0\nfYkW/m8pE9y9mPj62rqnAt4yS/GrSvKk1za8PkB1P5ehOWtxHeo79A==\n-----END AGE ENCRYPTED FILE-----\n" + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB0dkI2RXR4aDlSaGhxQS9j\naXlXdjdscjJqQVQzblNQdkJxaWpSZG5DaGdFCmcwK0JKVHJZaDNzU1FlR21FUkd0\neW5nWHFhTDZNTnJzZFI1SEdTOExwN1UKLS0tIEhaclEvbXllRG5EWFBYQVoxMXBZ\nSXltdFM1a3Y5bFREVzhBRm1YS0orMHcKUrHg6YkMF20G+GdLI0J1pj1zLe06Xn3Q\nWwxgB6ctI8D1qs+0GMoXB7sOsfczgYtVt1hC6EvYpmiHW+g54GGRHA==\n-----END AGE ENCRYPTED FILE-----\n" }, { "recipient": "age1cmzh82q8k59yzceuuy2epmqu22g7m84gqvq056mhgehwpmvjadfsc3glc8", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBNM3R6VUxMczBwTGY3UUl0\nR21taWdlc1dpUnhOY1o0OFF5ZFp5TjNMQzNzCkNsbkN3SSt1TVFsMVgvS1FGaVZF\nMzlkdjkzbGVhR25HZmZZSThSUktUVVEKLS0tIGR4bzZRR1B6WVM2dHpkbTBNaEsw\nZkdYV2dnbmptb1pZeWJuVXRROXhzbjQKzranKLFffOHVcIhPq7oHn/KWGIIH+P8n\nBJAx54HVYVxWUH4XifkgDmYNsGrENOAO3RR3NZ5NlUG0FcTD5zP4Kg==\n-----END AGE ENCRYPTED FILE-----\n" + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlU1NCRFRYUVkwZ1VMTVlx\nVStFYlBaY3NGTThpS1NYMnVSdzVCU29NV0NVCmdKU0dPM2lySStVOENZajVRU21C\ndmJHVkhqNWFpbEpqOXdxd1kydjNFTXcKLS0tIFI1QWdmSVRHaG8zV29MSFJhRWU5\nREt1THJJR0RrK2ZzWlAzQ3dobjVOM1EK9sczJcZo9sW/rlKczTo3PYRmgm+mJ/ek\nyRniR+IaC+cdiSIuDS6nz6XwGqIrTpBjizpq3a5uWTzwzscU+uMuig==\n-----END AGE ENCRYPTED FILE-----\n" }, { "recipient": "age1wmx8y2hs83j2u5srdnfxljrzxm8jtxl6fr0mq7xf2ldxyglpzf2qq89rpx", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArMkI4VmRqc1paVG9lTFVF\nQ2t1UnhmUElkSy9rdUxUMFpDaHVQQWxFWUNZCm5Rcm53MDFNcnRTaFRxZW15STJ1\nZnErWTN5b1Q5ZzRGdGRBYS8wbWt0OUEKLS0tIHZ5TFo1RUhLU2V6Tk9tTEJSZFhY\nUTVxbGNWVmpQQVIzU1l3REh3WHpGR00KgJibUNT/pdv+Zf5EJaGEuiJTBYq8lyLz\nsjOLWTw9iNXA2bIPhk3PDGyttjcW2RrbPSU4cuN/O7w+ZMw//HWhYQ==\n-----END AGE ENCRYPTED FILE-----\n" + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA4L0VvQXk5bWNVc2FUTk83\ndGp2KzdrclF3bW4yQlV4emdCQkIvTmV3WER3CitTOVZVWmp6Um5NVEs0aGg2TFhF\nRWtueVluSUMySVBjYVFmaHlYM1ZhalkKLS0tIDl5ZlZ6bEpIZW0wQ0phbnAwbThV\nVHZ6R1lCZE52alJGS1ZVMDljQzZ0TTAKptXEII9sr9zkdAXJLLCKVd3E3pOsgnIB\nLNqoJfg336E2aquo6LCjtBA0nSBxHkAnw0FFz81Kph7zK+Z1+tFB7A==\n-----END AGE ENCRYPTED FILE-----\n" }, { "recipient": "age1mjgw3nxlnqdj04mgjz3wn7fj2nl2nxla4p2r2fn4nkvayfgp09pqllxzyh", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4WndZWUVMdEU0NGlBTk4x\nOVB3MjFvRjJqekgrM1BTN1NIL0tYWlRlOUZFCkZ4emtCZ2R2Njc5K1RseTF2TUxY\nL2FQK2VPN1Z3UXZiM2Zxb2lBOU1CSVEKLS0tIGtET282RDhYNVlOQXZ1SStrMWNJ\nS2E3ZlRPcEJQL3l0Ry8vMDIzUFFiYU0KqjPaREHI1rP25hH0Gd6rTMgMjy1NId/m\nCC12d6R28kVq1rGNO0mJw+FJ+Eu972Q87Nte/zz6ndcQmfAmXbfxsA==\n-----END AGE ENCRYPTED FILE-----\n" + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBBcEZVc1RpbC9vck5zYmh5\neWxObTlpMjlpUFZYWExCUDhQU1BHS3EvL1IwCk5xelVyRisyZUVLK3ZueUdRTmxI\nWlhZSlJQT1RSZXNZNWlxRFJpenhvb2MKLS0tIGlxeWl4VnFXQ1VVMXNmM2x2WXI4\nejd6djF5d1k1cm1Kd1JuNVBiWnI5TEEKnjIih/AMCQOakcmiEpG4Fqa+yYDafML/\nSepnQsojq99JF6oxtGAa1P6ibmAIglRtyo5ypRRTVMm6LWt8Nul1yA==\n-----END AGE ENCRYPTED FILE-----\n" }, { "recipient": "age16vzhcvz8tyxj8e0f47fy0z4p3dsg0ak4vl52ut3l07a0tz465cxslmhevl", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBNRU5Nc2E5ZWhZZlU3OW5N\nMU91TGttdXp5RERlNlNmcHkyejdsb3JXMTA0ClJKTll3RjhrcGJyLy81c0pkYzhV\nYUhnTEY1dm9wTUlzTkE5cGF2alFrTFUKLS0tIDJtbDROS3V4Wm4wZXNzRnkrWk51\ncW5MbVNFSEROTlpOUFJFTE9BUXlJYWsKZiGBXaFa0mtbFoIG8pTjVgSSms22C5R+\n8EQ4k0fffsX2Ua4RL1DLiUxyNc9ck07E8hbp3L2lHRYBr/3sqtcSUQ==\n-----END AGE ENCRYPTED FILE-----\n" + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1ZXpKOHRwWVByVXpUdHBS\nTEJ6dUJJSUQrSzc3MHZ0WHV6ZnM0dzlwTURFCjlxLzVDTDZhbGVpL3pQay9Md2lh\nTStxem5adyt0TVBjRnhsNmR0NnhLek0KLS0tIFhlWkQrKzFHall6Z0JXWEVMY0tT\nR040ZnhzTWFVbnlXdktVWnNuY00yU3cKmNYw+zRsH+KvxPW5o+fSKPKAVbxiVIoJ\nLZNXdAr6NGYMgKmvhq68M0C0mbLmmo505qb/oBntbfoGZJtSULAUnQ==\n-----END AGE ENCRYPTED FILE-----\n" }, { "recipient": "age15cx90pnp54xp5gxlt02yn9j2pz968wp3l5ukdkx55xuecp34e5pszjku4m", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBRRkV1eVNGdGozRTNpWEpP\nTit5Zko3bEZWUStxc0lESWhTSGgvRGRtbUh3Ck9ra1ZoUHd2dEs1NGtiU2l1Wm9r\nMHprYkpQd3NLMk1ESmROemFyUjk2azAKLS0tIFlLeGZBUms2ZDQwK3dlWFh1N244\nVHR3TDNVcVhidWxPOVNNTUZiNHdUYjQKpNrP38lrlmazwR4MELuJphY5Ci7ZIqx6\nqefmDlF1yVyxIwoALq+Rl5yOrwYWr0CFXZxYeqfvgdQ22E5+AyzDoA==\n-----END AGE ENCRYPTED FILE-----\n" + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJeUVRU2hGM1RxS0d0Ykw2\nazc1MWd1ZEZweU9PRFRPRG1BblZTakx3WUdjCmlyZitaZGVwZHRZWnRrOGdSazMw\na25McWhRZ053Y2lRNmdMc04rNGkyTWMKLS0tIGtXRkZWVHdmRTNYcG9xZThZY1VY\nZ0gwQlptSHRzOUVCS3QrNkQ3aG9YelEKt+43DmQ0Zn1x7NIWl57eGxG3gsALxhMv\nImpMryrccmEV3Ddc2LPpxIaRYnFBJhTKDNOyuSOSD+I471PJlOu9yg==\n-----END AGE ENCRYPTED FILE-----\n" }, { "recipient": "age18quey88vge7xytclg2nuq4ncme86dg04lxwczqxczmdchnjg3p0saehsnh", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA4ME5PVjJCZ2FWUWtHUm9p\ndUc2UXg5RmhZNGo3YUR6WjQ4WjNaVnZHVlZrClZVb2NZYzVEOUpjdENIV1VDSUVs\nU1JvRzN4dTRXenh4RkNQY1VObGpHZWMKLS0tIG9hUHlaWjVUajVmMzFUQWFnUVpP\nVmxaNFRHWHJyYjFmSG16ZnJIdkNkb2sKlpc81NiI9AolycNrGaiLDYUvRrCbsMUe\nlUxLdCKo70CEnXTdLr2emhcxNwcSMljDPekMiPWcIFt9ScA639pFsQ==\n-----END AGE ENCRYPTED FILE-----\n" + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA3emtuVmF1c1hnZFNtcFkz\neVlnbW4xKzJIVE10Y0NtWWZLMXRQNkI2L1JNCjhpb0dmQU5pa0VOZnNaejZSa01I\nRldSSUxOKy8zekpCQVZtUTFMOGptZ2MKLS0tIDJIWDk0OWc0TThTTDJvdkF4RnFn\nZFowbEppZFdBc21SNWlFQVo1bE44ejQK2YVQILlu2yxCkTdwAP7J25NOgSHeNgJj\nOCrH1TU7mjp6dlB/hxeUWDRCbmqHs9ko054ZdXrxY+9BeodA02B1bg==\n-----END AGE ENCRYPTED FILE-----\n" }, { "recipient": "age1ax5hqk6e2ekgfx5u7pl8ayc3vvhrehyvtvf07llaxhs5azpnny0qpltrns", - "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGNi96SEpTVU1wa29DeDdw\nYm1weGNFVU1kdEdZMmRZTEJCeWliTm5mVGhrCkZhbmZzTGZoVHRmVTE0ZFcvMno2\nUkhVNFU5RGdOS1dEN3pZeGtBbHlhTW8KLS0tIEcwQk1LN1dydEpjSjlna3BwYlBZ\nZjNwdU5leHhJWTZqRElGZERVbFg0SkkKR5LI86bCwuWnzzF/+sYgnsEdHSCMYwr7\nhEaK43fFnD1zUKve/D8NmscJO0VAZIt7duzdeMLv7h+a2ebSXAJ7fA==\n-----END AGE ENCRYPTED FILE-----\n" + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB1NXlqMHAzUUZtbDl4Nnc5\nNmxISjVNUFJsNHlSUDl3U2RpWUV1b2phb0RVCkowVitwa2ViaEpFZUVIMG1LK0ZL\nVG1FU2tUdUFhNzRTeGI4bnNoc3ZuQ1UKLS0tIHZ3amttT0VkbkVuTG9yWkFQcEJN\nc0VqQ2RKb3k1Q0pkV0RjZEg4YTM5dG8KkUaAqAlF5hPuENwYWjVXFETePjtW7w25\nUUHOYmr+3j77FuT44TKYlmN4PJNAabbkDAcXDhBvtN4Ja41SGucy4A==\n-----END AGE ENCRYPTED FILE-----\n" + }, + { + "recipient": "age1c2enwel9un28dcs4wg0vcyamx9a4a6g3walkhu8w5lqhmd804paq9d24as", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB3dGRuaW5XTFdPYlZMNkN4\najZRc1BNWUs1TExCdTAzTCs0RGpDM1lPMXhBCkxac2NmY1lpZmsvbzA4cEF4ekt0\nb3IvQTNrbk03ZWFKREhPZnp3OW5pNDAKLS0tIDdYYWR6NEIrOUtoYWxZdEJDWmdm\nT25XdkYvL2tXOEtFR1U1WlE4RjVGcHMKZ5MM7iTvGFi34ZoTNg65FHrJKa8SONcT\nCjdgJIcU/7wFbgTm295vvjfrXw54+pKUT3u9NqmPGVkA0o1rsBbZoQ==\n-----END AGE ENCRYPTED FILE-----\n" } ], - "lastmodified": "2025-12-24T13:04:51Z", - "mac": "ENC[AES256_GCM,data:ktWaSwQTDR1bu9ztdt/KzvItcPxxCzhjAUqBKipe0SUZjxqmjGpQDBD1+p6dH6vSQEdcHDk9vaSMUZOMbOQYrQiEEqL+DLhAtRnc7zf8Mcd03uWjXm/4/J1CETKXlAIY/qsh6aHJoCpaSPOBnR0/AnP8xw7ae6c5j5wVYRQMmVY=,iv:Db56gtwzmAZuSFMAeyMTJLT3LMOTvDtc0CCCtCJzuRM=,tag:RtnhRlavbUxaSDMKp44VgA==,type:str]", + "lastmodified": "2026-01-02T22:08:10Z", + "mac": "ENC[AES256_GCM,data:dwn1YbVF8pXOItkLrdPHIXN3ZoXsCuoCcCuv9Cz9UQBKaIISS7xDpwVLbpf2DMxPmpzjq7cbJHTe714Zi7uN9t80wN91Kt1YKIsr14kOAuJkoCZPh35Sanqo0tL347C24E3PrpvB26RWGPqA6CrKp13swDPpljMrdhz9GO5bzo8=,iv:NiEV6uQXthBauTe6otoPxaoPNsaEKQ0JlzZNYna8eg4=,tag:mUc+AUMoDADysZCSin5XQA==,type:str]", "pgp": [ { - "created_at": "2025-12-15T21:53:39Z", - "enc": "-----BEGIN PGP MESSAGE-----\n\nhQIMAwDh3VI7VctTAQ/+NHWvBUy/8BbZsCHEEXhTI3FC372TPFUSj6Scr1Nfv/s+\nYozQw+up05n2t3f4WXro1zpfrxZeXmRv/wrWMb56OvNjCh27ZTyx/wooo0aljFkZ\nYPK5+nk/BvGBVXopiwF9Kj9IY964IdIi0rahYyGgZOigE40e9sPVAtFWQLefMpFl\nWG0iOrMez/4IUeqWhTZaNHfm0uo4y/69hEGtv4GA332IgdOPIslyzs5KvrvZci8R\nmqn88SUGNsyONFtwvrY5NVN4hr9UzklhecBD7NPGjCU0xW2PoZEjYD2SHP2eAp4Z\nVwq/lMux6oV62t/nTced43wIyGni5luwToPwrgWquSF73WaO3PiMFHH/4MXJxGOa\n+EKVehatyxK8tGo/H6fLhbK19vFlZFLKOmHZjQAikFVOfIxQSptnVIU+5j2wajkC\ncaMYYoUwlQGLxuG1X9RMdbEMQZRwKw5GfxSd0LlSFOOnBeWgm9W/0UawIjS/TIs2\ndZJ8TbInPyCjUWSUO1HK8qV534LfbuFCB4vzv1fBBrt3wwZ0M+QT4hBg6PeOrxox\nxToqsD3KBmmcnInMx0LJ3e5rh9X9CtzJ1DKgZweDpcawpSNg/BphruILlAKx8OEF\nOEPBou9XJTcDiPuExqPo4WDwfELHrjEDu2xJGgpSWS0e00KPHmFqTs38l0XB0++F\nAgwDC9FRLmchgYQBEACOCDUxvZ9S0fzxRc29eA8JdVtGslGtneZlHjoHAyRjpYGv\nfKFGR/HcFNcdPCahQoYyv9BdKJf1g9/jOhmX5TdE63fIcZEOrdeKAWFBaLdcOjOF\nUxT4wpiYGuOVslPkhGXIuQurjZipePVnt1/VFixrrENyEWm4DqGmq1Nr9FdJtwhJ\ndUL2zlBd9/jBKHV6+AKfmI2ZcSXu9gNelhUU0keU85TRBSa8tAWSBgxEOsMAxP+G\ndb1dp1EVtlwgce99GH5dyodsMyX3sMfjhmf3y7EELW3mg9h4gY88F4bRmbQUaHom\nGypHWWjqz5gXSKMh5sx0Mz3TNiQnxu4AGZUtuFffx5RjYyEw0J7xxtTfiHhPQ3C4\nL0iJ30f3SgNJZWQX1Cg90BFKc3yOEzK3ePzVoTX4BCnAGMKDRnU7GCSM4z1X0DdF\ncEstNxeWSWZ7UzlmQXl0WjT8i/fac4WL6rC9DiWnMDzu5ckyJi+LTZvbmftaHQhh\n1kB8jZ6n6pRSQFFZENNK+v1gBaNuBrmLzeGw865L7CEp8WjIkTdgXrD0Y4LUJnf+\nIQy8caHmcn0RyHsnYYiuKGvbknKR8ehm2N491I0khtKmxN7mupPaN9zdNjuSrpdh\nUw8ZK2A0OuiQuuDljbsa1HuhknR2QhPLoTrdFxV51T7pgEFSCiKiimw8LGD2ndJc\nAbVCL+b3lArUcgevMl02e7GUn9AjXJhaqRFtI8T20Zq4fM2ndBCeJU9ipvAF6q17\n4qCtUCx0M5xsS+2sWwTwaUH867c/JrvP2DQao/AUb58acvGZ4jRrZRoWZgE=\n=GidG\n-----END PGP MESSAGE-----", + "created_at": "2026-01-02T21:17:51Z", + "enc": "-----BEGIN PGP MESSAGE-----\n\nhQIMAwDh3VI7VctTAQ/8DSXgLzTxvatqeyCClZwBZ1fczXzy1eCVMRCtd49ci9Fy\nZ28xzCeWf78i5QAEr7UtrkfcDYXojsaiTROQ1VbWm+Xx5SP00sOTsmlMlz4qAVpQ\n2uhIEX+v14jkTh4xluqVmqMnq2cFQCNSoCEJfeUzYiH+IR0JOpltGAsewGc8BVPF\nKCl+/PnybTFeNXNvJV+k6jdw9pboAid6ehJVFW0K7iJ5Ine4fCsnlTSs1xTbofhB\nfzxUDZqLc5VpwCvlM/aN/pFGwGhxezXwYo5arFlcW5ZTpakVWlpBQ9+qF4gSTfTU\nfOaLnKexJnbEIpfK3IfmftZnoSEucs+DG5Fyxf0TNXnovVBk1Q24f3r3b25lL6Pw\nLCkTVc1WB3EVPDkLho1KGx58A2BTLvl8beKXoBDpk+rIFnABVYjqjJcxRSmqgkV0\nSgYxAghk+6UZs9iREvn9ruWRaYMJz3Zyda1cfdbyeqRDi7cT/C2jVaq7LLDSuUQW\nwyKjrAXaHtuNa5goUwPnT4hDgImekXC8N1qR5fzI47/ZoYymeCxGt9z8geE3hEg7\nw4jYcIV+oHFenu7RB3b3NOZwchUEeBFPGlA274oM3nHrtKpL3fclnV1hvHmNEB0u\nirclqP9EYh3DLfvtcXXTpE8Pm5/8QPVbFTIqcc3RMA7b+SE8ZvE0xgMC1PxJN6uF\nAgwDC9FRLmchgYQBD/47FReVLf9fl8aqqiDUnWz3/3Rcz+KUp7UisUgDrdi3L2wv\ne8mYl32v8qOPVPkKyEPIdjLfLhdcv1TxP3U6UT8C9TeP3Edile2s7MbuO6u1V1yj\nSuCez5naXww4KiJozwl6FtAZZZ9aKRLLkxsPcfItkCPKlxcKc2DvEDWKOdHBWB0L\nKYBf0u8bF2SUHR8ykeKybc5imMg3/jI8Vbu0gt8TfihU2btqwR7Pd291421S6irT\nK4/8VCsB4uPz9JxF91XfW5m86NPCTODcTB2C9VIWmdkxnk4jpQmv6qwkQqVKdApN\njJO8P4vgu2iB12zq+dsmx9W+TfSizocrCbuUBieq64EOx80QtZEgGc8ORf7MWq+1\nrO6iC0QjlUnAW4oRNV/Qv3l7NSm0pCA9XQL9x7PMh0f3rTYLajOrYDI7cZticAA9\nC7ZuVjUiMB8ykj8nfflOsXUJyHVYT8njeMqVEixEWehj6aq9eMx1VStphS6C/cbr\nXQT96ISnIG0fa6fNqEqzXBES1HYvd6Wo/jjH70wzNCWezvJ5IDANPkEhvThc+MmW\nxgPSK1+1UEzJGQ7uDo4Aadwn/Fym3P558icaEdjt8Ykay+gZsIi7ANVhJa0SI5tk\nNKAXvcSA6Dq9tnILre+oLewf6A/AfFdUesiantEaKjUgWQtad2P3iDqyJHtsz9Jc\nAXI8hZQSfOmDjpgjG27pTajjhmwNQ7XD2ttm1iHp5ho16zYtS8bDtX7bEB35YyM+\nFBuxrwcUNDD/mfi9uAtcjQm3x/IdLU4e+NL07GvSEWRU5H8pUBPcaOeUqLI=\n=pnj0\n-----END PGP MESSAGE-----", "fp": "4BE7925262289B476DBBC17B76FD3810215AE097" } ], diff --git a/secrets/repo/wg.yaml b/secrets/repo/wg.yaml index fe2e361..f7ad2a3 100644 --- a/secrets/repo/wg.yaml +++ b/secrets/repo/wg.yaml @@ -2,149 +2,170 @@ wireguard-twothreetunnel-winters-wgProxy-presharedKey: ENC[AES256_GCM,data:FZWAR wireguard-twothreetunnel-moonside-wgProxy-presharedKey: ENC[AES256_GCM,data:Y/EwbaVbGljiz9XZmr7+/udBfeaY+CLMfnKzekXP50Hu8ek8aA1/SKs2qd0=,iv:BijEDkfpRWox8CPwCoZLA42WihYIqHJJgSgOfsOGcG4=,tag:8+qEnqTsqyNdD4oVPiuQuQ==,type:str] wireguard-twothreetunnel-eagleland-wgProxy-presharedKey: ENC[AES256_GCM,data:dF8VPApd6iYKIZjBXB2rjIXIxyy2+U76TdyFuyUW0zSbtjzqn1ZKrhX4w/M=,iv:GqOHsS97di9sHqjndlq0EdWLcJ1EMLmDOnFJlBgTvYU=,tag:PdxEYlg3lPShUJYlANLjhg==,type:str] wireguard-twothreetunnel-belchsfactory-wgProxy-presharedKey: ENC[AES256_GCM,data:NAbVE7ysGDD6TT0RxdL6bTNloac4RBU1JWeTFqYo9PO6ZU2f/yq6aboi2AA=,iv:Ky4UvgRDEG1UgDmi+m5mHWHO+yUGzphQPYIuyAXDkhw=,tag:WP+/8q8jfitNC/rXN5Mp2A==,type:str] +wireguard-twothreetunnel-hintbooth-adguardhome-wgProxy-presharedKey: ENC[AES256_GCM,data:Gr9tP9SkizZOR4brFO3+7PqDC9pG5RojWa+xDDtqfZT4Xo6ZAN2002WGUXQ=,iv:Zj7oy/d/o5sNYTsyPaUQ9Th/xDuG7o82NpnP6TZzqeE=,tag:OI/DCxjJNpZC1C7foO4xsw==,type:str] wireguard-hintbooth-winters-wgHome-presharedKey: ENC[AES256_GCM,data:57KGUxn1BibZ+9H9mXg9EYmcy1JBX+M79ACL3Qt0XEMl0dFlk9Wq6cr3JTg=,iv:9QHdykNlUU1H0uco21zA8leQH73PAeL+xTVi6V9zx7U=,tag:6mfSCddnVGMBEAqCHDIIrw==,type:str] +wireguard-hintbooth-hintbooth-adguardhome-wgHome-presharedKey: ENC[AES256_GCM,data:zBfV5HMy3h6q7yEF6Cu7sCTONa8nJRmMDHgVFvHVinKmyj0qpf/WGfnyb3g=,iv:3JlNgsJHkE1EawLWIJaZsiZlu7pN9ymFQ5ZqdM7W+AU=,tag:pGfsh34HnL8mUhKyFkLniA==,type:str] +wireguard-hintbooth-hintbooth-nginx-wgHome-presharedKey: ENC[AES256_GCM,data:BGdVjcCyhrSgIIZtdoBUscCitNjBat56YjZOFx9Vao3vBMjoHFGY1WFV7zk=,iv:wxVClKJE6R178vCp/RE3PgBYYLIvOdSV97AfJagWZ8o=,tag:zbuMwlJYIKtV7ylwvgLutw==,type:str] sops: age: - recipient: age1s0vssf9fey2l456hucppzx2x58xep279nsdcglvkqm30sr9ht37s8rvpza enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBaQ1VJOVQra2htZXhTQ1Jj - Q2xxYzBtQlJBcHp3OWdvK0phYmprNnY3S1Y0CnZ2c3lRWThEVnNmeGs2aGZXNlZZ - cEZaUnRrUFFZZGFRd25nTnBrVjhuc1kKLS0tIHg0WXBQVGh2eVl2TEl0RkFsNElP - dXk5NmlTRHVENHRjbVVRd00vYkpoTGcKn4+hUSvHLU6xJA4UJSPWadFwPtyfKMii - 4wFBv1pRwUmxehIpS8C2HDnyBVnT0X/Sv00n27I2K4Z4/WzYTXPTdg== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBOcGpVNWtxdTJLYWwxakNk + dW5vRTNEOCtBNzlPbEpGR2g5MDhIYU5pWmlzCmo2YnRGU3FCZXBXUXNTdm5qNzBD + dWNHYy9kZTJwaWxHeFFmZFh2ZTBtbjgKLS0tIEliVHZIbWFFYUdOUDljNVNwUTNS + N0c2ZStaaU8wVFRKeTAwRmtDV25wc2sKjFO0FeMx3v5Zn4Ipm2549/boRorripM/ + q11ro/tNhsSt+GJ4FTQA2hnmRj+fG7BQuTw/ewXC0posKS5optxAbw== + -----END AGE ENCRYPTED FILE----- + - recipient: age18cgqlely56hgmhscllkmafwpjdk6dwep6ej3vkk97dzemp8jtuksqrrjjl + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsTUVIQTFYanlUQWswRUg4 + bSs4ZWZYcnFMVUlUUFdyVnE0T21IVkdWbTFnCkd4dndFcWR1RXRLeCtXWlYvTUFn + Uk81dHZXdTBSekJkaEZxUWZnZnUwR1kKLS0tIHc1a2tpSmNOc0s4akRCVzVjcVQv + cVNDL1JGd3dGNnIxSHhNckFLcTFJUWMKzqsDS/S3wHigjHBSgztEmg2na/2fR9As + JxUe/ZSdEaMKDj2ZJ3EM8u5aY6Sp79aNypHsPcOXjhX0VoAwPtRqFg== -----END AGE ENCRYPTED FILE----- - recipient: age1g7atkxdlt4ymeh7v7aa2yzr2hq2qkvzrc4r49ugttm3n582ymv9qrmpk8d enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBOM1VWRG5yRUx3MGNMK293 - RWJTQmgrUVRocHVuYkYwU0UwZXFrRm43cUZzCkFmMU9WT0M0cS9JQXJIMU1VZ1pu - TVZhc1lFNklRNCtzTmp0eldVaWhqUU0KLS0tIHJNbXV0U2VoejRCNWlFMG1RK2Nu - UWNoak1BS3RIY0NGRTlxcDIyYVlHM3MKHaA3riInA4NMkWQDg69uxGdR5f5l6rm5 - vywj80Mg/6Cy+mFtGc1y9RqqsFx3IpBW/ECeGrwtCLXvBKOnZ0gaZw== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBrMjVXZVFnTEczbEg1ZXBG + RmQ0c0RoZS9pWXdhT0ZVSURLVGxTWFBWM0FjCjV4cjVER05YZnFaclJnVEllSURy + YmtzUzdOK0pldjJiaFVKVDcwY3VzQncKLS0tIC9xMUd2OVlsZ3lmRDYyYlZpYjUv + anFDWE1CNGljY3ltN1lpdk9ZK1JySXMK58PNBo74DHptQTfT1ozV3Ikgf5BccRIH + Bw26F4GF8hjJL1Gp8szouJ9iN/mSNR47NdQN0nWy0GfOs5fClFfWvA== -----END AGE ENCRYPTED FILE----- - recipient: age1ly2endyt0y9xyddj6yuj4nw6fa3ltvzlvew4cr4lzs6dv8dkavpqadmyxx enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBUZDNzVFdlU0NYVTFsUkVB - OVhzbStSQTlEblhZdXh5cmZGTXVXNWIyQUhNCjc3ZlBHWDB5czlZcmVWU0dWUEVN - OHp3YmFRaTl3Q0FxSGxheU9RSUl1R1UKLS0tIDhNUmpkbkFlNzRlbWQ2dXdGcXVn - dlJVaFEwNVN4RGNVNVN3djM5c244SmsKO/jb+qiWJUu7BE3VoVQDquSxAQWDcSj2 - g+vLyNsrc1WsxyegfRYExGAx+DJHOCg9GyACXxifCP7xTVwfvH+yfg== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA4T014MWhrRjdpRGkvallr + cDIwME0yUzZseXZVUlBra2d5aXgvOFh1SXdzClM4QS9zanRhWU0xZVFSdktDaWc2 + TnFWL0N2M3VmUFVMWFN1OGxqSk05QWsKLS0tIG14L1BiVnJ0NFJCYUZnWXl6TU9v + VTU1ajNOTWxWTG5OUWRFMStLRXJlTEkKR8JA3U5MLBwMnFDZtlbrkJxmY9nENsza + JCmxPW1BTH+VadbefjGPAEwVj8XqgTCw5f8awNNZFUUe0SIud80pmQ== -----END AGE ENCRYPTED FILE----- - recipient: age15klj4t7gpfp69472mne4ue62pp6m4e04dmjyw7yf30qtqd3vl3uqjmcyxm enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzRVBMTXcyV29kVVcyRTlY - SExraURsT2ZkamlGRGphRUpYYUJUb3hzV1JjClVTTzRHU0MvVkMvRnNid1ZYdkpP - ZTlUYnVhTHlKREtIK1N3MnRnZTNHWTQKLS0tIDdaTHRvV0s1dEJseXVSdTFXUjlY - bndzMzRNSHVScDhCZkV3MDQwYnllcGcKOHUp8Q2Qmgt0CcOZZL6obGKDT8fTJ5eZ - hOeJzPoS0MY8BEICcxekOl0MICOhlIySXKx6m4MpRjgnMVX1Y5OMIQ== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBFWmpBbXY4YVZmVkVzQ3FW + eGl1SWZqZWFSa3lmNFBNRUh4UG9qREhQR2ljCk00bElJcHk3dTFNaG5FT1NiOU81 + aENlNHpkckFoRUVpakpmb3lIYU4zOEUKLS0tIFBpQjFEa2xiMmNHM3NhKzMrMFl3 + Vm91bU15TFJwSUd5WXI3VER5WnFEZlUK89Iu2PeG/7mXaTaCUClCVL44VFLJEZpM + 1s6QFAemy/B5UYeZBQsWiN1v9yAktvA+B7BeS0lyZohpV6SypLgjGQ== -----END AGE ENCRYPTED FILE----- - recipient: age1k73gy5em3js9zklnnkzp5hme9k04lny32fgahmzddknjw5c295asdyr4x6 enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBOZnR5YlZXN3N1WnFUcm1o - clJ4cVFoNng3WFNKWEd5K3l2TTUzWW54dUdjCnc1L2l6WkJBVHQ4RXNrV25kSW5s - dE0rdnJqUmllUS9VVE93OG5idUdzUk0KLS0tIDNFaGQ4R2lwRUI3L1NxL0UyYVdM - VjJJdzBuNGsvazVEdzNKRW5OdTFFQlUK5Rz0X+FopwNYu8AuC7eJPxPAr/yiZSg7 - s0Di5Wy1kfKXdwRbgSgXFZifg7bC4wsetEGi3SPVOa5yIduoMyYmaw== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA5RW9MOXMxcmZQRkhKcSt4 + cXJRYnVPVytOVDY1dlAveFg1eWlMZmxqcTNZCmVhaVZyZU9VcmNVczFLemNuYTJJ + SG5WWnNJNExBanVJb1VlT0VIWUtmYzAKLS0tIFBtSDN5aEJRVC90ek4weENZSmhQ + QUMrY0dlMStHZXY3VzBkZ3NZMFVnYzAK9C1GRRqXNWesrK3JYwol3hDkmjaBBUNW + 3CroUXgWDEQ5U8M3aFKSdhXND3WkPdDAKpt9cn34lEtlUOoy4MF/AQ== -----END AGE ENCRYPTED FILE----- - recipient: age1cmzh82q8k59yzceuuy2epmqu22g7m84gqvq056mhgehwpmvjadfsc3glc8 enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBoUkErTEtHSzJubXZXOEZa - Ymx3aTBjb01KbEtHUGROSllpMXV0UlpwVmgwCkRFcWs4RWJlUXIzeEtDVnJrQVpa - R09Ra0VOMHpGUUpUYWJyeks3TzRRRTgKLS0tIFA1cmJ4SmJEWXdaVGdwZjZZN01y - aTF1ZnZvOHYvZDhxSG5JSDR4Zm41NlkKijbFP2InlSCYZNR/kPWiedeRCFpLDx0M - susZTvBARIzPqR+nRNFQ/W7CvwPFPtWIBHR4lOhDmrFYjkCh085Ffg== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2TTV6QTNBNzV1T1BVQUlq + OEVGUFpvNDlUT1lWanlQZ0E0cDhaY29vRUJVCkVVbzR2d2gwWGNESUZNSGJianBC + ZDk5anJ2V0hwQUVEMUg1dmZFLzFLODAKLS0tIFlGR0xGamFKY3lwUkhaRFMzK1ds + U2dyTlNkdFVTYXJKQ0pBRnE2S295czQKy0E1Iajqkzg8cyTYNXoqf9bas8BaBP4+ + x7kjIbKIgBbWpT3niLA6Zl+SN0tLva67ddau4undz7YA7OZ4VzZ2Sw== -----END AGE ENCRYPTED FILE----- - recipient: age1wmx8y2hs83j2u5srdnfxljrzxm8jtxl6fr0mq7xf2ldxyglpzf2qq89rpx enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJWEF2Vk5ZaWRFK29MTGht - SGFPSmRndDA2L0E0Qlhxd2RVeVV1UDhMMTI4ClhORmRrb1c4RnZDTmdQZ0ROYkE2 - eHhNbE95RkE1ZHYzYXluN3prUFRmSTAKLS0tIGVTcndEOWdvV0krOTRjd0dyZHow - NG9PZlBEaURzbDF0cGo2d2VvQnFocWsKmZk7kBvKqJMDBl8rIaLw3NrUbVxzrRMN - 1xSb0iK/JDsCBDZiz+dmb4Kha/LGV2DM+AyXD0p/qSQ23yJM8WMukw== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnajNaMk9JcE1xSHpyOHV5 + L05FL2lPd0ZBNVBEQ2thRDZEdHFYL3Nnd2hJCjltSnJ3MUVvSGNyclppNTA5TGsw + OVozcGJ6c3RCMXkxTnN0M25RcFhyVDQKLS0tIGljdHZUaTNhVEZKZE1FbXNaOGVu + T2k5RlNWWGt6a3NSSE9CQjFvK01mMncKuUQ8PeSkfj0RJgDRWbFXBFAuWAOvKAox + LxBKr1TZ4oHIYJjtgP9mnKpNL5ikda+EkdEAt7V7exy2m3SvV59frg== -----END AGE ENCRYPTED FILE----- - recipient: age1mjgw3nxlnqdj04mgjz3wn7fj2nl2nxla4p2r2fn4nkvayfgp09pqllxzyh enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4OS9Ib1ZITjVyTC9VS1JP - dkJRdGhEVlJab25zWmc1S0J0Tk9PL0lZdVFFClo1S2Rabk5qZ1R0NzZIT211N0Nw - M21scUlmZ1hVL2lSdGk4dEoyZW80UGsKLS0tIDIyMTdyWlNrMkxnSmlLamJJdksr - QWpZejBTSHp2T0F1YWtwQmdjRmtKVXcKM4Hr5Y2eDOfwadbTSAJu62KGisljfOfI - BiIqArmfyjo1upw676EyV5JeV3vbi2jTT1rn8QyodsSdnI9ms9Z4KQ== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB3TFR0dG5YVjRYZUs0bk5W + SE5aK0lRWGhMVFhNNmNKbCtLa2tibXhJL0JzCldGN1hNdldXVWJ0b3VwaVRFMzk5 + OTdjTGpMS1NUTU50RjFRaVAxSHcrY1kKLS0tIHNvaEdING1kc2RMalhZL1FqL09K + d29KMENYVEVyNzUwNEVvbitXSHVjUncKokOmKLepXUcsYf/5QUBlsd0G8ZyCsV6T + z9otd0AdEPeRTSbh5pJ05WYEIR929aEPlaLLGhIdEI3XCOHey6GpvQ== -----END AGE ENCRYPTED FILE----- - recipient: age16vzhcvz8tyxj8e0f47fy0z4p3dsg0ak4vl52ut3l07a0tz465cxslmhevl enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBhSjlMNU5KNHEwdUZLejZq - SlIvVUlvTyt5QktHeGs1QWYvcjN5Y2RrMkM4Ckkwcmx1MTUxWmoza0gxMW9pcVd4 - Q0IyL3hSNFAzQVliOHZjelpjQXZJZk0KLS0tIFJzRys5czdmNUwxeUJtVFk2Z0VU - cTZHRFRMczNvbjdLNGsvZEtTNHQySWcKgiQ+amm1xrvTrU0lZKIKg6VMaxiSkd1O - +ib0vqwWUXDjxxTYaLitRCB92fwem3lsUJgmhN1kcRS3v1COKimbtA== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4ZFRFcDc1cWRLeHhmZ0dB + RFNOVGVpMENydmRMRkxuWXpCaHdxUWVwa1IwCmRUMmUzampKS3BidFVLYjJEOU1F + eEFqVXRCakpTclpGOEpMbUdVbkZORkUKLS0tIGdjUkg5UmRuV1M2Rmc2Uk5VeS9t + Z0ZtWWFRdkl5NDQwVjcvYmxKVG1wNFEKrP04VYIFR2V48RqpAMXgp1sDbr1NH0+0 + kvWoYuJcaKB1Tw3xBaLmrCjKt5Xd54linwpgidX4dOAqHjHWyNIHjQ== -----END AGE ENCRYPTED FILE----- - recipient: age15cx90pnp54xp5gxlt02yn9j2pz968wp3l5ukdkx55xuecp34e5pszjku4m enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAxUGRlWGplcGtDb25Ua3pV - d3lIZWdmbno4bTJVMEtZVERUdkR2UlB1ekc4CjlnLzhMamxnYVd5OVQ0VzZuVjdy - NVQxOU5UcXk3M0Y5M1hLWU1WM0o3S2MKLS0tIG9xQWRRZS9SQW9sTS9FOUtjQThE - ZDI1Um1RWm9jeFllY2dmTUdEZTJqU1kKh2Vm61sylFJwsLOjdFVRUXtabg0YzHvL - jHfr0ihHqMWQLQnMVFDrkTH84cnHDZm/yTijXXRF/yAkxlVUF7Tv/w== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBIeFFhbTRvQWNtaUNZWm5l + bnlBbVBlb28zd3hMRTFxRnlacUU5dng3SENVCnFDT0J0VWJmZXhueFFmTGs2TUlP + a1I4TkVnY2JPSkJlUmU2U3R0cFUzNmMKLS0tIGNLb2JVNnBZWDM0QngraUFkSXB4 + ZHR1N1Ivejc2VzFVdUlKL0lvdXJja0UKENB2JzmrXT/sFVpVzxR64OpoGwq65Q3H + KamGdqdsD1+MYsbnklUdHSJqqAbfTH+BYpjKtF+ZsPqaXcKB9VjKyQ== -----END AGE ENCRYPTED FILE----- - recipient: age18quey88vge7xytclg2nuq4ncme86dg04lxwczqxczmdchnjg3p0saehsnh enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBIR3MyS3NxWWNwRktuUGFy - THJ6SkhuZEhRWWNXNzhIYW0yekV6bG9MNkZVCnVNZU5ITDRJUlVWLy9hUUZrNFps - bGtsTWxIT2NrRFR2ZlhDZndubUlUOTQKLS0tIFl4Z093dHlGRXk5MytLMk40ekRy - Q2ZNT3NKZTRvdEZKWGhnQmhqVGp6a2cKbgjp8N1EOVRt1mcLBhWOl5vkNeeomu3L - ur5Orku3qMdm52Ff0msNzlPnrUmmblBcHSrsDvkWb3YI/Pe/zrRlJg== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBoWXJ0NUlEbXZNdHlzcmlC + N3VoaEFKVXdMTjJSYXV3Vlp3dmtFVCtNang4Cm93RDFVVXgrQnM4K2lraWNSVjBo + emR1MUlEdWJxY2h6RlhTdEx1SnpmZm8KLS0tIFlLenQ5WC8vZnpGVEZmVG1MbFZU + NkRQb3c0YjV1RnhzUm9CRVRtc3Z2MUEKbwFjrd9OxDUmw7SSMGYXjlzsMVL12iNv + 31E4a7ZCPw0zucoJpB3kFkQ/0vveOHZU3bjj0htm89H1BIaW0AZkzw== -----END AGE ENCRYPTED FILE----- - recipient: age1ax5hqk6e2ekgfx5u7pl8ayc3vvhrehyvtvf07llaxhs5azpnny0qpltrns enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBaY3ZqNHJSeVJlMlhnTXgy - QW92SXV2dlFsbjVTSlltWXJ6ajEvaEl6S1FVCk1oTkViQ2EvK0NiYlBZWGszRzVk - T0U1UzNqTXE5aW5xL3JwU3kwMnlacDAKLS0tIElBVGhncUpvNE1BS1UvZ0VSZUww - THZaSXhMNldUbHpBMVZQWXYwaVVQYWsKpUbG+lC37W6bzOuu9MaUmEZ5T1b5EC9k - VIY9XUoA7h0Z6G5Jrx/lrf6qsghMqd59gPA1qh8KlCJBAUJPHzQKVg== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBCdDZ1QThYRGJ0Ni9QNk9D + eE41Y3BsMmJWVkRkV05FWjhlcXF6ZlhsOEFzClBzZGxpU1Y1TmJVOHN0czNNcjZn + UGVQSm5RRWhwZjVDMDV6Qzk5RmljMjQKLS0tIGp5dUljSEExUGNxUnRZcHVTMFdj + ZW5MY1NJMmRJUmcyRTJnVDMzUzB3cHcK54+0I7202n4P1kZGaCI5kM5BRAnNh/ts + 7ygeuksGvi+7gs2UFn3LCUdsaL7RsBuX3NBiVyBvE02gfKecT7W/XQ== -----END AGE ENCRYPTED FILE----- - lastmodified: "2025-12-22T09:11:50Z" - mac: ENC[AES256_GCM,data:D6qKXhSuYGPm9K7Al/la3O++DmOTQN5++96k44IIvgSR5Q3kTkMYxPsf3PWNyjMm09+9aRauuHPHj098+W1rXaq43Iqr24JAuaZNwLyxtqkaibv/Zhx1RgEWGXyOHDtlfIPoULNKVZ0ls2mtk40oQHsfhnRyS42m+HMQnL+tCF4=,iv:uzKbpZu9P05KbaXnBUxN4rA9nYOXpeK+E/scWoFxpcs=,tag:PuMdBx3JOT1EfFkOPV0G2g==,type:str] + - recipient: age1c2enwel9un28dcs4wg0vcyamx9a4a6g3walkhu8w5lqhmd804paq9d24as + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBKQ04yNzR6MXU3aEdFNU1J + c29qdk5pbjA0Q1FGcUZTaytrWFNyM3RXRVNVCmVyU2czclVjYmpmQmNuTmhHdUlv + MkpES2FmK1JLc0tmN1BBRW95YXl6SWcKLS0tIHhCendLQ0ZmbThYb2phTG1CWk53 + TTErRmhuaDlkenNSdkxiWEVTSGFEazgKZj2nEn4bgT8+oVUG38UDGmNrq9wAk7sq + 9PzYw3cjGwRUFwT4lV3vK8ZNU6DAepga7ebwxfPNjP35h9eMtRVQIg== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2026-01-03T05:23:41Z" + mac: ENC[AES256_GCM,data:D1iqvJh7qLoC6GlwFItacyb15X7LrnXZfSQQVhFRQRnVbFc/ECqM6P9OP4PlCfAH5Up0c/iv84MsE/UFubGKGR0i19lCQ78nLPNkFfvJ+NXg0sr03FiFmW0Q8hwzauzdMUiQr9/36D/ax+BVBTQyWgZEneiCY7KoDaQKJWzOGT4=,iv:+AZ+1u+TZNqrLMaDcHU0fgPsvx5HFprlKQExH5pIjyM=,tag:ytnj7FQQ6YivKncyZUHigg==,type:str] pgp: - - created_at: "2025-12-15T21:53:40Z" + - created_at: "2026-01-02T21:17:55Z" enc: |- -----BEGIN PGP MESSAGE----- - hQIMAwDh3VI7VctTARAAq7Q/kBF7Xgf1sPEfLLNnue7G+uErtVHLwyGwqDRAE9aw - NQ9lg2xJIro3sJhaVz0EkKyuydkWw3vjAwltnRd6FVtfKnBeDirFJdoM2zOLNdQe - OYbpGp0/C2szgYkBqVU5hDh0Gzgho0UzO0rhwf/c+uVNTKPfUJBym72p2OKJSUcm - NWhf1On/aaHsLQ46y8TJxzoxI2PsziBXqMQGbKofAgnNPSyYRvzRWR5+1lShgt4e - KYLgzGKbLp+ENptPOmqLbKuuVhFX8WXULyRvS3hRTKjezncPuRqxG/GE+4BzS7ih - 6WZ+WBCeG4sNQaeTXDjtL18DyyzT9y+UihE8O+icIPvNpGkJgY5OiKfJU3W/Wqih - wxl3Pqgb9BDvz/jHMUt2GN07JuX1at/Plx/kG3xMmgrSJfDi5ZA6oiBi5B4AqOl5 - okn635mBXFnfGuz65hrP05P7p58BXLXvMZMsrk8kNknQu7mCeXUpDvXCFRo9xZnz - OeTc7LUP9Dp5rlSJSzkBibrZ4BxebFL4ujPQ+8cibIu2XAoSQPQUCmscq1D6K5aY - 37GS/HWwNzz3PimT/D2FlKpbE9Krn9z+H9a19DIpfdWhxD06pu9pLO9cPBmYyeHs - jAnd0RRg20mF2MBy2f24nu8+ziCARJQC0H9heR386zEDCKMCo2XkC0DqbN5CL16F - AgwDC9FRLmchgYQBEAC+cfiPynGPEFfdnt+Hta9/FMI5WMXMbV4XFqD0XW6VVRLe - 1o/kANe3TM2hFzf2qVfQ4oLPxOlQtJNylxYUl2Z9EJB+YLtuHgJ9ffx1Bkc2a6md - dxb2Qv6DaKv53EiD8quAIPhaC3XzVsfrAhwSL4pga11wFYEge1wXpGGFp2LNbghH - IW273sFPv6fBIqjM0dF4QtLFsphc6dRZdW6qzuFEkU++RvfZmoKdnrzMFTaYhiX1 - lwuHuiIWP3wX7YNGgTuKcecvlUo3kI67D6SGHBXVkeyejYH+wfzrApUGdHJ4NxY7 - l3xGkrWIwqWgqh5aNErgKAA8sUS/Gcn50UHlsmO5RY3qJQPmPK1p7nlH9IKktG+A - 6AvzdgWqHnqaA/Cb90bGJZP4m447XQHIv9UrwB+6rffRA7Dvw7oFsNnh8I3Nl/W3 - WNlJ1vuXmZXQhlBS/95DvWDOzOpqIh8XSYAR44506n9u9ZRh00dB/SQ5yTA7AjSX - +Jz4SAo+8I43CiBEUbGCoxRgwHa7h6UWK7LsXCGHkqHHS7VRWZ3MiZkrVdbwv8+D - fkcwHmxJQGxjhU5F/cdmMX2KYWXu93I2Ohk+vZ7IpDbP2H/G5epgn+pUHbEbBYrl - J874Pj2gKA8vJS7bm7tbMVyCebsRwzt2eh8qwAfJL9HAup7a+hIQaIVqxrvnz9Je - AcPIsNoAqTsZeGvL0LoLQJ5myKPsb9hs/pOYjRjjM6Fdyy1GwNCdTdcLFjTthVLI - 7JuiudM3Ce/c+mW7+57oKFAqGPB3HtAfye0A9bJ3R9co4LAvWSMk52ia+LanvQ== - =Iisf + hQIMAwDh3VI7VctTARAA001QiHU3uO1goHUTsx+QnhCrfIBDjVAHhZtj8CEx1tqk + YaxNUNDra1uyGQqwdH1Ly8h6cSzeUfEtPfkjsvegYo58PSkz5F6mrpROkB1mzzfG + /H3MpCHTzroXPmrT3lNxXZyYqxGzcfLBLU05etEiFV+sV2mjoV1nYY2gTnH8XtV9 + HVayfxR4cYZOS7ox8SkGoTvZHsbNT5+jaJn7GqRjrcjmjd7S3gvq83ROe82YHNA0 + 1O04PSGwnaGC88E9Y/7segZC8Tl1e25Z0c8yMd8JHsn/RL8beHwUHTn6nuyo65Bj + Xmic9M5C16ur5yQtVr/+xU/FCbmkecsOL4u5YgECFt5w7zJZzfGUHa2Z5dIQ9T5o + 2oY/h+GsN9xTM/6ZVcgCbZrcHnIvP+5VmIEmXkdTtb4cvTFJMkDWxx7NrS8dI/Rh + 76hoCStleuLpw8+zwIYC4QdJfFWBaQyw8w0nON/qp4E+kyU8E3syMsjqFOqclSeU + vFDTvgl4/Z0nPxTl+IBzG0cBPMjUXgmvGZj4NAhZhE5nNwkA34GvmMcfjVhy8ROX + WWwgiFBP20Po7p3aHz57KA4UTkgIZfhK4BRbb4BhQoaYEcXL8Sata/SJOR3RT2vQ + 8uB8H8orPpugdafS4wLv/qcwYn6pa2ubEQG59WfihIomDWh4jl+i+kfnM5fYwX+F + AgwDC9FRLmchgYQBEAC4bp8seECcmzLFnRZsABQI/TJg7jKDEZuudEiEGmKUhhOf + Mz1pQEEHyh5aTFSJDVq2bNgPKt4NJ/bQL1QOq1lnsprpAAL6+HoyezA6ygfh6YmW + 8kJOwJrVXlgo+H3XD4XeoA7VFVdcHkszFYgq4KCd8KX+haSkt0Wbj0s7sQkKQo/j + PUUQN34pxfyJ0IiqlKuLgpu7ggMJkDqZGoYnXH91Qt9TYi/liMMx4wo82YOQFH0d + xJUewsVvUpjwmFotOBKdhk+zZlOoNNr7QnyZ2SRyXNUFI66fsw5QsbwDSXJ4Hy8C + 3RVRZ7baxw2p5wMyZQBtMyqu9quS/XED0cZA+sxvKOTrC24YbD+nb1ktc/9ZJghH + TZjhQLLhmrbHPNnrHIrnEJ6XrxbgNHWTFE+yX/e2lRIRGwz0cfgbY2pLFJ+6uhSu + PkodKyq/PCerYZPnUMjnLxogNtgZhf6jK4AzG2xuFZne37NBEaXY3q1ktwafpRia + 1UrO4PC2cwJpNuIpMfHbA8xieppwRZbqBSSAM80V12963cFakUxLmZdMG0ApFTDU + 5NoAxwBaRooHjTgYwdfgtjikwsPOf/VFWrB2IkBqF/QhxdV2sxasmJCM04e1gidi + 0prCX7KoMNtBnHpubfwV7sTNdhN15IKmyHNLw9hXfZZmN9UAh6LxUS/KHMZSv9Je + AR+Ty00mCszxvBco6ee6Tv7gEhBU2ZBhu4yyWZC5Kd0pNvr4ZtCGNNY5wIjv00jd + nNnMW7MrEF4Tz/So+1TJHiLLFuap2RpXifCHRiXDMHqOUuFUgGFSNNlhYXycXQ== + =lDR9 -----END PGP MESSAGE----- fp: 4BE7925262289B476DBBC17B76FD3810215AE097 unencrypted_suffix: _unencrypted