at 25.11-pre 7.3 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7 8with lib; 9 10let 11 12 cfg = config.services.openvpn; 13 14 inherit (pkgs) openvpn; 15 16 makeOpenVPNJob = 17 cfg: name: 18 let 19 20 path = makeBinPath (getAttr "openvpn-${name}" config.systemd.services).path; 21 22 upScript = '' 23 export PATH=${path} 24 25 # For convenience in client scripts, extract the remote domain 26 # name and name server. 27 for var in ''${!foreign_option_*}; do 28 x=(''${!var}) 29 if [ "''${x[0]}" = dhcp-option ]; then 30 if [ "''${x[1]}" = DOMAIN ]; then domain="''${x[2]}" 31 elif [ "''${x[1]}" = DNS ]; then nameserver="''${x[2]}" 32 fi 33 fi 34 done 35 36 ${cfg.up} 37 ${optionalString cfg.updateResolvConf "${pkgs.update-resolv-conf}/libexec/openvpn/update-resolv-conf"} 38 ''; 39 40 downScript = '' 41 export PATH=${path} 42 ${optionalString cfg.updateResolvConf "${pkgs.update-resolv-conf}/libexec/openvpn/update-resolv-conf"} 43 ${cfg.down} 44 ''; 45 46 configFile = pkgs.writeText "openvpn-config-${name}" '' 47 errors-to-stderr 48 ${optionalString (cfg.up != "" || cfg.down != "" || cfg.updateResolvConf) "script-security 2"} 49 ${cfg.config} 50 ${optionalString ( 51 cfg.up != "" || cfg.updateResolvConf 52 ) "up ${pkgs.writeShellScript "openvpn-${name}-up" upScript}"} 53 ${optionalString ( 54 cfg.down != "" || cfg.updateResolvConf 55 ) "down ${pkgs.writeShellScript "openvpn-${name}-down" downScript}"} 56 ${optionalString (cfg.authUserPass != null) 57 "auth-user-pass ${pkgs.writeText "openvpn-credentials-${name}" '' 58 ${cfg.authUserPass.username} 59 ${cfg.authUserPass.password} 60 ''}" 61 } 62 ''; 63 64 in 65 { 66 description = "OpenVPN instance ${name}"; 67 68 wantedBy = optional cfg.autoStart "multi-user.target"; 69 after = [ "network.target" ]; 70 71 path = [ 72 pkgs.iptables 73 pkgs.iproute2 74 pkgs.nettools 75 ]; 76 77 serviceConfig.ExecStart = "@${openvpn}/sbin/openvpn openvpn --suppress-timestamps --config ${configFile}"; 78 serviceConfig.Restart = "always"; 79 serviceConfig.Type = "notify"; 80 }; 81 82 restartService = optionalAttrs cfg.restartAfterSleep { 83 openvpn-restart = { 84 wantedBy = [ "sleep.target" ]; 85 path = [ pkgs.procps ]; 86 script = 87 let 88 unitNames = map (n: "openvpn-${n}.service") (builtins.attrNames cfg.servers); 89 in 90 "systemctl try-restart ${lib.escapeShellArgs unitNames}"; 91 description = "Sends a signal to OpenVPN process to trigger a restart after return from sleep"; 92 }; 93 }; 94 95in 96 97{ 98 imports = [ 99 (mkRemovedOptionModule [ "services" "openvpn" "enable" ] "") 100 ]; 101 102 ###### interface 103 104 options = { 105 106 services.openvpn.servers = mkOption { 107 default = { }; 108 109 example = literalExpression '' 110 { 111 server = { 112 config = ''' 113 # Simplest server configuration: https://community.openvpn.net/openvpn/wiki/StaticKeyMiniHowto 114 # server : 115 dev tun 116 ifconfig 10.8.0.1 10.8.0.2 117 secret /root/static.key 118 '''; 119 up = "ip route add ..."; 120 down = "ip route del ..."; 121 }; 122 123 client = { 124 config = ''' 125 client 126 remote vpn.example.org 127 dev tun 128 proto tcp-client 129 port 8080 130 ca /root/.vpn/ca.crt 131 cert /root/.vpn/alice.crt 132 key /root/.vpn/alice.key 133 '''; 134 up = "echo nameserver $nameserver | ''${pkgs.openresolv}/sbin/resolvconf -m 0 -a $dev"; 135 down = "''${pkgs.openresolv}/sbin/resolvconf -d $dev"; 136 }; 137 } 138 ''; 139 140 description = '' 141 Each attribute of this option defines a systemd service that 142 runs an OpenVPN instance. These can be OpenVPN servers or 143 clients. The name of each systemd service is 144 `openvpn-«name».service`, 145 where «name» is the corresponding 146 attribute name. 147 ''; 148 149 type = 150 with types; 151 attrsOf (submodule { 152 153 options = { 154 155 config = mkOption { 156 type = types.lines; 157 description = '' 158 Configuration of this OpenVPN instance. See 159 {manpage}`openvpn(8)` 160 for details. 161 162 To import an external config file, use the following definition: 163 `config = "config /path/to/config.ovpn"` 164 ''; 165 }; 166 167 up = mkOption { 168 default = ""; 169 type = types.lines; 170 description = '' 171 Shell commands executed when the instance is starting. 172 ''; 173 }; 174 175 down = mkOption { 176 default = ""; 177 type = types.lines; 178 description = '' 179 Shell commands executed when the instance is shutting down. 180 ''; 181 }; 182 183 autoStart = mkOption { 184 default = true; 185 type = types.bool; 186 description = "Whether this OpenVPN instance should be started automatically."; 187 }; 188 189 updateResolvConf = mkOption { 190 default = false; 191 type = types.bool; 192 description = '' 193 Use the script from the update-resolv-conf package to automatically 194 update resolv.conf with the DNS information provided by openvpn. The 195 script will be run after the "up" commands and before the "down" commands. 196 ''; 197 }; 198 199 authUserPass = mkOption { 200 default = null; 201 description = '' 202 This option can be used to store the username / password credentials 203 with the "auth-user-pass" authentication method. 204 205 WARNING: Using this option will put the credentials WORLD-READABLE in the Nix store! 206 ''; 207 type = types.nullOr ( 208 types.submodule { 209 210 options = { 211 username = mkOption { 212 description = "The username to store inside the credentials file."; 213 type = types.str; 214 }; 215 216 password = mkOption { 217 description = "The password to store inside the credentials file."; 218 type = types.str; 219 }; 220 }; 221 } 222 ); 223 }; 224 }; 225 226 }); 227 228 }; 229 230 services.openvpn.restartAfterSleep = mkOption { 231 default = true; 232 type = types.bool; 233 description = "Whether OpenVPN client should be restarted after sleep."; 234 }; 235 236 }; 237 238 ###### implementation 239 240 config = mkIf (cfg.servers != { }) { 241 242 systemd.services = 243 (listToAttrs ( 244 mapAttrsToList ( 245 name: value: nameValuePair "openvpn-${name}" (makeOpenVPNJob value name) 246 ) cfg.servers 247 )) 248 // restartService; 249 250 environment.systemPackages = [ openvpn ]; 251 252 boot.kernelModules = [ "tun" ]; 253 254 }; 255 256}