Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nixos/wireguard: setup interface with systemd-networkd #45392

Merged
merged 2 commits into from Aug 21, 2019

Conversation

dguibert
Copy link
Member

Motivation for this change

This patch make the wireguard interface more resilient and should fix #41874.

Things done
  • Tested using sandboxing (nix.useSandbox on NixOS, or option sandbox in nix.conf on non-NixOS)
  • Built on platform(s)
    • NixOS
    • macOS
    • other Linux distributions
  • Tested via one or more NixOS test(s) if existing and applicable for the change (look inside nixos/tests)
  • Tested compilation of all pkgs that depend on this change using nix-shell -p nox --run "nox-review wip"
  • Tested execution of all binary files (usually in ./result/bin/)
  • Determined the impact on package closure size (by running nix path-info -S before and after)
  • Fits CONTRIBUTING.md.

Copy link
Member

@Mic92 Mic92 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have not tested the actual module, but the transition makes sense.

type = types.addCheck (types.attrsOf unitOption) checkWireGuard;
description = ''
Each attribute in this set specifies an option in the
<literal>[WIREGUARD]</literal> section of the unit. See
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[WireGuard]

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

type = with types; listOf (submodule wireguardPeerOptions);
description = ''
Each attribute in this set specifies an option in the
<literal>[WIREGUARDPEER]</literal> section of the unit. See
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[WireGuardPeer]

type = types.addCheck (types.attrsOf unitOption) checkWireGuardPeer;
description = ''
Each attribute in this set specifies an option in the
<literal>[Route]</literal> section of the unit. See
Copy link
Member

@Mic92 Mic92 Aug 20, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[WireguardPeer]

@Mic92
Copy link
Member

Mic92 commented Aug 20, 2018

Now we would only need suppress_prefixlength 0 in [RoutingPolicyRule] to support routes for 0.0.0.0.

@Mic92 Mic92 changed the title wireguard via sytemd netlink wireguard via systemd netlink Aug 20, 2018
@Mic92 Mic92 changed the title wireguard via systemd netlink nixos/wireguard: setup interface with systemd-networkd Aug 20, 2018
nameValuePair "wireguard-${name}"
{
description = "WireGuard Tunnel - ${name}";
after = [ "network.target" ];
after = [ "sys-subsystem-net-devices-${name}.device" ];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't we wait for DNS/network configuration?

I am not sure what the behavior ended up being (@Mic92) within systemd.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should.
(btw systemd handles the resolution of endpoints)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added exponential backoff for dns resolution, so it will retry failed endpoints just like openvpn: https://github.com/systemd/systemd/blob/master/src/network/netdev/wireguard.c#L268

It make sense to not wait for dns to avoid leaking traffic.

@andir
Copy link
Member

andir commented Aug 21, 2018 via email

Copy link
Member

@globin globin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This misses creating routes, which was done before.

Patch for the inline comment on privateKey below.
Also added back postShutdown using ip set link down. We might not want to do this in case we handle routes with networkd, too.

From 854ce74e6eb0e7362ec9a749bdda3a83b7343795 Mon Sep 17 00:00:00 2001
From: Robin Gloster <mail@glob.in>
Date: Wed, 22 Aug 2018 04:01:51 +0200
Subject: [PATCH] wireguard: fix privateKey handling, add back postShutdown

---
 nixos/modules/services/networking/wireguard.nix | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/nixos/modules/services/networking/wireguard.nix b/nixos/modules/services/networking/wireguard.nix
index 43eecca1b6c..75301179706 100644
--- a/nixos/modules/services/networking/wireguard.nix
+++ b/nixos/modules/services/networking/wireguard.nix
@@ -225,14 +225,19 @@ let
         };
 
         script = ''
-          wg set ${name} private-key ${values.privateKeyFile}
+          ${optionalString (values.privateKeyFile != null) "wg set ${name} private-key ${values.privateKeyFile}"}
           ${concatMapStringsSep "\n" (peer:
-              optionalString (peer.presharedKeyFile != null) "wg set ${name} peer  preshared-key ${peer.presharedKeyFile}"
+              optionalString (peer.presharedKeyFile != null) "wg set ${name} peer preshared-key ${peer.presharedKeyFile}"
             ) values.peers}
 
           ip link set up dev ${name}
           ${values.postSetup}
         '';
+
+        preStop = ''
+          ip link set down dev ${name}
+          ${values.postShutdown}
+        '';
       };
 
 in
-- 
2.16.4

@@ -46,11 +46,26 @@ let
"bond" "bridge" "dummy" "gre" "gretap" "ip6gre" "ip6tnl" "ip6gretap" "ipip"
"ipvlan" "macvlan" "macvtap" "sit" "tap" "tun" "veth" "vlan" "vti" "vti6"
"vxlan" "geneve" "vrf" "vcan" "vxcan" "wireguard" "netdevsim"
"wireguard"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wireguard is in the line above already

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've missed that. Thanks for pointing this out.

wg set ${name} private-key ${privKey} ${
optionalString (values.listenPort != null) " listen-port ${toString values.listenPort}"}

wg set ${name} private-key ${values.privateKeyFile}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be wrapped in optionalString (values.privateKeyFile != null) to handle the use of privateKey

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure.

@dguibert
Copy link
Member Author

@globin, I've applied your patch and restored the option allowedIPsAsRoutes.

globin
globin previously approved these changes Aug 22, 2018
@globin
Copy link
Member

globin commented Aug 22, 2018

Ah I have one more minor nitpick to the route handling that already had existed before this PR. When adding routes without specifying proto, this makes some dynamic routing daemons ignore them (e.g. babel, probably more). Citing from ip(8):

If the routing protocol ID is not given, ip assumes protocol boot (i.e. it assumes the route was added by someone who doesn't understand what they are doing).

Patch to add proto static:

From 9e699b21199d2d05025c069efe60afd03546742a Mon Sep 17 00:00:00 2001
From: Robin Gloster <mail@glob.in>
Date: Wed, 22 Aug 2018 19:10:32 +0200
Subject: [PATCH] wireguard: make wg routes proto static

This fixes distributing routes with e.g. babel that ignores `proto boot`
---
 nixos/modules/services/networking/wireguard.nix | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nixos/modules/services/networking/wireguard.nix b/nixos/modules/services/networking/wireguard.nix
index f6d45c9d357..46fcb03d32f 100644
--- a/nixos/modules/services/networking/wireguard.nix
+++ b/nixos/modules/services/networking/wireguard.nix
@@ -234,7 +234,7 @@ let
 
           ${optionalString (values.allowedIPsAsRoutes != false) (concatStringsSep "\n" (concatMap (peer:
               (map (allowedIP:
-                "ip route replace ${allowedIP} dev ${name} table ${values.table}"
+                "ip route replace ${allowedIP} dev ${name} table ${values.table} proto static"
               ) peer.allowedIPs)
             ) values.peers))}
 
-- 
2.16.4

@globin
Copy link
Member

globin commented Aug 23, 2018

Also this will not work without having networking.useNetworkd enabled right? I don't think we can require people to use networkd with wireguard right now, there are still to many issues that need to be fixed..

@globin globin dismissed their stale review August 23, 2018 12:39

useNetworkd concerns

@Mic92
Copy link
Member

Mic92 commented Aug 23, 2018

It is possible to use systemd.network.enable without using networking.useNetworkd. Networkd only cares about interfaces it was explicitly configured for.

@globin
Copy link
Member

globin commented Aug 23, 2018

Then this should mkDefault enable it :)

@dguibert
Copy link
Member Author

Commits have been fix up to use systemd.network.enable and keep the DEVICE env. variable in the unit...

I've noticed that the unit is not reloaded when the device is reconfigured with networkd. The privateKey is not set anymore on the wireguard link after "systemctl restart system-networkd".
Ideas how to fix it?

@Mic92
Copy link
Member

Mic92 commented Aug 23, 2018

Indeed, networkd will overwrite the key whenever it configures an interface https://github.com/systemd/systemd/blob/master/src/network/netdev/wireguard.c#L69

The only way this could be prevented would be to create an .netdev unit that has the private key included.
I think we should at least merge nixos/modules/system/boot/networkd.nix since they are independent from the module, I have no idea how to cleanly combine privateKeyFile and networkd module.

@dguibert
Copy link
Member Author

For sure it could be prevented when the privateKey (and presharedKey) is defined instead of privateKeyFile.
The last patch aims to solve the reloading issue.

wg set ${name} private-key ${privKey} ${
optionalString (values.listenPort != null) " listen-port ${toString values.listenPort}"}
# wait for ${name} to be configured
while ! networkctl status $DEVICE | grep configured 1>/dev/null; do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh, this is ugly. What if the users restart systemd-networkd manually?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be fixed with PartOf=systemd-networkd.service?

Also the indentation is off in a few places.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe?

Copy link
Member

@Mic92 Mic92 Aug 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also then module should set the device up with ip after networkctl has configured the device. Does not networkd also set the link up correctly?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be fixed by waiting systemd-network-wait-online@${name}.service. How could we make this unit from systemd-network-wait-online@.service?

The while loop could be replaced with ${config.systemd.package}/lib/systemd/systemd-networkd-wait-online -i ${name}. If systemd-networkd is restarted, the keys are restored as expected.

nameValuePair "wireguard-${name}"
{
description = "WireGuard Tunnel - ${name}";
after = [ "network.target" ];
after = [ "sys-subsystem-net-devices-${name}.device" "systemd-networkd-online" ];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The target is spelled wrong and I don't see why systemd-networkd-online.target is required.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also use utils.escapeSystemdPath on name in the unit definition as systemd will escape e.g. hyphens with \x2d

while ! networkctl status $DEVICE | grep configured 1>/dev/null; do
sleep 1
done
sleep 1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this sleep?

@@ -201,43 +230,30 @@ let
};

script = ''
modprobe wireguard
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you load the module anywhere?

Copy link
Member

@Mic92 Mic92 Aug 27, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When an unknown netlink type is requested the kernel tries to load the kernel module:

╭─[~/git/nixpkgs]─[go-1_11]
╰─ % sudo rmmod wireguard
╭─[~/git/nixpkgs]─[go-1_11]
╰─ % lsmod | grep wireguard
╭─[~/git/nixpkgs]─[go-1_11]
╰─ % sudo ip l add dev foobar type wireguard
╭─[~/git/nixpkgs]─[go-1_11]
╰─ % lsmod | grep wireguard
wireguard             229376  0
ip6_udp_tunnel         16384  1 wireguard
udp_tunnel             16384  1 wireguard
ipv6                  503808  248 nf_conntrack_ipv6,bridge,ip6t_rpfilter,nf_defrag_ipv6,nf_nat_ipv6,wireguard

@xeji
Copy link
Contributor

xeji commented Sep 2, 2018

I merged #45569 because I am not sure this is ready yet, and we should get at least one of these improvements into 18.09. The merge does not imply a vote against this change.
It just needs a trivial rebase. Sorry for the inconvenience.

@dguibert
Copy link
Member Author

dguibert commented Sep 4, 2018

I've rebased this branch.

I'm using it on my machines:

  • with networkd enabled.
  • with each privateKey coming from key file deployed with nixops (and not stored world-readable in nix store).

@d6e
Copy link

d6e commented Jan 1, 2019

@dguibert This PR has been stuck for several months now. Is it just the merge conflict that's blocking this? Is there any way I can help?

@dguibert
Copy link
Member Author

dguibert commented Jan 1, 2019

Just rebased. Works for me since I've pushed the first patch series.
Is there still anything blocking this to be merged?

@arianvp
Copy link
Member

arianvp commented May 5, 2019

Heya. I recently got a patch merged in systemd that adds a PrivateKeyFile option to the netdev spec systemd/systemd#11861 . However we do not ship this yet but would be nice to add that to the future instead of the scripts.

Anyhow. I'd like to see this merged. So I'll happily take another pass to see if we didn't miss anything . I'll put it on my list.
Also ping @grahamc as he recently changed some things on master concerning keyfiles

@asymmetric
Copy link
Contributor

This is the specific commit in the PR that makes the change, btw.

@dguibert
Copy link
Member Author

dguibert commented May 7, 2019

Heya. I recently got a patch merged in systemd that adds a PrivateKeyFile option to the netdev spec systemd/systemd#11861 . However we do not ship this yet but would be nice to add that to the future instead of the scripts.

Great! I've just updated to whole branch with the last commit supporting this new option.

PS: Maybe a presharedKeyFile option could be also added to systemd ;-)

@zarelit
Copy link
Member

zarelit commented Aug 7, 2019

How is this PR related to #64040 ? Should we keep both / merge them / close one?

@dguibert
Copy link
Member Author

dguibert commented Aug 7, 2019

PR #64040 introduces wireguard related options as well as this PR, but there are some differences.

  • This PR converts the options config.networking.wireguard to networkd options, this part could be removed as it conflicts with some committed changes.
  • It supports multiple peers per interface (as far as I can tell not the new PR).

@picnoir
Copy link
Member

picnoir commented Aug 9, 2019

It supports multiple peers per interface (as far as I can tell not the new PR).

It does support it.

If you prefer merging this one, please do add the nixos test I wrote in #64040 .

@dguibert
Copy link
Member Author

dguibert commented Aug 21, 2019

It supports multiple peers per interface (as far as I can tell not the new PR).

It does support it.

I don't figure it out. I've tried to add this to the test

wireguardPeerConfig = { Endpoint = "192.168.1.3:51820"; PublicKey = pubk; PresharedKeyFile = pkgs.writeText "psk.key" "yTL3sCOL33Wzi6yCnf9uZQl/Z8laSE+zwpqOHC4HhFU="; AllowedIPs = [ "10.0.0.3/32" ]; PersistentKeepalive = 15; };

but error: attribute 'wireguardPeerConfig' at /nixpkgs/nixos/tests/systemd-networkd-wireguard.nix:26:13 already defined at /nixpkgs/nixos/tests/systemd-networkd-wireguard.nix:19:13

If you prefer merging this one, please do add the nixos test I wrote in #64040 .

done.

I've also removed the controversal part generating the systemd-networkd units from the options of networking.wireguard module.

@picnoir
Copy link
Member

picnoir commented Aug 21, 2019

Thanks @dguibert. Closed the other one.

My bad for the multi-peer faulty behavior.

Looking forward to see this PR merged.

Copy link
Contributor

@flokli flokli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks!

@flokli flokli merged commit 9f237fe into NixOS:master Aug 21, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Wireguard Interface Setup should be more resilient & user friendly