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 '';
33
34 downScript = ''
35 #! /bin/sh
36 export PATH=${path}
37 ${cfg.down}
38 '';
39
40 configFile = pkgs.writeText "openvpn-config-${name}"
41 ''
42 errors-to-stderr
43 ${optionalString (cfg.up != "" || cfg.down != "") "script-security 2"}
44 ${cfg.config}
45 ${optionalString (cfg.up != "") "up ${pkgs.writeScript "openvpn-${name}-up" upScript}"}
46 ${optionalString (cfg.down != "") "down ${pkgs.writeScript "openvpn-${name}-down" downScript}"}
47 '';
48
49 in {
50 description = "OpenVPN instance ‘${name}’";
51
52 wantedBy = optional cfg.autoStart "multi-user.target";
53 after = [ "network-interfaces.target" ];
54
55 path = [ pkgs.iptables pkgs.iproute pkgs.nettools ];
56
57 serviceConfig.ExecStart = "@${openvpn}/sbin/openvpn openvpn --config ${configFile}";
58 serviceConfig.Restart = "always";
59 serviceConfig.Type = "notify";
60 };
61
62in
63
64{
65
66 ###### interface
67
68 options = {
69
70 /* !!! Obsolete. */
71 services.openvpn.enable = mkOption {
72 default = true;
73 description = "Whether to enable OpenVPN.";
74 };
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 = types.attrsOf types.optionSet;
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 };
155
156 };
157
158 };
159
160
161 ###### implementation
162
163 config = mkIf (cfg.servers != {}) {
164
165 systemd.services = listToAttrs (mapAttrsFlatten (name: value: nameValuePair "openvpn-${name}" (makeOpenVPNJob value name)) cfg.servers);
166
167 environment.systemPackages = [ openvpn ];
168
169 boot.kernelModules = [ "tun" ];
170
171 };
172
173}