Tailscale
The NAT instance can optionally join your tailnet as a subnet router (exposing VPC CIDRs to the tailnet) and/or an exit node. It is a natural fit: the instance already forwards traffic, and the same HA/cattle model applies — nodes are disposable and replacements join automatically.
Configuration variables are documented in Configuration; this page covers the recommended tailnet setup and why it works with instance lifecycles.
Recommended key shape
Use a reusable, ephemeral, pre-approved, tagged auth key:
| Property | Why |
|---|---|
| Reusable | ASG-replaced instances present the same key from SSM. A one-time key would fail on the second launch. |
| Ephemeral | Dead nodes are removed from the tailnet automatically shortly after going offline — no offline corpses to clean up in the admin console. |
| Pre-approved | The node itself needs no manual device approval (relevant if your tailnet has device approval enabled). |
Tagged (e.g. tag:nat) |
Enables autoApprovers for routes/exit-node (see below), and tagged nodes have no node-key expiry — long-lived instances never drop off the tailnet at the default 180-day expiry. |
Without tags + autoApprovers, every ASG replacement is a new node whose
advertised routes sit unapproved in the admin console — the tunnel comes
up but no traffic routes through it until a human clicks approve.
Setup
1. Define the tag and auto-approvals in the tailnet policy
In the admin console → Access controls, add to the policy file (adjust CIDRs to the routes your instances advertise):
{
"tagOwners": {
"tag:nat": ["autogroup:admin"],
},
"autoApprovers": {
"routes": {
"10.0.0.0/16": ["tag:nat"],
"10.1.0.0/16": ["tag:nat"],
},
"exitNode": ["tag:nat"],
},
}
autoApprovers means any node joining with tag:nat gets its advertised
routes (and exit-node status) approved automatically — zero-touch
replacements. An advertised route is auto-approved when it equals or is a
subset of an approver entry, so with tailscale_advertise_routes=auto use an
entry broad enough to cover any VPC your instances may land in (e.g.
"10.0.0.0/8": ["tag:nat"]) — a detected CIDR with no matching entry sits
unapproved until a human clicks approve.
2. Create the auth key
Admin console → Settings → Keys → Generate auth key:
- Reusable: on
- Expiration: up to 90 days (see rotation note below)
- Ephemeral: on
- Pre-approved: on
- Tags:
tag:nat
Because the key carries the tag, nodes join tagged automatically — no
--advertise-tags needed in tailscale_extra_args.
Rotation: auth keys expire after at most 90 days. Existing nodes keep running past key expiry (their identity is already established); only new launches fail. Rotate the key into SSM before it expires, or automate rotation via the Tailscale API.
3. Store the key in SSM Parameter Store
aws ssm put-parameter \
--name /pelotech-nat/tailscale-authkey \
--type SecureString \
--value 'tskey-auth-...'
4. Grant the instance profile access
ssm:GetParameteron the parameter (pluskms:Decryptif it uses a customer-managed KMS key)ec2:DescribeTags(only needed for the Name-tag hostname default; skip it if you settailscale_hostnameexplicitly)
5. Configure the instance
# /etc/pelotech-nat.conf
tailscale_enabled=true
tailscale_auth_key_ssm=/pelotech-nat/tailscale-authkey
tailscale_advertise_routes=auto
tailscale_exit_node=true
tailscale_advertise_routes=auto detects the VPC's IPv4 CIDRs (primary +
secondary) from instance metadata at boot — no per-VPC configuration and no
extra IAM permissions. Auto-detection only sees the instance's own VPC; if
the instance should also route to peered VPCs, Transit Gateway attachments,
or on-prem ranges, list all CIDRs explicitly instead.
Instance lifecycle behavior
Restart / reboot. A node's identity (node key, tailnet IP) lives in
/var/lib/tailscale — the auth key is only a join credential. On reboot,
tailscaled reconnects with the saved identity and the boot script skips
tailscale up entirely. Same node, same IP; single-use or expired keys don't
matter here.
Replacement (ASG). A fresh instance has no Tailscale state (the AMI bakes
none), so it presents the auth key and joins as a new node with a new
tailnet IP. The old node goes offline and — with an ephemeral key — is
removed automatically. With autoApprovers, the new node's routes are live
immediately. Hostname collisions (old node still lingering with the same
Name tag) are cosmetic: the new node becomes e.g. pelotech-nat-1 in
MagicDNS until the old entry is garbage-collected.
Multiple instances, one key. Fully supported — a reusable key handed to N instances produces N independent nodes, each with its own identity. Instances advertising the same routes form an HA subnet router set: Tailscale keeps one active per route and fails over to another advertiser within seconds if it goes offline. The same applies to exit nodes (clients pick exit nodes by name, so distinct hostnames help).
Config changes. tailscale up only runs on a node's first login. After
changing tailscale_* variables on a running instance, re-run tailscale up
manually with the new flags (or tailscale logout and restart
pelotech-nat.service). Replacement instances always pick up the current
config.
FIPS consideration
Tailscale's WireGuard tunnel uses ChaCha20-Poly1305, which is not FIPS 140-3 approved; the system-wide FIPS crypto policy still governs TLS, SSH, and other OpenSSL-based protocols. Treat the overlay as an accepted risk if you require strict FIPS-only operation — see the note in Configuration.