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 ''; 33 34 downScript = '' 35 #! /bin/sh 36 export PATH=${path} 37 ${cfg.down} 38 ''; 39 40 configFile = pkgs.writeText "openvpn-config-${name}" 41 '' 42 errors-to-stderr 43 ${optionalString (cfg.up != "" || cfg.down != "") "script-security 2"} 44 ${cfg.config} 45 ${optionalString (cfg.up != "") "up ${pkgs.writeScript "openvpn-${name}-up" upScript}"} 46 ${optionalString (cfg.down != "") "down ${pkgs.writeScript "openvpn-${name}-down" downScript}"} 47 ''; 48 49 in { 50 description = "OpenVPN instance ${name}"; 51 52 wantedBy = optional cfg.autoStart "multi-user.target"; 53 after = [ "network-interfaces.target" ]; 54 55 path = [ pkgs.iptables pkgs.iproute pkgs.nettools ]; 56 57 serviceConfig.ExecStart = "@${openvpn}/sbin/openvpn openvpn --config ${configFile}"; 58 serviceConfig.Restart = "always"; 59 serviceConfig.Type = "notify"; 60 }; 61 62in 63 64{ 65 66 ###### interface 67 68 options = { 69 70 services.openvpn.servers = mkOption { 71 default = {}; 72 73 example = literalExample '' 74 { 75 server = { 76 config = ''' 77 # Simplest server configuration: http://openvpn.net/index.php/documentation/miscellaneous/static-key-mini-howto.html. 78 # server : 79 dev tun 80 ifconfig 10.8.0.1 10.8.0.2 81 secret /root/static.key 82 '''; 83 up = "ip route add ..."; 84 down = "ip route del ..."; 85 }; 86 87 client = { 88 config = ''' 89 client 90 remote vpn.example.org 91 dev tun 92 proto tcp-client 93 port 8080 94 ca /root/.vpn/ca.crt 95 cert /root/.vpn/alice.crt 96 key /root/.vpn/alice.key 97 '''; 98 up = "echo nameserver $nameserver | ''${pkgs.openresolv}/sbin/resolvconf -m 0 -a $dev"; 99 down = "''${pkgs.openresolv}/sbin/resolvconf -d $dev"; 100 }; 101 } 102 ''; 103 104 description = '' 105 Each attribute of this option defines a systemd service that 106 runs an OpenVPN instance. These can be OpenVPN servers or 107 clients. The name of each systemd service is 108 <literal>openvpn-<replaceable>name</replaceable>.service</literal>, 109 where <replaceable>name</replaceable> is the corresponding 110 attribute name. 111 ''; 112 113 type = types.attrsOf types.optionSet; 114 115 options = { 116 117 config = mkOption { 118 type = types.lines; 119 description = '' 120 Configuration of this OpenVPN instance. See 121 <citerefentry><refentrytitle>openvpn</refentrytitle><manvolnum>8</manvolnum></citerefentry> 122 for details. 123 ''; 124 }; 125 126 up = mkOption { 127 default = ""; 128 type = types.lines; 129 description = '' 130 Shell commands executed when the instance is starting. 131 ''; 132 }; 133 134 down = mkOption { 135 default = ""; 136 type = types.lines; 137 description = '' 138 Shell commands executed when the instance is shutting down. 139 ''; 140 }; 141 142 autoStart = mkOption { 143 default = true; 144 type = types.bool; 145 description = "Whether this OpenVPN instance should be started automatically."; 146 }; 147 148 }; 149 150 }; 151 152 }; 153 154 155 ###### implementation 156 157 config = mkIf (cfg.servers != {}) { 158 159 systemd.services = listToAttrs (mapAttrsFlatten (name: value: nameValuePair "openvpn-${name}" (makeOpenVPNJob value name)) cfg.servers); 160 161 environment.systemPackages = [ openvpn ]; 162 163 boot.kernelModules = [ "tun" ]; 164 165 }; 166 167}