Tailscale. A VPN you forget is even there.


There’s a two-part series on this blog from 2017 about setting up Cisco ASA with FreeRADIUS and two-factor authentication. The respectful suggestion in 2026 is: don’t do any of that anymore.

Specifically, if you are a small team or an individual and you need a VPN, you almost certainly do not need to operate a VPN gateway. The whole category got reinvented around WireGuard, and the practical face of it for most people is Tailscale.

What Tailscale is

Tailscale is a control plane on top of WireGuard. WireGuard itself is a kernel module (on Linux) or a userspace library, and a tiny, beautiful protocol. The hard part of WireGuard, historically, has been key distribution, peer discovery, and NAT traversal. Tailscale handles those.

You install the client on every device you own — laptop, server, phone, NAS, whatever — log into a SSO provider, and they all show up as machines on a private network with stable IPs in the 100.64/10 range. Connections between any two of those machines go peer-to-peer over WireGuard, with the Tailscale coordination server only used for handshake.

The user experience is: ssh prod-bastion and you’re in. There’s no VPN client to start, no tunnel to bring up, no kill switch toggle. Tailscale is running in the background. That is the whole product.

Setting it up on a server

For comparison, the 2017 Cisco posts were about 3,000 words of config between them. Here is the 2026 equivalent for a Linux server:

$ curl -fsSL https://tailscale.com/install.sh | sh
$ sudo tailscale up --ssh

That is it. The --ssh flag opts the host into Tailscale SSH, which uses your Tailscale identity instead of static SSH keys. ACLs are configured centrally and applied across all your machines.

For an unattended server you want non-interactive setup, which uses an auth key:

$ sudo tailscale up \
    --authkey=tskey-auth-… \
    --hostname=prod-bastion \
    --ssh \
    --advertise-tags=tag:server

Auth keys can be ephemeral (the machine disappears from the tailnet when it’s offline for a while), reusable, or one-shot. For ECS / Kubernetes workloads I use ephemeral, tagged keys generated from Terraform.

ACLs are actually good

The ACL file is a single JSON / HuJSON document. It is short, readable, and lives in version control. A starter that covers most teams:

{
  "tagOwners": {
    "tag:server":  ["group:admins"],
    "tag:db":      ["group:admins"],
    "tag:dev":     ["autogroup:member"]
  },
  "groups": {
    "group:admins": ["artem@example.com"]
  },
  "acls": [
    // everyone can talk to dev boxes
    { "action": "accept", "src": ["autogroup:member"], "dst": ["tag:dev:*"] },
    // only admins can talk to db boxes
    { "action": "accept", "src": ["group:admins"],     "dst": ["tag:db:*"]  },
    // server-to-server within the tailnet
    { "action": "accept", "src": ["tag:server"],       "dst": ["tag:server:*"] }
  ],
  "ssh": [
    { "action": "check", "src": ["autogroup:member"], "dst": ["autogroup:self"], "users": ["autogroup:nonroot"] }
  ]
}

The "check" action on the SSH rule means the connection still requires you to re-authenticate via the IdP — basically a soft 2FA prompt — before SSH lets you in. The first time you see this in production it feels like cheating.

What about my private network?

If you have a VPC or an on-prem subnet that you want reachable from the tailnet, you put a Tailscale client on one box in that subnet and tell it to advertise the routes. The Cisco ASA equivalent of this used to be a series of routing tables and IPSec phase-2 selectors. Now it is one flag:

$ sudo tailscale up \
    --advertise-routes=10.0.0.0/16 \
    --ssh

Then in the admin console you approve the advertised route once. Done.

The honest trade-offs

That is the recommendation. There is no good reason to run a corporate VPN gateway by hand in 2026 unless something specific forces your hand. The 2017 Cisco posts are preserved here for historical interest — and as a reminder that the right answer to a problem changes if you wait long enough.