Admittedly, I’m pretty unfamiliar with VPNs, especially with setting up VPNs on routers. I’ve followed the excellent guide by egc to set up VPNs for my individual clients, such as my desktop or my laptop. However, one of the next logical steps is to learn how to set up a site-to-site VPN with DD-WRT. One of the issues with deploying it right now is that I’m currently an hour away from the other location. While I could easily set up a remote desktop solution with the likes of Rustdesk or TeamViewer, it’s still responsible to test it first in a virtual environment before deploying it in a production environment.
The Virtual Environment
To emulate the production deployment as closely as possible, I spun up two virtual machines in VMWare Workstation using DD-WRT build r50906 from 11/18/2022, specifically the x86_64 build. I also ended up using a Kali Linux and a Windows 11 Development image, both of which were the official OVF files from their respective websites. These would emulate web servers that would confirm connectivity between the two endpoints. In addition to that, I created two host-only network adapters with no DHCP service running. I deliberately chose the host-only because I wanted the servers to only communicate to each other through the DD-WRT virtual machines.
VM | DD-WRT Site 1 | DD-WRT Site 2 | Windows 11 VM | Kali Linux VM |
---|---|---|---|---|
WAN IP | 10.0.0.202 | 10.0.0.94 | 10.0.0.202 | 10.0.0.94 |
LAN IP | 10.77.77.1 | 10.75.75.1 | 10.77.77.91 | 10.75.75.96 |
Subnet | 255.255.255.0 | 255.255.255.0 | 255.255.255.0 | 255.255.255.0 |
Promiscuous Mode Nightmares
One of the first hurdles that I ran into that I was not expecting to run into was DD-WRT being adamant on using promiscuous mode, of which VMWare was very unhappy about.
Across both virtual machines and both network adapters per VM, this error would get thrown
While it’s plausible I configured something incorrectly, I followed the link provided to solve the issue. The solution suggested was to create a group that had read and write permissions for the virtual network adapters and add myself to that group. After doing just that, it booted without any issues.
First Time Setup
I would be lying if I said that the setup process was painless, because it was far from it. I spent probably a good part of a couple hours just troubleshooting my network connectivity issues. Sometimes I would get have a connection for 5 minutes, sometimes an IP address wouldn’t be given out at all, sometimes it would get an IP address but wouldn’t connect to the web interface, sometimes it would cooperate with one VM while the other didn’t want to even ping its router, and all in all it was wildly inconsistent. I tried mirroring the same build that I was using on my Netgear routers in my production deployment (r50671), I tried the latest version (as of me setting it up, build r50906) in x86, I even tried a pre-made OVF that I found on the DD-WRT forums, none of which worked out.
After enough trial and error with different configurations, I found that, in order for DHCP to assign IP addresses, I needed the DD-WRT VMs to have a bridged connection. As a result, my production LAN served as a WAN for those DD-WRT VMs, which was fine by me as I could also determine how internet traffic was routed. While there were still some IP assignment issues with the default subnet, these were resolved after assigning its subnets previously mentioned.
Initial VPN Configuration
To begin toying around with VPNs, I followed the DD-WRT OpenVPN configuration guide using the Windows VM as the machine issuing the profiles and the site 1 router serving as the OpenVPN server. This guide was a known good guide as this is what I used to deploy my current VPN setup and is frequently updated (as of writing this the last edit was on 11/12/2022, just over a week ago as of when I’m writing this). After setting this up I ensured that the site 2 router could ping the site 1 router and, sure enough, it could. However, not everything was set up, as my Kali VM couldn’t see the Windows VM, and the Windows VM couldn’t see the Kali VM, either. However, this was expected, as I was just making sure that I could even connect the site 2 router to the site 1 router.
Uncharted Territory
Note: in this section I’m posting lines from my configuration. If you are following this post as a guide, replace 10.77.77.0 with the subnet of the network running the server and 10.75.75.0 with the subnet of your remote network
At this point, I needed to route the specific traffic to each other. Surprisingly, it wasn’t that difficult. Referencing the guide before, all I needed to do was ensure that the inbound firewall on NAT was disabled on both the client and the server and add some routing statements to the “additional config” section, adding the following lines to the server:
route 10.77.77.0 255.255.255.0 vpn_gateway
push "route 10.75.75.0 255.255.255.0 vpn_gateway"
Additionally, I needed to add the following line to the “CCD-Dir DEFAULT File” section:
iroute 10.75.75.0 255.255.255.0
Another option that I did not initially check was changing the “Push Client Route” from “Default Gateway” to “Servers Subnet”. As a result of me missing this, it had caused me to lose internet connectivity on the remote network. After correcting this issue, internet connectivity was restored.
Firewall Fun
Now that, on paper, I had two remote sites set up, it was time to check it. I tried pinging the Kali VM with success. With the ping being successful, I was able to successfully access the Kali VM’s website. The other way around, on the other hand, I was unsuccessful with pinging the Windows VM or accessing the Windows VM’s website. I initially suspected it was a routing issue, so I tried pinging the site 1 router from the Kali VM with success. Trying to access that webpage using its local IP address was just as successful. That eliminated a possible routing issue, so I checked what was going on in the Windows VM.
Initially, I checked the WAMP server running on the Windows VM as I suspected there was a configuration issue in the Apache config. While I did end up making a configuration change (in httpd-vhosts.conf, adding Require all granted), I doubt that played a significant part. After doing a bit of research, I found that Windows, by default, only accepts incoming network traffic from addresses on the same subnet as it. While the bodge solution was to just disable the firewall outright, the correct solution would’ve been to manually add a firewall entry for the server as Windows didn’t prompt if I wanted to add a firewall rule upon first run, although that may have been due to the firewall profile being set to public. After disabling the firewall, I was able to successfully ping the Windows VM from Kali and access the WAMP server.
Conclusion
As I’ve found out through the firewall issues, there are plenty of red herrings when setting up something as inherently complex as a site to site VPN. The most obvious example of this is the firewall blocking traffic on external subnets, with it being masked behind routing misconfigurations and Apache misconfigurations. In hindsight, I should’ve tested this with two Linux VMs running web servers as I’m admittedly not familiar with using Windows as a server. Even if I wanted to stick with Windows as one of the endpoints, I probably should’ve gone with Windows Server to test it, but I’ll hide under the guise of “This is how I’m going to deploy it in my production environment, so it was a good thing I used Windows 11.”
I’ve also learned that DD-WRT is extremely unstable in x86 virtual machines. It’s unclear if this would fair any better on physical hardware as I currently don’t have the time or space to test that, but if I had to hazard a guess it probably wouldn’t. I did also use the “public” image which does have limitations compared to the “full” image, the most notable of which is the vastly reduced number of connections. That may have played a part in some of the issues that I had, but I it goes without saying at this point that DD-WRT, in its current state, is not designed for x86 hardware. If you are looking to build your own router using x86 hardware, you might want to look at pfSense or opnSense. I could’ve also simulated this using Cisco Packet Tracer, and while I would’ve been able to take away a few things from it, it likely wouldn’t be as representative of me deploying it in my current environment.