Initial prototype for Wireguard based PIA VPN - not quite 'ready' yet
This commit is contained in:
		
							parent
							
								
									8fb45a7ee5
								
							
						
					
					
						commit
						cea9b9452b
					
				| @ -9,6 +9,7 @@ in | ||||
|   imports = [ | ||||
|     ./hosts.nix | ||||
|     ./pia-openvpn.nix | ||||
|     ./pia-wireguard.nix | ||||
|     ./ping.nix | ||||
|     ./tailscale.nix | ||||
|     ./vpn.nix | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| { config, pkgs, lib, ... }: | ||||
| 
 | ||||
| let | ||||
|   cfg = config.pia; | ||||
|   cfg = config.pia.openvpn; | ||||
|   vpnfailsafe = pkgs.stdenv.mkDerivation { | ||||
|     pname = "vpnfailsafe"; | ||||
|     version = "0.0.1"; | ||||
| @ -14,7 +14,7 @@ let | ||||
|   }; | ||||
| in | ||||
| { | ||||
|   options.pia = { | ||||
|   options.pia.openvpn = { | ||||
|     enable = lib.mkEnableOption "Enable private internet access"; | ||||
|     server = lib.mkOption { | ||||
|       type = lib.types.str; | ||||
|  | ||||
							
								
								
									
										201
									
								
								common/network/pia-wireguard.nix
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								common/network/pia-wireguard.nix
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,201 @@ | ||||
| { config, lib, pkgs, ... }: | ||||
| 
 | ||||
| # Server list: | ||||
| #   https://serverlist.piaservers.net/vpninfo/servers/v6 | ||||
| # Reference materials: | ||||
| #   https://github.com/pia-foss/manual-connections | ||||
| #   https://github.com/thrnz/docker-wireguard-pia/blob/master/extra/wg-gen.sh | ||||
| 
 | ||||
| let | ||||
|   cfg = config.pia.wireguard; | ||||
| 
 | ||||
|   getPIAToken = '' | ||||
|     PIA_USER=`sed '1q;d' /run/agenix/pia-login.conf` | ||||
|     PIA_PASS=`sed '2q;d' /run/agenix/pia-login.conf` | ||||
|     # PIA_TOKEN only lasts 24hrs | ||||
|     PIA_TOKEN=`curl -s -u "$PIA_USER:$PIA_PASS" https://www.privateinternetaccess.com/gtoken/generateToken | jq -r '.token'` | ||||
|   ''; | ||||
| in { | ||||
|   options.pia.wireguard = { | ||||
|     enable = lib.mkEnableOption "Enable private internet access"; | ||||
|     serverHostname = lib.mkOption { | ||||
|       type = lib.types.str; | ||||
|       default = "zurich406"; | ||||
|     }; | ||||
|     serverIp = lib.mkOption { | ||||
|       type = lib.types.str; | ||||
|       default = "156.146.62.153"; | ||||
|     }; | ||||
|     interfaceName = lib.mkOption { | ||||
|       type = lib.types.str; | ||||
|       default = "piaw"; | ||||
|     }; | ||||
|     portForwarding = lib.mkEnableOption "Enables PIA port fowarding"; | ||||
| 
 | ||||
|     # TODO implement this such that the wireguard VPN doesn't have to live in a container | ||||
|     useInVPNContainer = lib.mkEnableOption "Configures the PIA WG VPN for use in the VPN container"; | ||||
|   }; | ||||
| 
 | ||||
|   config = lib.mkIf cfg.enable { | ||||
|     vpn-container.mounts = [ "/tmp/${cfg.interfaceName}.conf" "/tmp/${cfg.interfaceName}-address.conf" ]; | ||||
|     containers.vpn.interfaces = [ cfg.interfaceName ]; | ||||
| 
 | ||||
|     # allow traffic for wireguard interface to pass since wireguard trips up rpfilter | ||||
|     # networking.firewall = { | ||||
|     #   extraCommands = '' | ||||
|     #     ip46tables -t raw -I nixos-fw-rpfilter -p udp -m udp --sport 51820 -j RETURN | ||||
|     #     ip46tables -t raw -I nixos-fw-rpfilter -p udp -m udp --dport 51820 -j RETURN | ||||
|     #   ''; | ||||
|     #   extraStopCommands = '' | ||||
|     #     ip46tables -t raw -D nixos-fw-rpfilter -p udp -m udp --sport 51820 -j RETURN || true | ||||
|     #     ip46tables -t raw -D nixos-fw-rpfilter -p udp -m udp --dport 51820 -j RETURN || true | ||||
|     #   ''; | ||||
|     # }; | ||||
|     networking.firewall.checkReversePath = "loose"; | ||||
| 
 | ||||
|     systemd.services.pia-vpn-wireguard-init = { | ||||
|       description = "Creates PIA VPN Wireguard Interface"; | ||||
| 
 | ||||
|       requires = [ "network-online.target" ]; | ||||
|       after = [ "network.target" "network-online.target" ]; | ||||
|       before = [ "container@vpn.service" ]; | ||||
|       requiredBy = [ "container@vpn.service" ]; | ||||
|       wantedBy = [ "multi-user.target" ]; | ||||
| 
 | ||||
|       path = with pkgs; [ wireguard-tools jq curl iproute ]; | ||||
| 
 | ||||
|       serviceConfig = { | ||||
|         Type = "oneshot"; | ||||
|         RemainAfterExit = true; | ||||
|       }; | ||||
| 
 | ||||
|       script = '' | ||||
|         # Prepare to connect by generating wg secrets and auth'ing with PIA since the container | ||||
|         # cannot do without internet to start with. NAT'ing the host's internet would address this | ||||
|         # issue but is not ideal because then leaking network outside of the VPN is more likely. | ||||
| 
 | ||||
|         WG_HOSTNAME=${cfg.serverHostname} | ||||
|         WG_SERVER_IP=${cfg.serverIp} | ||||
| 
 | ||||
|         ${getPIAToken} | ||||
| 
 | ||||
|         privKey=$(wg genkey) | ||||
|         pubKey=$(echo "$privKey" | wg pubkey) | ||||
| 
 | ||||
|         wireguard_json=`curl -s -G --connect-to "$WG_HOSTNAME::$WG_SERVER_IP:" --cacert "${./ca.rsa.4096.crt}" --data-urlencode "pt=$PIA_TOKEN" --data-urlencode "pubkey=$pubKey" https://$WG_HOSTNAME:1337/addKey` | ||||
| 
 | ||||
|         rm -f /tmp/${cfg.interfaceName}.conf /tmp/${cfg.interfaceName}-address.conf | ||||
|         touch /tmp/${cfg.interfaceName}.conf /tmp/${cfg.interfaceName}-address.conf | ||||
|         chmod 700 /tmp/${cfg.interfaceName}.conf /tmp/${cfg.interfaceName}-address.conf | ||||
|         echo "                | ||||
|         [Interface] | ||||
|         # Address = $(echo "$wireguard_json" | jq -r '.peer_ip') | ||||
|         PrivateKey = $privKey | ||||
|         ListenPort = 51820 | ||||
|         [Peer] | ||||
|         PersistentKeepalive = 25 | ||||
|         PublicKey = $(echo "$wireguard_json" | jq -r '.server_key') | ||||
|         AllowedIPs = 0.0.0.0/0 | ||||
|         Endpoint = $WG_SERVER_IP:$(echo "$wireguard_json" | jq -r '.server_port') | ||||
|         " >> /tmp/${cfg.interfaceName}.conf | ||||
|         echo "$wireguard_json" | jq -r '.peer_ip' >> /tmp/${cfg.interfaceName}-address.conf | ||||
| 
 | ||||
|         # Create wg interface now so it inherits from the namespace with internet access | ||||
|         # the container will handle actually connecting the interface since that info is | ||||
|         # not preserved upon moving into the container's networking namespace | ||||
|         # Roughly following this guide https://www.wireguard.com/netns/#ordinary-containerization | ||||
|         [[ -z $(ip link show dev ${cfg.interfaceName} 2>/dev/null) ]] || exit | ||||
|         ip link add ${cfg.interfaceName} type wireguard | ||||
|       ''; | ||||
| 
 | ||||
|       preStop = '' | ||||
|         ip link del ${cfg.interfaceName} | ||||
|       ''; | ||||
|     }; | ||||
| 
 | ||||
|     vpn-container.config.systemd.services.pia-vpn-wireguard = { | ||||
|       description = "PIA VPN WireGuard Tunnel"; | ||||
| 
 | ||||
|       requires = [ "network-online.target" ]; | ||||
|       after = [ "network.target" "network-online.target" ]; | ||||
|       wantedBy = [ "multi-user.target" ]; | ||||
| 
 | ||||
|       path = with pkgs; [ wireguard-tools iproute curl jq ]; | ||||
| 
 | ||||
|       serviceConfig = { | ||||
|         Type = "oneshot"; | ||||
|         RemainAfterExit = true; | ||||
|       }; | ||||
| 
 | ||||
|       script = '' | ||||
|         # pseudo calls wg-quick | ||||
|         # wg-quick down /tmp/${cfg.interfaceName}.conf | ||||
|         # cannot actually call wg-quick because the interface has to be already created at this point | ||||
| 
 | ||||
|         # assumes wg interface was already created | ||||
|         # ip link add ${cfg.interfaceName} type wireguard | ||||
| 
 | ||||
|         myaddress=`cat /tmp/${cfg.interfaceName}-address.conf` | ||||
| 
 | ||||
|         wg setconf ${cfg.interfaceName} /tmp/${cfg.interfaceName}.conf | ||||
|         ip -4 address add $myaddress dev ${cfg.interfaceName} | ||||
|         ip link set mtu 1420 up dev ${cfg.interfaceName} | ||||
|         wg set ${cfg.interfaceName} fwmark 51820 | ||||
|         ip -4 route add 0.0.0.0/0 dev ${cfg.interfaceName} table 51820 | ||||
| 
 | ||||
|         # TODO is this needed? | ||||
|         ip -4 rule add not fwmark 51820 table 51820 | ||||
|         ip -4 rule add table main suppress_prefixlength 0 | ||||
|         # sysctl -q net.ipv4.conf.all.src_valid_mark=1 | ||||
| 
 | ||||
|         # Reserve port | ||||
|         ${getPIAToken} | ||||
|         payload_and_signature=`curl -s -m 5 --connect-to "${cfg.serverHostname}::${cfg.serverIp}:" --cacert "${./ca.rsa.4096.crt}" -G --data-urlencode "token=$PIA_TOKEN" "https://${cfg.serverHostname}:19999/getSignature"` | ||||
|         signature=$(echo "$payload_and_signature" | jq -r '.signature') | ||||
|         payload=$(echo "$payload_and_signature" | jq -r '.payload') | ||||
|         port=$(echo "$payload" | base64 -d | jq -r '.port') | ||||
| 
 | ||||
|         # write reserved port to file readable for all users | ||||
|         echo $port > /tmp/${cfg.interfaceName}-port | ||||
|         chmod 644 /tmp/${cfg.interfaceName}-port | ||||
| 
 | ||||
|         rm -f /tmp/${cfg.interfaceName}-port-renewal | ||||
|         touch /tmp/${cfg.interfaceName}-port-renewal | ||||
|         chmod 700 /tmp/${cfg.interfaceName}-port-renewal | ||||
|         echo $signature >> /tmp/${cfg.interfaceName}-port-renewal | ||||
|         echo $payload >> /tmp/${cfg.interfaceName}-port-renewal | ||||
|       ''; | ||||
| 
 | ||||
|       preStop = '' | ||||
|         wg-quick down /tmp/${cfg.interfaceName}.conf | ||||
|       ''; | ||||
|     }; | ||||
| 
 | ||||
|     vpn-container.config.systemd.services.pia-vpn-wireguard-forward-port = { | ||||
|       enable = cfg.portForwarding; | ||||
|       description = "PIA VPN WireGuard Tunnel Port Forwarding"; | ||||
|       after = [ "pia-vpn-wireguard.service" ]; | ||||
|       requires = [ "pia-vpn-wireguard.service" ]; | ||||
|       path = with pkgs; [ curl ]; | ||||
|       script = '' | ||||
|         signature=`sed '1q;d' /tmp/${cfg.interfaceName}-port-renewal` | ||||
|         payload=`sed '2q;d' /tmp/${cfg.interfaceName}-port-renewal` | ||||
|         bind_port_response=`curl -Gs -m 5 --connect-to "${cfg.serverHostname}::${cfg.serverIp}:" --cacert "${./ca.rsa.4096.crt}" --data-urlencode "payload=$payload" --data-urlencode "signature=$signature" "https://${cfg.serverHostname}:19999/bindPort"` | ||||
|       ''; | ||||
|     }; | ||||
| 
 | ||||
|     vpn-container.config.systemd.timers.pia-vpn-wireguard-forward-port = { | ||||
|       enable = cfg.portForwarding; | ||||
|       # partOf = [ "pia-vpn-wireguard-forward-port.service" ]; | ||||
|       wantedBy = [ "timers.target" ]; | ||||
|       timerConfig.OnCalendar = "*:0/10"; # 10 minutes | ||||
|     }; | ||||
| 
 | ||||
|     # TODO enable firewall on the PIA interface | ||||
|     # TODO handle errors | ||||
|     # TODO handle 2 month limit for port | ||||
|     # TODO print status, success, and failures to the console | ||||
| 
 | ||||
|     age.secrets."pia-login.conf".file = ../../secrets/pia-login.conf; | ||||
|   }; | ||||
| } | ||||
| @ -26,6 +26,8 @@ in | ||||
|       ''; | ||||
|     }; | ||||
| 
 | ||||
|     useOpenVPN = mkEnableOption "Uses OpenVPN instead of wireguard for PIA VPN connection"; | ||||
| 
 | ||||
|     config = mkOption { | ||||
|       type = types.anything; | ||||
|       default = {}; | ||||
| @ -41,6 +43,8 @@ in | ||||
|   }; | ||||
| 
 | ||||
|   config = mkIf cfg.enable { | ||||
|     pia.wireguard.enable = !cfg.useOpenVPN; | ||||
| 
 | ||||
|     containers.${cfg.containerName} = { | ||||
|       ephemeral = true; | ||||
|       autoStart = true; | ||||
| @ -59,7 +63,7 @@ in | ||||
|         } | ||||
|       ))); | ||||
| 
 | ||||
|       enableTun = true; | ||||
|       enableTun = cfg.useOpenVPN; | ||||
|       privateNetwork = true; | ||||
|       hostAddress = "172.16.100.1"; | ||||
|       localAddress = "172.16.100.2"; | ||||
| @ -67,12 +71,13 @@ in | ||||
|       config = { | ||||
|         imports = allModules ++ [cfg.config]; | ||||
| 
 | ||||
|         # speeds up evaluation | ||||
|         nixpkgs.pkgs = pkgs; | ||||
| 
 | ||||
|         networking.firewall.enable = mkForce false; | ||||
| 
 | ||||
|         pia.enable = true; | ||||
|         pia.server = "swiss.privacy.network"; # swiss vpn | ||||
|         pia.openvpn.enable = cfg.useOpenVPN; | ||||
|         pia.openvpn.server = "swiss.privacy.network"; # swiss vpn | ||||
| 
 | ||||
|         # TODO fix so it does run it's own resolver again | ||||
|         # run it's own DNS resolver | ||||
| @ -85,12 +90,12 @@ in | ||||
|     # load secrets the container needs | ||||
|     age.secrets = config.containers.${cfg.containerName}.config.age.secrets; | ||||
| 
 | ||||
|     # forwarding for vpn container | ||||
|     networking.nat.enable = true; | ||||
|     networking.nat.internalInterfaces = [ | ||||
|     # forwarding for vpn container (only for OpenVPN) | ||||
|     networking.nat.enable = mkIf cfg.useOpenVPN true; | ||||
|     networking.nat.internalInterfaces = mkIf cfg.useOpenVPN [ | ||||
|       "ve-${cfg.containerName}" | ||||
|     ]; | ||||
|     networking.ip_forward = true; | ||||
|     networking.ip_forward = mkIf cfg.useOpenVPN true; | ||||
| 
 | ||||
|     # assumes only one potential interface | ||||
|     networking.usePredictableInterfaceNames = false; | ||||
|  | ||||
| @ -35,71 +35,6 @@ | ||||
| 
 | ||||
|   services.spotifyd.enable = true; | ||||
| 
 | ||||
|   # vpn-container.enable = true; | ||||
|   # containers.vpn.interfaces = [ "piaw" ]; | ||||
| 
 | ||||
|   # allow traffic for wireguard interface to pass | ||||
|   # networking.firewall = { | ||||
|   #   # wireguard trips rpfilter up | ||||
|   #   extraCommands = '' | ||||
|   #     ip46tables -t raw -I nixos-fw-rpfilter -p udp -m udp --sport 51820 -j RETURN | ||||
|   #     ip46tables -t raw -I nixos-fw-rpfilter -p udp -m udp --dport 51820 -j RETURN | ||||
|   #   ''; | ||||
|   #   extraStopCommands = '' | ||||
|   #     ip46tables -t raw -D nixos-fw-rpfilter -p udp -m udp --sport 51820 -j RETURN || true | ||||
|   #     ip46tables -t raw -D nixos-fw-rpfilter -p udp -m udp --dport 51820 -j RETURN || true | ||||
|   #   ''; | ||||
|   # }; | ||||
| 
 | ||||
|   # systemd.services.pia-vpn-wireguard = { | ||||
|   #   enable = true; | ||||
|   #   description = "PIA VPN WireGuard Tunnel"; | ||||
|   #   requires = [ "network-online.target" ]; | ||||
|   #   after = [ "network.target" "network-online.target" ]; | ||||
|   #   wantedBy = [ "multi-user.target" ]; | ||||
|   #   environment.DEVICE = "piaw"; | ||||
|   #   path = with pkgs; [ kmod wireguard-tools jq curl ]; | ||||
| 
 | ||||
|   #   serviceConfig = { | ||||
|   #     Type = "oneshot"; | ||||
|   #     RemainAfterExit = true; | ||||
|   #   }; | ||||
| 
 | ||||
|   #   script = '' | ||||
|   #     WG_HOSTNAME=zurich406 | ||||
|   #     WG_SERVER_IP=156.146.62.153 | ||||
| 
 | ||||
|   #     PIA_USER=`sed '1q;d' /run/agenix/pia-login.conf` | ||||
|   #     PIA_PASS=`sed '2q;d' /run/agenix/pia-login.conf` | ||||
|   #     PIA_TOKEN=`curl -s -u "$PIA_USER:$PIA_PASS" https://www.privateinternetaccess.com/gtoken/generateToken | jq -r '.token'` | ||||
|   #     privKey=$(wg genkey) | ||||
|   #     pubKey=$(echo "$privKey" | wg pubkey) | ||||
|   #     wireguard_json=`curl -s -G --connect-to "$WG_HOSTNAME::$WG_SERVER_IP:" --cacert "${./ca.rsa.4096.crt}" --data-urlencode "pt=$PIA_TOKEN" --data-urlencode "pubkey=$pubKey" https://$WG_HOSTNAME:1337/addKey` | ||||
| 
 | ||||
|   #     echo "                | ||||
|   #     [Interface] | ||||
|   #     Address = $(echo "$wireguard_json" | jq -r '.peer_ip') | ||||
|   #     PrivateKey = $privKey | ||||
|   #     ListenPort = 51820 | ||||
|   #     [Peer] | ||||
|   #     PersistentKeepalive = 25 | ||||
|   #     PublicKey = $(echo "$wireguard_json" | jq -r '.server_key') | ||||
|   #     AllowedIPs = 0.0.0.0/0 | ||||
|   #     Endpoint = $WG_SERVER_IP:$(echo "$wireguard_json" | jq -r '.server_port') | ||||
|   #     " > /tmp/piaw.conf | ||||
| 
 | ||||
|   #     # TODO make /tmp/piaw.conf ro to root | ||||
| 
 | ||||
|   #     ${lib.optionalString (!config.boot.isContainer) "modprobe wireguard"} | ||||
|   #     wg-quick up /tmp/piaw.conf | ||||
|   #   ''; | ||||
| 
 | ||||
|   #   preStop = '' | ||||
|   #     wg-quick down /tmp/piaw.conf | ||||
|   #   ''; | ||||
|   # }; | ||||
|   # age.secrets."pia-login.conf".file = ../../secrets/pia-login.conf; | ||||
| 
 | ||||
|   virtualisation.docker.enable = true; | ||||
| 
 | ||||
|   services.zerotierone.enable = true; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user