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;
20 # For convenience in client scripts, extract the remote domain
21 # name and name server.
22 for var in ''${!foreign_option_*}; do
24 if [ "''${x[0]}" = dhcp-option ]; then
25 if [ "''${x[1]}" = DOMAIN ]; then domain="''${x[2]}"
26 elif [ "''${x[1]}" = DNS ]; then nameserver="''${x[2]}"
32 ${optionalString cfg.updateResolvConf
33 "${pkgs.update-resolv-conf}/libexec/openvpn/update-resolv-conf"}
39 ${optionalString cfg.updateResolvConf
40 "${pkgs.update-resolv-conf}/libexec/openvpn/update-resolv-conf"}
44 configFile = pkgs.writeText "openvpn-config-${name}"
47 ${optionalString (cfg.up != "" || cfg.down != "" || cfg.updateResolvConf) "script-security 2"}
49 ${optionalString (cfg.up != "" || cfg.updateResolvConf)
50 "up ${pkgs.writeScript "openvpn-${name}-up" upScript}"}
51 ${optionalString (cfg.down != "" || cfg.updateResolvConf)
52 "down ${pkgs.writeScript "openvpn-${name}-down" downScript}"}
53 ${optionalString (cfg.authUserPass != null)
54 "auth-user-pass ${pkgs.writeText "openvpn-credentials-${name}" ''
55 ${cfg.authUserPass.username}
56 ${cfg.authUserPass.password}
61 description = "OpenVPN instance ‘${name}’";
63 wantedBy = optional cfg.autoStart "multi-user.target";
64 after = [ "network.target" ];
66 path = [ pkgs.iptables pkgs.iproute2 pkgs.nettools ];
68 serviceConfig.ExecStart = "@${openvpn}/sbin/openvpn openvpn --suppress-timestamps --config ${configFile}";
69 serviceConfig.Restart = "always";
70 serviceConfig.Type = "notify";
77 (mkRemovedOptionModule [ "services" "openvpn" "enable" ] "")
84 services.openvpn.servers = mkOption {
87 example = literalExpression ''
91 # Simplest server configuration: https://community.openvpn.net/openvpn/wiki/StaticKeyMiniHowto
94 ifconfig 10.8.0.1 10.8.0.2
95 secret /root/static.key
97 up = "ip route add ...";
98 down = "ip route del ...";
104 remote vpn.example.org
109 cert /root/.vpn/alice.crt
110 key /root/.vpn/alice.key
112 up = "echo nameserver $nameserver | ''${pkgs.openresolv}/sbin/resolvconf -m 0 -a $dev";
113 down = "''${pkgs.openresolv}/sbin/resolvconf -d $dev";
118 description = lib.mdDoc ''
119 Each attribute of this option defines a systemd service that
120 runs an OpenVPN instance. These can be OpenVPN servers or
121 clients. The name of each systemd service is
122 `openvpn-«name».service`,
123 where «name» is the corresponding
127 type = with types; attrsOf (submodule {
133 description = lib.mdDoc ''
134 Configuration of this OpenVPN instance. See
135 {manpage}`openvpn(8)`
138 To import an external config file, use the following definition:
139 `config = "config /path/to/config.ovpn"`
146 description = lib.mdDoc ''
147 Shell commands executed when the instance is starting.
154 description = lib.mdDoc ''
155 Shell commands executed when the instance is shutting down.
159 autoStart = mkOption {
162 description = lib.mdDoc "Whether this OpenVPN instance should be started automatically.";
165 updateResolvConf = mkOption {
168 description = lib.mdDoc ''
169 Use the script from the update-resolv-conf package to automatically
170 update resolv.conf with the DNS information provided by openvpn. The
171 script will be run after the "up" commands and before the "down" commands.
175 authUserPass = mkOption {
177 description = lib.mdDoc ''
178 This option can be used to store the username / password credentials
179 with the "auth-user-pass" authentication method.
181 WARNING: Using this option will put the credentials WORLD-READABLE in the Nix store!
183 type = types.nullOr (types.submodule {
186 username = mkOption {
187 description = lib.mdDoc "The username to store inside the credentials file.";
191 password = mkOption {
192 description = lib.mdDoc "The password to store inside the credentials file.";
207 ###### implementation
209 config = mkIf (cfg.servers != {}) {
211 systemd.services = listToAttrs (mapAttrsFlatten (name: value: nameValuePair "openvpn-${name}" (makeOpenVPNJob value name)) cfg.servers);
213 environment.systemPackages = [ openvpn ];
215 boot.kernelModules = [ "tun" ];