Tutorial: Configure Deny Rules with Domain Routing Using the Access Server CLI
Configure deny rules with Domain Routing in OpenVPN Access Server using CLI. Learn how to block specific domains, combine allow and deny rules, and verify behavior using DNS proxy logs.
Overview
By default, Access Server supports allowlist rules for domain routing. This tutorial explains how to configure and validate deny rules using the CLI.
You can apply domain routing rules at different levels:
Globally (applies to all users).
Per Group.
Per individual user.
You can use one or more levels at the same time, depending on your access requirements.
Domain routing supports:
Exact domains (for example,
example.com) — matches only that domain.Wildcard domains (for example,
*.example.com) — matches the domain and all subdomains.Wildcard TLDs (for example,
*.com) — matches all domains with that top-level domain (TLD).
Deny rules let you block traffic to specific domains, subdomains, or TLDs.
Prerequisites
Access Server 3.2.0 or newer.
Root privileges on your Access Server's console.
DNS server proxy enabled globally.
One or more users or groups configured.
Note
In our documentation, we use example IPv4 addresses and subnets reserved for documentation, 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).
Important
Before you begin, take note of the following:
Deny rules for domains are supported starting in Access Server 3.2.0.
You need to configure deny rules using the CLI.
Rule order matters. Deny rules need to come before allow rules when both apply.
Scenario 1: Deny access to a specific domain
In this scenario, you block access to app1.example.com for users connected to the VPN.
Tip
This scenario applies to both full and split tunneling.
Connect to the console and get root privileges.
Create a global ruleset:
sacli --name "__DEFAULT___default_ruleset" --comment "Default ruleset for global domain rules" AccessControlRulesetsAdd
Example output:
1
Note this as the returned ruleset ID.
Create a deny rule for
app1.example.com:sacli --ruleset_id "1" --rule_type "domain_routing" --match_type "domain" --match_data "app1.example.com" --action "deny" --position "1" --comment "" AccessControlRulesAdd
Parameter
Value
ruleset_idUse the value from the global ruleset output.
match_typewithdomainMatch an exact domain. For example,
app1.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.
actionwithdenyThis will block the traffic to the domain.
positionUse the value for the next available position noted above.
Attach the ruleset globally:
sacli --user "__DEFAULT__" --position "0" --ruleset_id "1" AccessControlUserRulesetsAdd
Tip
Global rules use
__DEFAULT__. Replace this value when applying rules to specific users or groups.Apply the changes:
sacli start
List rulesets:
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 ruleset ID.
List rules in the ruleset:
sacli --ruleset_id '1'1 AccessControlRulesList
Replace the
ruleset_idvalue with the value from the ruleset list output.Example output:
[ { "action": "deny", "comment": "", "id": 1, "match_data": "app1.example.com", "match_type": "domain", "position": 0, "ruleset_id": 1, "type": "domain_routing" } ]
Confirm the rule includes your deny list values. Example:
action: denymatch_data: app1.example.com
In this step, connect to the VPN as a user and generate traffic to app1.example.com to verify it's denied.
Connect a VPN client.
From the Access Server console, enable DNS proxy debug logging:
echo "DEBUG_DNSPROXY=true" >> /usr/local/openvpn_as/etc/as.conf && systemctl restart openvpnas
From the client, run:
ping app1.example.com
Example output:
Ping request could not find host app1.example.com. Please check the name and try again.
On the server, run:
grep "DNSPROXY" /var/log/openvpnas.log
Example output:
2026-04-16T21:05:11+0000 [stdout#info] [DNSPROXY] OUT: '[335] Received query from 172.27.232.2:53380' 2026-04-16T21:05:11+0000 [stdout#info] [DNSPROXY] OUT: '[335] Deny query for app1.example.com. IN A: domain is blocked'
Tip
Debug flags can generate large amounts of log data. After collecting the necessary information, remove the debug flag and restart Access Server to prevent excessive logging.
Scenario 2: Deny a specific domain and allow a wildcard
In this scenario:
Deny:
app1.example.comAllow:
*.example.com
Tip
This scenario applies to both full and split tunneling.
Connect to the console and get root privileges.
Create a global ruleset:
sacli --name "__DEFAULT___default_ruleset" --comment "Default ruleset for global domain rules" AccessControlRulesetsAdd
Example output:
1
Note this as the returned ruleset ID.
Add rules to deny the specific domain and allow a wildcard domain:
sacli --ruleset_id "1" --rule_type "domain_routing" --match_type "domain" --match_data "app1.example.com" --action "deny" --position "1" --comment "" AccessControlRulesAdd sacli --ruleset_id "1" --rule_type "domain_routing" --match_type "domain_or_subdomain" --match_data "example.com" --action "nat" --position "2" --comment "" AccessControlRulesAdd
Parameter
Value
ruleset_idUse the value from the global ruleset output.
match_typewithdomainMatch an exact domain. For example,
app1.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.
actionwithdenyThis will block the traffic to the domain.
positionUse the value for the next available position noted above.
Attach the ruleset:
sacli --user "__DEFAULT__" --position "0" --ruleset_id "1" AccessControlUserRulesetsAdd
Tip
Global rules use
__DEFAULT__. Replace this value when applying rules to specific users or groups.Apply changes:
sacli start
List rulesets:
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 ruleset ID.
List rules in the ruleset:
sacli --ruleset_id '1'1 AccessControlRulesList
Replace the
ruleset_idvalue with the value from the ruleset list output.Example output:
[ { "action": "deny", "comment": "", "id": 1, "match_data": "app1.example.com", "match_type": "domain", "position": 1, "ruleset_id": 1, "type": "domain_routing" }, { "action": "nat", "comment": "", "id": 2, "match_data": "example.com", "match_type": "domain_or_subdomain", "position": 2, "ruleset_id": 1, "type": "domain_routing" } ]
Confirm your rules are correct. From our example:
The deny rule is first.
The allow rule follows.
In this step, connect to the VPN as a user and generate traffic to app1.example.com to verify it's denied, while traffic to the rest of *.example.com is allowed.
Connect a VPN client.
From the Access Server console, enable DNS proxy debug logging:
echo "DEBUG_DNSPROXY=true" >> /usr/local/openvpn_as/etc/as.conf && systemctl restart openvpnas
From the client, run:
ping app1.example.com
Example output:
Ping request could not find host app1.example.com. Please check the name and try again.
Test the allowed domain:
ping app2.example.com
Example output:
Reply from 100.64.0.1: bytes=32 time=116ms TTL=49 Reply from 100.64.0.1: bytes=32 time=116ms TTL=49 Reply from 100.64.0.1: bytes=32 time=114ms TTL=49 Reply from 100.64.0.1: bytes=32 time=115ms TTL=49
On the server, run:
grep "DNSPROXY" /var/log/openvpnas.log
Example output:
2026-04-16T21:16:41+0000 [stdout#info] [DNSPROXY] OUT: '[867] Received query from 172.27.236.2:60873' 2026-04-16T21:16:41+0000 [stdout#info] [DNSPROXY] OUT: '[867] Forwarding query for app2.example.com. IN A' 2026-04-16T21:16:41+0000 [stdout#info] [DNSPROXY] OUT: '[867] Received response' 2026-04-16T21:16:41+0000 [stdout#info] [DNSPROXY] OUT: '[867] Changed address for app2.example.com: 192.0.2.1 -> 100.64.0.1'
Tip
Debug flags can generate large amounts of log data. After collecting the necessary information, remove the debug flag and restart Access Server to prevent excessive logging.