WireGuard + Headscale → Cisco AnyConnect
WireGuard + Headscale ↔ Cisco AnyConnect: integration to migration path.
Remote access is load-bearing — break the tunnel and your whole workforce is locked out of corp at once. So WireGuard + Headscale goes live behind Cisco AnyConnect first, both clients running side by side while AnyConnect keeps owning every route, and only takes over one cohort's CIDR at a time. No flag day, no forced re-credentialing, and every phase rolls back in minutes.
The honest end state is often partial: AnyConnect is not a tunnel alone — it is a tunnel plus a posture engine plus an inspection point. SmartTunnel apps and HostScan's at-session posture have no clean OSS equal, so a minimal AnyConnect tier may stay. We name that gap up front, because the rest of this only matters if you can trust it.
The idea
Run behind AnyConnect first. Retire it route by route last.
The topology that makes this zero-downtime is parallel tunnels with cohort routing. Both clients install on the endpoint; AnyConnect keeps its existing split-include CIDRs against the ASA while a Headscale namespace advertises only the cohort's CIDR set through WireGuard's AllowedIPs cryptokey routing — with the ASA's split-exclude dropping exactly that CIDR the moment the cohort moves. Both halves federate to the same IdP, so one group claim flips a user across both sides at once. Cohort routes hit WireGuard; everything else stays on AnyConnect — each route migrated independently, each reversible, never a single big cutover.
The phases
Seven steps. Each one reversible.
Baseline & inventory
We read every ASA/FTD headend: per-user group-policy and tunnel-group, the full split-include and split-exclude ACL set, the SmartTunnel application list, every HostScan check, AMP integration scope, and your CGNAT-versus-public-IP distribution. Read-only — nothing moves.
Headscale goes live behind AnyConnect
Headscale stands up in HA — two control-plane nodes, shared Postgres, two self-hosted DERP relays — and federates to the same IdP that backs the ASA's SAML tunnel-group. A canary cohort of internal IT installs WireGuard alongside AnyConnect, reaching only a synthetic test CIDR. No production CIDR moves.
New routes land on WireGuard
Every net-new internal CIDR — new dev VPCs, new prod accounts — is advertised only via Headscale and explicitly added to AnyConnect's split-exclude so the ASA never offers it. Your existing AnyConnect routes are untouched.
Existing cohorts move, route by route
Each cohort's CIDR is advertised in Headscale, pushed to a 10% slice while AnyConnect's split-exclude removes it for that slice, SIEM-watched for stray legacy traffic, then ramped to 100% and soaked seven days before it leaves AnyConnect's split-include. Waves run low to high blast radius. The IdP still owns credentials throughout.
Replace posture and the inspection point
Posture moves to osquery feeding a signed IdP claim that gates Headscale ACL membership (EDR present, disk encrypted, patch current). WireGuard egress either routes through a corp NGFW or carries an explicit DLP-tolerance decision in the SSP. AMP/Secure Endpoint keeps running as an EDR agent, decoupled from any tunnel.
Shrink AnyConnect to SmartTunnel residual
AnyConnect is trimmed to the SmartTunnel apps that genuinely need a per-app TCP shim, plus the long-tail. Path A downgrades the licence to the minimum SmartTunnel-capable tier and consolidates the headend; Path B converts SmartTunnel apps to native routes and uninstalls AnyConnect in waves, holding the ASA failover-only for 30 days.
Decommission (Path B only)
AnyConnect is uninstalled fleet-wide, ASA/FTD headends are archived or repurposed, and the SAML tunnel-group SP entry is removed from the IdP. The Cisco contract terminates at next renewal.
Feature parity
Where WireGuard + Headscale matches AnyConnect, and where it does not.
| Capability | WireGuard + Headscale | Cisco AnyConnect | Parity |
|---|---|---|---|
| Tunnel / transport | WireGuard Noise IK, fixed ChaCha20-Poly1305 + Curve25519 + BLAKE2s | TLS 1.2/1.3 + DTLS or IKEv2/ESP, negotiated suite | At parity |
| Identity federation | OIDC PKCE to namespace, allowed-groups claim | SAML tunnel-group to group-policy | At parity |
| ACL policy model | HuJSON ACL (groups, acls, tagOwners, autoApprovers) via MapResponse | ASA group-policy + DAP + tunnel-group attribute maps | At parity |
| Cohort / split routing | Per-node AllowedIPs + ACL src/dst | split-tunnel-policy include/exclude ACL | At parity |
| Device posture | osquery to IdP claim to ACL posture tag (episodic, minutes) | HostScan at session-establish, DAP-gated | Partial |
| NAT traversal / relay | PersistentKeepalive=25 + self-hosted DERP TCP/443 fallback | DTLS rekey; the ASA is the only relay | At parity |
| Per-app TCP shim | None — native L4 routing only | SmartTunnel (per-app TCP proxy through the browser) | SaaS only |
| EDR tunnel pivot | None — EDR runs decoupled from the tunnel | AMP / Secure Endpoint terminates the VPN session on detection | SaaS only |
| NGFW headend inspection | None — the endpoint is not an inspection point | ASA/FTD egress inspection + Umbrella URL filtering | SaaS only |
| DNS | Headscale MagicDNS / extra_records / split DNS | ASA DNS-server push per group-policy | At parity |
| RBAC / admin | Headscale CLI/API, HuJSON in git | ASDM/FMC GUI | Partial |
| Deployment & HA | Self-hosted 2+ control nodes + Postgres + DERP, your on-call | Vendor headend + 24x7 TAC | SaaS only |
| Cost model | Headscale free; compute + ops | Per-seat Plus/Apex + ASA/FTD hardware | Partial |
| Compliance | Boundary inside your SSP; WG FIPS 140-3 status to confirm | Inherited SOC 2 / FedRAMP; FIPS-mode AnyConnect available | SaaS only |
What we're honest about
The caveats most vendors leave out.
HostScan posture has no first-party OSS equal
AnyConnect checks posture at session establish and gates the tunnel through Dynamic Access Policy. The honest OSS substitute is osquery feeding a signed IdP claim that Headscale gates an ACL on — periodic, not at-session-establish. For a five-minute osquery interval and a one-hour token TTL, worst-case revocation is about an hour. We document that latency window in the SSP and either accept or compensate for it; we do not claim parity with HostScan's enforcement, because it isn't.
AMP tunnel-session pivot and SmartTunnel don't port
"Terminate the VPN session on AMP detection" is a Cisco feature WireGuard has no equivalent for — AMP still runs on the endpoint, it just can't kill a WireGuard session on its own. SmartTunnel's per-app TCP shim is the same story: apps that only work by proxying TCP through the browser have nothing for native routing to inherit. We flag every SmartTunnel app in inventory, by app not by user, and keep a minimal AnyConnect tier for the residual where that's the honest call.
No NGFW headend inspection, FedRAMP, or 24x7 TAC
The ASA/FTD was an inspection point with Umbrella URL filtering; a WireGuard endpoint is not. Cisco's SOC 2 / FedRAMP inheritance and 24x7 TAC for the tunnel control plane go with it. We either route WireGuard egress through a corp NGFW or document the gap, keep AnyConnect for any FedRAMP-scoped users, and check WireGuard's FIPS 140-3 status against your auditor before topology 1.4 — if a validated module is mandatory and WG isn't accepted, we stop at the SmartTunnel-residual end state with FIPS-mode AnyConnect on it.
Self-hosting means you own the control plane uptime
Once AnyConnect is gone, Headscale being down means no new tunnel sessions — existing sessions keep working, but there is no vendor backstop. That is exactly why we run it as a managed service: two-plus control-plane nodes behind a load balancer, shared Postgres, two-plus self-hosted DERP relays in separated regions, a documented break-glass admin key, and a DR runbook with a tested realm-restore and a measured RTO. Managed, not just installed.
Why this beats a flag day
Reversible at every step, with a real soak before anything is cut.
Every phase rolls back in under 15 minutes while both clients remain installed — a split-tunnel toggle on the ASA or a Headscale ACL revert, never a rebuild. And before the Cisco contract is cancelled, each migrated cohort soaks at least 30 consecutive days at 100% WireGuard carry with zero stray legacy-tunnel traffic to the migrated CIDRs, and the DR realm-restore is exercised end to end. A flag day gives you neither; this gives you both.
See whether your remote access migrates cleanly.
A 30-minute call with a senior network engineer. We map your routes by cohort, count the SmartTunnel apps that can't move, classify every HostScan check as replicate, replace, or accept-the-gap, and tell you honestly whether your end state is full retirement or a minimal AnyConnect tier — before you commit to anything.
Map my migration →