diff --git a/hosts.nix b/hosts.nix index 503a62f..e467c8e 100644 --- a/hosts.nix +++ b/hosts.nix @@ -115,13 +115,29 @@ with lib; wireguardKey = "birdLVh8roeZpcVo308Ums4l/aibhAxbi7MBsglkJyA="; }; - # kalessin = { - # # hostKey = ""; - # endpoint = "kalessin.birdsong.network"; - # ipv4 = "10.127.1.3"; - # ipv6 = "fd70:81ca:0f8f:1::3"; - # # wireguardKey = ""; - # }; + kalessin = { + hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOPt3iSSmgnlsv1/jafgZgI7o8UuXzcAL45hID2ThfS8 root@kalessin"; + endpoint = "kalessin.birdsong.network"; + ipv4 = "10.127.1.3"; + ipv6 = "fd70:81ca:0f8f:1::3"; + wireguardKey = "9vyIoXuu1UVjV+aFeuX9LoHRBeAAsiHbrLmYQY4nsQQ="; + }; + + reese = { + hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPd0qGxvcMLDwX1bqYpwOUL5c/CIgBllMFr+bGkwiwAn root@reese"; + endpoint = "reese.internetcat.org"; + ipv4 = "10.127.1.5"; + ipv6 = "fd70:81ca:0f8f:1::5"; + wireguardKey = "K/wArdwedMwjQULPOjOrPoEwsevaRhBZ3rUrH7pNWHM="; + }; + + bear = { + hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIZ9Kn1CIcDHaleKHf7zO6O30Rbxs/FwL0/Ie+mEjZJr root@bear"; + endpoint = "bear.internetcat.org"; + ipv4 = "10.127.1.6"; + ipv6 = "fd70:81ca:0f8f:1::6"; + wireguardKey = "mXWQo1Vn2YFr7OhFNc3g7Zbcf/qnwuLx8HHCn6HKwBc="; + }; tohru = { hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOk8wuGzF0Y7SaH9aimo3SmCz99MTQwL+rEVhx0jsueU root@tohru"; @@ -132,14 +148,39 @@ with lib; wireguardKey = "lk3PCQM1jmZoI8sM/rWSyKNuZOUnjox3n9L9geJD+18="; }; - # kilgharrah = { - # # hostKey = ""; - # subnet = "weyrhold"; - # endpoint = "192.168.2.1"; - # ipv4 = "10.127.3.1"; - # ipv6 = "fd70:81ca:0f8f:3::1"; - # # wireguardKey = ""; - # }; + groves = { + hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPQNZ/Q+x7mDYfYXftpZpWkfPByyMBbYmVFobM4vSDW2 root@groves"; + subnet = "roaming"; + ipv4 = "10.127.2.2"; + ipv6 = "fd70:81ca:0f8f:2::2"; + wireguardKey = "Tsbw77gIsoa3eBWZpQwZAM6EPpq+7yS33qoFHS4tXlY="; + }; + + carter = { + hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEHHHYG6A995Po05+JXQsvB79ZoIiSOJnW6AiJgVYPic root@carter"; + subnet = "roaming"; + ipv4 = "10.127.2.3"; + ipv6 = "fd70:81ca:0f8f:2::3"; + wireguardKey = "bJVSqLNtcRsZH3fki9sZ8TofgKd8JgRf7z/oHzUjMGk="; + }; + + kilgharrah = { + hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOgGF3gzzlMbxxk3UAAgHJ7sDdjqtrw7UW16M1XhXtz2 root@kilgharrah"; + subnet = "weyrhold"; + endpoint = "192.168.2.1"; + ipv4 = "10.127.3.1"; + ipv6 = "fd70:81ca:0f8f:3::1"; + wireguardKey = "LXQVU0MFKVO/mml5krHnf6NcL4GxF6XFJmvpmjrLBFA="; + }; + + elucredassa = { + hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA+Y/vqGNc1wXUAg4XMAAcLupkggywj2LpYDwA16ONbH root@elucredassa"; + subnet = "weyrhold"; + endpoint = "192.168.2.2"; + ipv4 = "10.127.3.2"; + ipv6 = "fd70:81ca:0f8f:3::2"; + wireguardKey = "hwgf4efFNOtQwCDMpZI4uyPDjzPzHF056pWgobGaxRY="; + }; shaw = { hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMC0AomCZZiUV/BCpImiV4p/vGvFaz5QNc+fJLXmS5p root@shaw"; diff --git a/peering.nix b/peering.nix index eea580f..4fee698 100644 --- a/peering.nix +++ b/peering.nix @@ -1,7 +1,7 @@ { config, lib, pkgs, ... }: -with lib; let + inherit (lib) types mkIf mkMerge mkOption mkEnableOption optionals filterAttrs mapAttrsToList; cfg = config.birdsong.peering; hostName = if null != cfg.hostName then cfg.hostName else config.networking.hostName; hosts = config.birdsong.hosts; @@ -31,12 +31,27 @@ in type = types.bool; }; privateKeyFile = mkOption { + default = null; description = '' - Path to the private key for this peer, as generated by `wg genkey`. Must - be readable by the user "systemd-network"; systemd.netdev(5) recommends - it be owned by "root:systemd-network" with a "0640" file mode. + Path to the private key for this peer, as generated by + {command}`wg genkey`. Must be readable by the user "systemd-network"; + systemd.netdev(5) recommends it be owned by "root:systemd-network" with + a "0640" file mode. Set exactly one of this or + {option}`birdsong.peering.privateKeyCredential`. ''; - type = types.path; + type = with types; nullOr str; + }; + privateKeyCredential = mkOption { + default = null; + description = '' + Name of a systemd credential containing a private key for this peer, as + generated by {command}`wg genkey`. Set exactly one of this or + {option}`birdsong.peering.privateKeyFile`. + + To load the credential from an encrypted credential file, set + {option}`systemd.services.systemd-networkd.serviceConfig.LoadCredentialEncrypted`. + ''; + type = with types; nullOr str; }; persistentKeepalive = mkOption { default = 0; @@ -60,8 +75,8 @@ in message = "birdsong depends on networkd. systemd.network.enable must be true"; } { - assertion = cfg ? privateKeyFile; - message = "birdsong.peering.privateKeyFile must be set"; + assertion = (cfg.privateKeyCredential != null && cfg.privateKeyFile == null) || (cfg.privateKeyFile != null && cfg.privateKeyCredential == null); + message = "exactly one of birdsong.peering.privateKeyCredential or birdsong.peering.privateKeyFile must be set"; } { assertion = hostName != null; @@ -77,33 +92,38 @@ in networking.firewall.allowedUDPPorts = mkIf cfg.openPorts [ host.port ]; systemd.network = { - enable = true; - netdevs."30-birdsong" = { netdevConfig = { Name = cfg.interface; Kind = "wireguard"; Description = "wireguard tunnel to the birdsong network"; }; - wireguardConfig = { - PrivateKeyFile = cfg.privateKeyFile; - ListenPort = host.port; - }; + + wireguardConfig = mkMerge [ + { + ListenPort = host.port; + } + (mkIf (cfg.privateKeyCredential != null) { + PrivateKey = "@${cfg.privateKeyCredential}"; + }) + (mkIf (cfg.privateKeyFile != null) { + PrivateKeyFile = cfg.privateKeyFile; + }) + ]; + wireguardPeers = let canDirectPeer = host: peer: peer.subnet == "internet" || (host.subnet != "roaming" && peer.subnet == host.subnet); in mapAttrsToList (name: peer: { - wireguardPeerConfig = { - PublicKey = peer.wireguardKey; - AllowedIPs = [ peer.ipv4 peer.ipv6 ] - ++ optionals peer.isRouter [ "10.127.0.0/16" "fd70:81ca:0f8f::/48" ]; - Endpoint = mkIf (canDirectPeer host peer) "${peer.endpoint}:${toString peer.port}"; - PersistentKeepalive = mkIf (peer.subnet != host.subnet) cfg.persistentKeepalive; - }; + PublicKey = peer.wireguardKey; + AllowedIPs = [ peer.ipv4 peer.ipv6 ] + ++ optionals peer.isRouter [ "10.127.0.0/16" "fd70:81ca:0f8f::/48" ]; + Endpoint = mkIf (canDirectPeer host peer) "${peer.endpoint}:${toString peer.port}"; + PersistentKeepalive = mkIf (peer.subnet != host.subnet) cfg.persistentKeepalive; }) - (filterAttrs (name: peer: peer != host && (host.subnet == "internet" || canDirectPeer host peer)) hosts); + (filterAttrs (name: peer: peer != host && (canDirectPeer host peer || canDirectPeer peer host)) hosts); }; networks."30-birdsong" = { @@ -112,5 +132,12 @@ in ++ optionals host.isRouter [ "10.127.0.0/16" "fd70:81ca:0f8f::/48" ]; }; }; + + programs.ssh.knownHosts = lib.mapAttrs' + (name: host: lib.nameValuePair ("birdsong_" + name) { + publicKey = host.hostKey; + hostNames = [ host.ipv4 host.ipv6 ]; + }) + hosts; }; }