> ## Content Index
> Fetch the complete content index at: https://www.pinglabz.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# ISE dACL Lab: Contractor Gets VLAN 862, but DNS Is Blocked
- URL: https://www.pinglabz.com/ise-dacl-dns-implicit-deny/
- Published: 2026-10-30T03:56:23.000Z
- Updated: 2026-10-30T03:56:23.000Z
- Description: The contractor authenticated, landed in VLAN 862 and got the web-only dACL from ISE. HTTPS to the portal's IP worked, but the portal's name never resolved. The dACL's implicit deny was dropping DNS. Here's the lab, the evidence and the fix.
- Author: Jaime
- Tags: 802.1X, Labs, Troubleshooting, Network Security

Contractors need one thing on the network: the portal they work in. They don't need file shares, printers or the management range. The cleanest way to enforce that with Cisco ISE is a single authorization result that does two jobs. It puts the contractor in their own VLAN and pushes a **downloadable ACL (dACL)** that only allows the portal.

In this lab that result works on the first try, at least on paper. The contractor authenticates, lands in VLAN 862 and gets the dACL. HTTPS to the portal's IP address works. Then they type the portal's name into a browser and nothing happens. Nobody wrote a DNS rule, so the dACL's implicit `deny ip any any` drops every lookup.

I built this in my home lab with Cisco ISE 3.5 and a Cisco IOSv-L2 15.2 access switch. All the output below is real. Addresses and MACs are documentation values, and secrets are placeholders.

If you want the theory first, read the [Cisco dACL Guide: Downloadable ACLs with ISE and 802.1X](https://www.pinglabz.com/dacl-downloadable-acl-cisco-ise-802-1x/). If a dACL isn't showing up on the port at all, that's a different problem: see [dACL Not Applying in 802.1X](https://www.pinglabz.com/dacl-not-applying-802-1x-troubleshooting/). This lab is about a dACL that applies perfectly and still breaks the user.

## The lab

| Device  | Role                                                | Addressing                                                                      |
| ------- | --------------------------------------------------- | ------------------------------------------------------------------------------- |
| ise35   | Cisco ISE 3.5, RADIUS server                        | 192.0.2.10                                                                      |
| acc-sw3 | 802.1X authenticator, gateway for VLANs 862 and 890 | Gi0/0 10.86.253.10/24 in VRF MGMT; Vlan862 10.86.62.1/24; Vlan890 10.86.90.1/24 |
| ctr-01  | Contractor laptop with a PEAP supplicant            | 10.86.62.21/24, MAC 00:00:5E:00:53:62, on Gi0/1, resolver 10.86.90.53           |
| srv-web | Contractor portal and DNS server                    | HTTPS on 10.86.90.10, DNS on 10.86.90.53, on Gi0/2                              |

The switch has three VLANs:

- **VLAN 999 PARKING** is the configured access VLAN on Gi0/1\. It has no SVI. An unauthorized contractor sits here and goes nowhere.
- **VLAN 862 CONTRACTORS** is where ISE puts the contractor.
- **VLAN 890 SERVERS** holds the portal and the DNS server.

The switch routes between 862 and 890, so without a dACL the contractor could reach anything in the server VLAN. The dACL is the only thing that narrows it down to the portal.

## ISE side

The ISE side is four objects plus one rule. The 802.1X basics (network device, PEAP, the Wired-Access policy set) carry over from the first lab in this series, [ISE 802.1X Lab: Fixing 11007 "Could not locate Network Device"](https://www.pinglabz.com/ise-11007-could-not-locate-network-device/). The rule matches on the user identity group rather than a literal value, for the same reason as in the second lab, [ISE MAB Lab: Why a Known Printer Still Gets 15039 DenyAccess](https://www.pinglabz.com/ise-mab-15039-denyaccess-printer/).

1. **Network device** `acc-sw3` at 10.86.253.10/32 with the RADIUS shared secret `<RADIUS_SHARED_SECRET>`.
2. **User identity group** `Wired-Contractors`, with an internal user `ctr-01` (password `<CONTRACTOR_PASSWORD>`).
3. **Downloadable ACL** `CTR-WEB-ONLY`. This is the version that broke things:

```text
permit tcp any host 10.86.90.10 eq 443
deny ip any any

```

[![Downloadable ACL CTR-WEB-ONLY, first version: HTTPS to the portal and nothing else](https://storage.ghost.io/c/ff/28/ff28db4a-11e1-4835-b928-158974cf96c1/content/images/2026/10/ise-dacl-13-dacl-v1.png)](https://storage.ghost.io/c/ff/28/ff28db4a-11e1-4835-b928-158974cf96c1/content/images/2026/10/ise-dacl-13-dacl-v1.png?ref=pinglabz.com)

1. **Authorization profile** `Contractor-VLAN862-WEB`. It's `ACCESS_ACCEPT` with **DACL Name** set to `CTR-WEB-ONLY` and **VLAN** set to tag 1, `862`. One result does both jobs.

[![Authorization profile Contractor-VLAN862-WEB with DACL Name CTR-WEB-ONLY and VLAN 862](https://storage.ghost.io/c/ff/28/ff28db4a-11e1-4835-b928-158974cf96c1/content/images/2026/10/ise-dacl-05-authz-profile.png)](https://storage.ghost.io/c/ff/28/ff28db4a-11e1-4835-b928-158974cf96c1/content/images/2026/10/ise-dacl-05-authz-profile.png?ref=pinglabz.com)

Then one authorization rule at the top of the Wired-Access policy set:

- **Contractors-Web-Only:** `IdentityGroup·Name EQUALS User Identity Groups:Wired-Contractors` → `Contractor-VLAN862-WEB`

[![Wired-Access policy set: Contractors-Web-Only above Wired-Staff-Permit](https://storage.ghost.io/c/ff/28/ff28db4a-11e1-4835-b928-158974cf96c1/content/images/2026/10/ise-dacl-14-policy-set-rules.png)](https://storage.ghost.io/c/ff/28/ff28db4a-11e1-4835-b928-158974cf96c1/content/images/2026/10/ise-dacl-14-policy-set-rules.png?ref=pinglabz.com)

### A side note: ISE won't let you write the source

My first draft of the dACL wrote the server as the source of the rule, the way you'd write the return direction on a router ACL. ISE refused to save it:

> While creating DACL, the keyword 'Any' must be the source in all ACE in DACL. Once the DACL is pushed, the 'Any' in the source is replaced with the IP address of the client that is connecting to the switch.

That's worth understanding. A dACL is always written from the client's point of view, inbound on the port. The source is always "the client", so ISE makes you write `any`. The switch fills in the client's address when it applies the dACL, so the switch has to know the client's IP. That's what device tracking is for. On this IOSv-L2 image, device tracking turned on by itself when I enabled 802.1X on the port.

## Switch side

This is the AAA and port config. The shared secret is a placeholder.

```text
aaa new-model
radius server ISE-1
 address ipv4 192.0.2.10 auth-port 1812 acct-port 1813
 key <RADIUS_SHARED_SECRET>
aaa group server radius ISE
 server name ISE-1
 ip vrf forwarding MGMT
 ip radius source-interface GigabitEthernet0/0
aaa authentication dot1x default group ISE
aaa authorization network default group ISE
aaa accounting dot1x default start-stop group ISE
radius-server attribute 6 on-for-login-auth
radius-server attribute 8 include-in-access-req
radius-server vsa send accounting
radius-server vsa send authentication
dot1x system-auth-control
!
interface GigabitEthernet0/1
 description ctr-01 contractor (802.1X)
 switchport mode access
 switchport access vlan 999
 authentication port-control auto
 dot1x pae authenticator
 dot1x timeout tx-period 10
 spanning-tree portfast edge

```

Two lines matter most for dACLs:

- `aaa authorization network default group ISE` lets the switch accept the VLAN and the dACL name from ISE. Without it you get neither.
- `radius-server vsa send authentication` lets the Cisco AV pairs through. The dACL name arrives as a Cisco AV pair.

## Break it: the name never resolves

The contractor authenticates with PEAP. The switch shows the session is authorized, in VLAN 862, with the dACL applied:

```text
acc-sw3#show authentication sessions interface Gi0/1 details
            Interface:  GigabitEthernet0/1
          MAC Address:  0000.5e00.5362
         IPv4 Address:  10.86.62.21
            User-Name:  ctr-01
               Status:  Authorized
               Domain:  DATA
       Oper host mode:  single-host
Server Policies:
           Vlan Group:  Vlan: 862
              ACS ACL:  xACSACLx-IP-CTR-WEB-ONLY-6ae3f12e
Method status list:
      Method            State
      dot1x              Authc Success

```

Everything a ticket would ask for checks out: right user, right VLAN, right dACL. Now the contractor tries to use it:

```text
ctr-01$ cat /etc/resolv.conf
nameserver 10.86.90.53
ctr-01$ nslookup portal.lab.example
;; connection timed out; no servers could be reached

ctr-01$ curl -sk -m 8 https://portal.lab.example/ -o /dev/null -w "%{http_code}\n"
000
curl exit code: 28
ctr-01$ curl -sk -m 5 https://10.86.90.10/ -o /dev/null -w "%{http_code}\n"
200

```

The portal works by IP and fails by name. The lookup didn't come back with "not found". It timed out, so the query never got an answer.

The DNS server's log confirms the query never arrived:

```text
srv-web$ grep -c query /tmp/dns.log
0

```

### What the switch is enforcing

```text
acc-sw3#show ip access-lists interface Gi0/1
     permit tcp any host 10.86.90.10 eq 443 (11 matches)
     deny ip any any (8 matches)

```

The deny counter is climbing. Every DNS query from the contractor is a UDP packet to 10.86.90.53 port 53, and nothing above the final line matches it.

The same command also prints `Extended IP access list Auth-Default-ACL`, which does permit DNS. Don't let it fool you. That's the switch's built-in default ACL, not the contractor's dACL. The authorized session's traffic hit the dACL, and the dACL dropped it.

### What ISE sees

From ISE's side, nothing is wrong. Live Logs shows a clean 5200 for `ctr-01` and a separate **5232 DACL Download Succeeded** line for the dACL:

[![Live Logs: 5200 for ctr-01 and the 5232 dACL download line](https://storage.ghost.io/c/ff/28/ff28db4a-11e1-4835-b928-158974cf96c1/content/images/2026/10/ise-dacl-10-livelog-fault.png)](https://storage.ghost.io/c/ff/28/ff28db4a-11e1-4835-b928-158974cf96c1/content/images/2026/10/ise-dacl-10-livelog-fault.png?ref=pinglabz.com)

The authentication detail shows the result ISE sent:

[![Live Logs detail: 5200, Wired-Access >> Contractors-Web-Only, Contractor-VLAN862-WEB](https://storage.ghost.io/c/ff/28/ff28db4a-11e1-4835-b928-158974cf96c1/content/images/2026/10/ise-dacl-11-livelog-fault-detail.png)](https://storage.ghost.io/c/ff/28/ff28db4a-11e1-4835-b928-158974cf96c1/content/images/2026/10/ise-dacl-11-livelog-fault-detail.png?ref=pinglabz.com)

- **Event:** `5200 Authentication succeeded`
- **Authentication Protocol:** `PEAP (EAP-MSCHAPv2)`
- **Authorization Policy:** `Wired-Access >> Contractors-Web-Only`
- **Authorization Result:** `Contractor-VLAN862-WEB`
- **Result:** `Tunnel-Private-Group-ID (tag=1) 862` and `cisco-av-pair ACS:CiscoSecure-Defined-ACL=#ACSACL#-IP-CTR-WEB-ONLY-6ae3f12e`
- **Steps:** `11022 Added the dACL specified in the Authorization Profile`

That last AV pair carries only the dACL's *name*. The switch then asks ISE for the contents in a second RADIUS request, which is the 5232 line. Its detail shows exactly what the switch got:

[![Live Logs detail: 5232 DACL Download Succeeded with inacl#1 and inacl#2](https://storage.ghost.io/c/ff/28/ff28db4a-11e1-4835-b928-158974cf96c1/content/images/2026/10/ise-dacl-12-livelog-dacl-download.png)](https://storage.ghost.io/c/ff/28/ff28db4a-11e1-4835-b928-158974cf96c1/content/images/2026/10/ise-dacl-12-livelog-dacl-download.png?ref=pinglabz.com)

```text
ip:inacl#1=permit tcp any host 10.86.90.10 eq 443
ip:inacl#2=deny ip any any

```

So ISE, the switch and the contractor all agree. The policy is doing exactly what it says. What it says is wrong.

## Fix it: let the contractor resolve names

The portal is only usable by name, so the contractor needs DNS to the one resolver they use. The fix is to add DNS to the dACL above the HTTPS line. Add TCP 53 as well as UDP, for large responses.

```text
permit udp any host 10.86.90.53 eq domain
permit tcp any host 10.86.90.53 eq domain
permit tcp any host 10.86.90.10 eq 443
deny ip any any

```

[![Downloadable ACL CTR-WEB-ONLY, fixed: DNS to 10.86.90.53, HTTPS to the portal, deny the rest](https://storage.ghost.io/c/ff/28/ff28db4a-11e1-4835-b928-158974cf96c1/content/images/2026/10/ise-dacl-23-dacl-v2.png)](https://storage.ghost.io/c/ff/28/ff28db4a-11e1-4835-b928-158974cf96c1/content/images/2026/10/ise-dacl-23-dacl-v2.png?ref=pinglabz.com)

### Saving the dACL doesn't fix the user

This is the part that catches people. I saved the dACL in ISE and tested again straight away. It still failed:

```text
ctr-01$ nslookup portal.lab.example
;; connection timed out; no servers could be reached

```

```text
acc-sw3#show authentication sessions interface Gi0/1 details | include ACS ACL
              ACS ACL:  xACSACLx-IP-CTR-WEB-ONLY-6ae3f12e

```

The session still has the old dACL, with the same `-6ae3f12e` suffix. The switch downloaded the contents once, when the session came up, and it doesn't go back to ISE until the session re-authenticates. In production you'd send a CoA reauth from the session in ISE. In the lab I logged the supplicant off and on again.

The suffix on the name is how you tell the versions apart. ISE changes it whenever the dACL contents change:

```text
acc-sw3#show authentication sessions interface Gi0/1 details
            User-Name:  ctr-01
               Status:  Authorized
           Vlan Group:  Vlan: 862
              ACS ACL:  xACSACLx-IP-CTR-WEB-ONLY-6ae3f66d
      dot1x              Authc Success

```

`-6ae3f12e` became `-6ae3f66d`, so this session has the new contents.

## Prove it

### Contractor

```text
ctr-01$ nslookup -type=a portal.lab.example
Server:     10.86.90.53
Address:    10.86.90.53:53

Name:   portal.lab.example
Address: 10.86.90.10

ctr-01$ curl -sk -m 8 https://portal.lab.example/ -o /dev/null -w "%{http_code}\n"
200

```

The name resolves and the portal loads by name. The dACL is still doing its job for everything else. Plain HTTP to the same server and ping are both dropped:

```text
ctr-01$ curl -s -m 5 http://10.86.90.10/ -o /dev/null -w "%{http_code}\n"
000
curl exit code: 28
ctr-01$ ping -c 3 -W 1 10.86.90.10
3 packets transmitted, 0 packets received, 100% packet loss

```

### Switch

```text
acc-sw3#show ip access-lists interface Gi0/1
     permit udp any host 10.86.90.53 eq domain (6 matches)
     permit tcp any host 10.86.90.53 eq domain
     permit tcp any host 10.86.90.10 eq 443 (11 matches)
     deny ip any any (8 matches)

```

The new DNS line is matching.

### ISE

Live Logs shows the new session, with a second 5232 download for the new suffix:

[![Live Logs: the new ctr-01 session and the 5232 download for CTR-WEB-ONLY-6ae3f66d](https://storage.ghost.io/c/ff/28/ff28db4a-11e1-4835-b928-158974cf96c1/content/images/2026/10/ise-dacl-20-livelog-pass.png)](https://storage.ghost.io/c/ff/28/ff28db4a-11e1-4835-b928-158974cf96c1/content/images/2026/10/ise-dacl-20-livelog-pass.png?ref=pinglabz.com)

[![Live Logs detail: 5232 DACL Download Succeeded with the four new inacl entries](https://storage.ghost.io/c/ff/28/ff28db4a-11e1-4835-b928-158974cf96c1/content/images/2026/10/ise-dacl-22-livelog-dacl-download-v2.png)](https://storage.ghost.io/c/ff/28/ff28db4a-11e1-4835-b928-158974cf96c1/content/images/2026/10/ise-dacl-22-livelog-dacl-download-v2.png?ref=pinglabz.com)

```text
ip:inacl#1=permit udp any host 10.86.90.53 eq domain
ip:inacl#2=permit tcp any host 10.86.90.53 eq domain
ip:inacl#3=permit tcp any host 10.86.90.10 eq 443
ip:inacl#4=deny ip any any

```

## Checklist for "authorized, right VLAN, but the app doesn't work"

When ISE says the session is fine and the user says it isn't:

1. Run `show authentication sessions interface <port> details`. Check **Vlan Group** and **ACS ACL**. If the dACL is missing, that's a different fault. Start with the `aaa authorization network` and `radius-server vsa send` lines.
2. Run `show ip access-lists interface <port>` and watch which line's counter moves while the user retries. A climbing `deny ip any any` is your answer.
3. Write down every service the application really depends on before you write the dACL: DNS, sometimes DHCP renewals, any SSO or identity provider, and the app itself. "Web only" almost always means "web and DNS".
4. In ISE, open the **5232** line, not just the 5200\. Its `inacl` entries are the exact ACEs the switch received.
5. After editing a dACL, compare the name suffix on the switch. Same suffix means the session still has the old contents, so reauthenticate the session (CoA) before you retest.
6. Write every ACE with `any` as the source. ISE won't save anything else, and the switch fills in the client address.

If you've done per-user ACLs with FreeRADIUS before, [FreeRADIUS Filter-Id Lab: Per-User ACLs on a Cisco Switch](https://www.pinglabz.com/freeradius-filter-id-per-user-acl/) is the local-ACL version of the same idea. For how ISE hands VLANs to the switch, see [Dynamic VLAN Assignment with 802.1X and Cisco ISE](https://www.pinglabz.com/dynamic-vlan-assignment-802-1x-cisco-ise/).