at 25.11-pre 3.9 kB view raw
1{ 2 config, 3 pkgs, 4 lib, 5 ... 6}: 7let 8 inherit (lib) 9 mkEnableOption 10 mkPackageOption 11 mkOption 12 types 13 ; 14 15 cfg = config.services.wg-access-server; 16 17 settingsFormat = pkgs.formats.yaml { }; 18 configFile = settingsFormat.generate "config.yaml" cfg.settings; 19in 20{ 21 22 options.services.wg-access-server = { 23 enable = mkEnableOption "wg-access-server"; 24 25 package = mkPackageOption pkgs "wg-access-server" { }; 26 27 settings = mkOption { 28 type = lib.types.submodule { 29 freeformType = settingsFormat.type; 30 options = { 31 dns.enabled = mkOption { 32 type = types.bool; 33 default = true; 34 description = '' 35 Enable/disable the embedded DNS proxy server. 36 This is enabled by default and allows VPN clients to avoid DNS leaks by sending all DNS requests to wg-access-server itself. 37 ''; 38 }; 39 storage = mkOption { 40 type = types.str; 41 default = "sqlite3://db.sqlite"; 42 description = "A storage backend connection string. See [storage docs](https://www.freie-netze.org/wg-access-server/3-storage/)"; 43 }; 44 }; 45 }; 46 description = "See <https://www.freie-netze.org/wg-access-server/2-configuration/> for possible options"; 47 }; 48 49 secretsFile = mkOption { 50 type = types.path; 51 description = '' 52 yaml file containing all secrets. this needs to be in the same structure as the configuration. 53 54 This must to contain the admin password and wireguard private key. 55 As well as the secrets for your auth backend. 56 57 Example: 58 ```yaml 59 adminPassword: <admin password> 60 wireguard: 61 privateKey: <wireguard private key> 62 auth: 63 oidc: 64 clientSecret: <client secret> 65 ``` 66 ''; 67 }; 68 }; 69 70 config = lib.mkIf cfg.enable { 71 assertions = 72 map 73 (attrPath: { 74 assertion = !lib.hasAttrByPath attrPath config.services.wg-access-server.settings; 75 message = '' 76 {option}`services.wg-access-server.settings.${lib.concatStringsSep "." attrPath}` must definded 77 in {option}`services.wg-access-server.secretsFile`. 78 ''; 79 }) 80 [ 81 [ "adminPassword" ] 82 [ 83 "wireguard" 84 "privateKey" 85 ] 86 [ 87 "auth" 88 "sessionStore" 89 ] 90 [ 91 "auth" 92 "oidc" 93 "clientSecret" 94 ] 95 [ 96 "auth" 97 "gitlab" 98 "clientSecret" 99 ] 100 ]; 101 102 boot.kernel.sysctl = { 103 "net.ipv4.conf.all.forwarding" = "1"; 104 "net.ipv6.conf.all.forwarding" = "1"; 105 }; 106 107 systemd.services.wg-access-server = { 108 description = "WG access server"; 109 wantedBy = [ "multi-user.target" ]; 110 requires = [ "network-online.target" ]; 111 after = [ "network-online.target" ]; 112 script = '' 113 # merge secrets into main config 114 yq eval-all "select(fileIndex == 0) * select(fileIndex == 1)" ${configFile} $CREDENTIALS_DIRECTORY/SECRETS_FILE \ 115 > "$STATE_DIRECTORY/config.yml" 116 117 ${lib.getExe cfg.package} serve --config "$STATE_DIRECTORY/config.yml" 118 ''; 119 120 path = with pkgs; [ 121 iptables 122 # needed by startup script 123 yq-go 124 ]; 125 126 serviceConfig = 127 let 128 capabilities = [ 129 "CAP_NET_ADMIN" 130 ] ++ lib.optional cfg.settings.dns.enabled "CAP_NET_BIND_SERVICE"; 131 in 132 { 133 WorkingDirectory = "/var/lib/wg-access-server"; 134 StateDirectory = "wg-access-server"; 135 136 LoadCredential = [ 137 "SECRETS_FILE:${cfg.secretsFile}" 138 ]; 139 140 # Hardening 141 DynamicUser = true; 142 AmbientCapabilities = capabilities; 143 CapabilityBoundingSet = capabilities; 144 }; 145 }; 146 }; 147}