at 23.11-pre 7.1 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 = 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.down != "" || cfg.updateResolvConf) 50 "down ${pkgs.writeShellScript "openvpn-${name}-down" downScript}"} 51 ${optionalString (cfg.authUserPass != null) 52 "auth-user-pass ${pkgs.writeText "openvpn-credentials-${name}" '' 53 ${cfg.authUserPass.username} 54 ${cfg.authUserPass.password} 55 ''}"} 56 ''; 57 58 in 59 { 60 description = "OpenVPN instance ${name}"; 61 62 wantedBy = optional cfg.autoStart "multi-user.target"; 63 after = [ "network.target" ]; 64 65 path = [ pkgs.iptables pkgs.iproute2 pkgs.nettools ]; 66 67 serviceConfig.ExecStart = "@${openvpn}/sbin/openvpn openvpn --suppress-timestamps --config ${configFile}"; 68 serviceConfig.Restart = "always"; 69 serviceConfig.Type = "notify"; 70 }; 71 72 restartService = optionalAttrs cfg.restartAfterSleep { 73 openvpn-restart = { 74 wantedBy = [ "sleep.target" ]; 75 path = [ pkgs.procps ]; 76 script = "pkill --signal SIGHUP --exact openvpn"; 77 #SIGHUP makes openvpn process to self-exit and then it got restarted by systemd because of Restart=always 78 description = "Sends a signal to OpenVPN process to trigger a restart after return from sleep"; 79 }; 80 }; 81 82in 83 84{ 85 imports = [ 86 (mkRemovedOptionModule [ "services" "openvpn" "enable" ] "") 87 ]; 88 89 ###### interface 90 91 options = { 92 93 services.openvpn.servers = mkOption { 94 default = { }; 95 96 example = literalExpression '' 97 { 98 server = { 99 config = ''' 100 # Simplest server configuration: https://community.openvpn.net/openvpn/wiki/StaticKeyMiniHowto 101 # server : 102 dev tun 103 ifconfig 10.8.0.1 10.8.0.2 104 secret /root/static.key 105 '''; 106 up = "ip route add ..."; 107 down = "ip route del ..."; 108 }; 109 110 client = { 111 config = ''' 112 client 113 remote vpn.example.org 114 dev tun 115 proto tcp-client 116 port 8080 117 ca /root/.vpn/ca.crt 118 cert /root/.vpn/alice.crt 119 key /root/.vpn/alice.key 120 '''; 121 up = "echo nameserver $nameserver | ''${pkgs.openresolv}/sbin/resolvconf -m 0 -a $dev"; 122 down = "''${pkgs.openresolv}/sbin/resolvconf -d $dev"; 123 }; 124 } 125 ''; 126 127 description = lib.mdDoc '' 128 Each attribute of this option defines a systemd service that 129 runs an OpenVPN instance. These can be OpenVPN servers or 130 clients. The name of each systemd service is 131 `openvpn-«name».service`, 132 where «name» is the corresponding 133 attribute name. 134 ''; 135 136 type = with types; attrsOf (submodule { 137 138 options = { 139 140 config = mkOption { 141 type = types.lines; 142 description = lib.mdDoc '' 143 Configuration of this OpenVPN instance. See 144 {manpage}`openvpn(8)` 145 for details. 146 147 To import an external config file, use the following definition: 148 `config = "config /path/to/config.ovpn"` 149 ''; 150 }; 151 152 up = mkOption { 153 default = ""; 154 type = types.lines; 155 description = lib.mdDoc '' 156 Shell commands executed when the instance is starting. 157 ''; 158 }; 159 160 down = mkOption { 161 default = ""; 162 type = types.lines; 163 description = lib.mdDoc '' 164 Shell commands executed when the instance is shutting down. 165 ''; 166 }; 167 168 autoStart = mkOption { 169 default = true; 170 type = types.bool; 171 description = lib.mdDoc "Whether this OpenVPN instance should be started automatically."; 172 }; 173 174 updateResolvConf = mkOption { 175 default = false; 176 type = types.bool; 177 description = lib.mdDoc '' 178 Use the script from the update-resolv-conf package to automatically 179 update resolv.conf with the DNS information provided by openvpn. The 180 script will be run after the "up" commands and before the "down" commands. 181 ''; 182 }; 183 184 authUserPass = mkOption { 185 default = null; 186 description = lib.mdDoc '' 187 This option can be used to store the username / password credentials 188 with the "auth-user-pass" authentication method. 189 190 WARNING: Using this option will put the credentials WORLD-READABLE in the Nix store! 191 ''; 192 type = types.nullOr (types.submodule { 193 194 options = { 195 username = mkOption { 196 description = lib.mdDoc "The username to store inside the credentials file."; 197 type = types.str; 198 }; 199 200 password = mkOption { 201 description = lib.mdDoc "The password to store inside the credentials file."; 202 type = types.str; 203 }; 204 }; 205 }); 206 }; 207 }; 208 209 }); 210 211 }; 212 213 services.openvpn.restartAfterSleep = mkOption { 214 default = true; 215 type = types.bool; 216 description = lib.mdDoc "Whether OpenVPN client should be restarted after sleep."; 217 }; 218 219 }; 220 221 222 ###### implementation 223 224 config = mkIf (cfg.servers != { }) { 225 226 systemd.services = (listToAttrs (mapAttrsFlatten (name: value: nameValuePair "openvpn-${name}" (makeOpenVPNJob value name)) cfg.servers)) 227 // restartService; 228 229 environment.systemPackages = [ openvpn ]; 230 231 boot.kernelModules = [ "tun" ]; 232 233 }; 234 235}