nix: update to nixos-25.05
[julm/julm-nix.git] / nixpkgs / patches / openvpn / 0002-nixos-openvpn-add-netns-support.patch
index fe933f793c2b1886f6a8bc640d10c89350b38e28..0be1bbab1887271db0084d5792fda2b4a553837d 100644 (file)
@@ -1,17 +1,17 @@
-From 600839a7759be481904d029798e563e23df930db Mon Sep 17 00:00:00 2001
+From 2ae3f92dcd1ffe66ddd1bb87627d9c08756df39b Mon Sep 17 00:00:00 2001
 From: Julien Moutinho <julm+nixpkgs@sourcephile.fr>
 Date: Sun, 17 Jan 2021 15:28:25 +0100
 Subject: [PATCH 2/2] nixos/openvpn: add netns support
 
 ---
- nixos/modules/services/networking/openvpn.nix | 395 ++++++++++++++----
- 1 file changed, 314 insertions(+), 81 deletions(-)
+ nixos/modules/services/networking/openvpn.nix | 509 +++++++++++++-----
+ 1 file changed, 382 insertions(+), 127 deletions(-)
 
 diff --git a/nixos/modules/services/networking/openvpn.nix b/nixos/modules/services/networking/openvpn.nix
-index 56b1f6f5ab8f..9805099789b1 100644
+index 0231e43447..d0a95e6e5a 100644
 --- a/nixos/modules/services/networking/openvpn.nix
 +++ b/nixos/modules/services/networking/openvpn.nix
-@@ -5,55 +5,205 @@ with lib;
+@@ -10,56 +10,210 @@ with lib;
  let
  
    cfg = config.services.openvpn;
@@ -21,31 +21,33 @@ index 56b1f6f5ab8f..9805099789b1 100644
  
 +  PATH = name: makeBinPath config.systemd.services."openvpn-${name}".path;
 +
-   makeOpenVPNJob = cfg: name:
+   makeOpenVPNJob =
+     cfg: name:
      let
  
 -      path = makeBinPath (getAttr "openvpn-${name}" config.systemd.services).path;
 +      configFile = pkgs.writeText "openvpn-config-${name}" (
-+        generators.toKeyValue
-+          {
-+            mkKeyValue = key: value:
-+              if hasAttr key scripts
-+              then "${key} " + pkgs.writeShellScript "openvpn-${name}-${key}" (scripts.${key} value)
-+              else if builtins.isBool value
-+              then optionalString value key
-+              else if builtins.isPath value
-+              then "${key} ${value}"
-+              else if builtins.isList value
-+              then concatMapStringsSep "\n" (v: "${key} ${generators.mkValueStringDefault {} v}") value
-+              else "${key} ${generators.mkValueStringDefault {} value}";
-+          }
-+          cfg.settings
++        generators.toKeyValue {
++          mkKeyValue =
++            key: value:
++            if hasAttr key scripts then
++              "${key} " + pkgs.writeShellScript "openvpn-${name}-${key}" (scripts.${key} value)
++            else if builtins.isBool value then
++              optionalString value key
++            else if builtins.isPath value then
++              "${key} ${value}"
++            else if builtins.isList value then
++              concatMapStringsSep "\n" (v: "${key} ${generators.mkValueStringDefault { } v}") value
++            else
++              "${key} ${generators.mkValueStringDefault { } value}";
++        } cfg.settings
 +      );
  
 -      upScript = ''
 -        export PATH=${path}
 +      scripts = {
-+        up = script:
++        up =
++          script:
 +          let
 +            init = ''
 +              export PATH=${PATH name}
@@ -72,11 +74,9 @@ index 56b1f6f5ab8f..9805099789b1 100644
 +              done
  
 -        ${cfg.up}
--        ${optionalString cfg.updateResolvConf
--           "${pkgs.update-resolv-conf}/libexec/openvpn/update-resolv-conf"}
+-        ${optionalString cfg.updateResolvConf "${pkgs.update-resolv-conf}/libexec/openvpn/update-resolv-conf"}
 -      '';
-+              ${optionalString cfg.updateResolvConf
-+                 "${pkgs.update-resolv-conf}/libexec/openvpn/update-resolv-conf"}
++              ${optionalString cfg.updateResolvConf "${pkgs.update-resolv-conf}/libexec/openvpn/update-resolv-conf"}
 +            '';
 +            # Add DNS settings given in foreign DHCP options to the resolv.conf of the netns.
 +            # Note that JoinsNamespaceOf="netns-${cfg.netns}.service" will not
@@ -110,159 +110,166 @@ index 56b1f6f5ab8f..9805099789b1 100644
 +              done
 +            '';
 +          in
-+          if cfg.netns == null
-+          then ''
-+            ${init}
-+            ${script}
-+          ''
-+          else ''
-+            export PATH=${PATH name}
-+            set -eux
-+            ${setNetNSResolvConf}
-+            ip link set dev '${cfg.settings.dev}' up netns '${cfg.netns}' mtu "$tun_mtu"
-+            ip netns exec '${cfg.netns}' ${pkgs.writeShellScript "openvpn-${name}-up-netns.sh" ''
++          if cfg.netns == null then
++            ''
 +              ${init}
-+              set -eux
++              ${script}
++            ''
++          else
++            ''
 +              export PATH=${PATH name}
++              set -eux
++              ${setNetNSResolvConf}
++              ip link set dev '${cfg.settings.dev}' up netns '${cfg.netns}' mtu "$tun_mtu"
++              ip netns exec '${cfg.netns}' ${pkgs.writeShellScript "openvpn-${name}-up-netns.sh" ''
++                ${init}
++                set -eux
++                export PATH=${PATH name}
  
 -      downScript = ''
 -        export PATH=${path}
--        ${optionalString cfg.updateResolvConf
--           "${pkgs.update-resolv-conf}/libexec/openvpn/update-resolv-conf"}
+-        ${optionalString cfg.updateResolvConf "${pkgs.update-resolv-conf}/libexec/openvpn/update-resolv-conf"}
 -        ${cfg.down}
 -      '';
-+              ip link set dev lo up
++                ip link set dev lo up
  
--      configFile = pkgs.writeText "openvpn-config-${name}"
--        ''
--          errors-to-stderr
--          ${optionalString (cfg.up != "" || cfg.down != "" || cfg.updateResolvConf) "script-security 2"}
--          ${cfg.config}
--          ${optionalString (cfg.up != "" || cfg.updateResolvConf)
--              "up ${pkgs.writeShellScript "openvpn-${name}-up" upScript}"}
--          ${optionalString (cfg.down != "" || cfg.updateResolvConf)
--              "down ${pkgs.writeShellScript "openvpn-${name}-down" downScript}"}
--          ${optionalString (cfg.authUserPass != null)
--              "auth-user-pass ${pkgs.writeText "openvpn-credentials-${name}" ''
--                ${cfg.authUserPass.username}
--                ${cfg.authUserPass.password}
--              ''}"}
--        '';
-+              netmask4="''${ifconfig_netmask:-30}"
-+              netbits6="''${ifconfig_ipv6_netbits:-112}"
-+              if [ -n "''${ifconfig_local-}" ]; then
-+                if [ -n "''${ifconfig_remote-}" ]; then
-+                  ip -4 addr replace \
-+                    local "$ifconfig_local" \
-+                    peer "$ifconfig_remote/$netmask4" \
-+                    ''${ifconfig_broadcast:+broadcast "$ifconfig_broadcast"} \
-+                    dev '${cfg.settings.dev}'
-+                else
-+                  ip -4 addr replace \
-+                    local "$ifconfig_local/$netmask4" \
-+                    ''${ifconfig_broadcast:+broadcast "$ifconfig_broadcast"} \
-+                    dev '${cfg.settings.dev}'
+-      configFile = pkgs.writeText "openvpn-config-${name}" ''
+-        errors-to-stderr
+-        ${optionalString (cfg.up != "" || cfg.down != "" || cfg.updateResolvConf) "script-security 2"}
+-        ${cfg.config}
+-        ${optionalString (
+-          cfg.up != "" || cfg.updateResolvConf
+-        ) "up ${pkgs.writeShellScript "openvpn-${name}-up" upScript}"}
+-        ${optionalString (
+-          cfg.down != "" || cfg.updateResolvConf
+-        ) "down ${pkgs.writeShellScript "openvpn-${name}-down" downScript}"}
+-        ${optionalString (cfg.authUserPass != null)
+-          "auth-user-pass ${pkgs.writeText "openvpn-credentials-${name}" ''
+-            ${cfg.authUserPass.username}
+-            ${cfg.authUserPass.password}
+-          ''}"
+-        }
+-      '';
++                netmask4="''${ifconfig_netmask:-30}"
++                netbits6="''${ifconfig_ipv6_netbits:-112}"
++                if [ -n "''${ifconfig_local-}" ]; then
++                  if [ -n "''${ifconfig_remote-}" ]; then
++                    ip -4 addr replace \
++                      local "$ifconfig_local" \
++                      peer "$ifconfig_remote/$netmask4" \
++                      ''${ifconfig_broadcast:+broadcast "$ifconfig_broadcast"} \
++                      dev '${cfg.settings.dev}'
++                  else
++                    ip -4 addr replace \
++                      local "$ifconfig_local/$netmask4" \
++                      ''${ifconfig_broadcast:+broadcast "$ifconfig_broadcast"} \
++                      dev '${cfg.settings.dev}'
++                  fi
 +                fi
-+              fi
-+              if [ -n "''${ifconfig_ipv6_local-}" ]; then
-+                if [ -n "''${ifconfig_ipv6_remote-}" ]; then
-+                  ip -6 addr replace \
-+                    local "$ifconfig_ipv6_local" \
-+                    peer "$ifconfig_ipv6_remote/$netbits6" \
-+                    dev '${cfg.settings.dev}'
-+                else
-+                  ip -6 addr replace \
-+                    local "$ifconfig_ipv6_local/$netbits6" \
-+                    dev '${cfg.settings.dev}'
++                if [ -n "''${ifconfig_ipv6_local-}" ]; then
++                  if [ -n "''${ifconfig_ipv6_remote-}" ]; then
++                    ip -6 addr replace \
++                      local "$ifconfig_ipv6_local" \
++                      peer "$ifconfig_ipv6_remote/$netbits6" \
++                      dev '${cfg.settings.dev}'
++                  else
++                    ip -6 addr replace \
++                      local "$ifconfig_ipv6_local/$netbits6" \
++                      dev '${cfg.settings.dev}'
++                  fi
 +                fi
-+              fi
-+              set +eux
-+              ${script}
-+            ''}
-+          '';
-+        route-up = script:
-+          if cfg.netns == null
-+          then script
-+          else ''
-+            export PATH=${PATH name}
-+            set -eux
-+            ip netns exec '${cfg.netns}' ${pkgs.writeShellScript "openvpn-${name}-route-up-netns" ''
++                set +eux
++                ${script}
++              ''}
++            '';
++        route-up =
++          script:
++          if cfg.netns == null then
++            script
++          else
++            ''
 +              export PATH=${PATH name}
 +              set -eux
-+              i=1
-+              while
-+                eval net=\"\''${route_network_$i-}\"
-+                eval mask=\"\''${route_netmask_$i-}\"
-+                eval gw=\"\''${route_gateway_$i-}\"
-+                eval mtr=\"\''${route_metric_$i-}\"
-+                [ -n "$net" ]
-+              do
-+                ip -4 route replace "$net/$mask" via "$gw" ''${mtr:+metric "$mtr"}
-+                i=$(( i + 1 ))
-+              done
++              ip netns exec '${cfg.netns}' ${pkgs.writeShellScript "openvpn-${name}-route-up-netns" ''
++                export PATH=${PATH name}
++                set -eux
++                i=1
++                while
++                  eval net=\"\''${route_network_$i-}\"
++                  eval mask=\"\''${route_netmask_$i-}\"
++                  eval gw=\"\''${route_gateway_$i-}\"
++                  eval mtr=\"\''${route_metric_$i-}\"
++                  [ -n "$net" ]
++                do
++                  ip -4 route replace "$net/$mask" via "$gw" ''${mtr:+metric "$mtr"}
++                  i=$(( i + 1 ))
++                done
 +
-+              if [ -n "''${route_vpn_gateway-}" ]; then
-+                ip -4 route replace default via "$route_vpn_gateway"
-+              fi
++                if [ -n "''${route_vpn_gateway-}" ]; then
++                  ip -4 route replace default via "$route_vpn_gateway"
++                fi
 +
-+              i=1
-+              while
-+                # There doesn't seem to be $route_ipv6_metric_<n>
-+                # according to the manpage.
-+                eval net=\"\''${route_ipv6_network_$i-}\"
-+                eval gw=\"\''${route_ipv6_gateway_$i-}\"
-+                [ -n "$net" ]
-+              do
-+                ip -6 route replace  "$net"  via "$gw"  metric 100
-+                i=$(( i + 1 ))
-+              done
++                i=1
++                while
++                  # There doesn't seem to be $route_ipv6_metric_<n>
++                  # according to the manpage.
++                  eval net=\"\''${route_ipv6_network_$i-}\"
++                  eval gw=\"\''${route_ipv6_gateway_$i-}\"
++                  [ -n "$net" ]
++                do
++                  ip -6 route replace  "$net"  via "$gw"  metric 100
++                  i=$(( i + 1 ))
++                done
 +
-+              # There's no $route_vpn_gateway for IPv6. It's not
-+              # documented if OpenVPN includes default route in
-+              # $route_ipv6_*. Set default route to remote VPN
-+              # endpoint address if there is one. Use higher metric
-+              # than $route_ipv6_* routes to give preference to a
-+              # possible default route in them.
-+              if [ -n "''${ifconfig_ipv6_remote-}" ]; then
-+                ip -6 route replace default \
-+                  via "$ifconfig_ipv6_remote" metric 200
-+              fi
-+              ${script}
-+            ''}
-+          '';
-+        down = script:
++                # There's no $route_vpn_gateway for IPv6. It's not
++                # documented if OpenVPN includes default route in
++                # $route_ipv6_*. Set default route to remote VPN
++                # endpoint address if there is one. Use higher metric
++                # than $route_ipv6_* routes to give preference to a
++                # possible default route in them.
++                if [ -n "''${ifconfig_ipv6_remote-}" ]; then
++                  ip -6 route replace default \
++                    via "$ifconfig_ipv6_remote" metric 200
++                fi
++                ${script}
++              ''}
++            '';
++        down =
++          script:
 +          let
 +            init = ''
 +              export PATH=${PATH name}
-+              ${optionalString cfg.updateResolvConf
-+                 "${pkgs.update-resolv-conf}/libexec/openvpn/update-resolv-conf"}
++              ${optionalString cfg.updateResolvConf "${pkgs.update-resolv-conf}/libexec/openvpn/update-resolv-conf"}
 +            '';
 +          in
-+          if cfg.netns == null
-+          then ''
-+            ${init}
-+            ${script}
-+          ''
-+          else ''
-+            export PATH=${PATH name}
-+            ip netns exec '${cfg.netns}' ${pkgs.writeShellScript "openvpn-${name}-down-netns.sh" ''
++          if cfg.netns == null then
++            ''
 +              ${init}
 +              ${script}
-+            ''}
-+            rm -f /etc/netns/'${cfg.netns}'/resolv.conf
-+          '';
++            ''
++          else
++            ''
++              export PATH=${PATH name}
++              ip netns exec '${cfg.netns}' ${pkgs.writeShellScript "openvpn-${name}-down-netns.sh" ''
++                ${init}
++                ${script}
++              ''}
++              rm -f /etc/netns/'${cfg.netns}'/resolv.conf
++            '';
 +      };
  
      in
      {
-@@ -61,11 +211,14 @@ let
+@@ -67,6 +221,8 @@ let
  
        wantedBy = optional cfg.autoStart "multi-user.target";
        after = [ "network.target" ];
 +      bindsTo = optional (cfg.netns != null) "netns-${cfg.netns}.service";
 +      requires = optional (cfg.netns != null) "netns-${cfg.netns}.service";
  
-       path = [ pkgs.iptables pkgs.iproute2 pkgs.nettools ];
+       path = [
+         pkgs.iptables
+@@ -76,6 +232,7 @@ let
  
        serviceConfig.ExecStart = "@${openvpn}/sbin/openvpn openvpn --suppress-timestamps --config ${configFile}";
        serviceConfig.Restart = "always";
@@ -270,7 +277,7 @@ index 56b1f6f5ab8f..9805099789b1 100644
        serviceConfig.Type = "notify";
      };
  
-@@ -97,30 +250,30 @@ in
+@@ -109,30 +266,30 @@ in
        example = literalExpression ''
          {
            server = {
@@ -320,174 +327,271 @@ index 56b1f6f5ab8f..9805099789b1 100644
            };
          }
        '';
-@@ -134,36 +287,80 @@ in
-         attribute name.
-       '';
+@@ -148,82 +305,180 @@ in
  
--      type = with types; attrsOf (submodule {
-+      type = with types; attrsOf (submodule ({ name, config, options, ... }: {
+       type =
+         with types;
+-        attrsOf (submodule {
++        attrsOf (
++          submodule (
++            {
++              name,
++              config,
++              options,
++              ...
++            }:
++            {
  
--        options = {
-+        options = rec {
-+          enable = mkEnableOption "OpenVPN server" // { default = true; };
+-          options = {
++              options = rec {
++                enable = mkEnableOption "OpenVPN server" // {
++                  default = true;
++                };
  
--          config = mkOption {
--            type = types.lines;
-+          settings = mkOption {
-             description = ''
-               Configuration of this OpenVPN instance.  See
-               {manpage}`openvpn(8)`
-               for details.
+-            config = mkOption {
+-              type = types.lines;
+-              description = ''
+-                Configuration of this OpenVPN instance.  See
+-                {manpage}`openvpn(8)`
+-                for details.
++                settings = mkOption {
++                  description = ''
++                    Configuration of this OpenVPN instance.  See
++                    {manpage}`openvpn(8)`
++                    for details.
  
-               To import an external config file, use the following definition:
--              `config = "config /path/to/config.ovpn"`
--            '';
--          };
+               To import an external config file, use the following definition:
+-                `config = "config /path/to/config.ovpn"`
+-              '';
+-            };
 -
--          up = mkOption {
--            default = "";
--            type = types.lines;
--            description = ''
--              Shell commands executed when the instance is starting.
--            '';
--          };
+-            up = mkOption {
+-              default = "";
+-              type = types.lines;
+-              description = ''
+-                Shell commands executed when the instance is starting.
+-              '';
+-            };
 -
--          down = mkOption {
--            default = "";
--            type = types.lines;
--            description = ''
--              Shell commands executed when the instance is shutting down.
-+              config = /path/to/config.ovpn;
-             '';
-+            default = { };
-+            type = types.submodule {
-+              freeformType = with types;
-+                attrsOf (
-+                  nullOr (
-+                    oneOf [
-+                      bool
-+                      int
-+                      str
-+                      path
-+                      (listOf (oneOf [ bool int str path ]))
-+                    ]
-+                  )
-+                );
-+              options.dev = mkOption {
-+                default = null;
-+                type = types.str;
-+                description = ''
-+                  Shell commands executed when the instance is starting.
-+                '';
-+              };
-+              options.down = mkOption {
-+                default = "";
-+                type = types.lines;
-+                description = ''
-+                  Shell commands executed when the instance is shutting down.
-+                '';
-+              };
-+              options.errors-to-stderr = mkOption {
-+                default = true;
-+                type = types.bool;
-+                description = ''
-+                  Output errors to stderr instead of stdout
-+                  unless log output is redirected by one of the `--log` options.
-+                '';
-+              };
-+              options.route-up = mkOption {
-+                default = "";
-+                type = types.lines;
-+                description = ''
-+                  Run command after routes are added.
-+                '';
-+              };
-+              options.up = mkOption {
-+                default = "";
-+                type = types.lines;
-+                description = ''
-+                  Shell commands executed when the instance is starting.
-+                '';
-+              };
-+              options.script-security = mkOption {
-+                default = 1;
-+                type = types.enum [ 1 2 3 ];
-+                description = ''
-+                  - 1 — (Default) Only call built-in executables such as ifconfig, ip, route, or netsh.
-+                  - 2 — Allow calling of built-in executables and user-defined scripts.
-+                  - 3 — Allow passwords to be passed to scripts via environmental variables (potentially unsafe).
-+                '';
-+              };
-+            };
-           };
-           autoStart = mkOption {
-@@ -172,6 +369,10 @@ in
-             description = "Whether this OpenVPN instance should be started automatically.";
-           };
+-            down = mkOption {
+-              default = "";
+-              type = types.lines;
+-              description = ''
+-                Shell commands executed when the instance is shutting down.
+-              '';
+-            };
+-
+-            autoStart = mkOption {
+-              default = true;
+-              type = types.bool;
+-              description = "Whether this OpenVPN instance should be started automatically.";
+-            };
+-
+-            updateResolvConf = mkOption {
+-              default = false;
+-              type = types.bool;
+-              description = ''
+-                Use the script from the update-resolv-conf package to automatically
+-                update resolv.conf with the DNS information provided by openvpn. The
+-                script will be run after the "up" commands and before the "down" commands.
+-              '';
+-            };
+-
+-            authUserPass = mkOption {
+-              default = null;
+-              description = ''
+-                This option can be used to store the username / password credentials
+-                with the "auth-user-pass" authentication method.
+-
+-                WARNING: Using this option will put the credentials WORLD-READABLE in the Nix store!
+-              '';
+-              type = types.nullOr (
+-                types.submodule {
+-
+-                  options = {
+-                    username = mkOption {
+-                      description = "The username to store inside the credentials file.";
++                    To import an external config file, use the following definition:
++                    config = /path/to/config.ovpn;
++                  '';
++                  default = { };
++                  type = types.submodule {
++                    freeformType =
++                      with types;
++                      attrsOf (
++                        nullOr (oneOf [
++                          bool
++                          int
++                          str
++                          path
++                          (listOf (oneOf [
++                            bool
++                            int
++                            str
++                            path
++                          ]))
++                        ])
++                      );
++                    options.dev = mkOption {
++                      default = null;
+                       type = types.str;
++                      description = ''
++                        Shell commands executed when the instance is starting.
++                      '';
+                     };
+-
+-                    password = mkOption {
+-                      description = "The password to store inside the credentials file.";
+-                      type = types.str;
++                    options.down = mkOption {
++                      default = "";
++                      type = types.lines;
++                      description = ''
++                        Shell commands executed when the instance is shutting down.
++                      '';
++                    };
++                    options.errors-to-stderr = mkOption {
++                      default = true;
++                      type = types.bool;
++                      description = ''
++                        Output errors to stderr instead of stdout
++                        unless log output is redirected by one of the `--log` options.
++                      '';
++                    };
++                    options.route-up = mkOption {
++                      default = "";
++                      type = types.lines;
++                      description = ''
++                        Run command after routes are added.
++                      '';
++                    };
++                    options.up = mkOption {
++                      default = "";
++                      type = types.lines;
++                      description = ''
++                        Shell commands executed when the instance is starting.
++                      '';
++                    };
++                    options.script-security = mkOption {
++                      default = 1;
++                      type = types.enum [
++                        1
++                        2
++                        3
++                      ];
++                      description = ''
++                        - 1 — (Default) Only call built-in executables such as ifconfig, ip, route, or netsh.
++                        - 2 — Allow calling of built-in executables and user-defined scripts.
++                        - 3 — Allow passwords to be passed to scripts via environmental variables (potentially unsafe).
++                      '';
+                     };
+                   };
+-                }
+-              );
+-            };
+-          };
++                };
  
-+          # Legacy options
-+          down = (elemAt settings.type.functor.payload.modules 0).options.down;
-+          up = (elemAt settings.type.functor.payload.modules 0).options.up;
+-        });
++                autoStart = mkOption {
++                  default = true;
++                  type = types.bool;
++                  description = "Whether this OpenVPN instance should be started automatically.";
++                };
 +
-           updateResolvConf = mkOption {
-             default = false;
-             type = types.bool;
-@@ -205,9 +406,41 @@ in
-               };
-             });
-           };
++                # Legacy options
++                down = (elemAt settings.type.functor.payload.modules 0).options.down;
++                up = (elemAt settings.type.functor.payload.modules 0).options.up;
 +
-+          netns = mkOption {
-+            default = null;
-+            type = with types; nullOr str;
-+            description = "Network namespace.";
-+          };
-         };
--      });
-+        config.settings = mkMerge
-+          [
-+            (mkIf (config.netns != null) {
-+              # Useless to setup the interface
-+              # because moving it to the netns will reset it
-+              ifconfig-noexec = true;
-+              route-noexec = true;
-+              script-security = 2;
-+            })
-+            (mkIf (config.authUserPass != null) {
-+              auth-user-pass = pkgs.writeText "openvpn-auth-user-pass-${name}" ''
-+                ${config.authUserPass.username}
-+                ${config.authUserPass.password}
-+              '';
-+            })
-+            (mkIf config.updateResolvConf {
-+              script-security = 2;
-+            })
-+            {
-+              # Aliases legacy options
-+              down = modules.mkAliasAndWrapDefsWithPriority id (options.down or { });
-+              up = modules.mkAliasAndWrapDefsWithPriority id (options.up or { });
-+            }
-+          ];
++                updateResolvConf = mkOption {
++                  default = false;
++                  type = types.bool;
++                  description = ''
++                    Use the script from the update-resolv-conf package to automatically
++                    update resolv.conf with the DNS information provided by openvpn. The
++                    script will be run after the "up" commands and before the "down" commands.
++                  '';
++                };
 +
++                authUserPass = mkOption {
++                  default = null;
++                  description = ''
++                    This option can be used to store the username / password credentials
++                    with the "auth-user-pass" authentication method.
 +
-+      }));
++                    WARNING: Using this option will put the credentials WORLD-READABLE in the Nix store!
++                  '';
++                  type = types.nullOr (
++                    types.submodule {
++
++                      options = {
++                        username = mkOption {
++                          description = "The username to store inside the credentials file.";
++                          type = types.str;
++                        };
++
++                        password = mkOption {
++                          description = "The password to store inside the credentials file.";
++                          type = types.str;
++                        };
++                      };
++                    }
++                  );
++                };
++
++                netns = mkOption {
++                  default = null;
++                  type = with types; nullOr str;
++                  description = "Network namespace.";
++                };
++              };
++
++              config.settings = mkMerge [
++                (mkIf (config.netns != null) {
++                  # Useless to setup the interface
++                  # because moving it to the netns will reset it
++                  ifconfig-noexec = true;
++                  route-noexec = true;
++                  script-security = 2;
++                })
++                (mkIf (config.authUserPass != null) {
++                  auth-user-pass = pkgs.writeText "openvpn-auth-user-pass-${name}" ''
++                    ${config.authUserPass.username}
++                    ${config.authUserPass.password}
++                  '';
++                })
++                (mkIf config.updateResolvConf {
++                  script-security = 2;
++                })
++                {
++                  # Aliases legacy options
++                  down = modules.mkAliasAndWrapDefsWithPriority id (options.down or { });
++                  up = modules.mkAliasAndWrapDefsWithPriority id (options.up or { });
++                }
++              ];
++
++            }
++          )
++        );
  
      };
  
-@@ -222,9 +455,9 @@ in
+@@ -237,13 +492,13 @@ in
  
    ###### implementation
  
 -  config = mkIf (cfg.servers != { }) {
 +  config = mkIf (enabledServers != { }) {
  
--    systemd.services = (listToAttrs (mapAttrsToList (name: value: nameValuePair "openvpn-${name}" (makeOpenVPNJob value name)) cfg.servers))
-+    systemd.services = (listToAttrs (mapAttrsToList (name: value: nameValuePair "openvpn-${name}" (makeOpenVPNJob value name)) enabledServers))
+     systemd.services =
+       (listToAttrs (
+         mapAttrsToList (
+           name: value: nameValuePair "openvpn-${name}" (makeOpenVPNJob value name)
+-        ) cfg.servers
++        ) enabledServers
+       ))
        // restartService;
  
-     environment.systemPackages = [ openvpn ];
 -- 
-2.44.1
+2.47.2