Tuesday, July 14, 2020

Cisco ISE and Azure AD - Part 1

COVID times have been strange for all of us I'm sure. One of the strangest things to emerge from my customer base during these times was a desire to authenticate users in Azure Active Directory with ISE. Some for VPN authentication, and even a couple of requests for 802.1X authentication. So that desire lead me down two different paths, and I'll cover the first (slightly more involved and less obvious) path in this post. That path can briefly be summed up as "Azure AD as an authentication source, over the Internet".


Ingredients:

1. Azure Active Directory Domain Services
2. Cisco ISE, duh.
3. Cisco AnyConnect Network Access Manager (NAM).

Starting State

In the interest of not pretending this is an Azure blog, or that I'm an Azure expert, I'm not going to cover some of the basics in this setup. The assumption will be you have access Azure Active Directory, have configured your desired custom domain name (not required, but definitely looks nicer) and have a basic understanding about how subscriptions work and how to create resources. I'll cover some of this, but I'm trying really hard to keep this focused on ISE + Azure AD, not get to deep into the nuances of Azure AD itself.

1. Azure Active Directory Domain Services (AADDS)

Alright, so many of you know setting up an Azure directory creates an Active Directory for you store users and groups. However to actually interact with it, we'll need AADDS. This resource spins up two domain controllers, allowing for AD joins, LDAP binds, etc. To create this resource, search for 'Azure AD Domain Services'.



The process for creating this resource is much like spinning up any resource in Azure, and is largely next/next/finish. For later labs, I would be careful to create a VNET that doesn't overlap with any on-prem subnets. More on that in Part 2 though.

After creation you'll be able to view a fairly basic domain services configuration. There's some good info, like the private IPs of the Domain Controllers that were spun up as part of enabling this resource, but what we're interested in is enabling Secure LDAP.


This will require a server certificate to be uploaded in PFX format, the requirements for which Microsoft outlines here. To create this certificate I'll use openSSL to generate a wildcard csr for the domain of hop16.com, and sign it with a CSR1Kv I have configured as a certificate authority.

1. Generate CSR with OpenSSL

~$ openssl genrsa -out ldaps.key 2048

~$ openssl req -new -key ldaps.key -out ldaps.csr

[output]
 
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:North Carolina
Locality Name (eg, city) []:Raleigh
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Hop16
Organizational Unit Name (eg, section) []:IT
Common Name (e.g. server FQDN or YOUR name) []:*.hop16.com
Email Address []:admin@hop16.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
[/output]



~$ cat ldaps.csr
  
[output] 
-----BEGIN CERTIFICATE REQUEST-----
MIIC0TCCAbkCAQAwgYsxCzAJBgNVBAYTAlVTMRcwFQYDVQQIDA5Ob3J0aCBDYXJv
[...]

-----END CERTIFICATE REQUEST-----
 
[/output]
 

 2. Configure CSR as Certificate Authority

conf t
!
crypto key generate rsa gen mod 4096 label hop16-CA
!
crypto pki server hop16-CA
!
crypto pki server hop16-CA
database level names
no database archive
grant auto
hash sha256
eku server-auth client-auth
!
# Note eku config here, our templates for both parts need #server and client auth.

!
no shutdown
!
end
!
(commands run in privilege exec, not config from here)

crypto pki server hop16-CA request pkcs10 terminal pem
 

[output]
 
PKCS10 request in base64 or pem

% Enter Base64 encoded or PEM formatted PKCS10 enrollment request.
% End with a blank line or "quit" on a line by itself.
-----BEGIN CERTIFICATE REQUEST-----
MIIC0TCCAbkCAQAwgYsxCzAJBgNVBAYTAlVTMRcwFQYDVQQIDA5Ob3J0aCBDYXJv
[...]
-----END CERTIFICATE REQUEST-----

quit
% Granted certificate:
-----BEGIN CERTIFICATE-----
MIIEezCCAmOgAwIBAgIBAjANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDEwhob3Ax
[...]
-----END CERTIFICATE-----
 
[/output]

3. Create PFX file with openSSL

To do this we'll also need the CA's certificate, which we can retrieve in PEM format from the CSR with 'show crypto pki cert pem hop16-CA'. Then we can copy that also with our issued certificate back to our computer running openSSL (WSL for me).

~$ openssl pkcs12 -export -out ldaps.pfx -inkey ldaps.key -in ldaps.crt \
-certfile ca.crt
Enter Export Password:
Verifying - Enter Export Password:


4. Enable Secure LDAP

Back in our Azure portal we'll navigate back to Azure AD Domain Services | Secure LDAP and toggle Secure LDAP to 'Enable', toggle Allow secure LDAP access over the internet to 'Enable', upload our freshly created PFX and decryption password then click save. Fair warning, if your template doesn't have to correct EKUs, the CA certificate is missing from the chain or any other similar Certificate issues this process will fail. Speaking 100% from experience here lol.

Also note the message about needing to modify the network security group associated with your AADDS subnet.



Once this process completes successfully, you can pull the public IP for Secure LDAP from Azure AD Domain Services -> Properties.


We'll either want a DNS record (e.g. ldaps[.]yourdomain[.]com or aadds[.]yourdomain[.]com) or we can had a host record in ISE if we're in a pinch. Since we're using a wildcard certificate you can use whatever DNS name makes the most sense to you. For this post I'm going with ldaps.hop16.com. We also need to make sure LDAPS is allowed inbound on our NSG (as highlighted in the previous screen shot).


2. ISE Configuration

Most of the ISE configuration is pretty straight forward, we're going to make sure the CA certificate from our CSR1000v is a trusted CA, configure an external ID source that points to Azure Secure LDAP, then make a quick tweak to the subject name attribute. So let's jump in!

1. Navigate to Administration -> System | Certificates -> Trusted Certificates and upload CSR CA certificate.




2. After clicking submit, navigate to Administration -> Identity Management | External Identity Sources -> LDAP and click 'Add' to add an LDAP Identity Source. Most of the settings are fairly intuitive, but the part you'll really want to pay special attention to is setting the schema to Active Directory, but changing the 'subnet name attribute' to sAMAccountName (default is userPrincipalName). Doing this will change the schema from Active Directory to Custom, but leaves all the defaults from Active Directory.


3. Now we'll configure our Secure LDAP connection, specifiying a bind account as well. I've previously added a user in my Azure AD called 'ldapbind' that has basic user permissions.


4. Fill in your appropriate Directory Organization settings, I'm opting to strip anything before "\" in user authentications in a (mostly hopeless) attempt to keep ISE from trying to authenticate users as "azuread\un".


5. Now add groups you plan to use in Authorization Policies later.


6. Add some attributes for ISE to retrieve, most importantly sAMAccountName and memberOf.


7. Finally, we can click submit.

8. I already have my c3750-X added as a Network Device, using shared secret 'supersecretsharedpasswd'. Also I have a policy-set setup to catch all Wired dot1X and Wired MAB authentications.


9. Inside that policy-set, I'm going to modify the authentication policy to point only to LDAP. You could also just add your LDAP source to an existing identity source sequence.


10. I'm also going to setup a basic rule in my authorization policy to allow network access to members of the AAD Domain Users group.

3. Super Quick Switch Config.

Here's a quick and dirty dot1X config for a 3750-X, with my test PC connected to Gig1/0/48.

aaa new-model
aaa group server radius ISE27_RADIUS
 server-private 192.168.45.10 auth-port 1812 acct-port 1813 key supersecretsharedpasswd
!
aaa authentication login default local
aaa authentication dot1x default group ISE27_RADIUS
aaa authorization exec default local
aaa authorization network default group ISE27_RADIUS
aaa accounting dot1x default start-stop group ISE27_RADIUS
aaa accounting network default start-stop group ISE27_RADIUS
aaa server radius dynamic-author
 client 192.168.45.10 server-key supersecretsharedpasswd
!
dot1x system-auth-control
!
radius-server vsa send accounting
radius-server vsa send authentication
radius-server attribute 6 on-for-login-auth
radius-server attribute 8 include-in-access-req
radius-server attribute 25 access-request include
!
interface GigabitEthernet1/0/48
 switchport mode access
 authentication host-mode multi-auth
 authentication order mab dot1x
 authentication priority dot1x mab
 authentication port-control auto
 authentication periodic
 authentication violation restrict
 dot1x pae authenticator
 spanning-tree portfast

4. AnyConnect NAM Configuration

Now we reach the downside of using Secure LDAP (or LDAP in general for that matter). We only have two supported authentication methods, EAP-TLS or EAP-GTC. EAP-TLS would require client certificates, but would be supported by Window's native supplicant. EAP-GTC supports password authentication, but we need NAM to use it. So, we'll setup a profile for NAM to use PEAP-EAP-GTC, luckily this isn't particularly difficult. I have Network Access Manager Profile Editor installed locally, however you could configure a policy and push it out to other clients. I'm editing the default wired connection, and saving this as configuration.xml in 'C:\ProgramData\Cisco\Cisco AnyConnect Secure Mobility Client\Network Access Manager\newConfigFiles'. Revelant screen shots below:

PEAP-EAP-GTC

Use SSO Credentials

After saving this profile as configuration.xml in newConfigFiles as mentioned above, we have our moment of truth!

AnyConnect Confirmed
AnyConnect looking happy

ISE looking happy

And as a final verification, we'll check what the switch sees.

switch# show authentication sessions int gig1/0/48 details
            Interface:  GigabitEthernet1/0/48
          MAC Address:  000a.f721.edff
         IPv6 Address:  Unknown
         IPv4 Address:  192.168.45.100
            User-Name:  jon
               Status:  Authorized
               Domain:  DATA
       Oper host mode:  multi-auth
     Oper control dir:  both
      Session timeout:  3600s (local), Remaining: 1873s
       Timeout action:  Reauthenticate
    Common Session ID:  000000000000005529C90DB6
      Acct Session ID:  0x00000022
               Handle:  0x4B000034
       Current Policy:  POLICY_Gi1/0/48

Local Policies:
        Service Template: DEFAULT_LINKSEC_POLICY_SHOULD_SECURE (priority 150)
      Security Policy:  Should Secure
      Security Status:  Link Unsecure

Server Policies:

Method status list:
       Method           State
       dot1x            Authc Success

Well, that about does it for this post! Next time we'll look at actually connecting to Azure AD as though it were a traditional Active Directory environment.