WireGuard: Difference between revisions

Tie-ling (talk | contribs)
Proxy client setup: Route for specific user
Peer setup: add comment to indicate permissions needed for networkd secret
 
(17 intermediate revisions by one other user not shown)
Line 15: Line 15:
traffic on a per user basis.  For example, you can route all torrenting traffic
traffic on a per user basis.  For example, you can route all torrenting traffic
through a wireguard tunnel, see below.
through a wireguard tunnel, see below.
systemd.network is recommended due to its powerful configuration interface.
wg-quick is suitable for common usage patterns.  networking.wireguard seems to
have issues with routing.  NetworkManager does not supoort Proxy server setup, and
is cubersome to use.
Skip to Generate Keys section if you are in a hurry.


= Use cases =
= Use cases =
Line 25: Line 32:
Internet via another peer.
Internet via another peer.


== DNS for the proxy client ==
== Secure DNS for the proxy client ==


=== External DNS with dnscrypt ===
You can use a secure DNS client such as knot dns resolver,
 
which comes with a set of authenticated dns servers ips
You can use an external, encrypted DNS such as
built in.


<syntaxhighlight lang="nix">
<syntaxhighlight lang="nix">
Line 46: Line 53:
</syntaxhighlight>
</syntaxhighlight>


=== Proxy DNS with dnsmasq ===
Secure DNS hinders usage of captive portals. See [[systemd-resolved]] for solutions.
 
You can also use the proxy server as DNS server with
dnsmasq.
 
<syntaxhighlight lang="nix">
{
  networking.firewall = {
    allowedTCPPorts = [ 53 ];
    allowedUDPPorts = [ 53 ];
  };
  services = {
    dnsmasq = {
      enable = true;
      settings.interface = "wg0";
    };
  };
}
</syntaxhighlight>
 
For wg-quick peer, use the
following option
 
<syntaxhighlight lang="nix">
{
  networking.wg-quick.interfaces.wg0.dns =
  [ {internal v4 & v6 ip addr of server} ];
}
</syntaxhighlight>
 


= AllowedIPs =
= AllowedIPs =
Line 144: Line 122:
   };
   };
</syntaxHighlight>
</syntaxHighlight>
= networking.wireguard =
Note: does not automatically configure routes, see comments.
== Peer setup ==
<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>
== 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 = {
    enable = true;
    enableIPv6 = true;
    externalInterface = "ens6";
    internalInterfaces = [ "wg0" ];
  };
  networking.wireguard.interfaces.wg0 = {
      # 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 -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
      postShutdown = ''
        ${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.
= wg-quick =
== Peer setup ==
<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>
== Proxy server setup ==
Same as peer setup, skip the endpoint option, with the following
addition:
<syntaxhighlight lang="nix">
{
  # enable NAT
  networking.nat = {
    enable = true;
    enableIPv6 = true;
    externalInterface = "ens6";
    internalInterfaces = [ "wg0" ];
  };
  networking.wg-quick.interfaces = {
    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.
You can start it by typing the following in your terminal:
<syntaxHighlight lang="sh">
sudo systemctl start wg-quick-wg0.service
</syntaxHighlight>
To stop the service:
<syntaxHighlight lang="sh">
sudo systemctl stop wg-quick-wg0.service
</syntaxHighlight>
== 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">
networking.wg-quick.interfaces.wg0.configFile = "/etc/nixos/files/wireguard/wg0.conf";
</syntaxHighlight>
This will set up a <code>wg-quick-wg0.service</code> systemd unit.


= systemd.network =
= systemd.network =


Credit: this section is adapted from ArchWiki.
Credit: this section is adapted from ArchWiki.
This section should fully support IPv4 and v6 dual stack.


== Peer setup ==
== Peer setup ==
Line 394: Line 166:
       wireguardConfig = {
       wireguardConfig = {
         ListenPort = 51820;
         ListenPort = 51820;
        # ensure file is readable by `systemd-network` user
        PrivateKeyFile = config.age.secrets.wg-key-vps.path;


         # To automatically create routes for everything in AllowedIPs,
         # To automatically create routes for everything in AllowedIPs,
Line 402: Line 177:
         # with the number 42, which can be used to define policy rules on these packets.  
         # with the number 42, which can be used to define policy rules on these packets.  
         FirewallMark = 42;
         FirewallMark = 42;
        PrivateKeyFile = config.age.secrets.wg-key-vps.path;
       };
       };
       wireguardPeers = [
       wireguardPeers = [
Line 473: Line 246:
       # only works with systemd-resolved
       # only works with systemd-resolved
       domains = [ "~." ];
       domains = [ "~." ];
       dns = [ "192.168.26.9" ];
       dns = [ "{proxy server internal ip}" ];
       DNSDefaultRoute = true;
       networkConfig = {
        DNSDefaultRoute = true;
      };
     };
     };
   };
   };
Line 488: Line 263:
   systemd.network = {
   systemd.network = {
     netdevs."50-wg0" = {
     netdevs."50-wg0" = {
      # FirewallMark simply marks all packets send and received by this wireguard
      # interface with the number 42, which can be used to define policy rules on these packets.
      wireguardConfig.FirewallMark = 42;
       wireguardPeers = [
       wireguardPeers = [
         {
         {
Line 502: Line 281:
           # for the wireguard interface, and no rules are set on the main routing table.
           # for the wireguard interface, and no rules are set on the main routing table.
           RouteTable = 1000;
           RouteTable = 1000;
          # FirewallMark simply marks all packets send and received by this wireguard
          # interface with the number 42, which can be used to define policy rules on these packets.
          FirewallMark = 42;
         }
         }
       ];
       ];
Line 520: Line 295:
           FirewallMark = 42;
           FirewallMark = 42;


           # we specify that the routing table 1000 must be used  
           # (... continued) we specify that the routing table 1000 must be used  
           # (which is the wireguard routing table). This rule routes all traffic through wireguard.
           # (which is the wireguard routing table). This rule routes all traffic through wireguard.
           # inside routingPolicyRules section is called Table, not RouteTable
           # inside routingPolicyRules section is called Table, not RouteTable
Line 539: Line 314:
           # We exempt our endpoint with a higher priority by routing it
           # We exempt our endpoint with a higher priority by routing it
           # through the main table (Table=main is default).  
           # through the main table (Table=main is default).  
          Family = "both";
           To = "2a01::1/128";
           To = "2a01::1/128";
           Priority = 5;
           Priority = 5;
Line 561: Line 335:
   ]
   ]
</syntaxhighlight>
</syntaxhighlight>
=== Manually start and stop wg0 ===
The above steps will set up a <tt>wg0</tt> interface, managed by networkctl command.
You can start it by typing the following in your terminal:
<syntaxHighlight lang="sh">
sudo networkctl up wg0
</syntaxHighlight>
To stop the service:
<syntaxHighlight lang="sh">
sudo networkctl down wg0
</syntaxHighlight>


=== Route for specific user ===
=== Route for specific user ===
Line 598: Line 388:
       Family = "both";
       Family = "both";
     }
     }
   ]
   ];
  # Configure port forwarding for Transmission under NAT
  networking.nat.forwardPorts =
      [
        {
          destination = "10.0.0.1:80";
          proto = "tcp";
          sourcePort = 8080;
        }
        {
          destination = "[fc00::2]:80";
          proto = "tcp";
          sourcePort = 8080;
        }
      ];
</syntaxhighlight>
 
== Test and Troubleshooting ==
 
Test the proxy with
 
# ipv4
$ curl -4 zx2c4.com/ip
# ipv6
$ curl -6 zx2c4.com/ip
 
Check systemd-networkd log for any error and warning messages.
 
$ journalctl -u systemd-networkd.service
 
Invoke <code>wg</code> command from <code>wireguard-tools</code>.
 
Use <code>ip route</code> to inspect the route table
 
$ ip route show table 1000
default dev wg0 proto static scope link
 
$ ip route show table all
... many entries ...
 
$ ip rule list
10: not from all fwmark 0x2a lookup 1000 proto static
 
$ ip route get  136.144.57.121
136.144.57.121 dev wg0 table 1000 src 192.168.26.9 uid 1000
 
$ ip route get 2600:1406::1
2600:1406::1 from :: dev wg0 table 1000 proto static src fd31:bf08:57cb::9 metric 1024 pref medium
 
= wg-quick =
 
== Peer setup ==
 
<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>
 
== Proxy server setup ==
 
Same as peer setup, skip the endpoint option, with the following
addition:
 
<syntaxhighlight lang="nix">
{
  # enable NAT
  networking.nat = {
    enable = true;
    enableIPv6 = true;
    externalInterface = "ens6";
    internalInterfaces = [ "wg0" ];
  };
 
  networking.wg-quick.interfaces = {
    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.
 
=== Proxy DNS with dnsmasq ===
 
You can also use the proxy server as DNS server with
dnsmasq.
 
<syntaxhighlight lang="nix">
{
  networking.firewall = {
    allowedTCPPorts = [ 53 ];
    allowedUDPPorts = [ 53 ];
  };
  services = {
    dnsmasq = {
      enable = true;
      settings.interface = "wg0";
    };
  };
}
</syntaxhighlight>
 
For wg-quick peer, use the
following option
 
<syntaxhighlight lang="nix">
{
  networking.wg-quick.interfaces.wg0.dns =
  [ {internal v4 & v6 ip addr of server} ];
}
</syntaxhighlight>
 
== Manually start and stop wg-quick ==
 
The above steps will set up a <tt>wg-quick-wg0.service</tt> systemd unit.
 
You can start it by typing the following in your terminal:
 
<syntaxHighlight lang="sh">
sudo systemctl start wg-quick-wg0.service
</syntaxHighlight>
 
To stop the service:
 
<syntaxHighlight lang="sh">
sudo systemctl stop wg-quick-wg0.service
</syntaxHighlight>
 
== 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">
networking.wg-quick.interfaces.wg0.configFile = "/etc/nixos/files/wireguard/wg0.conf";
</syntaxHighlight>
 
This will set up a <code>wg-quick-wg0.service</code> systemd unit.
 
= networking.wireguard =
 
Note: does not automatically configure routes, see comments.
 
== Peer setup ==
 
<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>
 
== 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 = {
    enable = true;
    enableIPv6 = true;
    externalInterface = "ens6";
    internalInterfaces = [ "wg0" ];
  };
 
  networking.wireguard.interfaces.wg0 = {
      # 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 -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
      postShutdown = ''
        ${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>
</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.


= NetworkManager Proxy client setup =
= NetworkManager Proxy client setup =
Line 717: Line 792:
* [https://www.youtube.com/watch?v=us7V2NvsQRA Talk by @fpletz at NixCon 2018 about networkd and his WireGuard setup]
* [https://www.youtube.com/watch?v=us7V2NvsQRA Talk by @fpletz at NixCon 2018 about networkd and his WireGuard setup]
* [https://web.archive.org/web/20210101230654/https://www.the-digital-life.com/wiki/wireguard-troubleshooting/ WireGuard Troubleshooting (on Web Archive)] shows how to enable debug logs
* [https://web.archive.org/web/20210101230654/https://www.the-digital-life.com/wiki/wireguard-troubleshooting/ WireGuard Troubleshooting (on Web Archive)] shows how to enable debug logs
= Additional routing setups =
For documentation on more routing and topology setups, such as
* Point to Point Configuration,
* Hub and Spoke Configuration,
* Point to Site Configuration,
* Site to Site Configuration,
see [https://docs.procustodibus.com/guide/wireguard/ Pro Custodibus Documentation], [https://web.archive.org/web/20250920231827/https://docs.procustodibus.com/guide/wireguard/ Mirror on Internet Archive].


[[Category:Networking]]
[[Category:Networking]]
[[Category:VPN]]
[[Category:VPN]]