at 18.03-beta 6.3 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 --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 }; 136 137 up = mkOption { 138 default = ""; 139 type = types.lines; 140 description = '' 141 Shell commands executed when the instance is starting. 142 ''; 143 }; 144 145 down = mkOption { 146 default = ""; 147 type = types.lines; 148 description = '' 149 Shell commands executed when the instance is shutting down. 150 ''; 151 }; 152 153 autoStart = mkOption { 154 default = true; 155 type = types.bool; 156 description = "Whether this OpenVPN instance should be started automatically."; 157 }; 158 159 updateResolvConf = mkOption { 160 default = false; 161 type = types.bool; 162 description = '' 163 Use the script from the update-resolv-conf package to automatically 164 update resolv.conf with the DNS information provided by openvpn. The 165 script will be run after the "up" commands and before the "down" commands. 166 ''; 167 }; 168 169 authUserPass = mkOption { 170 default = null; 171 description = '' 172 This option can be used to store the username / password credentials 173 with the "auth-user-pass" authentication method. 174 175 WARNING: Using this option will put the credentials WORLD-READABLE in the Nix store! 176 ''; 177 type = types.nullOr (types.submodule { 178 179 options = { 180 username = mkOption { 181 description = "The username to store inside the credentials file."; 182 type = types.string; 183 }; 184 185 password = mkOption { 186 description = "The password to store inside the credentials file."; 187 type = types.string; 188 }; 189 }; 190 }); 191 }; 192 }; 193 194 }); 195 196 }; 197 198 }; 199 200 201 ###### implementation 202 203 config = mkIf (cfg.servers != {}) { 204 205 systemd.services = listToAttrs (mapAttrsFlatten (name: value: nameValuePair "openvpn-${name}" (makeOpenVPNJob value name)) cfg.servers); 206 207 environment.systemPackages = [ openvpn ]; 208 209 boot.kernelModules = [ "tun" ]; 210 211 }; 212 213}