at 18.09-beta 6.5 kB view raw
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 ${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 73in 74 75{ 76 77 ###### interface 78 79 options = { 80 81 services.openvpn.servers = mkOption { 82 default = {}; 83 84 example = literalExample '' 85 { 86 server = { 87 config = ''' 88 # Simplest server configuration: http://openvpn.net/index.php/documentation/miscellaneous/static-key-mini-howto.html. 89 # server : 90 dev tun 91 ifconfig 10.8.0.1 10.8.0.2 92 secret /root/static.key 93 '''; 94 up = "ip route add ..."; 95 down = "ip route del ..."; 96 }; 97 98 client = { 99 config = ''' 100 client 101 remote vpn.example.org 102 dev tun 103 proto tcp-client 104 port 8080 105 ca /root/.vpn/ca.crt 106 cert /root/.vpn/alice.crt 107 key /root/.vpn/alice.key 108 '''; 109 up = "echo nameserver $nameserver | ''${pkgs.openresolv}/sbin/resolvconf -m 0 -a $dev"; 110 down = "''${pkgs.openresolv}/sbin/resolvconf -d $dev"; 111 }; 112 } 113 ''; 114 115 description = '' 116 Each attribute of this option defines a systemd service that 117 runs an OpenVPN instance. These can be OpenVPN servers or 118 clients. The name of each systemd service is 119 <literal>openvpn-<replaceable>name</replaceable>.service</literal>, 120 where <replaceable>name</replaceable> is the corresponding 121 attribute name. 122 ''; 123 124 type = with types; attrsOf (submodule { 125 126 options = { 127 128 config = mkOption { 129 type = types.lines; 130 description = '' 131 Configuration of this OpenVPN instance. See 132 <citerefentry><refentrytitle>openvpn</refentrytitle><manvolnum>8</manvolnum></citerefentry> 133 for details. 134 135 To import an external config file, use the following definition: 136 <literal>config = "config /path/to/config.ovpn"</literal> 137 ''; 138 }; 139 140 up = mkOption { 141 default = ""; 142 type = types.lines; 143 description = '' 144 Shell commands executed when the instance is starting. 145 ''; 146 }; 147 148 down = mkOption { 149 default = ""; 150 type = types.lines; 151 description = '' 152 Shell commands executed when the instance is shutting down. 153 ''; 154 }; 155 156 autoStart = mkOption { 157 default = true; 158 type = types.bool; 159 description = "Whether this OpenVPN instance should be started automatically."; 160 }; 161 162 updateResolvConf = mkOption { 163 default = false; 164 type = types.bool; 165 description = '' 166 Use the script from the update-resolv-conf package to automatically 167 update resolv.conf with the DNS information provided by openvpn. The 168 script will be run after the "up" commands and before the "down" commands. 169 ''; 170 }; 171 172 authUserPass = mkOption { 173 default = null; 174 description = '' 175 This option can be used to store the username / password credentials 176 with the "auth-user-pass" authentication method. 177 178 WARNING: Using this option will put the credentials WORLD-READABLE in the Nix store! 179 ''; 180 type = types.nullOr (types.submodule { 181 182 options = { 183 username = mkOption { 184 description = "The username to store inside the credentials file."; 185 type = types.string; 186 }; 187 188 password = mkOption { 189 description = "The password to store inside the credentials file."; 190 type = types.string; 191 }; 192 }; 193 }); 194 }; 195 }; 196 197 }); 198 199 }; 200 201 }; 202 203 204 ###### implementation 205 206 config = mkIf (cfg.servers != {}) { 207 208 systemd.services = listToAttrs (mapAttrsFlatten (name: value: nameValuePair "openvpn-${name}" (makeOpenVPNJob value name)) cfg.servers); 209 210 environment.systemPackages = [ openvpn ]; 211 212 boot.kernelModules = [ "tun" ]; 213 214 }; 215 216}