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