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