WireGuard: Difference between revisions
→Server setup: Change systemd-networkd networkConfig from deprecated IPForwarding option to new IPv4/IPv6Forwarding |
refactor; add ipv6 support |
||
| Line 1: | Line 1: | ||
= | = Configuration Modules = | ||
In NixOS, there are several configuration modules for WireGuard. | |||
Depending on how your network is currently managed, refer to the | |||
relevant section for details. | |||
They have different options and capabilities. For example, | |||
< | <code>systemd.network</code> allows you to redirect network traffic | ||
based on the user, such as redirecting torrenting traffic, with | |||
RoutingPolicyRule option. See ArchWiki for further details. | |||
wg | |||
* NetworkManager | |||
* wg-quick | |||
* networking.wireguard | |||
* systemd.network | |||
= Use cases = | |||
This page describes how to set up WireGuard for two use cases. | |||
The first use case is Virtual Private Network, which makes several peers | |||
available on a private subnet. This is the basis for further | |||
configuration. | |||
The second use case is Internet proxy, which allows you to access the | |||
Internet via another peer. This use case depends on the first use | |||
case working correctly. | |||
== | == Network address translation == | ||
NAT maps the internal private IP address of the VPN to the public IP | |||
address of another peer. For all proxying setups, enable the | |||
following configuration | |||
<syntaxhighlight lang="nix"> | <syntaxhighlight lang="nix"> | ||
{ | { | ||
. | networking.nat = { | ||
enable = true; | |||
enableIPv6 = true; | |||
externalInterface = "ens6"; | |||
internalInterfaces = [ "wg0" ]; | |||
}; | }; | ||
} | |||
</syntaxhighlight> | |||
== External DNS with dnscrypt == | |||
You can use an external, encrypted DNS such as | |||
<syntaxhighlight lang="nix"> | |||
{ | |||
services.dnscrypt-proxy2 = { | |||
enable = true; | |||
upstreamDefaults = true; | |||
settings = { | |||
ipv6_servers = true; | |||
}; | |||
}; | |||
networking.nameservers = [ "127.0.0.1" ]; | |||
} | |||
</syntaxhighlight> | |||
== Proxy DNS with dnsmasq == | |||
On the proxy server, use the following config | |||
<syntaxhighlight lang="nix"> | |||
{ | |||
networking.firewall = { | |||
allowedTCPPorts = [ 53 ]; | |||
allowedUDPPorts = [ 53 ]; | |||
}; | |||
services = { | |||
dnsmasq = { | |||
enable = true; | |||
settings.interface = "wg0"; | |||
}; | }; | ||
}; | }; | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== | On the proxy client, configure DNS options. For wg-quick, use the | ||
following | |||
<syntaxhighlight lang="nix"> | |||
{ | |||
networking.wg-quick.interfaces.wg0.dns = | |||
[ {internal v4 & v6 ip addr of server} ]; | |||
} | |||
</syntaxhighlight> | |||
= AllowedIPs = | |||
"Allowed IPs" are IP addresses or ranges. It is specified on a | |||
per-peer basis. All traffic to these addresses and ranges will be | |||
redirected to the peer. Common forms of allowed IPs are the | |||
following. | |||
* 192.168.26.9/32, a single internal IPv4 address | |||
* 192.168.26.0/24, a subnet | |||
* fd31:bf08:57cb::9/128, a single internal IPv6 address | |||
* fd31:bf08:57cb::/60, a subnet | |||
* 0.0.0.0/0, entire IPv4 address space, for proxying | |||
* ::/0, entire IPv6 address space, for proxying | |||
Notice that, in specifiying its subnet mask, some configuration | |||
modules can automatically configure network routes. | |||
Allowed IPs are unique to each peer. If there are peers with the same | |||
allowed IPs, network traffic will only be redirected to one of them. | |||
= WireGuard UDP Port = | |||
The default port is 51820. Some literature recommends changing this | |||
port to circumvent intentional blocking of WireGuard traffic. | |||
= Generate keys = | |||
WireGuard works with public-private key pairs. Computers, called peers | |||
in WireGuard, are identified by their unique public keys. Data is | |||
encrypted with the corresponding private key before transmission. | |||
Peers can only connect to a computer, if its public key is known to | |||
this computer. | |||
To generate a private key, and then derive the public key from it, you | |||
need the <code>wg</code> utility, available in | |||
<code>wireguard-tools</code> package. | |||
After installation, use the following commands to generate keys: | |||
<syntaxHighlight> | |||
$ umask 077 | |||
$ wg genkey > privatekey | |||
$ wg pubkey < privatekey > publickey | |||
</syntaxHighlight> | |||
You need to generate a new key for each peer. If you are setting up | |||
multiple WireGuard interfaces on the same computer, you can reuse the | |||
same key. | |||
Pay attention to the permission of the file. File permission may | |||
cause the WireGuard service to fail. Check system log to rule out | |||
this scenario. | |||
You can use ryamtm/agenix to declaratively store and manage the | |||
WireGuard key. | |||
= networking.wireguard = | |||
Note: does not automatically configure routes. Use | |||
<code>wg-quick</code> instead. | |||
== Peer setup == | |||
<syntaxhighlight lang="nix"> | <syntaxhighlight lang="nix"> | ||
{ config, ... }: | |||
{ | { | ||
.. | age.secrets.wg-key-peer0 = { | ||
file = "./secrets/wg-key-peer0.age"; | |||
}; | }; | ||
networking.firewall.allowedUDPPorts = [ 51820 ]; | |||
networking.wireguard = { | |||
# | enable = true; | ||
interfaces = { | |||
# network interface name. | |||
# You can name the interface arbitrarily. | |||
wg0 = { | |||
# the IP address and subnet of this peer | |||
ips = [ "fd31:bf08:57cb::9/128" "192.168.26.9/32" ]; | |||
# WireGuard Port | |||
# Must be accessible by peers | |||
listenPort = 51820; | |||
# Path to the private key file. | |||
# | |||
# Note: can also be included inline via the privateKey option, | |||
# but this makes the private key world-readable; | |||
# using privateKeyFile is recommended. | |||
privateKeyFile = config.age.secrets.wg-key-laptop.path; | |||
peers = [ | |||
{ | |||
name = "home nas"; | |||
publicKey = "ejmbag/fcc9OLp8K62zfV0NCbp056DnA0qpNixLXwCo="; | |||
allowedIPs = [ | |||
"fd31:bf08:57cb::8/128" | |||
"192.168.26.8/32" | |||
]; | |||
endpoint = "192.168.1.56:51820"; | |||
# ToDo: route to endpoint not automatically configured | |||
# https://wiki.archlinux.org/index.php/WireGuard#Loop_routing | |||
# https://discourse.nixos.org/t/solved-minimal-firewall-setup-for-wireguard-client/7577 | |||
# Send keepalives every 25 seconds. Important to keep NAT tables alive. | |||
# persistentKeepalive = 25; | |||
} | |||
]; | |||
}; | |||
}; | }; | ||
} | } | ||
# it’s not imperative but it does not know how to do it : | |||
# sudo ip route add 11.111.11.111 via 192.168.1.11 dev wlo1 | |||
# the ip adresse 11: external and 192: local. | |||
</syntaxhighlight> | </syntaxhighlight> | ||
== Proxy server setup == | |||
Same as peer setup, skip the endpoint option, with the following | |||
addition, Remember to update the internal IP addresses in the script: | |||
< | <syntaxhighlight lang="nix"> | ||
{ | { | ||
# enable NAT | |||
# | |||
networking.nat = { | networking.nat = { | ||
enable = true; | enable = true; | ||
enableIPv6 = true; | enableIPv6 = true; | ||
externalInterface = " | externalInterface = "ens6"; | ||
internalInterfaces = [ "wg0" ]; | internalInterfaces = [ "wg0" ]; | ||
}; | }; | ||
networking.wireguard.interfaces.wg0 = { | |||
# This allows the wireguard server to route your traffic to the internet and hence be like a VPN | # This allows the wireguard server to route your traffic to the internet and hence be like a VPN | ||
postSetup = '' | |||
${pkgs.iptables}/bin/iptables -A FORWARD -i wg0 -j ACCEPT | ${pkgs.iptables}/bin/iptables -A FORWARD -i wg0 -j ACCEPT | ||
${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -s 10.0.0.1/24 -o eth0 -j MASQUERADE | ${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -s 10.0.0.1/24 -o eth0 -j MASQUERADE | ||
| Line 220: | Line 234: | ||
# Undo the above | # Undo the above | ||
postShutdown = '' | |||
${pkgs.iptables}/bin/iptables -D FORWARD -i wg0 -j ACCEPT | ${pkgs.iptables}/bin/iptables -D FORWARD -i wg0 -j ACCEPT | ||
${pkgs.iptables}/bin/iptables -t nat -D POSTROUTING -s 10.0.0.1/24 -o eth0 -j MASQUERADE | ${pkgs.iptables}/bin/iptables -t nat -D POSTROUTING -s 10.0.0.1/24 -o eth0 -j MASQUERADE | ||
| Line 226: | Line 240: | ||
${pkgs.iptables}/bin/ip6tables -t nat -D POSTROUTING -s fdc9:281f:04d7:9ee9::1/64 -o eth0 -j MASQUERADE | ${pkgs.iptables}/bin/ip6tables -t nat -D POSTROUTING -s fdc9:281f:04d7:9ee9::1/64 -o eth0 -j MASQUERADE | ||
''; | ''; | ||
}; | }; | ||
} | } | ||
</ | </syntaxhighlight> | ||
== Proxy client setup == | |||
Same as peer setup, specify proxy server ip or domain in the endpoint | |||
option. Use <code>[ "0.0.0.0/0" "::/0" ]</code> as allowed IPs. | |||
= wg-quick = | |||
== Peer setup == | |||
<syntaxhighlight lang="nix"> | <syntaxhighlight lang="nix"> | ||
{ | { | ||
.. | networking.wg-quick.interfaces = { | ||
wg0 = { | |||
address = [ | |||
"fd31:bf08:57cb::9/128" | |||
"192.168.26.9/32" | |||
]; | |||
# use dnscrypt, or proxy dns as described above | |||
dns = [ "127.0.0.1" ]; | |||
privateKeyFile = config.age.secrets.wg-key-laptop.path; | |||
peers = [ | |||
{ | |||
# bt wg conf | |||
publicKey = "ejmbag/fcc9OLp8K62zfV0NCbp056DnA0qpNixLXwCo="; | |||
allowedIPs = [ | |||
"fd31:bf08:57cb::8/128" | |||
"192.168.26.8/32" | |||
]; | |||
endpoint = "192.168.1.56:51820"; | |||
} | |||
]; | |||
}; | }; | ||
}; | }; | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== | == Proxy server setup == | ||
< | Same as peer setup, skip the endpoint option, with the following | ||
addition: | |||
<syntaxhighlight lang="nix"> | |||
{ | { | ||
networking.wg-quick.interfaces = { | networking.wg-quick.interfaces = { | ||
wg0 = { | wg0 = { | ||
# This allows the wireguard server to route your traffic to the internet and hence be like a VPN | |||
postUp = '' | |||
${pkgs.iptables}/bin/iptables -A FORWARD -i wg0 -j ACCEPT | |||
${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -s 10.0.0.1/24 -o eth0 -j MASQUERADE | |||
${pkgs.iptables}/bin/ip6tables -A FORWARD -i wg0 -j ACCEPT | |||
{ | ${pkgs.iptables}/bin/ip6tables -t nat -A POSTROUTING -s fdc9:281f:04d7:9ee9::1/64 -o eth0 -j MASQUERADE | ||
''; | |||
# Undo the above | |||
preDown = '' | |||
${pkgs.iptables}/bin/iptables -D FORWARD -i wg0 -j ACCEPT | |||
} | ${pkgs.iptables}/bin/iptables -t nat -D POSTROUTING -s 10.0.0.1/24 -o eth0 -j MASQUERADE | ||
${pkgs.iptables}/bin/ip6tables -D FORWARD -i wg0 -j ACCEPT | |||
${pkgs.iptables}/bin/ip6tables -t nat -D POSTROUTING -s fdc9:281f:04d7:9ee9::1/64 -o eth0 -j MASQUERADE | |||
''; | |||
}; | }; | ||
}; | }; | ||
} | } | ||
</ | </syntaxhighlight> | ||
== Proxy client setup == | |||
Same as peer setup, specify proxy server ip or domain in the endpoint | |||
option. Use <code>[ "0.0.0.0/0" "::/0" ]</code> as allowed IPs. | |||
Optionally, configure proxy server as DNS server as described above. | |||
== Manually start and stop wg-quick == | |||
The above steps will set up a <tt>wg-quick-wg0.service</tt> systemd unit. | The above steps will set up a <tt>wg-quick-wg0.service</tt> systemd unit. | ||
| Line 299: | Line 332: | ||
</syntaxHighlight> | </syntaxHighlight> | ||
If you have WireGuard configuration files that you want to use as-is (similarly how you would [https://wiki.debian.org/WireGuard#Step_2_-_Configuration configure WireGuard e.g. in Debian], without converting them to a declarative NixOS configuration, you can also configure <code>wg-quick</code> to use them. For example, if you have a configuration file <code>/etc/nixos/wireguard/wg0.conf</code>, add the following line to your <code>configuration.nix</code>: | == Reuse existing wg-quick config file == | ||
If you have WireGuard configuration files that you want to use as-is | |||
(similarly how you would | |||
[https://wiki.debian.org/WireGuard#Step_2_-_Configuration configure | |||
WireGuard e.g. in Debian], without converting them to a declarative | |||
NixOS configuration, you can also configure <code>wg-quick</code> to | |||
use them. For example, if you have a configuration file | |||
<code>/etc/nixos/wireguard/wg0.conf</code>, add the following line to | |||
your <code>configuration.nix</code>: | |||
<syntaxHighlight lang="nix"> | <syntaxHighlight lang="nix"> | ||
| Line 307: | Line 349: | ||
This will set up a <code>wg-quick-wg0.service</code> systemd unit. | This will set up a <code>wg-quick-wg0.service</code> systemd unit. | ||
= | = systemd.network = | ||
== | == Peer setup == | ||
<syntaxhighlight lang="nix"> | <syntaxhighlight lang="nix"> | ||
{ | { | ||
networking.firewall.allowedUDPPorts = [ 51820 ]; | |||
networking.useNetworkd = true; | |||
networking.firewall.allowedUDPPorts = [51820]; | |||
networking.useNetworkd = true; | |||
systemd.network = { | systemd.network = { | ||
enable = true; | enable = true; | ||
networks."50-wg0" = { | |||
matchConfig.Name = "wg0"; | |||
address = [ | |||
# /32 and /128 specifies a single address | |||
# for use on this wg peer machine | |||
"fd31:bf08:57cb::7/128" | |||
"192.168.26.7/32" | |||
]; | |||
}; | |||
netdevs."50-wg0" = { | |||
netdevConfig = { | |||
Kind = "wireguard"; | |||
Name = "wg0"; | |||
}; | }; | ||
wireguardConfig = { | |||
ListenPort = 51820; | |||
# routing table identifier for addresses in AllowedIP | |||
# if empty, no route is configured. | |||
# see systemd netdev man page | |||
RouteTable = "main"; | |||
PrivateKeyFile = config.age.secrets.wg-key-vps.path; | |||
}; | }; | ||
wireguardPeers = [ | |||
{ | |||
# laptop wg conf | |||
PublicKey = "ronr+8v670J0CPb0xT5QLGMWDfE7+1g7HmC6YMdCIDk="; | |||
AllowedIPs = [ | |||
"fd31:bf08:57cb::9/128" | |||
"192.168.26.9/32" | |||
]; | |||
Endpoint = "192.168.1.26:51820"; | |||
} | |||
]; | |||
}; | }; | ||
}; | }; | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== | == Proxy server setup == | ||
Same as peer setup, skip the endpoint option, with the following | |||
addition: | |||
<syntaxhighlight lang="nix"> | <syntaxhighlight lang="nix"> | ||
{ | { | ||
networking.nat = { | |||
enable = true; | |||
enableIPv6 = true; | |||
externalInterface = "ens6"; | |||
internalInterfaces = [ "wg0" ]; | |||
}; | |||
systemd.network = { | systemd.network = { | ||
enable = true; | enable = true; | ||
networks."50-wg0" = { | |||
networkConfig = { | networkConfig = { | ||
IPv4Forwarding = true; | |||
IPv6Forwarding = true; | |||
}; | }; | ||
}; | }; | ||
}; | }; | ||
} | } | ||
</syntaxhighlight> | |||
</ | == Proxy client setup == | ||
Same as peer setup, specify proxy server ip or domain in the endpoint | |||
option. Use <code>[ "0.0.0.0/0" "::/0" ]</code> as allowed IPs. | |||
Optionally, configure proxy server as DNS server as described above. | |||
Note, systemd.network client seems to have issues. Use wg-quick | |||
client instead. | |||
= NetworkManager Proxy client setup = | |||
This is probably only useful on clients. Functionality is present in NetworkManager since version 1.20 but network-manager-applet can show and control wireguard connections since version 1.22 only (available since NixOS 21.05). | This is probably only useful on clients. Functionality is present in NetworkManager since version 1.20 but network-manager-applet can show and control wireguard connections since version 1.22 only (available since NixOS 21.05). | ||
| Line 468: | Line 488: | ||
{{Commands|nmcli connection import type wireguard file thefile.conf}} | {{Commands|nmcli connection import type wireguard file thefile.conf}} | ||
The new VPN connection should be available, you still have to click on it to activate it. | The new VPN connection should be available, you still have to click on | ||
it to activate it. | |||
=Troubleshooting= | =Troubleshooting= | ||