at master 7.9 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7 8with lib; 9let 10 cfg = config.services.easytier; 11 settingsFormat = pkgs.formats.toml { }; 12 13 genFinalSettings = 14 inst: 15 attrsets.filterAttrsRecursive (_: v: v != { }) ( 16 attrsets.filterAttrsRecursive (_: v: v != null) ( 17 { 18 inherit (inst.settings) 19 instance_name 20 hostname 21 ipv4 22 dhcp 23 listeners 24 ; 25 network_identity = { 26 inherit (inst.settings) network_name network_secret; 27 }; 28 peer = map (p: { uri = p; }) inst.settings.peers; 29 } 30 // inst.extraSettings 31 ) 32 ); 33 34 genConfigFile = 35 name: inst: 36 if inst.configFile == null then 37 settingsFormat.generate "easytier-${name}.toml" (genFinalSettings inst) 38 else 39 inst.configFile; 40 41 activeInsts = filterAttrs (_: inst: inst.enable) cfg.instances; 42 43 settingsModule = name: { 44 options = { 45 instance_name = mkOption { 46 type = types.str; 47 default = name; 48 description = "Identify different instances on same host"; 49 }; 50 51 hostname = mkOption { 52 type = with types; nullOr str; 53 default = null; 54 description = "Hostname shown in peer list and web console."; 55 }; 56 57 network_name = mkOption { 58 type = with types; nullOr str; 59 default = null; 60 description = "EasyTier network name."; 61 }; 62 63 network_secret = mkOption { 64 type = with types; nullOr str; 65 default = null; 66 description = '' 67 EasyTier network credential used for verification and 68 encryption. It can also be set in environmentFile. 69 ''; 70 }; 71 72 ipv4 = mkOption { 73 type = with types; nullOr str; 74 default = null; 75 description = '' 76 IPv4 cidr address of this peer in the virtual network. If 77 empty, this peer will only forward packets and no TUN device 78 will be created. 79 ''; 80 example = "10.144.144.1/24"; 81 }; 82 83 dhcp = mkOption { 84 type = types.bool; 85 default = false; 86 description = '' 87 Automatically determine the IPv4 address of this peer based on 88 existing peers on network. 89 ''; 90 }; 91 92 listeners = mkOption { 93 type = with types; listOf str; 94 default = [ 95 "tcp://0.0.0.0:11010" 96 "udp://0.0.0.0:11010" 97 ]; 98 description = '' 99 Listener addresses to accept connections from other peers. 100 Valid format is: `<proto>://<addr>:<port>`, where the protocol 101 can be `tcp`, `udp`, `ring`, `wg`, `ws`, `wss`. 102 ''; 103 }; 104 105 peers = mkOption { 106 type = with types; listOf str; 107 default = [ ]; 108 description = '' 109 Peers to connect initially. Valid format is: `<proto>://<addr>:<port>`. 110 ''; 111 example = [ 112 "tcp://example.com:11010" 113 ]; 114 }; 115 }; 116 }; 117 118 instanceModule = 119 { name, ... }: 120 { 121 options = { 122 enable = mkOption { 123 type = types.bool; 124 default = true; 125 description = "Enable the instance."; 126 }; 127 128 configServer = mkOption { 129 type = with types; nullOr str; 130 default = null; 131 description = '' 132 Configure the instance from config server. When this option 133 set, any other settings for configuring the instance manually 134 except `hostname` will be ignored. Valid formats are: 135 136 - full uri for custom server: `udp://example.com:22020/<token>` 137 - username only for official server: `<token>` 138 ''; 139 example = "udp://example.com:22020/myusername"; 140 }; 141 142 configFile = mkOption { 143 type = with types; nullOr path; 144 default = null; 145 description = '' 146 Path to easytier config file. Setting this option will 147 override `settings` and `extraSettings` of this instance. 148 ''; 149 }; 150 151 environmentFiles = mkOption { 152 type = with types; listOf path; 153 default = [ ]; 154 description = '' 155 Environment files for this instance. All command-line args 156 have corresponding environment variables. 157 ''; 158 example = literalExpression '' 159 [ 160 /path/to/.env 161 /path/to/.env.secret 162 ] 163 ''; 164 }; 165 166 settings = mkOption { 167 type = types.submodule (settingsModule name); 168 default = { }; 169 description = '' 170 Settings to generate {file}`easytier-${name}.toml` 171 ''; 172 }; 173 174 extraSettings = mkOption { 175 type = settingsFormat.type; 176 default = { }; 177 description = '' 178 Extra settings to add to {file}`easytier-${name}.toml`. 179 ''; 180 }; 181 182 extraArgs = mkOption { 183 type = with types; listOf str; 184 default = [ ]; 185 description = '' 186 Extra args append to the easytier command-line. 187 ''; 188 }; 189 }; 190 }; 191 192in 193{ 194 options.services.easytier = { 195 enable = mkEnableOption "EasyTier daemon"; 196 197 package = mkPackageOption pkgs "easytier" { }; 198 199 allowSystemForward = mkEnableOption '' 200 Allow the system to forward packets from easytier. Useful when 201 `proxy_forward_by_system` enabled. 202 ''; 203 204 instances = mkOption { 205 description = '' 206 EasyTier instances. 207 ''; 208 type = types.attrsOf (types.submodule instanceModule); 209 default = { }; 210 example = { 211 settings = { 212 network_name = "easytier"; 213 network_secret = "easytier"; 214 ipv4 = "10.144.144.1/24"; 215 peers = [ 216 "tcp://public.easytier.cn:11010" 217 "wss://example.com:443" 218 ]; 219 }; 220 extraSettings = { 221 flags.dev_name = "tun1"; 222 }; 223 }; 224 }; 225 }; 226 227 config = mkIf cfg.enable { 228 environment.systemPackages = [ cfg.package ]; 229 230 systemd.services = mapAttrs' ( 231 name: inst: 232 let 233 configFile = genConfigFile name inst; 234 in 235 nameValuePair "easytier-${name}" { 236 description = "EasyTier Daemon - ${name}"; 237 wants = [ 238 "network-online.target" 239 "nss-lookup.target" 240 ]; 241 after = [ 242 "network-online.target" 243 "nss-lookup.target" 244 ]; 245 wantedBy = [ "multi-user.target" ]; 246 path = with pkgs; [ 247 cfg.package 248 iproute2 249 bash 250 ]; 251 restartTriggers = inst.environmentFiles ++ (optionals (inst.configServer == null) [ configFile ]); 252 serviceConfig = { 253 Type = "simple"; 254 Restart = "on-failure"; 255 EnvironmentFile = inst.environmentFiles; 256 StateDirectory = "easytier/easytier-${name}"; 257 StateDirectoryMode = "0700"; 258 WorkingDirectory = "/var/lib/easytier/easytier-${name}"; 259 ExecStart = escapeShellArgs ( 260 [ 261 "${cfg.package}/bin/easytier-core" 262 ] 263 ++ optionals (inst.configServer != null) ( 264 [ 265 "-w" 266 "${inst.configServer}" 267 ] 268 ++ (optionals (inst.settings.hostname != null) [ 269 "--hostname" 270 "${inst.settings.hostname}" 271 ]) 272 ) 273 ++ optionals (inst.configServer == null) [ 274 "-c" 275 "${configFile}" 276 ] 277 ++ inst.extraArgs 278 ); 279 }; 280 } 281 ) activeInsts; 282 283 boot.kernel.sysctl = mkIf cfg.allowSystemForward { 284 "net.ipv4.conf.all.forwarding" = mkOverride 97 true; 285 "net.ipv6.conf.all.forwarding" = mkOverride 97 true; 286 }; 287 }; 288 289 meta.maintainers = with maintainers; [ 290 ltrump 291 ]; 292}