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 = [
|
imports = [
|
||||||
./hosts.nix
|
./hosts.nix
|
||||||
./pia-openvpn.nix
|
./pia-openvpn.nix
|
||||||
|
./pia-wireguard.nix
|
||||||
./ping.nix
|
./ping.nix
|
||||||
./tailscale.nix
|
./tailscale.nix
|
||||||
./vpn.nix
|
./vpn.nix
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{ config, pkgs, lib, ... }:
|
{ config, pkgs, lib, ... }:
|
||||||
|
|
||||||
let
|
let
|
||||||
cfg = config.pia;
|
cfg = config.pia.openvpn;
|
||||||
vpnfailsafe = pkgs.stdenv.mkDerivation {
|
vpnfailsafe = pkgs.stdenv.mkDerivation {
|
||||||
pname = "vpnfailsafe";
|
pname = "vpnfailsafe";
|
||||||
version = "0.0.1";
|
version = "0.0.1";
|
||||||
@ -14,7 +14,7 @@ let
|
|||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.pia = {
|
options.pia.openvpn = {
|
||||||
enable = lib.mkEnableOption "Enable private internet access";
|
enable = lib.mkEnableOption "Enable private internet access";
|
||||||
server = lib.mkOption {
|
server = lib.mkOption {
|
||||||
type = lib.types.str;
|
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 {
|
config = mkOption {
|
||||||
type = types.anything;
|
type = types.anything;
|
||||||
default = {};
|
default = {};
|
||||||
@ -41,6 +43,8 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
|
pia.wireguard.enable = !cfg.useOpenVPN;
|
||||||
|
|
||||||
containers.${cfg.containerName} = {
|
containers.${cfg.containerName} = {
|
||||||
ephemeral = true;
|
ephemeral = true;
|
||||||
autoStart = true;
|
autoStart = true;
|
||||||
@ -59,7 +63,7 @@ in
|
|||||||
}
|
}
|
||||||
)));
|
)));
|
||||||
|
|
||||||
enableTun = true;
|
enableTun = cfg.useOpenVPN;
|
||||||
privateNetwork = true;
|
privateNetwork = true;
|
||||||
hostAddress = "172.16.100.1";
|
hostAddress = "172.16.100.1";
|
||||||
localAddress = "172.16.100.2";
|
localAddress = "172.16.100.2";
|
||||||
@ -67,12 +71,13 @@ in
|
|||||||
config = {
|
config = {
|
||||||
imports = allModules ++ [cfg.config];
|
imports = allModules ++ [cfg.config];
|
||||||
|
|
||||||
|
# speeds up evaluation
|
||||||
nixpkgs.pkgs = pkgs;
|
nixpkgs.pkgs = pkgs;
|
||||||
|
|
||||||
networking.firewall.enable = mkForce false;
|
networking.firewall.enable = mkForce false;
|
||||||
|
|
||||||
pia.enable = true;
|
pia.openvpn.enable = cfg.useOpenVPN;
|
||||||
pia.server = "swiss.privacy.network"; # swiss vpn
|
pia.openvpn.server = "swiss.privacy.network"; # swiss vpn
|
||||||
|
|
||||||
# TODO fix so it does run it's own resolver again
|
# TODO fix so it does run it's own resolver again
|
||||||
# run it's own DNS resolver
|
# run it's own DNS resolver
|
||||||
@ -85,12 +90,12 @@ in
|
|||||||
# load secrets the container needs
|
# load secrets the container needs
|
||||||
age.secrets = config.containers.${cfg.containerName}.config.age.secrets;
|
age.secrets = config.containers.${cfg.containerName}.config.age.secrets;
|
||||||
|
|
||||||
# forwarding for vpn container
|
# forwarding for vpn container (only for OpenVPN)
|
||||||
networking.nat.enable = true;
|
networking.nat.enable = mkIf cfg.useOpenVPN true;
|
||||||
networking.nat.internalInterfaces = [
|
networking.nat.internalInterfaces = mkIf cfg.useOpenVPN [
|
||||||
"ve-${cfg.containerName}"
|
"ve-${cfg.containerName}"
|
||||||
];
|
];
|
||||||
networking.ip_forward = true;
|
networking.ip_forward = mkIf cfg.useOpenVPN true;
|
||||||
|
|
||||||
# assumes only one potential interface
|
# assumes only one potential interface
|
||||||
networking.usePredictableInterfaceNames = false;
|
networking.usePredictableInterfaceNames = false;
|
||||||
|
@ -35,71 +35,6 @@
|
|||||||
|
|
||||||
services.spotifyd.enable = true;
|
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;
|
virtualisation.docker.enable = true;
|
||||||
|
|
||||||
services.zerotierone.enable = true;
|
services.zerotierone.enable = true;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user