at 25.11-pre 5.7 kB view raw
1{ 2 lib, 3 config, 4 pkgs, 5 ... 6}: 7with lib; 8let 9 cfg = config.services.wgautomesh; 10 settingsFormat = pkgs.formats.toml { }; 11 configFile = 12 # Have to remove nulls manually as TOML generator will not just skip key 13 # if value is null 14 settingsFormat.generate "wgautomesh-config.toml" ( 15 filterAttrs (k: v: v != null) ( 16 mapAttrs (k: v: if k == "peers" then map (e: filterAttrs (k: v: v != null) e) v else v) cfg.settings 17 ) 18 ); 19 runtimeConfigFile = 20 if cfg.enableGossipEncryption then "/run/wgautomesh/wgautomesh.toml" else configFile; 21in 22{ 23 options.services.wgautomesh = { 24 enable = mkEnableOption "the wgautomesh daemon"; 25 logLevel = mkOption { 26 type = types.enum [ 27 "trace" 28 "debug" 29 "info" 30 "warn" 31 "error" 32 ]; 33 default = "info"; 34 description = "wgautomesh log level."; 35 }; 36 enableGossipEncryption = mkOption { 37 type = types.bool; 38 default = true; 39 description = "Enable encryption of gossip traffic."; 40 }; 41 gossipSecretFile = mkOption { 42 type = types.path; 43 description = '' 44 File containing the gossip secret, a shared secret key to use for gossip 45 encryption. Required if `enableGossipEncryption` is set. This file 46 may contain any arbitrary-length utf8 string. To generate a new gossip 47 secret, use a command such as `openssl rand -base64 32`. 48 ''; 49 }; 50 enablePersistence = mkOption { 51 type = types.bool; 52 default = true; 53 description = "Enable persistence of Wireguard peer info between restarts."; 54 }; 55 openFirewall = mkOption { 56 type = types.bool; 57 default = true; 58 description = "Automatically open gossip port in firewall (recommended)."; 59 }; 60 settings = mkOption { 61 type = types.submodule { 62 freeformType = settingsFormat.type; 63 options = { 64 65 interface = mkOption { 66 type = types.str; 67 description = '' 68 Wireguard interface to manage (it is NOT created by wgautomesh, you 69 should use another NixOS option to create it such as 70 `networking.wireguard.interfaces.wg0 = {...};`). 71 ''; 72 example = "wg0"; 73 }; 74 gossip_port = mkOption { 75 type = types.port; 76 description = '' 77 wgautomesh gossip port, this MUST be the same number on all nodes in 78 the wgautomesh network. 79 ''; 80 default = 1666; 81 }; 82 lan_discovery = mkOption { 83 type = types.bool; 84 default = true; 85 description = "Enable discovery of peers on the same LAN using UDP broadcast."; 86 }; 87 upnp_forward_external_port = mkOption { 88 type = types.nullOr types.port; 89 default = null; 90 description = '' 91 Public port number to try to redirect to this machine's Wireguard 92 daemon using UPnP IGD. 93 ''; 94 }; 95 peers = mkOption { 96 type = types.listOf ( 97 types.submodule { 98 options = { 99 pubkey = mkOption { 100 type = types.str; 101 description = "Wireguard public key of this peer."; 102 }; 103 address = mkOption { 104 type = types.str; 105 description = '' 106 Wireguard address of this peer (a single IP address, multiple 107 addresses or address ranges are not supported). 108 ''; 109 example = "10.0.0.42"; 110 }; 111 endpoint = mkOption { 112 type = types.nullOr types.str; 113 description = '' 114 Bootstrap endpoint for connecting to this Wireguard peer if no 115 other address is known or none are working. 116 ''; 117 default = null; 118 example = "wgnode.mydomain.example:51820"; 119 }; 120 }; 121 } 122 ); 123 default = [ ]; 124 description = "wgautomesh peer list."; 125 }; 126 }; 127 128 }; 129 default = { }; 130 description = "Configuration for wgautomesh."; 131 }; 132 }; 133 134 config = mkIf cfg.enable { 135 services.wgautomesh.settings = { 136 gossip_secret_file = mkIf cfg.enableGossipEncryption "$CREDENTIALS_DIRECTORY/gossip_secret"; 137 persist_file = mkIf cfg.enablePersistence "/var/lib/wgautomesh/state"; 138 }; 139 140 systemd.services.wgautomesh = { 141 path = [ pkgs.wireguard-tools ]; 142 environment = { 143 RUST_LOG = "wgautomesh=${cfg.logLevel}"; 144 }; 145 description = "wgautomesh"; 146 serviceConfig = { 147 Type = "simple"; 148 149 ExecStart = "${getExe pkgs.wgautomesh} ${runtimeConfigFile}"; 150 Restart = "always"; 151 RestartSec = "30"; 152 LoadCredential = mkIf cfg.enableGossipEncryption [ "gossip_secret:${cfg.gossipSecretFile}" ]; 153 154 ExecStartPre = mkIf cfg.enableGossipEncryption [ 155 '' 156 ${pkgs.envsubst}/bin/envsubst \ 157 -i ${configFile} \ 158 -o ${runtimeConfigFile}'' 159 ]; 160 161 DynamicUser = true; 162 StateDirectory = "wgautomesh"; 163 StateDirectoryMode = "0700"; 164 RuntimeDirectory = "wgautomesh"; 165 AmbientCapabilities = "CAP_NET_ADMIN"; 166 CapabilityBoundingSet = "CAP_NET_ADMIN"; 167 }; 168 wantedBy = [ "multi-user.target" ]; 169 }; 170 networking.firewall.allowedUDPPorts = mkIf cfg.openFirewall [ cfg.settings.gossip_port ]; 171 }; 172}