Authentication, Authorization, and Accounting (AAA) on a Cisco ASA decides three things for every VPN session: who is the user, what are they allowed to do, and what activity should be logged. The three protocols you can wire into the ASA for these jobs are LDAP (commonly Microsoft Active Directory), RADIUS, and TACACS+. Each one has its sweet spot, and a real production ASA frequently uses all three at the same time, just for different functions. This article walks the configuration of all three on Cisco ASA 9.x and shows when to use which. All output is from a live ASAv 9.23(1) in the PingLabz ASA reference lab.
Before configuring AAA, you should already have a working AnyConnect connection profile (see Cisco ASA AnyConnect SSL VPN Configuration) using local users. The AAA server group then plugs into the existing tunnel-group's authentication-server-group attribute, replacing or augmenting the local fallback.
When to Use Which Protocol
| Protocol | Best for | Notes |
|---|---|---|
| LDAP (most often Microsoft AD) | VPN authentication and authorization with group memberships from your existing directory. | Native to AD; no extra server needed. Use LDAPS (TCP/636) for production. Map AD group memberships to ASA group-policy attributes via ldap attribute-map. |
| RADIUS | VPN authentication where you want to layer in MFA (Cisco Duo, Microsoft Entra MFA Server, RSA SecurID) or where you want clean accounting. | Industry-standard. Most MFA solutions front-end RADIUS. Cleaner accounting than LDAP because RADIUS has Accounting-Start / Stop / Interim packets. |
| TACACS+ | Administrator authentication into the ASA itself: SSH, console, ASDM. Very rarely for VPN. | Cisco-proprietary. Per-command authorization and per-command accounting (logs every CLI command run by an admin). Required for many regulatory regimes. |
The common production split: LDAP for VPN authentication and group lookup, RADIUS for VPN authentication when MFA is required, TACACS+ for admin access into the ASA itself.
AAA Server Groups
All AAA on the ASA is configured in two layers: an aaa-server group (the protocol and the list of servers) and the references to that group from features that consume AAA (tunnel-groups, aaa authentication ssh console, etc.).
From our lab, three server groups are configured (one per protocol):
ASA-PERIM# show running-config aaa-server
aaa-server RADIUS-VPN protocol radius
aaa-server RADIUS-VPN (inside) host 10.10.0.10
retry-interval 2
timeout 5
key *****
authentication-port 1812
accounting-port 1813
aaa-server LDAP-VPN protocol ldap
aaa-server LDAP-VPN (inside) host 10.10.0.20
server-port 636
ldap-base-dn dc=pinglabz,dc=lab
ldap-scope subtree
ldap-naming-attribute sAMAccountName
ldap-login-password *****
ldap-login-dn cn=svc-asa,ou=Service,dc=pinglabz,dc=lab
ldap-over-ssl enable
server-type microsoft
aaa-server TACACS-ADMIN protocol tacacs+
aaa-server TACACS-ADMIN (inside) host 10.10.0.30
timeout 5
key *****
Each server group has a name (RADIUS-VPN, LDAP-VPN, TACACS-ADMIN), a protocol, and one or more host entries. The ASA tries hosts in order; if the first is unreachable after the configured retry/timeout, it tries the second.
Configure RADIUS for VPN Authentication
Two-step pattern: declare the group with its protocol, then add hosts with their shared secret and timing.
ASA-PERIM(config)# aaa-server RADIUS-VPN protocol radius
ASA-PERIM(config)# aaa-server RADIUS-VPN (inside) host 10.10.0.10
ASA-PERIM(config-aaa-server-host)# key PingLabz-RADIUS-Secret
ASA-PERIM(config-aaa-server-host)# authentication-port 1812
ASA-PERIM(config-aaa-server-host)# accounting-port 1813
ASA-PERIM(config-aaa-server-host)# timeout 5
ASA-PERIM(config-aaa-server-host)# retry-interval 2
ASA-PERIM(config-aaa-server-host)# exit
Then bind the group into a tunnel-group:
ASA-PERIM(config)# tunnel-group SSL_PROFILE general-attributes
ASA-PERIM(config-tunnel-general)# authentication-server-group RADIUS-VPN LOCAL
ASA-PERIM(config-tunnel-general)# accounting-server-group RADIUS-VPN
The LOCAL after RADIUS-VPN means: try RADIUS first, fall back to the local username database if all RADIUS hosts are unreachable. Always include LOCAL fallback for VPN authentication; otherwise a RADIUS outage cuts off all remote access at exactly the worst moment (when admins need to log in to fix it).
Verify the server is at least reachable on the wire:
ASA-PERIM# test aaa-server authentication RADIUS-VPN host 10.10.0.10 username vpnuser password PingLabzVPN!
INFO: Attempting Authentication test to IP address <10.10.0.10> (timeout: 12 seconds)
The test aaa-server command is the fastest way to confirm the shared secret and reachability are correct. If the test hangs, your shared secret is right but the server is unreachable; if it returns "rejected" with an immediate response, the server is reachable but the credentials are wrong.
Configure LDAP (Active Directory) for Authorization
LDAP setup needs more attributes than RADIUS because there is no standard "give me the user's password and let me see if it matches" call. The ASA binds to the directory as a service account, then performs a sub-tree search to find the user by their login attribute, then re-binds as the user with their typed password.
ASA-PERIM(config)# aaa-server LDAP-VPN protocol ldap
ASA-PERIM(config)# aaa-server LDAP-VPN (inside) host 10.10.0.20
ASA-PERIM(config-aaa-server-host)# ldap-base-dn dc=pinglabz,dc=lab
ASA-PERIM(config-aaa-server-host)# ldap-scope subtree
ASA-PERIM(config-aaa-server-host)# ldap-naming-attribute sAMAccountName
ASA-PERIM(config-aaa-server-host)# ldap-login-dn cn=svc-asa,ou=Service,dc=pinglabz,dc=lab
ASA-PERIM(config-aaa-server-host)# ldap-login-password PingLabz-LDAP-Secret
ASA-PERIM(config-aaa-server-host)# server-type microsoft
ASA-PERIM(config-aaa-server-host)# ldap-over-ssl enable
ASA-PERIM(config-aaa-server-host)# server-port 636
ASA-PERIM(config-aaa-server-host)# exit
The eight attributes:
ldap-base-dn: the root of the LDAP tree to search from. Typically your AD domain DN.ldap-scope subtree: search the entire sub-tree below the base DN. The other choice (onelevel) only searches one level deep, which is rarely what you want.ldap-naming-attribute sAMAccountName: the attribute that identifies a user.sAMAccountNameis the AD short name (e.g.vpnuser). For OpenLDAP, it would beuid; for AD via UPN, useuserPrincipalName.ldap-login-dn+ldap-login-password: the service account the ASA binds as to perform the user search.server-type microsoft: tells the ASA how to interpret password-expiry and account-locked responses. Other choices includegeneric,sun,openldap.ldap-over-ssl enable+server-port 636: use LDAPS instead of clear-text LDAP. Always use LDAPS in production. Clear LDAP exposes the user's typed password to every device on the inside path between ASA and DC.
Bind to a tunnel-group as the authorization-server-group:
ASA-PERIM(config)# tunnel-group SSL_PROFILE general-attributes
ASA-PERIM(config-tunnel-general)# authorization-server-group LDAP-VPN
This authorizes the user post-authentication: the ASA performs a second LDAP search to retrieve group memberships and attributes, which can be mapped to ASA group-policies via ldap attribute-map. The full attribute-map mechanism is covered in the DAP article (it ties cleanly into Dynamic Access Policies).
Configure TACACS+ for Admin Access
TACACS+ is rarely used for VPN authentication. Its strength is per-command authorization for admins logging into the ASA itself: every CLI command an admin runs is authorized by the TACACS+ server, and every command is logged via accounting.
ASA-PERIM(config)# aaa-server TACACS-ADMIN protocol tacacs+
ASA-PERIM(config)# aaa-server TACACS-ADMIN (inside) host 10.10.0.30
ASA-PERIM(config-aaa-server-host)# key PingLabz-TACACS-Secret
ASA-PERIM(config-aaa-server-host)# server-port 49
ASA-PERIM(config-aaa-server-host)# timeout 5
ASA-PERIM(config-aaa-server-host)# exit
Wire into the ASA's admin auth/authz/acct planes:
ASA-PERIM(config)# aaa authentication ssh console TACACS-ADMIN LOCAL
ASA-PERIM(config)# aaa authorization exec authentication-server
ASA-PERIM(config)# aaa accounting ssh console TACACS-ADMIN
What each line does:
aaa authentication ssh console TACACS-ADMIN LOCAL: authenticate SSH logins via TACACS+, fall back to local. Theconsolekeyword here means console-mode SSH (the standard SSH login flow).aaa authorization exec authentication-server: when an authenticated admin enters exec mode, look up their privilege level on the same server that authenticated them. This drives the priv-15 vs priv-1 split.aaa accounting ssh console TACACS-ADMIN: log SSH session start and stop, including duration.
For per-command logging (which most TACACS+ deployments want), add aaa accounting command privilege 15 TACACS-ADMIN to log every priv-15 command. This produces a full audit trail of who-typed-what.
Authentication, Authorization, Accounting Separated
You can mix protocols by feature. A common production pattern:
| Feature | Authentication | Authorization | Accounting |
|---|---|---|---|
| VPN (SSL_PROFILE tunnel-group) | RADIUS-VPN (with MFA), fall back LOCAL | LDAP-VPN (group lookup, attribute mapping) | RADIUS-VPN (session start/stop with byte counts) |
| Admin SSH | TACACS-ADMIN, fall back LOCAL | TACACS-ADMIN (per-command) | TACACS-ADMIN (per-command) |
Each function points at the protocol best suited to it. From our lab, this is exactly what we configured: SSL_PROFILE uses RADIUS for auth + LDAP for authz + RADIUS for acct, while admin SSH uses TACACS for all three.
Common Gotchas
- No LOCAL fallback. RADIUS or LDAP server has an outage, and now no one can log in. Always end the auth chain with
LOCALfor VPN; always end withLOCALfor admin SSH. - Clear-text LDAP. Without
ldap-over-ssl enable, every VPN user's password traverses the inside network in clear-text inside an LDAP bind. Anyone on the path can sniff it. Use LDAPS (TCP/636) or STARTTLS (TCP/389 with negotiated TLS). - Wrong service account permissions. The
ldap-login-dnaccount must have read permission on the user OUs you intend to authenticate against. In AD, a domain user account is typically enough; service accounts often have explicit read-deny on Protected Users group, which breaks lookup. - RADIUS shared-secret typo. Symptom: silent failure. The ASA gets no response and times out without giving a useful error. Verify with
test aaa-server authentication; a working secret with bad credentials returns "Reject" while a bad secret hangs. - Confusing authentication-server-group with authorization-server-group. The two attributes are independent. You can authenticate against RADIUS but authorize against LDAP. If you forget the authorization-server-group, no LDAP attribute-map firing will happen and the user gets the tunnel-group default group-policy regardless of their AD group memberships.
Key Takeaways
AAA on the Cisco ASA is configured in two layers: aaa-server groups (the protocol + hosts), then references to those groups from features (tunnel-groups, admin auth). LDAP fits VPN auth + AD group authorization. RADIUS fits VPN auth especially when MFA is in the mix, and provides clean per-session accounting. TACACS+ fits admin auth into the ASA with per-command authorization and audit-grade logging. A real production ASA uses all three concurrently, each for the function it does best.
For the full Cisco ASA reference, including site-to-site IPsec, NAT, ACLs, failover, and the troubleshooting tools, see the Cisco ASA pillar. The next step after AAA is mapping the AAA-returned attributes (and endpoint posture) to runtime decisions: Cisco ASA Dynamic Access Policies (DAP) covers that. For the connection-profile foundation, see Cisco ASA VPN Group Policies and Tunnel Groups.