1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 cfg = config.services.openvpn;
8
9 inherit (pkgs) openvpn;
10
11 makeOpenVPNJob = cfg: name:
12 let
13
14 path = (getAttr "openvpn-${name}" config.systemd.services).path;
15
16 upScript = ''
17 #! /bin/sh
18 export PATH=${path}
19
20 # For convenience in client scripts, extract the remote domain
21 # name and name server.
22 for var in ''${!foreign_option_*}; do
23 x=(''${!var})
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]}"
27 fi
28 fi
29 done
30
31 ${cfg.up}
32 ${optionalString cfg.updateResolvConf
33 "${pkgs.update-resolv-conf}/libexec/openvpn/update-resolv-conf"}
34 '';
35
36 downScript = ''
37 #! /bin/sh
38 export PATH=${path}
39 ${optionalString cfg.updateResolvConf
40 "${pkgs.update-resolv-conf}/libexec/openvpn/update-resolv-conf"}
41 ${cfg.down}
42 '';
43
44 configFile = pkgs.writeText "openvpn-config-${name}"
45 ''
46 errors-to-stderr
47 ${optionalString (cfg.up != "" || cfg.down != "" || cfg.updateResolvConf) "script-security 2"}
48 ${cfg.config}
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 '';
54
55 in {
56 description = "OpenVPN instance ‘${name}’";
57
58 wantedBy = optional cfg.autoStart "multi-user.target";
59 after = [ "network.target" ];
60
61 path = [ pkgs.iptables pkgs.iproute pkgs.nettools ];
62
63 serviceConfig.ExecStart = "@${openvpn}/sbin/openvpn openvpn --config ${configFile}";
64 serviceConfig.Restart = "always";
65 serviceConfig.Type = "notify";
66 };
67
68in
69
70{
71
72 ###### interface
73
74 options = {
75
76 services.openvpn.servers = mkOption {
77 default = {};
78
79 example = literalExample ''
80 {
81 server = {
82 config = '''
83 # Simplest server configuration: http://openvpn.net/index.php/documentation/miscellaneous/static-key-mini-howto.html.
84 # server :
85 dev tun
86 ifconfig 10.8.0.1 10.8.0.2
87 secret /root/static.key
88 ''';
89 up = "ip route add ...";
90 down = "ip route del ...";
91 };
92
93 client = {
94 config = '''
95 client
96 remote vpn.example.org
97 dev tun
98 proto tcp-client
99 port 8080
100 ca /root/.vpn/ca.crt
101 cert /root/.vpn/alice.crt
102 key /root/.vpn/alice.key
103 ''';
104 up = "echo nameserver $nameserver | ''${pkgs.openresolv}/sbin/resolvconf -m 0 -a $dev";
105 down = "''${pkgs.openresolv}/sbin/resolvconf -d $dev";
106 };
107 }
108 '';
109
110 description = ''
111 Each attribute of this option defines a systemd service that
112 runs an OpenVPN instance. These can be OpenVPN servers or
113 clients. The name of each systemd service is
114 <literal>openvpn-<replaceable>name</replaceable>.service</literal>,
115 where <replaceable>name</replaceable> is the corresponding
116 attribute name.
117 '';
118
119 type = with types; attrsOf (submodule {
120
121 options = {
122
123 config = mkOption {
124 type = types.lines;
125 description = ''
126 Configuration of this OpenVPN instance. See
127 <citerefentry><refentrytitle>openvpn</refentrytitle><manvolnum>8</manvolnum></citerefentry>
128 for details.
129 '';
130 };
131
132 up = mkOption {
133 default = "";
134 type = types.lines;
135 description = ''
136 Shell commands executed when the instance is starting.
137 '';
138 };
139
140 down = mkOption {
141 default = "";
142 type = types.lines;
143 description = ''
144 Shell commands executed when the instance is shutting down.
145 '';
146 };
147
148 autoStart = mkOption {
149 default = true;
150 type = types.bool;
151 description = "Whether this OpenVPN instance should be started automatically.";
152 };
153
154 updateResolvConf = mkOption {
155 default = false;
156 type = types.bool;
157 description = ''
158 Use the script from the update-resolv-conf package to automatically
159 update resolv.conf with the DNS information provided by openvpn. The
160 script will be run after the "up" commands and before the "down" commands.
161 '';
162 };
163
164 };
165
166 });
167
168 };
169
170 };
171
172
173 ###### implementation
174
175 config = mkIf (cfg.servers != {}) {
176
177 systemd.services = listToAttrs (mapAttrsFlatten (name: value: nameValuePair "openvpn-${name}" (makeOpenVPNJob value name)) cfg.servers);
178
179 environment.systemPackages = [ openvpn ];
180
181 boot.kernelModules = [ "tun" ];
182
183 };
184
185}