Setting Up Domain-Based Policy Routing & Split Tunneling on VPN Server

Suppose we have two VPN servers: one located in Hong Kong and another in the United States.

What we want to achieve is default traffic from VPN clients in China Mainland goes through the HK VPN and specific domains go through the US VPN, so you can have access to Google Gemini and ChatGPT.

Desired behavior

Default traffic:
OpenConnect Client  -->   HK VPS(ocserv)    --> HK Internet


Specific domains: (chatgpt.com gemini.google.com play.google.com truthsocial.com, spotify.com)
OpenConnect Client --> HK VPS  --> WireGuard tunnel  --> US VPS   -->  US Internet

This can be done entirely on the HK server, without any special configuration on the clients.

To follow this guide, I assume you have already set up OpenConnect VPN server on the Hong Kong VPS and a WireGuard tunnel between HK and US VPS. My Hong Kong VPS is running Alma Linux 9. The commands below can be easily adapted to Debian/Ubuntu servers.


Domain → IP set → Policy Routing (recommended)

Linux itself cannot route directly by domain name, because routing decisions occur after DNS resolution, using IP addresses.

The usual approach is:

Domain
   ↓
DNS resolver
   ↓
IP set (dynamic)
   ↓
Firewall marking
   ↓
Policy routing

Step 1: Configure the OpenConnect VPN Client DNS

Domain-based policy routing only works reliably if OpenConnect clients use The Hong Kong VPN DNS server.

Install dnsmasq on the Hong Kong Server.

sudo dnf install dnsmasq

Start it.

sudo systemctl enable --now dnsmasq

Check status:

sudo systemctl status dnsmasq

If BIND is already running, you need to configure BIND to listen on 127.0.0.1 and dnsmasq listen on the VPN LAN address.

sudo nano /etc/dnsmasq.conf

Find the listen-address directive and add the VPN LAN address (such as 10.10.10.1).

listen-address=10.10.10.1

Also change the

interface=lo

to

interface=vpns0

vpns0 is the ocserv interface. If you want dnsmasq to forward DNS requests to BIND, then tell dnsmasq not to listen on localhost.

except-interface=lo

Then tell dnsmasq to forward DNS to BIND listening on 127.0.0.1.

no-resolv
server=127.0.0.1

Save and close the file. Restart dnsmasq.

sudo systemctl restart dnsmasq

Next, make sure the VPN clients are configured to use the VPN DNS resolver. In /etc/ocserv/ocserv.conf set

dns = 10.10.10.1

Step 2: Build a domain-based IP set

In /etc/dnsmasq.conf, add

ipset=/chatgpt.com/usvpn
ipset=/auth.chatgpt.com/usvpn
ipset=/openai.com/usvpn
ipset=/auth.openai.com/usvpn
ipset=/claude.ai/usvpn
ipset=/claude.com/usvpn
ipset=/gemini.google.com/usvpn
ipset=/play.google.com/usvpn
ipset=/truthsocial.com/usvpn
ipset=/spotify.com/usvpn

When clients resolve those domains through your DNS server:

chatgpt.com
      ↓
DNS lookup
      ↓
Returned IP automatically added to ipset

Restart dnsmasq.

sudo systemctl restart dnsmasq

Step 3: Create the IPSet

Create a persistent ipset:

sudo ipset create usvpn hash:ip timeout 86400

Check:

sudo ipset list

Output:

Name: usvpn
Type: hash:ip

Make ipset persistent

sudo dnf install ipset-service

Enable:

sudo systemctl enable ipset

Save:

sudo ipset save | sudo tee /etc/sysconfig/ipset

Verify ipset population by querying a domain from VPN client:

dig chatgpt.com @10.10.10.1

Then:

sudo ipset list usvpn

You should see:

Members:
142.251.151.2 timeout 86314
104.18.32.47 timeout 86397
142.251.153.2 timeout 86314
142.251.150.2 timeout 86314
142.251.152.2 timeout 86314
172.64.155.209 timeout 86397
142.251.156.2 timeout 86314
142.251.157.2 timeout 86314
142.251.154.2 timeout 86314
142.251.155.2 timeout 86314

Step 4: Create a routing table for the US WireGuard tunnel

Suppose:

WireGuard interface: wg0
US WireGuard peer: 10.0.0.2

Create a routing table:

echo "200 usvpn" | sudo tee /etc/iproute2/rt_tables

Add the default route:

sudo ip route add default via 10.0.0.2 dev wg0 table usvpn

Verify:

ip route show table usvpn

Example output:

default via 10.0.0.2 dev wg0

Step 5: Mark packets using Firewalld

This is where many tutorials become outdated. Since Firewalld on AlmaLinux 9 uses nftables internally, but ipset support is still exposed, use a direct rule.


Create rule (10.10.10.0/24 is the VPN LAN).

sudo firewall-cmd --permanent --direct --add-rule ipv4 mangle PREROUTING 0 -s 10.10.10.0/24 -m set --match-set usvpn dst -j MARK --set-mark 100

Reload:

sudo firewall-cmd --reload

Verify:

sudo iptables -t mangle -L -v

You should see:

MARK set 0x64
match-set usvpn dst

Step 6: Policy routing

Add rule:

sudo ip rule add fwmark 100 table usvpn

Check:

ip rule

Example output:

32765: from all fwmark 0x64 lookup usvpn


Step 7: Disable Reverse Path Filtering for VPN Interface

Find the OpenConnect VPN interface on the Hong Kong server.

ip addr

It’s usually named vpns0. But when there are multiple OpenConnect client connections, there will also be vpns1, vpns2, vpns3..

Edit the /etc/sysctl.d/60-custom.conf file.

sudo nano /etc/sysctl.d/60-custom.conf

Add

net.ipv4.conf.vpns0.rp_filter=0
net.ipv4.conf.vpns1.rp_filter=0
net.ipv4.conf.vpns2.rp_filter=0
net.ipv4.conf.vpns3.rp_filter=0
net.ipv4.conf.vpns4.rp_filter=0
net.ipv4.conf.vpns5.rp_filter=0

Save and close the file. Then apply the changes.

sudo sysctl -p /etc/sysctl.d/60-custom.conf

Step 8: WireGuard Configuration

You need to set AllowedIPs=0.0.0.0/0 and Table = off on the Hong Kong WireGuard node.

[Interface]
PrivateKey = ***************
Address = 10.0.0.2/24
# Prevent wg-quick from adding default route to the main table
Table = off

[Peer]
PublicKey = ***************
Endpoint = *******:51820
# Allow forwarding traffic for IP address of the specified domains. 
AllowedIPs = 0.0.0.0/0

Step 9: Firewall Configuration on the US Server

My U.S. server is running Ubuntu and UFW firewall.

sudo nano /etc/ufw/before.rules

Allow forwarding traffic from VPN clients. (10.0.0.0/24: WireGuard LAN, 10.10.10.0/24: OpenConnect VPN LAN).

-A ufw-before-forward -s 10.0.0.0/24 -j ACCEPT
-A ufw-before-forward -d 10.0.0.0/24 -j ACCEPT
-A ufw-before-forward -s 10.10.10.0/24 -j ACCEPT
-A ufw-before-forward -d 10.10.10.0/24 -j ACCEPT

Also set up IP Masquerading.

# NAT table rules
*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE
-A POSTROUTING -s 10.10.10.0/24 -o eth0 -j MASQUERADE

# End each table with the 'COMMIT' line or these rules won't be processed
COMMIT

Save and close the file. Then restart UFW.


Step 10: Test Your VPN Policy Routing

Visiting:

www.baidu.com

will follow:

Client  ->  OpenConnect -> HK VPS -> HK Internet

Visiting:

chatgpt.com

will follow:

Client  ->  OpenConnect -> HK VPS -> WireGuard -> US VPS -> ChatGPT

Modern alternative: nftables

If you’re using Alma Linux 10 or newer distributions, I would recommend nftables instead of iptables, but ipset also works.

The architecture remains identical:

dnsmasq
    ↓
ipset (or nft set)
    ↓
packet marking
    ↓
policy routing
    ↓
WireGuard US tunnel

Important requirement: DNS control

This only works reliably if OpenConnect clients use your HK DNS server.

If clients use:

8.8.8.8
1.1.1.1
DoH
DoT

the server may never see the DNS responses, and the IP sets won’t be populated correctly.

Many administrators therefore also implement DNS redirection:

VPN clients
     ↓
Any DNS request
     ↓
Force redirect to local resolver

Yes. OpenConnect clients can absolutely be subject to domain-based policy routing on the HK server, with selected domains transparently forwarded through a WireGuard tunnel to a US VPS. This is a clean and scalable design that works very well in practice.

Rate this tutorial
[Total: 0 Average: 0]

Leave a Comment

  • Comments with links are moderated by admin before published.
  • Your email address will not be published.
  • Use <pre> ... </pre> HTML tag to quote the output from your terminal/console.
  • Please use the community (https://community.linuxbabe.com) for questions unrelated to this article.
  • I don't have time to answer every question. Making a donation would incentivize me to spend more time answering questions.

The maximum upload file size: 2 MB. You can upload: image. Links to YouTube, Facebook, Twitter and other services inserted in the comment text will be automatically embedded. Drop file here