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.

0

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.

Users see: No user impact.

Rollback: N/A

1

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.

Users see: None for production users. The canary sees a new tray icon and reaches the test subnet over WireGuard.

Rollback: Uninstall WireGuard from the canary and remove Headscale — no production route trusts it. Under 15 minutes.

2

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.

Users see: None for production users; new-cohort users see WireGuard as the only path to the new subnets.

Rollback: Per CIDR: remove from the Headscale ACL and the AnyConnect split-exclude. Under 15 minutes.

3

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.

Users see: None — only which adapter carries the route changes. The tray icon shows WireGuard for migrated CIDRs.

Rollback: Per CIDR: revert the AnyConnect split-exclude, remove from the Headscale ACL. Under 15 minutes while both clients are installed.

4

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.

Users see: None on the happy path. Non-compliant devices lose access to gated CIDRs — that is the point; communicated at least 30 days ahead.

Rollback: Swap the Headscale ACL from posture groups back to plain tags. Under 15 minutes.

5

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.

Users see: Path A: most users uninstall AnyConnect; SmartTunnel users keep it. Path B: fleet-wide uninstall.

Rollback: Re-enable the group-policies and split-include ACLs. Under 15 minutes within the evidence window.

6

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.

Users see: None for app sign-in.

Rollback: Past the 30-day evidence window, rollback is no longer in scope — the prior phases are the reversible ones.

Feature parity

Where WireGuard + Headscale matches AnyConnect, and where it does not.

CapabilityWireGuard + HeadscaleCisco AnyConnectParity
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 →