]> Git — Sourcephile - sourcephile-nix.git/blob - nixos/modules/services/networking/openvpn.nix
openvpn: add riseup in net namespace
[sourcephile-nix.git] / nixos / modules / services / networking / openvpn.nix
1 { config, lib, pkgs, ... }:
2
3 with lib;
4
5 let
6
7 cfg = config.services.openvpn;
8
9 inherit (pkgs) openvpn;
10
11 makeOpenVPNJob = cfg: name:
12 let
13
14 path = makeBinPath (getAttr "openvpn-${name}" config.systemd.services).path;
15
16 upScript = ''
17 export PATH=${path}
18
19 # For convenience in client scripts, extract the remote domain
20 # name and name server.
21 for var in ''${!foreign_option_*}; do
22 x=(''${!var})
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]}"
26 fi
27 fi
28 done
29
30 ${cfg.up}
31 ${optionalString cfg.updateResolvConf
32 "${pkgs.update-resolv-conf}/libexec/openvpn/update-resolv-conf"}
33 '';
34
35 downScript = ''
36 export PATH=${path}
37 ${optionalString cfg.updateResolvConf
38 "${pkgs.update-resolv-conf}/libexec/openvpn/update-resolv-conf"}
39 ${cfg.down}
40 '';
41
42 configFile = pkgs.writeText "openvpn-config-${name}"
43 ''
44 errors-to-stderr
45 ${optionalString (cfg.up != "" || cfg.down != "" || cfg.updateResolvConf) "script-security 2"}
46 ${cfg.config}
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}
57 ''}"}
58 '';
59
60 in {
61 description = "OpenVPN instance ‘${name}’";
62
63 wantedBy = optional cfg.autoStart "multi-user.target";
64 after = [ "network.target" ];
65
66 path = [ pkgs.iptables pkgs.iproute pkgs.nettools ];
67
68 serviceConfig.ExecStart = "@${openvpn}/sbin/openvpn openvpn --suppress-timestamps --config ${configFile}";
69 serviceConfig.Restart = "always";
70 serviceConfig.Type = "notify";
71 };
72
73 in
74
75 {
76 imports = [
77 (mkRemovedOptionModule [ "services" "openvpn" "enable" ] "")
78 ];
79
80 ###### interface
81
82 options = {
83
84 services.openvpn.servers = mkOption {
85 default = {};
86
87 example = literalExample ''
88 {
89 server = {
90 config = '''
91 # Simplest server configuration: https://community.openvpn.net/openvpn/wiki/StaticKeyMiniHowto
92 # server :
93 dev tun
94 ifconfig 10.8.0.1 10.8.0.2
95 secret /root/static.key
96 ''';
97 up = "ip route add ...";
98 down = "ip route del ...";
99 };
100
101 client = {
102 config = '''
103 client
104 remote vpn.example.org
105 dev tun
106 proto tcp-client
107 port 8080
108 ca /root/.vpn/ca.crt
109 cert /root/.vpn/alice.crt
110 key /root/.vpn/alice.key
111 ''';
112 up = "echo nameserver $nameserver | ''${pkgs.openresolv}/sbin/resolvconf -m 0 -a $dev";
113 down = "''${pkgs.openresolv}/sbin/resolvconf -d $dev";
114 };
115 }
116 '';
117
118 description = ''
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
124 attribute name.
125 '';
126
127 type = with types; attrsOf (submodule {
128
129 options = {
130
131 config = mkOption {
132 type = types.lines;
133 description = ''
134 Configuration of this OpenVPN instance. See
135 <citerefentry><refentrytitle>openvpn</refentrytitle><manvolnum>8</manvolnum></citerefentry>
136 for details.
137
138 To import an external config file, use the following definition:
139 <literal>config = "config /path/to/config.ovpn"</literal>
140 '';
141 };
142
143 up = mkOption {
144 default = "";
145 type = types.lines;
146 description = ''
147 Shell commands executed when the instance is starting.
148 '';
149 };
150
151 routeUp = mkOption {
152 default = "";
153 type = types.lines;
154 description = ''
155 '';
156 };
157
158 down = mkOption {
159 default = "";
160 type = types.lines;
161 description = ''
162 Shell commands executed when the instance is shutting down.
163 '';
164 };
165
166 nftables = mkOption {
167 default = "";
168 type = types.lines;
169 description = ''
170 '';
171 };
172
173 autoStart = mkOption {
174 default = true;
175 type = types.bool;
176 description = "Whether this OpenVPN instance should be started automatically.";
177 };
178
179 updateResolvConf = mkOption {
180 default = false;
181 type = types.bool;
182 description = ''
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.
186 '';
187 };
188
189 authUserPass = mkOption {
190 default = null;
191 description = ''
192 This option can be used to store the username / password credentials
193 with the "auth-user-pass" authentication method.
194
195 WARNING: Using this option will put the credentials WORLD-READABLE in the Nix store!
196 '';
197 type = types.nullOr (types.submodule {
198
199 options = {
200 username = mkOption {
201 description = "The username to store inside the credentials file.";
202 type = types.str;
203 };
204
205 password = mkOption {
206 description = "The password to store inside the credentials file.";
207 type = types.str;
208 };
209 };
210 });
211 };
212 };
213
214 });
215
216 };
217
218 };
219
220
221 ###### implementation
222
223 config = mkIf (cfg.servers != {}) {
224
225 systemd.services = listToAttrs (mapAttrsFlatten (name: value: nameValuePair "openvpn-${name}" (makeOpenVPNJob value name)) cfg.servers);
226
227 environment.systemPackages = [ openvpn ];
228
229 boot.kernelModules = [ "tun" ];
230
231 };
232
233 }