You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/posts/2026/2026-03-14-Windows-WSL-DNS-and-VPN.md
+63-29Lines changed: 63 additions & 29 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,7 +8,7 @@ categories:
8
8
comments: true
9
9
date:
10
10
created: 2026-03-14
11
-
updated: 2026-03-14
11
+
updated: 2026-03-15
12
12
description: Windows 11 + WSL 2 DNS tunneling with OpenVPN split tunnel.
13
13
---
14
14
@@ -103,11 +103,13 @@ So if your main goal is "WSL should behave like Windows when the VPN connects,"
103
103
104
104
A split tunnel means only company routes go through the VPN. Your normal internet traffic keeps using the local connection, while internal company names still resolve correctly.
105
105
106
+
Here is the official OpenVPN Connect split-DNS documentation for mobile clients. The same DNS behavior also matches what this post relies on for Windows split DNS: https://openvpn.net/connect-docs/dns-servers-mobile-behavior.html
107
+
106
108
### OpenVPN Client Configuration
107
109
108
110
Use a client config like this:
109
111
110
-
```ovpn
112
+
```bash title="part of split-tunnel.ovpn for OpenVPN Community version"
111
113
# Prevent the VPN from replacing your normal internet route
# Set the VPN adapter's connection-specific DNS suffix
124
+
#Send DNS queries for company domains to the above VPN DNS servers
123
125
dhcp-option DOMAIN corp.example
124
-
125
-
# Optional: if your company uses more than one internal suffix
126
-
# dhcp-option DOMAIN-SEARCH eng.corp.example
127
-
# dhcp-option DOMAIN-SEARCH corp.example
126
+
dhcp-option DOMAIN internal.example
128
127
```
129
128
130
-
The key line here is `route-nopull`. Without it, many company VPN profiles push a full-tunnel default route, often via `redirect-gateway`, which sends all traffic through the VPN gateway instead of only company traffic.
129
+
### Option `route-nopull`
131
130
132
-
That usually makes normal internet access slower and more fragile. Public traffic such as Microsoft Teams calls, public Docker image pulls, package downloads, and ordinary web browsing can all end up going through the corporate VPN path even though they do not need to.
131
+
`redirect-gateway` is a common default company VPN config. It pushes a full-tunnel default route, which means it forces to send all traffic through the VPN gateway instead of only company traffic. But that usually makes normal internet access slower and more fragile. Public traffic such as Microsoft Teams calls, public Docker image pulls, package downloads, and ordinary web browsing can all end up going through the corporate VPN path even though they do not need to.
133
132
134
-
With `route-nopull`, you keep the VPN tunnel itself, but you only add the private routes you actually want to send through it.
133
+
`route-nopull` tells the client not to install server-pushed routes. That is what prevents the VPN from silently turning your split tunnel back into a full tunnel. In a typical split-tunnel setup, this already makes a pushed `redirect-gateway` ineffective, because the server can no longer replace your default route.
135
134
136
135
!!! note "Manual routes required"
137
136
138
137
The tradeoff is that you need to know the company private subnets in advance and add them manually. If you miss one, that internal network will stay on your normal local route and will not be reachable through the VPN.
138
+
Depending on your environment, you may be able to mitigate part of this caveat with domain-based routing instead of relying only on static subnet routes. OpenVPN Access Server documents this here: [Tutorial: Configure Domain Routing in Access Server](https://openvpn.net/as-docs/v3/tutorials/tutorial--domain-routing-in-access-server.html). This requires Access Server 3.1+ and DNS server proxy support.
139
+
140
+
### Option `dhcp-option DNS` and `dhcp-option DOMAIN`
139
141
140
-
### Why `dhcp-option DOMAIN` Matters
142
+
In split-tunnel mode, `dhcp-option DNS` and `dhcp-option DOMAIN` work together. `dhcp-option DNS 10.0.0.1` tells the client which VPN DNS server to use, and repeated `dhcp-option DOMAIN ...` lines tell the client which domain suffixes should be resolved through that VPN DNS server instead of your local DNS path.
143
+
144
+
This is the OpenVPN Connect split-DNS behavior described in the official docs. In other words, a config like this:
145
+
146
+
```bash
147
+
dhcp-option DNS 10.0.0.1
148
+
dhcp-option DNS 10.0.0.2
149
+
150
+
dhcp-option DOMAIN corp.example
151
+
dhcp-option DOMAIN internal.example
152
+
```
141
153
142
-
`dhcp-option DOMAIN corp.example`does not create Windows NRPT rules by itself. What it does is set the VPN adapter's connection-specific DNS suffix.
154
+
means "send DNS for `*.corp.example`and `*.internal.example` to `10.0.0.1` and `10.0.0.2`", all other DNS queries go through the local DNS path.
143
155
144
156
That matters for two reasons:
145
157
146
-
-short names like `app` can expand to `app.corp.example`;
147
-
-with WSL DNS tunneling enabled, Windows DNS suffixes are propagated into WSL's `search` line, so Linux tools benefit from the same suffix.
158
+
-`app.corp.example` can be resolved through the VPN DNS servers while unrelated public domains keep using your normal local DNS path;
159
+
-if the first domain is also used as the primary DNS suffix on Windows, short names like `app` can expand to `app.corp.example`, and WSL DNS tunneling can inherit that suffix into WSL's `search` line.
148
160
149
-
If your VPN already deploys NRPT rules, Windows will still use those rules for `*.corp.example`. The `DOMAIN` option complements NRPT by making suffix search consistent instead of leaving Windows and WSL to guess.
161
+
In practice, repeated `dhcp-option DOMAIN` lines can be used for multiple company domains. That matches the OpenVPN Connect split-DNS behavior described in the official docs, where added domains cause only those domains to use the VPN DNS servers.
150
162
151
-
This is also why `.local` is a poor example for corporate DNS. `.local` is typically treated as mDNS, not normal unicast DNS, so it can make a healthy VPN setup look broken.
163
+
OpenVPN Access Server docs note that Windows clients might only use the first domain provided in DNS Resolution Zones as the DNS domain suffix. So if short-name expansion matters, put your primary suffix first and verify it with `ipconfig /all` or `Get-DnsClient`.
152
164
153
-
### How to Find Your VPN DNS Servers
165
+
OpenVPN Connect docs describe split-tunnel DNS this way: if a pushed VPN DNS server is present with no added domains, all DNS requests go to that VPN DNS server; if added domains are present, only DNS requests for those domains go there.
166
+
167
+
This is also why `.local` is a poor example for corporate DNS. `.local` is typically treated as [mDNS](https://en.wikipedia.org/wiki/.local), not normal unicast DNS, so it can make a healthy VPN setup look broken.
168
+
169
+
### How to find company VPN DNS servers
154
170
155
171
To get the correct internal DNS IPs:
156
172
@@ -160,24 +176,29 @@ To get the correct internal DNS IPs:
160
176
4. Copy the IPs listed under `DNS Servers`.
161
177
5. Reuse those IPs in your split-tunnel config.
162
178
163
-
### OpenVPN 2.7+ and `pull-filter`
179
+
### OpenVPN Community Client 2.7 and `pull-filter`
164
180
165
-
`route-nopull` tells the client not to install server-pushed routes and DHCP-style DNS options. That is what prevents the VPN from silently turning your split tunnel back into a full tunnel. In OpenVPN 2.7+, you may still see warnings because the server did push those directives and your client is overriding them.
181
+
This section is about OpenVPN Community Client 2.7 (the latest version as of March 2026) config syntax, not OpenVPN Connect.
166
182
167
-
`pull-filter ignore` is the cleanup step: it drops matching server-pushed directives before OpenVPN processes them. That keeps the logs quieter and makes it explicit that your local client config is authoritative.
183
+
`pull-filter ignore` is the cleanup step: it drops matching server-pushed directives before OpenVPN processes them. In practice, that keeps the OpenVPN Community client logs quieter and makes it explicit that your local client config is authoritative.
168
184
169
-
```ovpn
170
-
pull-filter ignore "redirect-gateway"
171
-
pull-filter ignore "route"
172
-
pull-filter ignore "dhcp-option "
185
+
```bash
186
+
pull-filter ignore "dhcp-option"
187
+
pull-filter ignore "route "
173
188
```
174
189
175
190
Notes:
176
191
177
192
-`pull-filter` uses prefix matching.
178
-
-`pull-filter ignore "route"` drops pushed `route`, `route-gateway`, and other route-related directives.
179
-
-`pull-filter ignore "dhcp-option "` drops pushed DNS and domain suffix options so they do not conflict with the values you set locally.
180
-
- Only add these filters if the server really is pushing overlapping directives and you want the local client config to win.
193
+
- With `route-nopull` in place, you normally do not need `pull-filter ignore "redirect-gateway"`, because `route-nopull` already prevents the pushed full-tunnel route from being installed. We still add `pull-filter ignore "dhcp-option"` and `pull-filter ignore "route "` because they stop the OpenVPN Community client from trying to process those pushed options and cluttering the logs with warnings such as:
194
+
195
+
```bash
196
+
Options error: option 'dhcp-option' cannot be used in this context ([PUSH-OPTIONS])
197
+
Options error: option 'route' cannot be used in this context ([PUSH-OPTIONS])
198
+
```
199
+
200
+
!!! warning "pay attention to trailing whitespace in `"route "`"
201
+
Keep the trailing space in `pull-filter ignore "route "`. If you change it to `pull-filter ignore "route"`, it also matches pushed options such as `route-gateway`, which can break manual routes that rely on `vpn_gateway`.
If you use `systemd-resolved`, `/etc/resolv.conf` may instead point to the local stub at `127.0.0.53`. In that case, check the effective upstream DNS server:
That means Linux apps talk to the local stub first, but the real upstream is still WSL's Windows-side DNS tunneling IP (`10.255.255.254`).
225
+
192
226
Then test name resolution and routing:
193
227
194
228
-`getent hosts app.corp.example`: confirms that a fully qualified internal name resolves.
195
229
-`getent hosts app`: if you rely on short names, this confirms that the DNS suffix search list is working.
196
230
-`nc -zv app.corp.example 443`: confirms both DNS resolution and reachability over the VPN, if the target app is actually listening on TCP port 443.
197
-
-`curl ifconfig.me`: should still show your normal local public IP, proving that internet traffic is not being forced through the VPN.
231
+
-`curl ifconfig.me`: can be a quick sanity check that public traffic is not being forced through the VPN, but verify that the resolved `ifconfig.me` IP is not itself inside one of your VPN-routed prefixes.
198
232
199
-
If `/etc/resolv.conf` points at `127.0.0.53` or a manually configured public DNS server instead of `10.255.255.254`, you are not using the modern WSL DNS tunneling path described above.
233
+
If `/etc/resolv.conf` points directly at `10.255.255.254`, you are using the simple WSL DNS tunneling layout. If it points at `127.0.0.53` but `resolvectl status` still shows `10.255.255.254` upstream, you are still using WSL DNS tunneling underneath a local `systemd-resolved` stub.
0 commit comments