ASA

Cisco ASA AnyConnect SSL VPN Configuration

Cisco ASA AnyConnect SSL VPN Configuration
In: ASA

AnyConnect SSL VPN (rebranded as Cisco Secure Client) is the most common remote-access VPN you will configure on a Cisco ASA. It tunnels TLS over TCP/443, which gets through nearly every hotel, airport, and customer-site firewall, and it has a stable Windows / macOS / Linux / iOS / Android client. This article walks through the full configuration on Cisco ASA software 9.x: certificate, IP pool, group-policy, tunnel-group, AAA, and the WebVPN service that ties it all together. Every show command output below is from a live ASAv 9.23(1) in the PingLabz ASA reference lab.

Before you start, confirm three things: the ASA's outside interface has a routable address, you have an identity certificate (or are willing to use a self-signed one), and you have an AnyConnect / Cisco Secure Client package (.pkg) for at least one client OS. The .pkg upload is the one piece that needs a real client image from Cisco's download portal. Everything else is config.

The AnyConnect SSL VPN Building Blocks

Five building blocks have to come together before the first client can connect:

BlockPurposeConfigured under
Identity certificateServer cert presented during the TLS handshake. Clients pin to its CN or SAN.crypto ca trustpoint + ssl trust-point
IP local poolThe pool of inside addresses the ASA hands out to connected clients.ip local pool
Group-policyThe bundle of attributes (DNS, split-tunnel ACL, DPD, banner) pushed to the client.group-policy NAME internal
Tunnel-group (connection profile)The named connection profile clients select; ties group-policy + AAA + address-pool together.tunnel-group NAME type remote-access
WebVPN serviceEnables the SSL listener on the outside interface and turns AnyConnect on.webvpn global block

Get any one of those five wrong and the client fails in a different and confusing way. A missing trustpoint produces a TLS handshake error. A missing IP pool produces "user does not have permission to use this connection profile". A wrong group-policy produces a connection that succeeds but lands the client without DNS or routes. The order below builds them in the right sequence so each step has working dependencies.

Step 1: Generate the Identity Certificate

For lab and proof-of-concept work, a self-signed certificate is fine. For production, use an enterprise CA or a public CA so clients do not see certificate warnings.

ASA-PERIM(config)# domain-name pinglabz.lab
ASA-PERIM(config)# crypto key generate rsa label PINGLABZ-RSA modulus 2048 noconfirm
Keypair generation process begin. Please wait...
The RSA keypairs were successfully generated.
ASA-PERIM(config)# crypto ca trustpoint PINGLABZ-SELFSIGNED
ASA-PERIM(config-ca-trustpoint)#  enrollment self
ASA-PERIM(config-ca-trustpoint)#  fqdn vpn.pinglabz.lab
ASA-PERIM(config-ca-trustpoint)#  subject-name CN=vpn.pinglabz.lab,O=PingLabz,C=US
ASA-PERIM(config-ca-trustpoint)#  keypair PINGLABZ-RSA
ASA-PERIM(config-ca-trustpoint)#  exit
ASA-PERIM(config)# crypto ca enroll PINGLABZ-SELFSIGNED noconfirm
% The fully-qualified domain name in the certificate will be: vpn.pinglabz.lab
ASA-PERIM(config)# ssl trust-point PINGLABZ-SELFSIGNED outside

Verify with show crypto ca certificates:

ASA-PERIM# show crypto ca certificates
...
Certificate
  Status: Available
  Certificate Serial Number: 69ffc240
  Certificate Usage: General Purpose
  Public Key Type: RSA (2048 bits)
  Signature Algorithm: RSA-SHA256
  Issuer Name:
    unstructuredName=vpn.pinglabz.lab
    C=US
    O=PingLabz
    CN=vpn.pinglabz.lab
  Subject Name:
    unstructuredName=vpn.pinglabz.lab
    C=US
    O=PingLabz
    CN=vpn.pinglabz.lab
  Validity Date:
    start date: 00:37:39 UTC May 10 2026
    end   date: 00:37:39 UTC May 7 2036
  Storage: config
  Associated Trustpoints: PINGLABZ-SELFSIGNED

That is the cert clients will see on TLS handshake. The fact that issuer and subject are identical is what makes it self-signed. Note the 10-year validity (CA defaults to 1 year on enrollment for a real CA, but a self-signed cert defaults to 10 years on the ASA). If you want a CA-signed cert, the trustpoint enrollment changes from self to terminal or url, and you import the issuer chain. We cover the full cert workflow in Cisco ASA Certificate Management for AnyConnect.

Step 2: Create the Client IP Pool and a Local User

AnyConnect clients need an inside address. Carve out a separate /24 (or larger) that is not in use anywhere else and is routable from the inside network. In our lab we use 10.99.99.0/24 because it does not collide with the inside 10.10.0.0/16.

ASA-PERIM(config)# ip local pool VPN-POOL 10.99.99.10-10.99.99.250 mask 255.255.255.0
ASA-PERIM(config)# username vpnuser password PingLabzVPN! privilege 0
ASA-PERIM(config)# username vpnuser attributes
ASA-PERIM(config-username)#  service-type remote-access
ASA-PERIM(config-username)#  exit

The service-type remote-access line is important. Without it, that local user can shell into the ASA via SSH (priv 0 only, but still). With it, the user can authenticate for VPN but is rejected for SSH and ASDM.

Verify the pool:

ASA-PERIM# show ip local pool VPN-POOL
Begin           End             Mask            Free     Held     In use
10.99.99.10     10.99.99.250    255.255.255.0     241        0        0

241 addresses available, none assigned yet. Each connected client gets one until disconnected, at which point the address goes back to the pool after the idle timeout.

Step 3: Build the Group-Policy

The group-policy is the bag of attributes the ASA pushes to the client at login: which DNS servers, which split-tunnel ACL, what banner, DPD intervals, and most importantly which tunnel protocol(s) the client is allowed to use.

ASA-PERIM(config)# access-list SPLIT-TUNNEL standard permit 10.10.0.0 255.255.0.0
ASA-PERIM(config)# group-policy ANYCONNECT-SSL-GP internal
ASA-PERIM(config)# group-policy ANYCONNECT-SSL-GP attributes
ASA-PERIM(config-group-policy)#  vpn-tunnel-protocol ssl-client
ASA-PERIM(config-group-policy)#  split-tunnel-policy tunnelspecified
ASA-PERIM(config-group-policy)#  split-tunnel-network-list value SPLIT-TUNNEL
ASA-PERIM(config-group-policy)#  dns-server value 10.10.0.10
ASA-PERIM(config-group-policy)#  default-domain value pinglabz.lab
ASA-PERIM(config-group-policy)#  webvpn
ASA-PERIM(config-group-webvpn)#   anyconnect keep-installer installed
ASA-PERIM(config-group-webvpn)#   anyconnect ssl dtls enable
ASA-PERIM(config-group-webvpn)#   anyconnect ssl rekey time 60
ASA-PERIM(config-group-webvpn)#   anyconnect ssl rekey method ssl
ASA-PERIM(config-group-webvpn)#   anyconnect dpd-interval client 30
ASA-PERIM(config-group-webvpn)#   anyconnect dpd-interval gateway 30
ASA-PERIM(config-group-webvpn)#  exit

A few of those lines deserve called-out attention:

  • vpn-tunnel-protocol ssl-client: this group-policy only permits the SSL client. If the user attempts to use IKEv2, the connection is rejected. A separate group-policy for IKEv2 lets you keep the two profiles cleanly separated. We cover IKEv2 client setup in Cisco ASA AnyConnect IKEv2 VPN Configuration.
  • split-tunnel-policy tunnelspecified: only traffic destined to the networks in the SPLIT-TUNNEL ACL is sent through the VPN. Everything else (Netflix, Google, the public internet) goes direct. The other choices are tunnelall (all traffic via VPN) and excludespecified. Full details in Cisco ASA Split Tunneling Explained.
  • anyconnect ssl dtls enable: the client opens a UDP DTLS channel in parallel to the TCP channel. UDP is much faster for video and large transfers because TCP-over-TCP gets bogged down in nested retransmits.
  • anyconnect dpd-interval: dead-peer-detection. Both sides probe every 30 seconds; if 4 misses, the tunnel is torn down and the client reconnects.

Step 4: Tunnel-Group (Connection Profile)

The tunnel-group is the named profile clients select. It binds together address pool, group-policy, AAA servers, and (optionally) a custom URL or display alias.

ASA-PERIM(config)# tunnel-group SSL_PROFILE type remote-access
ASA-PERIM(config)# tunnel-group SSL_PROFILE general-attributes
ASA-PERIM(config-tunnel-general)#  default-group-policy ANYCONNECT-SSL-GP
ASA-PERIM(config-tunnel-general)#  address-pool VPN-POOL
ASA-PERIM(config-tunnel-general)#  authentication-server-group RADIUS-VPN LOCAL
ASA-PERIM(config-tunnel-general)#  exit
ASA-PERIM(config)# tunnel-group SSL_PROFILE webvpn-attributes
ASA-PERIM(config-tunnel-webvpn)#  group-alias EMPLOYEES enable
ASA-PERIM(config-tunnel-webvpn)#  group-url https://203.0.113.2/employees enable
ASA-PERIM(config-tunnel-webvpn)#  exit

The authentication-server-group RADIUS-VPN LOCAL line authenticates against RADIUS first, falling back to the local username database if RADIUS is unreachable. We covered the AAA server setup in detail in Cisco ASA AAA for VPN: LDAP, RADIUS, and TACACS+.

The two webvpn-attribute lines control how clients see the connection profile:

  • group-alias EMPLOYEES enable: the dropdown at https://203.0.113.2/ shows "EMPLOYEES" as a selectable profile.
  • group-url https://203.0.113.2/employees enable: clients hitting that exact URL automatically land on this profile without choosing.

A common pattern is to publish a different group-url for each org unit (employees, contractors, partners). Each maps to a different group-policy with a different split-tunnel ACL and a different banner.

Step 5: Enable WebVPN and AnyConnect Globally

Even with everything above configured, no SSL listener is running on the outside interface until you enable WebVPN. This is the global service that owns the TLS endpoint on TCP/443.

ASA-PERIM(config)# webvpn
ASA-PERIM(config-webvpn)#  enable outside
INFO: WebVPN and DTLS are enabled on 'outside'.
ASA-PERIM(config-webvpn)#  anyconnect enable
WARNING: No 'anyconnect image' commands have been issued
ASA-PERIM(config-webvpn)#  tunnel-group-list enable
ASA-PERIM(config-webvpn)#  exit

The warning about no anyconnect image command means the client package upload step is missing. In production you would copy the .pkg to flash and reference it:

ASA-PERIM(config-webvpn)# anyconnect image flash:/cisco-secure-client-win-5.1.10.233-webdeploy-k9.pkg 1

Without the image, browsers can land on the portal page but the AnyConnect client cannot auto-download. In our lab we leave it absent because we do not have a real client image, and we are exercising the configuration plane only. The control plane is fully functional regardless of whether the .pkg is present, which is what matters for verifying the config is correct.

Verification: Did Everything Take?

Three quick sanity-check commands:

ASA-PERIM# show ssl
Accept connections using SSLv3 or greater and negotiate to TLSv1.2 or greater
Start connections using TLSv1.2 and negotiate to TLSv1.2 or greater
SSL DH Group: group14 (2048-bit modulus, FIPS)
SSL ECDH Group: group19 (256-bit EC)

SSL trust-points:
  Self-signed (RSA 2048 bits RSA-SHA256) certificate available
  Self-signed (EC 256 bits ecdsa-with-SHA256) certificate available
  Interface outside: PINGLABZ-SELFSIGNED (RSA 2048 bits RSA-SHA256)
Certificate authentication is not enabled

ASA-PERIM# show webvpn anyconnect
AnyConnect Client is enabled. No images configured

ASA-PERIM# show vpn-sessiondb anyconnect
INFO: There are presently no active sessions of the type specified

The first output confirms our trustpoint is bound to the outside interface. The second confirms AnyConnect is on but no client image is uploaded (the lab constraint). The third just shows there are no active sessions, which is the expected state until a client connects.

Once a real client connects, show vpn-sessiondb anyconnect displays the session, the assigned IP from the pool, the encryption suite, the bytes transferred, and the duration. show vpn-sessiondb summary rolls those stats up for capacity planning.

Common Gotchas

The five mistakes that catch every engineer once:

  1. Trustpoint bound to the wrong interface. If ssl trust-point PINGLABZ-SELFSIGNED outside is missing or points at the inside, clients see the default ASA self-signed cert and warn about a CN mismatch. Always bind the trustpoint to the interface the clients will hit.
  2. NAT exemption not configured for the VPN pool. If 10.99.99.0/24 traffic to the inside subnet gets PAT-translated by your existing dynamic PAT rule, clients connect successfully but cannot reach inside resources because the return traffic goes to the wrong source. Fix with an identity NAT rule in Section 1. Full walkthrough in Cisco ASA Identity NAT / NAT Exemption for VPNs.
  3. Split-tunnel ACL is extended instead of standard. The split-tunnel-network-list attribute requires a standard ACL. Pasting an extended ACL silently fails to apply, and the client gets tunnelall behavior by default.
  4. Group-policy default-domain or DNS missing. Clients connect but cannot resolve fileserver.pinglabz.lab short names. Always set dns-server and default-domain in the group-policy.
  5. Outside ACL blocking TCP/443 from the internet. If your OUTSIDE_IN ACL has an explicit deny near the top, the SSL handshake never reaches the WebVPN service. Check with packet-tracer to see if the ACL phase is dropping the SYN.

Key Takeaways

AnyConnect SSL VPN on Cisco ASA breaks down into five building blocks: the identity certificate, the IP local pool, the group-policy, the tunnel-group connection profile, and the WebVPN service. Build them in that order and each step has the dependencies it needs. The trickiest piece for first-time setups is binding the trustpoint to the right interface and remembering to add NAT exemption for the VPN pool, both of which produce silent failures rather than clear errors.

For the full Cisco ASA reference, including site-to-site IPsec, NAT, ACLs, failover, and troubleshooting tools, see the Cisco ASA pillar. When connections fail, start with Troubleshoot AnyConnect Login and Certificate Problems on ASA; for the ACL side, Cisco ASA ACL Troubleshooting with packet-tracer covers the most common drops.

Written by
More from Ping Labz
Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to Ping Labz.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.