donaldzou / WGDashboard

Simple dashboard for WireGuard VPN written in Python & Vue.js
https://donaldzou.github.io/WGDashboard-Documentation/
Apache License 2.0
1.63k stars 252 forks source link

Full IPv6 support #411

Open akhepcat opened 1 month ago

akhepcat commented 1 month ago

Is your feature request related to a problem? Please describe. It seems there's lots of calls for IPv6 support, but due to the lack of IPv6 connectivity, or experience, it's falling by the wayside.

I'm a network engineer, with IPv6 deployment experience, and experience in running a manually curated wireguard tunnel brokerage for IPv6-over-IPv4 connectivity.

Describe the solution you'd like I can help with testing and feedback, as well as offer ideas and suggestions based on my prior experience for fully implementing IPv6 support within WGDashboard - and from there into the wireguard server.

My current setup is based on some scripting wrappers that I wrote to help me manage a brokerage after SIXXS was shutdown, and those scripts are published at https://github.com/akhepcat/sixbroker

It supports full routed IPv6 with nat'd IPv4 - I haven't implemented NAT'd IPv6, because that breaks the end-to-end IPv6 experience.

I did only just find this software, so haven't really played much with it except to set up a small home version of it on a proxmox container, and of course that means trying to figure out how to get IPv6 fully enabled here.

akhepcat commented 1 month ago

With a bit of manual configuration overriding, I can make this work.
However, the dashboard will need to support the additional configuration options, as well as some sane limitations on allocating IPv6 address space.

The relevant bits from the config file:

[Interface]
Address = 100.64.49.1/24
Address = 2001:0DB8:64:49::1/64

[Peer]
AllowedIPs = 100.64.49.2/32, 2001:0DB8:64:49::2/128

With this configuration enabled, and a static route on my router/firewall, as well as enabling IPv6 forwarding on the wg-vpn box, i'm able to connect to both local and remote ipv6 resources.

no nat, no static routing, it just works.

akhepcat commented 1 month ago

My suggestion is to limit the available IPv6 addresses to the same range as the IPv4 subnet.

i.e., if you're using a /24, then you're limited to the first 253 IPv6 addresses of your range. (given the first usable address in the net is for the server)

That will keep the provisioning engine from trying to allocate quadrillions of addresses that will not be utilized.

NOXCIS commented 1 month ago

@akhepcat I experimented with ipv6 on my fork when it was on v3, I kept having Regex issues and problems with the second address line. I would like your input.

akhepcat commented 1 month ago

Happy to help out where I can.

I will note that having two Address= lines in the config file makes the app a little confused about what IPs are allowed when first defining a client. but after it's built, you can add the second address family IP just fine when editing.

Ahhh,,, in "class WireguardConfiguration" Address is a string, but it should be an array of strings (until parsed) because the Address lines can be repeated, and also contain multiple IP definitions per line.

akhepcat commented 1 month ago

okay, so, help me out. What is the purpose of:

def iPv46RegexCheck(ip):
    return re.match(
        r'((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9a
-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){
3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9
a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(
([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3
}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-
9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|
1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-
9]?\d)){3}))|:)))(%.+)?\s*$))',
        ip)

    ip_patterns = (
        r"((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|\/)){4}([0-9]{1,2})(,|$)",
        r"[0-9a-fA-F]{0,4}(:([0-9a-fA-F]{0,4})){1,7}\/([0-9]{1,3})(,|$)"
    )

    ip_patterns = (
        r"((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}",
        r"[0-9a-fA-F]{0,4}(:([0-9a-fA-F]{0,4})){1,7}$"
    )

specifically? Because if you're trying to validate both IPv4 and IPv6 network objects as valid... why not just use the standard "ipaddress" module's "validate_ip" ?

donaldzou commented 1 month ago

Hi @akhepcat, thank you for bringing this up! Yes I don't have much experience in deploying IPv6 network so WGDashboard is not fully tested under IPv6, and I'm happy to have your input on this!

Regarding this,

However, the dashboard will need to support the additional configuration options, as well as some sane limitations on allocating IPv6 address space.

I think I fixed this issue by combining 2 address range into one line, i.e Address = 100.64.49.1/24,2001:0DB8:64:49::1/64 due to the standard library of reading the .conf file does not support duplicate keys in a section.

My suggestion is to limit the available IPv6 addresses to the same range as the IPv4 subnet.

Currently, the dashboard won't generate quadrillion addresses for IPv6 lol. I've prevented by only generating 255 addresses at a time so this is good ;) https://github.com/donaldzou/WGDashboard/blob/5d041b2fd380550c4bd9c1f566c56fe34b8ccae8/src/dashboard.py#L1411

why not just use the standard "ipaddress" module's "validate_ip"

Yes I should've lol, and I will implement it in the v4.1 release :)

akhepcat commented 1 month ago

configparse.zip

Here's a quick example of parsing config files with duplicate keys ("Address")

My python isn't as good as my perl, but hopefully this makes sense - it's cobbled together for sure. The MultiOrderedDict class isn't mine: it's a stackexchange answer from years ago

But this should give you a good start on being able to parse any version of the config file, normalizing it internally, and rewriting it into a canonically-acceptable style.

luiskugel commented 1 month ago

Hey, is there a timetable for when we can expect it to be usable. I don't want to push. I just need it relatively soon, and if it doesn't ship this year, I'll have to see if I can develop it myself.

But of course that would be unnecessary if it's almost ready. Then I'd rather help somewhere else.

DaanSelen commented 2 weeks ago

Perhaps version 4.2? We need to know what is specifically needed.

akhepcat commented 2 weeks ago

Perhaps version 4.2? We need to know what is specifically needed.

So I see there being two different applications for this.

  1. home-user wanting access to their internal IPv6 network
  2. User/LIR/ISP wanting to provide tunneled IPv6 to customers/friends/family over WG

the majority of work is the same for both, which is validating that the script understands and handles IPv6 addressing and routing correctly

For the second use-case (which is primarily where my interests lie. I have a /48 that I can use to hand out /56's over WG tunnels. And i've got some manual scripts for managing the routing, which is the more "interesting" part of this project - assigning the endpoint addresses, and assigning the "downstream subnet" that's routed across that endpoint.

And, of course, for IPv4, there continues to be the "NAT the traffic" but for IPv6, we definitely don't need any of that.

and of course, updating all the ACLs to allow all the various networks to pass correctly to each endpoint.

donaldzou commented 2 weeks ago

Hi @akhepcat and @luiskugel, currently with the latest version of WGDashboard, when creating Configurations, it accepts IPv6 network range, and when creating peers, it will also be able to automatically assign IPv6 addresses as well.

The only thing that is not implemented is the library I should use to validate IP address instead of using regex.

If you would like, to run the latest version on your side and let me know if anything is working :)

luiskugel commented 1 week ago

I updated to the new version and for now it looks like it is working. 👍

But when i create a peer there is either a selector for ipv4 or for ipv6 but not both. I always have to manually type in the other address. Is is possible to add this to the ui so that i can select both IPs from the ui or better let the system preselect one for both?

And thanks for the work! It's a really great tool.