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 services.openvpn.servers = mkOption {
71 default = {};
72
73 example = literalExample ''
74 {
75 server = {
76 config = '''
77 # Simplest server configuration: http://openvpn.net/index.php/documentation/miscellaneous/static-key-mini-howto.html.
78 # server :
79 dev tun
80 ifconfig 10.8.0.1 10.8.0.2
81 secret /root/static.key
82 ''';
83 up = "ip route add ...";
84 down = "ip route del ...";
85 };
86
87 client = {
88 config = '''
89 client
90 remote vpn.example.org
91 dev tun
92 proto tcp-client
93 port 8080
94 ca /root/.vpn/ca.crt
95 cert /root/.vpn/alice.crt
96 key /root/.vpn/alice.key
97 ''';
98 up = "echo nameserver $nameserver | ''${pkgs.openresolv}/sbin/resolvconf -m 0 -a $dev";
99 down = "''${pkgs.openresolv}/sbin/resolvconf -d $dev";
100 };
101 }
102 '';
103
104 description = ''
105 Each attribute of this option defines a systemd service that
106 runs an OpenVPN instance. These can be OpenVPN servers or
107 clients. The name of each systemd service is
108 <literal>openvpn-<replaceable>name</replaceable>.service</literal>,
109 where <replaceable>name</replaceable> is the corresponding
110 attribute name.
111 '';
112
113 type = types.attrsOf types.optionSet;
114
115 options = {
116
117 config = mkOption {
118 type = types.lines;
119 description = ''
120 Configuration of this OpenVPN instance. See
121 <citerefentry><refentrytitle>openvpn</refentrytitle><manvolnum>8</manvolnum></citerefentry>
122 for details.
123 '';
124 };
125
126 up = mkOption {
127 default = "";
128 type = types.lines;
129 description = ''
130 Shell commands executed when the instance is starting.
131 '';
132 };
133
134 down = mkOption {
135 default = "";
136 type = types.lines;
137 description = ''
138 Shell commands executed when the instance is shutting down.
139 '';
140 };
141
142 autoStart = mkOption {
143 default = true;
144 type = types.bool;
145 description = "Whether this OpenVPN instance should be started automatically.";
146 };
147
148 };
149
150 };
151
152 };
153
154
155 ###### implementation
156
157 config = mkIf (cfg.servers != {}) {
158
159 systemd.services = listToAttrs (mapAttrsFlatten (name: value: nameValuePair "openvpn-${name}" (makeOpenVPNJob value name)) cfg.servers);
160
161 environment.systemPackages = [ openvpn ];
162
163 boot.kernelModules = [ "tun" ];
164
165 };
166
167}