1 { config, lib, pkgs, ... }:
7 cfg = config.services.openvpn;
9 inherit (pkgs) openvpn;
11 makeOpenVPNJob = cfg: name:
14 path = makeBinPath (getAttr "openvpn-${name}" config.systemd.services).path;
19 # For convenience in client scripts, extract the remote domain
20 # name and name server.
21 for var in ''${!foreign_option_*}; do
23 if [ "''${x[0]}" = dhcp-option ]; then
24 if [ "''${x[1]}" = DOMAIN ]; then domain="''${x[2]}"
25 elif [ "''${x[1]}" = DNS ]; then nameserver="''${x[2]}"
31 ${optionalString cfg.updateResolvConf
32 "${pkgs.update-resolv-conf}/libexec/openvpn/update-resolv-conf"}
37 ${optionalString cfg.updateResolvConf
38 "${pkgs.update-resolv-conf}/libexec/openvpn/update-resolv-conf"}
42 configFile = pkgs.writeText "openvpn-config-${name}"
45 ${optionalString (cfg.up != "" || cfg.down != "" || cfg.updateResolvConf) "script-security 2"}
47 ${optionalString (cfg.up != "" || cfg.updateResolvConf)
48 "up ${pkgs.writeShellScript "openvpn-${name}-up" upScript}"}
49 ${optionalString (cfg.down != "" || cfg.updateResolvConf)
50 "down ${pkgs.writeShellScript "openvpn-${name}-down" downScript}"}
51 ${optionalString (cfg.authUserPass != null)
52 "auth-user-pass ${pkgs.writeText "openvpn-credentials-${name}" ''
53 ${cfg.authUserPass.username}
54 ${cfg.authUserPass.password}
60 description = "OpenVPN instance ‘${name}’";
62 wantedBy = optional cfg.autoStart "multi-user.target";
63 after = [ "network.target" ];
65 path = [ pkgs.iptables pkgs.iproute2 pkgs.nettools ];
67 serviceConfig.ExecStart = "@${openvpn}/sbin/openvpn openvpn --suppress-timestamps --config ${configFile}";
68 serviceConfig.Restart = "always";
69 serviceConfig.Type = "notify";
72 restartService = optionalAttrs cfg.restartAfterSleep {
74 wantedBy = [ "sleep.target" ];
75 path = [ pkgs.procps ];
77 unitNames = map (n: "openvpn-${n}.service") (builtins.attrNames cfg.servers);
78 in "systemctl try-restart ${lib.escapeShellArgs unitNames}";
79 description = "Sends a signal to OpenVPN process to trigger a restart after return from sleep";
87 (mkRemovedOptionModule [ "services" "openvpn" "enable" ] "")
94 services.openvpn.servers = mkOption {
97 example = literalExpression ''
101 # Simplest server configuration: https://community.openvpn.net/openvpn/wiki/StaticKeyMiniHowto
104 ifconfig 10.8.0.1 10.8.0.2
105 secret /root/static.key
107 up = "ip route add ...";
108 down = "ip route del ...";
114 remote vpn.example.org
119 cert /root/.vpn/alice.crt
120 key /root/.vpn/alice.key
122 up = "echo nameserver $nameserver | ''${pkgs.openresolv}/sbin/resolvconf -m 0 -a $dev";
123 down = "''${pkgs.openresolv}/sbin/resolvconf -d $dev";
129 Each attribute of this option defines a systemd service that
130 runs an OpenVPN instance. These can be OpenVPN servers or
131 clients. The name of each systemd service is
132 `openvpn-«name».service`,
133 where «name» is the corresponding
137 type = with types; attrsOf (submodule {
144 Configuration of this OpenVPN instance. See
145 {manpage}`openvpn(8)`
148 To import an external config file, use the following definition:
149 `config = "config /path/to/config.ovpn"`
157 Shell commands executed when the instance is starting.
165 Shell commands executed when the instance is shutting down.
169 autoStart = mkOption {
172 description = "Whether this OpenVPN instance should be started automatically.";
175 updateResolvConf = mkOption {
179 Use the script from the update-resolv-conf package to automatically
180 update resolv.conf with the DNS information provided by openvpn. The
181 script will be run after the "up" commands and before the "down" commands.
185 authUserPass = mkOption {
188 This option can be used to store the username / password credentials
189 with the "auth-user-pass" authentication method.
191 WARNING: Using this option will put the credentials WORLD-READABLE in the Nix store!
193 type = types.nullOr (types.submodule {
196 username = mkOption {
197 description = "The username to store inside the credentials file.";
201 password = mkOption {
202 description = "The password to store inside the credentials file.";
214 services.openvpn.restartAfterSleep = mkOption {
217 description = "Whether OpenVPN client should be restarted after sleep.";
223 ###### implementation
225 config = mkIf (cfg.servers != { }) {
227 systemd.services = (listToAttrs (mapAttrsToList (name: value: nameValuePair "openvpn-${name}" (makeOpenVPNJob value name)) cfg.servers))
230 environment.systemPackages = [ openvpn ];
232 boot.kernelModules = [ "tun" ];