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.routeUp != "")
50 "route-up ${pkgs.writeShellScript "openvpn-${name}-up" cfg.routeUp}"}
51 ${optionalString (cfg.down != "" || cfg.updateResolvConf)
52 "down ${pkgs.writeShellScript "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.iproute 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 = literalExample ''
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";
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 <literal>openvpn-<replaceable>name</replaceable>.service</literal>,
123 where <replaceable>name</replaceable> is the corresponding
127 type = with types; attrsOf (submodule {
134 Configuration of this OpenVPN instance. See
135 <citerefentry><refentrytitle>openvpn</refentrytitle><manvolnum>8</manvolnum></citerefentry>
138 To import an external config file, use the following definition:
139 <literal>config = "config /path/to/config.ovpn"</literal>
147 Shell commands executed when the instance is starting.
162 Shell commands executed when the instance is shutting down.
166 nftables = mkOption {
173 autoStart = mkOption {
176 description = "Whether this OpenVPN instance should be started automatically.";
179 updateResolvConf = mkOption {
183 Use the script from the update-resolv-conf package to automatically
184 update resolv.conf with the DNS information provided by openvpn. The
185 script will be run after the "up" commands and before the "down" commands.
189 authUserPass = mkOption {
192 This option can be used to store the username / password credentials
193 with the "auth-user-pass" authentication method.
195 WARNING: Using this option will put the credentials WORLD-READABLE in the Nix store!
197 type = types.nullOr (types.submodule {
200 username = mkOption {
201 description = "The username to store inside the credentials file.";
205 password = mkOption {
206 description = "The password to store inside the credentials file.";
221 ###### implementation
223 config = mkIf (cfg.servers != {}) {
225 systemd.services = listToAttrs (mapAttrsFlatten (name: value: nameValuePair "openvpn-${name}" (makeOpenVPNJob value name)) cfg.servers);
227 environment.systemPackages = [ openvpn ];
229 boot.kernelModules = [ "tun" ];