Tutorial: Configure Domain Routing Rules in Access Server from the Command-Line Interface
Need to route specific domains through OpenVPN Access Server? This tutorial shows how to configure domain routing using the CLI with practical command examples.
Overview
Domain routing lets you control whether traffic to specific domains is sent through the VPN tunnel instead of routed directly to the internet. This is useful when IP-based routing is unreliable due to dynamic IPs or CDN-backed services.
You can configure domain routing rules:
Globally (applies to all users).
Per group.
Per individual user.
You can use one, some, or all of these levels at the same time, depending on your access requirements.
Domain routing rules support:
Exact domains (for example,
example.com): Traffic is routed through the VPN only when the destination matches the exact domain. Subdomains (such asapp.example.com) aren't included.Wildcard domains (for example,
*.example.com): Traffic is routed through the VPN for the specified domain and all of its subdomains.Wildcard TLDs (for example,
*.com): Traffic is routed through the VPN for all the domains that use this top-level domain (TLD).
Prerequisites
Access Server 3.1.0 or newer.
Root privileges on your Access Server's console.
DNS server proxy enabled globally.
One or more users or groups configured in Access Server.
Important
Before you begin, be aware of the following behavior:
Traffic for domains not explicitly defined in domain routing rules behaves as follows:
When using full tunnel routing, traffic to other domains is still routed through the VPN if their resolved IP addresses are public IPs.
When using split tunnel routing, only traffic to domains explicitly defined in access rules is routed through the VPN. All other traffic is routed directly to your local internet.
When a domain is specified as the destination for a user or group rule:
Protocol and port options are disabled.
Domain-based access rules require DNS server proxy. This can only be disabled using split tunnel. If DNS server proxy is disabled, the rules remain visible but are marked as inactive with a warning icon.
Before creating a domain routing rule from the CLI, determine whether a ruleset already exists for the scope you want to modify.
Rulesets may already exist if you previously created domain routing rules in the Admin Web UI.
Check for existing rulesets:
sacli AccessControlRulesetsList
Look for a ruleset where the owner matches what you want to configure:
Scope
Owner value
Global rules
__DEFAULT__User rules
the username (example:
brandon)Group rules
the group name (example:
support)If you find a matching ruleset, follow Option A.
If no matching ruleset exists, follow Option B.
If domain routing rules were previously created in the Admin Web UI, a ruleset already exists. You must add new rules to that ruleset.
To identify the ruleset ID, run:
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 } ]
Note the id value (in this example:
1).To check for existing rules in the ruleset, run:
sacli --ruleset_id1 'id' AccessControlRulesList
Replace
ruleset_idwith the id value noted above.Example output:
[ { "action": "nat", "comment": "", "id": 1, "match_data": "app1.example.com", "match_type": "domain", "position": 0, "ruleset_id": 1, "type": "domain_routing" } ]
Note the position of the last rule.
New rules must use the next available position.
Example:
existing rule position = 0 next rule position = 1
Create a new, global domain rule:
sacli --ruleset_id "1" --rule_type "domain_routing" --match_type "domain" --match_data "app2.example.com" --action "nat" --position "1" --comment "" AccessControlRulesAdd
Parameter
Value
ruleset_idUse the value from the global ruleset output.
match_typewithdomainMatch an exact domain. For example,
app2.example.com.match_typewithdomain_or_subdomainMatch wildcard domains. For example,
example.com.Tip
Wildcard domain syntax differs between interfaces:
CLI:
example.comAdmin Web UI:
*.example.com
match_dataDomain to route.
actionwithnatorrouteHow to manage traffic for this domain. NAT: traffic leaves using the server IP. Route: traffic keeps the client VPN IP.
positionUse the value for the next available position noted above.
Restart Access Server:
sacli start
Verify the rule:
sacli --ruleset_id1 'id' AccessControlRulesList
Replace
ruleset_idwith the id noted above.
If no ruleset exists, you must create one before adding rules.
Create a new global ruleset:
sacli --name "__DEFAULT___default_ruleset" --comment "Default ruleset for global domain rules" AccessControlRulesetsAdd
Example output:
1
Take note of this number as the ruleset id.
Create your new global domain routing rule:
sacli --ruleset_id "1" --rule_type "domain_routing" --match_type "domain" --match_data "app2.example.com" --action "nat" --position "1" --comment "" AccessControlRulesAdd
Parameter
Value
ruleset_idUse the value from the global ruleset output.
match_typewithdomainMatch an exact domain. For example,
app2.example.com.match_typewithdomain_or_subdomainMatch wildcard domains. For example,
example.com.Tip
Wildcard domain syntax differs between interfaces:
CLI:
example.comAdmin Web UI:
*.example.com
match_dataDomain to route.
actionwithnatorrouteHow to manage traffic for this domain. NAT: traffic leaves using the server IP. Route: traffic keeps the client VPN IP.
positionUse the value for the next available position noted above.
Attach the ruleset to the target:
sacli --user "__DEFAULT__" --position "0" --ruleset_id "1" AccessControlUserRulesetsAdd
Tip
Global rules use the
__DEFAULT__user. For users or groups, replace__DEFAULT__with the username or group name.Restart Access Server:
sacli start
Verify the rule:
sacli --ruleset_id1 'id' AccessControlRulesList
Replace
ruleset_idwith the id noted above.
In this step, we assume the following user ruleset and rule.
Example ruleset:
[ "comment": "Auto-created ruleset for user brandonqa", "id": 2, "name": "Ruleset for brandonqa", "owner": "brandonqa", "owner_type": "user_connect", "position": 1 ]Example rule:
[ { "action": "nat", "comment": "", "id": 1, "match_data": "app3.example.com", "match_type": "domain", "position": 1, "ruleset_id": 2, "type": "domain_routing" } ]
For our example, we will update the rule to change the domain from app3.example.com to app4.example.com.
Run the following command to update the rule:
sacli --rule_id '1' --ruleset_id '2' --rule_type 'domain_routing' --match_type 'domain' --match_data 'app4.example.com' --action 'nat' --position '1' --comment '' AccessControlRulesUpdate
Parameter
Value
ruleset_idUse the value from the global ruleset output.
match_typewithdomainMatch an exact domain. For example,
app2.example.com.match_typewithdomain_or_subdomainMatch wildcard domains. For example,
example.com.Tip
Wildcard domain syntax differs between interfaces:
CLI:
example.comAdmin Web UI:
*.example.com
match_dataDomain to route.
actionwithnatorrouteHow to manage traffic for this domain. NAT: traffic leaves using the server IP. Route: traffic keeps the client VPN IP.
positionUse the value for the next available position noted above.
Tip
In our example, we modify only the domain, but you can modify any value based on your needs.
Restart services:
sacli start
Verify the rule:
sacli --ruleset_id1 'id' AccessControlRulesList
Replace
ruleset_idwith the id noted above.
In this step, we assume the following user ruleset and rule.
Example ruleset:
[ "comment": "Auto-created ruleset for user brandonqa", "id": 2, "name": "Ruleset for brandonqa", "owner": "brandonqa", "owner_type": "user_connect", "position": 1 ]Example rule:
[ { "action": "nat", "comment": "", "id": 1, "match_data": "app5.example.com", "match_type": "domain", "position": 1, "ruleset_id": 2, "type": "domain_routing" } ]