---
title: 'Tutorial: Step-by-Step Examples for Split Tunnel and Role-Based Access Using Domain Routing'
canonical: https://openvpn.net/as-docs/v3/tutorials/tutorial--domain-routing-split-tunnel-and-role-based-access/
product: Access Server Docs
description: How to use domain routing in Access Server to manage dynamic IP addresses and enforce group-based access in split tunnel VPN deployments.
keywords: Domain Routing
---

# Tutorial: Step-by-Step Examples for Split Tunnel and Role-Based Access Using Domain Routing

## Overview

This tutorial explains how to configure and validate domain routing in Access Server. Domain routing allows you to route traffic based on domain names instead of IP addresses, which is especially useful when application IPs change frequently or when access must be restricted by user or group.

This tutorial uses two practical scenarios in a split tunnel deployment:

### Scenario 1: Domain routing for applications with dynamic IPs

Brandon manages IT and networking for a company that uses Access Server in split tunnel mode to provide employees with remote access to a private application.

The private application is accessed via an internal domain name (`app1.example.com`). Previously, Brandon had to configure access control rules using the application's IP address. Because the IP changes over time, this required frequent manual updates.

Using domain routing, Brandon can configure rules based on the application's domain name instead of its IP address, eliminating the need for ongoing rule changes.

### Scenario 2: Domain routing with department-based access

The same company has two departments: Sales and Support.

- The Sales team needs access to `app2.example.com`.
- The Support team needs access to `app3.example.com`.

Brandon must ensure that users can access only the applications assigned to their department. Domain routing, combined with group-based access rules, allows him to enforce this separation.

## Prerequisites

- Access Server 3.1.0 or newer.
- Admin Web UI access.
- DNS server proxy enabled globally.
- One or more users or groups configured in Access Server.

> **Note**
>
> In our documentation, we use example IPv4 addresses and subnets [reserved for documentation](https://en.wikipedia.org/wiki/Reserved_IP_addresses#IPv4), such as `192.0.2.0/24`, `198.51.100.0/24`, and `203.0.113.0/24`.
>
> Ensure you replace them with valid IPv4 addresses and subnets for your network(s).

## Step-by-Step: Scenario 1 — Domain routing for an application with a dynamic IP

### Step 1: Configure a global domain routing rule

In this step, you create a global domain routing rule for `app1.example.com`. Global rules apply to all users.

1. Sign in to the Admin Web UI.
2. Click **Access Controls**.
   - The **Group and User Access Rules** tab displays.
3. Click the **Global Access Rules** tab.
4. Under **Domains**, select the global routing mode:
   - **NAT** — The source IP appears as the Access Server private IP address when traffic reaches internal resources.
5. Specify a domain or wildcard domain in the **Domains** text field:

   ```
   app1.example.com
   ```
6. Click **Save** and **Restart**.

### Step 2: Verify that the domain routing rule was created successfully

#### From the Admin Web UI

1. Click **Access Controls**.
   - The **Group and User Access Rules** tab displays.
2. Click the **Global Access Rules** tab.
3. Confirm the global domain rule appears under **Domains**.

#### From the CLI

1. Connect to your console and get root privileges.
2. List the global ruleset:

   ```
   sacli AccessControlRulesetsList
   ```

   - Example output:

     ```
     [
       {
         "comment": "Default ruleset for global domain rules",
         "id": 1,
         "name": "__DEFAULT___default_ruleset",
         "owner": "__DEFAULT__",
         "owner_type": "user_default",
         "position": 0
       }
     ]
     ```
3. Note the `id` value (in this example, `1`).
4. List the rules in the ruleset:

   ```
   sacli --ruleset_id '1' AccessControlRulesList
   ```

   Replace the `ruleset_id` value with the value from the previous example output.

   - Example output:

     ```
     [
       {
         "action": "nat",
         "comment": "",
         "id": 1,
         "match_data": "app1.example.com",
         "match_type": "domain",
         "position": 0,
         "ruleset_id": 1,
         "type": "domain_routing"
       }
     ]
     ```

This confirms that the domain routing rule is configured correctly.

### Step 3: Verify traffic routing

In this step, users connect to the VPN and generate traffic to `app1.example.com`, which we can verify.

1. Connect a VPN client to the server.
2. On the Access Server, start a packet capture:

   ```
   tcpdump -eni any icmp
   ```
3. From the VPN client, run:

   ```
   ping app1.example.com
   ```

   - Example output from the VPN client:

     ```
     root@brandon:~# ping app1.example.com -c 1
     PING app1.example.com (100.64.0.1) 56(84) bytes of data.
     64 bytes from 100.64.0.1: icmp_seq=1 ttl=53 time=3.46 ms
     --- app1.example.com ping statistics ---
     1 packets transmitted, 1 received, 0% packet loss, time 0ms
     rtt min/avg/max/mdev = 3.456/3.456/3.456/0.000 ms
     ```
   - Example tcpdump output from the Access Server:

     ```
     01:27:02.133777 as0t2 In  ifindex 340 ethertype IPv4 (0x0800), length 104: 172.27.232.2 > 100.64.0.1: ICMP echo request, id 36543, seq 1, length 64
     01:27:02.133798 eth0  Out ifindex 2 2a:db:03:a7:44:c9 ethertype IPv4 (0x0800), length 104: 64.227.6.171 > 192.0.2.1: ICMP echo request, id 36543, seq 1, length 64
     01:27:02.136814 eth0  In  ifindex 2 fe:00:00:00:01:01 ethertype IPv4 (0x0800), length 104: 192.0.2.1 > 64.227.6.171: ICMP echo reply, id 36543, seq 1, length 64
     01:27:02.136874 as0t2 Out ifindex 340 ethertype IPv4 (0x0800), length 104: 100.64.0.1 > 172.27.232.2: ICMP echo reply, id 36543, seq 1, length 64
     ```

The VPN client results show:

- The real IP of `app1.example.com` is `192.0.2.1`.
- Access Server maps this to an internal IP (`100.64.0.1`) from the DNS internal IP range (`100.64.0.0/10` by default).
- This mapping ensures traffic is routed through the VPN tunnel.

  > **Note**
  >
  > You can view or change this internal IP range in the Admin Web UI at: **VPN Server \> Subnets \> DNS internal IP range**.

The `tcpdump` output confirms NAT translation between the mapped and real IPs.

### Step 4: Verify nftables/iptables rules

In this step, you verify that firewall rules were created correctly for domain routing. The expected mappings are:

- **Real IP:** `192.0.2.1` (app1.example.com)
- **Mapped internal IP:** `100.64.0.1`

**Verify rules using nftables**

1. On the Access Server, run:

   ```
   nft list chain ip as_nat AS_DOMAIN_RT
   ```

   - Example output:

     ```
     table ip as_nat {
             chain AS_DOMAIN_RT {
                     ip saddr 172.27.232.2 ip daddr 100.64.0.1 meta mark set meta mark | 0x08000000
                     ip saddr 172.27.232.2 ip daddr 100.64.0.1 dnat to 192.0.2.1
             }
     }
     ```
   - This confirms that traffic from the VPN client IP is marked and DNATed from the mapped IP (`100.64.0.1`) to the real application IP (`192.0.2.1`).

**Verify rules using iptables**

**You can verify the same domain routing rules using either of the following commands.**

1. Display the rules in the domain routing chain:

   ```
   iptables -t nat -S AS0_DOMAIN_RT
   ```

   - Example output:

     ```
     -N AS0_DOMAIN_RT
     -A AS0_DOMAIN_RT -s 172.27.232.2/32 -d 100.64.0.1/32 -j MARK --set-xmark 0x8000000/0x8000000
     -A AS0_DOMAIN_RT -s 172.27.232.2/32 -d 100.64.0.1/32 -j DNAT --to-destination 192.0.2.1
     ```
2. Alternatively, view the rules with counters and line numbers:

   ```
   iptables -t nat -L AS0_DOMAIN_RT -n -v --line-numbers
   ```

   - Example output:

     ```
     Chain AS0_DOMAIN_RT (1 references)
     num   pkts bytes target     prot opt in     out     source               destination
     1        1    60 MARK       0    --  *      *       172.27.232.2         100.64.0.1           MARK or 0x8000000
     2        1    60 DNAT       0    --  *      *       172.27.232.2         100.64.0.1           to:192.0.2.1
     ```
3. These outputs confirm that the domain routing rule for `app1.example.com` has been correctly applied at the firewall level.

## Step-by-Step: Scenario 2 — Domain routing with group-based access

We'll create separate rules for the **Sales** and **Support** groups.

### Step 1: Configure domain routing rules per group

**Sales group**

1. Sign in to the Admin Web UI.
2. Click **Groups** and select **Sales**.
3. Click the **Access Rules** tab.
4. Click **New Access Rule**.
5. In the **Address** field, enter a domain:

   ```
   app2.example.com
   ```

   > **Note**
   >
   > Ensure you use an exact domain.
6. Select **NAT** as the destination type.
7. Observe that the **Protocol** and **Port** fields are disabled when a domain is entered.
8. Click **Save rule** and **Restart.**

**Support group**

Repeat the same steps for the **Support** group, using:

```
app3.example.com
```

### Step 2: Verify the group rules

**From the Admin Web UI**

1. Click **Access Controls**.
   - The **Group and User Access Rules** tab is displayed.
2. Confirm that each domain appears correctly in the **Destination** column for the corresponding group.

**From the CLI**

1. Connect to the console and get root privileges.
2. List group rulesets:

   ```
   sacli AccessControlRulesetsList
   ```

   - Example output:

     ```
     [
       {
         "comment": "Auto-created ruleset for group Sales",
         "id": 2,
         "name": "Ruleset for Sales",
         "owner": "Sales",
         "owner_type": "group",
         "position": 1
       },
       {
         "comment": "Auto-created ruleset for group Support",
         "id": 3,
         "name": "Ruleset for Support",
         "owner": "Support",
         "owner_type": "group",
         "position": 1
       }
     ]
     ```
3. Identify the ruleset IDs for the Sales and Support groups.
4. Using the ID values, list the rules for each ruleset:

   ```
   sacli --ruleset_id '2' AccessControlRulesList
   sacli --ruleset_id '3' AccessControlRulesList
   ```

   - Example outputs:

     ```
     [
       {
         "action": "nat",
         "comment": "",
         "id": 1,
         "match_data": "app2.example.com",
         "match_type": "domain",
         "position": 1,
         "ruleset_id": 2,
         "type": "domain_routing"
       }
     ]
     ```

     ```
     [
       {
         "action": "nat",
         "comment": "",
         "id": 2,
         "match_data": "app3.example.com",
         "match_type": "domain",
         "position": 1,
         "ruleset_id": 3,
         "type": "domain_routing"
       }
     ]
     ```
5. Verify that:
   - Sales has a rule for `app2.example.com`.
   - Support has a rule for `app3.example.com`.

### Step 3: Verify traffic per group

1. Start `tcpdump` on Access Server:

   ```
   tcpdump -eni any icmp
   ```
2. From a Sales user VPN client:

   ```
   ping app2.example.com
   ```

   - Example output:

     ```
     root@sales:~# ping app2.example.com -c 1
     PING app2.example.com (100.64.0.2) 56(84) bytes of data.
     64 bytes from 100.64.0.2: icmp_seq=1 ttl=53 time=3.64 ms

     --- app2.example.com ping statistics ---
     1 packets transmitted, 1 received, 0% packet loss, time 0ms
     rtt min/avg/max/mdev = 3.635/3.635/3.635/0.000 ms
     ```
   - The real IP of `app2.example.com` is `192.0.2.2`.
   - Access Server maps this to an internal IP (`100.64.0.2`) from the DNS internal IP range (`100.64.0.0/10` by default).
   - This mapping ensures traffic is routed through the VPN tunnel.
3. From a Support user VPN client:

   ```
   ping app3.example.com
   ```

   - Example output:

     ```
     root@support:~# ping app3.example.com -c 1
     PING app3.example.com (100.64.0.3) 56(84) bytes of data.
     64 bytes from 100.64.0.3: icmp_seq=1 ttl=53 time=3.64 ms

     --- app3.example.com ping statistics ---
     1 packets transmitted, 1 received, 0% packet loss, time 0ms
     rtt min/avg/max/mdev = 3.635/3.635/3.635/0.000 ms
     ```
   - The real IP of `app3.example.com` is `192.0.2.3`.
   - Access Server maps this to an internal IP (`100.64.0.3`) from the DNS internal IP range (`100.64.0.0/10` by default).
   - This mapping ensures traffic is routed through the VPN tunnel.
4. The Access Server `tcpdump` outputs confirm NAT translation between the real IP address and the mapped IP address.

   ```
   02:41:45.884195 as0t3 In  ifindex 341 ethertype IPv4 (0x0800), length 104: 172.27.248.2 > 100.64.0.3: ICMP echo request, id 36990, seq 1, length 64
   02:41:45.884219 eth0  Out ifindex 2 2a:db:03:a7:44:c9 ethertype IPv4 (0x0800), length 104: 64.227.6.171 > 192.0.2.3: ICMP echo request, id 36990, seq 1, length 64
   02:41:45.946690 eth0  In  ifindex 2 fe:00:00:00:01:01 ethertype IPv4 (0x0800), length 104: 192.0.2.3 > 64.227.6.171: ICMP echo reply, id 36990, seq 1, length 64
   02:41:45.946727 as0t3 Out ifindex 341 ethertype IPv4 (0x0800), length 104: 100.64.0.3 > 172.27.248.2: ICMP echo reply, id 36990, seq 1, length 64
   ```

Each domain resolves to a mapped IP in the internal pool and is routed correctly through the VPN tunnel.

### Step 4: Verify nftables/iptables rules

In this step, you verify that firewall rules were created correctly for domain routing and group-based access.

The expected mappings are:

- **Sales group**
  - Real IP: `192.0.2.2` (app2.example.com)
  - Mapped internal IP: `100.64.0.2`
- **Support group**
  - Real IP: `192.0.2.3` (app3.example.com)
  - Mapped internal IP: `100.64.0.3`

**Verify rules using nftables**

1. On the Access Server, run:

   ```
   nft list chain ip as_nat AS_DOMAIN_RT
   ```

   - Example output:

     ```
     table ip as_nat {
             chain AS_DOMAIN_RT {
                     ip saddr 172.27.240.2 ip daddr 100.64.0.2 meta mark set meta mark | 0x08000000
                     ip saddr 172.27.240.2 ip daddr 100.64.0.2 dnat to 192.0.2.2
                     ip saddr 172.27.248.3 ip daddr 100.64.0.3 meta mark set meta mark | 0x08000000
                     ip saddr 172.27.248.3 ip daddr 100.64.0.3 dnat to 192.0.2.3
             }
     }
     ```
   - This confirms that domain routing and group-based access are functioning as expected.

**Verify rules using iptables**

**You can verify the same domain routing rules using either of the following commands.**

1. Display the rules in the domain routing chain:

   ```
   iptables -t nat -S AS0_DOMAIN_RT
   ```

   - Example output:

     ```
     -N AS0_DOMAIN_RT
     -A AS0_DOMAIN_RT -s 172.27.240.2/32 -d 100.64.0.2/32 -j MARK --set-xmark 0x8000000/0x8000000
     -A AS0_DOMAIN_RT -s 172.27.240.2/32 -d 100.64.0.2/32 -j DNAT --to-destination 192.0.2.2
     -A AS0_DOMAIN_RT -s 172.27.248.3/32 -d 100.64.0.3/32 -j MARK --set-xmark 0x8000000/0x8000000
     -A AS0_DOMAIN_RT -s 172.27.248.3/32 -d 100.64.0.3/32 -j DNAT --to-destination 192.0.2.3
     ```
2. Alternatively, view the rules with counters and line numbers:

   ```
   iptables -t nat -L AS0_DOMAIN_RT -n -v --line-numbers
   ```

   - Example output:

     ```
     Chain AS0_DOMAIN_RT (1 references)
     num   pkts bytes target     prot opt in     out     source               destination
     1        1    60 MARK       0    --  *      *       172.27.240.2         100.64.0.2           MARK or 0x8000000
     2        1    60 DNAT       0    --  *      *       172.27.240.2         100.64.0.2           to:192.0.2.2
     3        1    60 MARK       0    --  *      *       172.27.248.3         100.64.0.3           MARK or 0x8000000
     4        1    60 DNAT       0    --  *      *       172.27.248.3         100.64.0.3           to:192.0.2.3
     ```
3. These outputs confirm that domain routing and group-based access rules are correctly applied at the firewall level.

---

Prev: [Tutorial: Configure Domain Routing in Access Server](https://openvpn.net/as-docs/v3/tutorials/tutorial--domain-routing-in-access-server.md)
Next: [Tutorial: Configure Domain Routing Rules in Access Server from the Command-Line Interface](https://openvpn.net/as-docs/v3/tutorials/tutorial--domain-routing-via-cli.md)
Index: https://openvpn.net/as-docs/llms.txt
