ISE dACL Lab: Contractor Gets VLAN 862, but DNS Is Blocked

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.

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. If a dACL isn't showing up on the port at all, that's a different problem: see dACL Not Applying in 802.1X. 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". 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.

  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:
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
  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

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

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.

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:

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:

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:

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

What the switch is enforcing

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

The authentication detail shows the result ISE sent:

Live Logs detail: 5200, Wired-Access >> Contractors-Web-Only, Contractor-VLAN862-WEB
  • 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
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.

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

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:

ctr-01$ nslookup portal.lab.example
;; connection timed out; no servers could be reached
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:

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

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:

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

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
Live Logs detail: 5232 DACL Download Succeeded with the four new inacl entries
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 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.

Read next