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 {
174 autoStart = mkOption {
177 description = "Whether this OpenVPN instance should be started automatically.";
180 updateResolvConf = mkOption {
184 Use the script from the update-resolv-conf package to automatically
185 update resolv.conf with the DNS information provided by openvpn. The
186 script will be run after the "up" commands and before the "down" commands.
190 authUserPass = mkOption {
193 This option can be used to store the username / password credentials
194 with the "auth-user-pass" authentication method.
196 WARNING: Using this option will put the credentials WORLD-READABLE in the Nix store!
198 type = types.nullOr (types.submodule {
201 username = mkOption {
202 description = "The username to store inside the credentials file.";
206 password = mkOption {
207 description = "The password to store inside the credentials file.";
222 ###### implementation
224 config = mkIf (cfg.servers != {}) {
226 systemd.services = listToAttrs (mapAttrsFlatten (name: value: nameValuePair "openvpn-${name}" (makeOpenVPNJob value name)) cfg.servers);
228 environment.systemPackages = [ openvpn ];
230 boot.kernelModules = [ "tun" ];