10

Is there a way to configure the Linux wireguard module to only listen on a specific IP address for incoming connections instead of it's default of listening on all available addresses? I cannot find any documentation for this.

5
  • Only with a little help from nftables / iptables. Wireguard itself doesn't care. Commented Dec 3, 2022 at 8:56
  • From a cursory look it's not possible but you can limit connections using iptables/nftables. Commented Dec 3, 2022 at 8:56
  • …another option may be network namespaces. Commented Dec 3, 2022 at 12:50
  • Since the answer is "no", perhaps you could give more details to your specific use case so you'd receive answers on how to address your problem, despite not being able to bind the wg interface to a single IP address. Commented Dec 3, 2022 at 15:20
  • @A.B ehhh, no is the actual answer to this question. My specific use case is pretty irrelevant and here's why. I run a pure IPv6 network and my wireguard drives my OCD crazy by binding to BOTH 0.0.0.0:port and [::]:port...I wanted to stop it from listening on v4 since no v4 address is present on my system, since the answer is no that's the answer. Commented Dec 4, 2022 at 3:49

3 Answers 3

6

WireGuard's Linux kernel module has no option to choose the IP address the interface will use for the tunnel.

In particuliar, following OP's comment about wanting it not to bind with IPv4 but only IPv6, it will always use IPv4, as seen in the external compat module or the upstreamed module:

int wg_socket_init(struct wg_device *wg, u16 port) { 
 struct socket *new4 = NULL, *new6 = NULL; struct udp_port_cfg port4 = { .family = AF_INET, .local_ip.s_addr = htonl(INADDR_ANY), .local_udp_port = htons(port), .use_udp_checksums = true }; #if IS_ENABLED(CONFIG_IPV6) int retries = 0; struct udp_port_cfg port6 = { 
 ret = udp_sock_create(net, &port4, &new4); if (ret < 0) { pr_err("%s: Could not create IPv4 socket\n", wg->dev->name); goto out; } 

The IPv4 socket creation (as well as IPv6 when available) is mandatory, and is also always done using INADDR_ANY.

The code should probably have to be amended for multiple things to address the title of the question:

  • select which protocol is used or disabled,
  • select which address to use instead of INADDR_ANY and IN6ADDR_ANY_INIT/&in6addr_any (with possible interactions with previous bullet),
  • and of course alter all parts of the code expecting differently.

Then for cross-OS compatibility with use cases, this would have to be also done to userspace variants of WireGuard, and other kernel (such as FreeBSD) variants.


Meanwhile for some use cases that would like to:

  • allow the WireGuard tunnel envelope to be reachable only at a single address or single interface or single IP version (eg IPv6 only):

    Use a firewall to limit access to this address or interface or IP version/family only

  • allow multiple different WireGuard interfaces to appear to use the same port on different interfaces

    Set them on different ports and use NAT rules (typically using both DNAT in prerouting for initial ingress case plus SNAT in postrouting for initial egress case) to have the visible ports match the actual ports in use.

  • avoid binding the UDP port on the host

    Hide WireGuard in its own namespace, at least at its initial creation, where the WireGuard port will stay. Then add an additional layer of routing (plus possibly NAT rules, but then it's almost the same as binding a port) to reach the tunnel envelope (ie: this UDP port wherever it is), and if left in an other namespace, yet other additional layers of routing for the tunneled payload.

1

The original question is too small, and it doesn't highlight all the pain that goes in the background. Why is it so important for wireguard to listen to a specific interface/specific IP address/specific v4/v6 inet type?

Wireguard client can't multiplex several connections (OpenVPN can). So it can connect to just a single IP endpoint. For example:

Endpoint = example.com:51820 

Wireguard will resolve example.com and connect to the first available IP address only. For example, example.com will be resolved to 1:2:3::4 and 1.2.3.4. Wireguard client will connect to 1:2:3::4 only, it won't connect to 1.2.3.4. Wireguard doesn't care about the state of v4 and v6 stacks on the client machine. It won't try to connect to another endpoint when the connection fails.

The client may have multiple network interfaces, and v6 stack may work using one of these interfaces. For example, v6 stack may be provided by a wifi interface only. Consequently, the client will disappear from the server when his wifi interface disappears. Meanwhile, the client will still be reachable using another interface and v4 stack.

Jason A. Donenfeld (author of Wireguard) provided a way how to fix it: reresolve-dns.sh script. He wants clients to re-resolve example.com every 30 seconds. After that, WG will update IP addresses on the target wg interface, and the client may be reconnected. Jason doesn't want to implement multiplexing, he wants to just make a fluffy workaround instead. Why? Multiplexing is a complex thing. This is not a bug, this is a mistake in Wireguard design, and I am sure that this mistake will never be fixed.

Of course, re-resolve script won't help and your clients will complain. People won't understand your solutions like: don't connect/disconnect from/to another network when Wireguard is on. You can try to improve re-resolve script and add filter for reachable IP addresses, but this path leads to a dead end. You won't be able to fix lag between wg reconnections.

The right solution will be to maintain 2 wg interfaces on the client machine: one for v4 stack and another for v6 stack. Of course, Wireguard doesn't support multiplexing, so you should have 2 separate server and client configs. You can use the same keys, but you have to use different subnets, routing tables, iptables rules, etc.

The second surprise is that Wireguard server doesn't support listening on specific interface, bind to specific IP address, etc. It can just listen for UDP connections on all interfaces on all stacks, and that's it. Why? There is no reason, it may be just laziness.

So you have to implement the following design:

Client1 Endpoint = example.com.v4:51822 -> socat 0.0.0.0:51822 -> :51820 -> Server ListenPort = 51820 Client2 Endpoint = example.com.v6:51822 -> socat [::]:51822 -> :51821 -> Server ListenPort = 51821 

The client will have 2 IP addresses: one for the v4 stack and one for the v6 stack. Yes, it is painful, but it works perfectly. The alternative is to use OpenVPN, it supports multiplexing out of the box.

1
  • 2
    I love this response, it really is exactly my frustration. I do typically use OpenVPN at this point primarily FOR this multiplexing. Wg is a lot nicer in a lot of ways and usually has higher throughput but OpenVPN works better for complex topologies. Unfortunately, the reason I asked this question is because I was doing something that I HAD to use wg for and was hoping to find a sane way of making it work...alas I could not. Commented May 7, 2024 at 1:20
0

WG is not ideal (c). Some bad things happen after you injected WG source not ready for production directly into the kernel (!). You can use socat for fixing it:

socat -T 3600 "UDP4-LISTEN:1.2.3.4:51821,reuseaddr,fork" "UDP:localhost:51820,reuseaddr" 

You can choose what interface socat will use for binding instead of binding to a specific IP:

socat -T 3600 "UDP4-LISTEN:51821,INTERFACE=eno1,reuseaddr,fork" "UDP:localhost:51820,reuseaddr" 

PS Don't forget to ban any external connections to port 58120.

0

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.