The KAGAMI mark КАГАМИ
kagami.bg/academy · lesson · machine-readable viewVERIFIED 2026-10-01 · UPDATED 2026-10-01
IDENTITY
module
KW I11 · Tailscale: subnet router, node sharing, key rotation, ACLs vs grants
series
KAGAMI Way · Track I — Infrastructure
level
Intermediate
duration
~12 min
trust_label
VERIFIED 2026-10-01 (against Tailscale docs) · UPDATED 2026-10-01
language
human view: en · bulgarian edition: /academy/moduli/KW_I11_Tailscale_Patterns.html
prev
KW_I10_Voice_AI_Stack.html
next
KW_I12_Data_Rescue.html · Data rescue
PURPOSE

Four recurring Tailscale patterns: (1) a subnet router that bridges the tailnet to devices that cannot run the client; (2) sharing a machine across accounts and why it does not carry subnet routes; (3) auth-key leakage, expiry and rotation, plus node-key expiry that makes a healthy node look offline; (4) access rules, ACLs versus grants.

KEY CONCEPTS
COMMANDS / PATHS (placeholders, no secrets)
CHECKLIST
NEXT MODULE

KW_I12_Data_Rescue.html · Data rescue · offer: Quick experiment (kagami.bg/stalbata/)

SOURCES
TAGS
tailscalesubnet-routernode-sharingauth-keyskey-rotationaclgrants
VERIFIED · 01.10.2026 UPDATED · 01.10.2026

Tailscale: subnet router, sharing and keys

Tailscale ties your machines into one private network, wherever they are. Four patterns repeat in every real installation: a subnet router to devices without a client, sharing across accounts and its limit, key leakage and rotation, and access rules written as grants.

⏱ ~12 min Intermediate Infrastructure Tailscale · routes · keys
Tailscale🌐 global Linux (sysctl, tailscale CLI)🔒 local

01What you will learn

🔄
Updated 01.10.2026 — what changed
Commands were checked against the current Tailscale documentation: tailscale set for routes, IP forwarding setup, auth-key lifetime (1–90 days), grants as the recommended syntax. Internal node and network names were replaced with neutral examples.

02Before you start

03Steps

  1. Subnet router — a bridge to devices without a client

    ℹ️
    The basics: how nodes are addressed
    Every node gets a stable private address from the 100.64.0.0/10 range (CGNAT, RFC 6598) that does not change when the machine changes networks. Traffic is direct and encrypted (WireGuard); it goes through Tailscale's relay servers (DERP) only when NAT prevents a direct path. In our trials a device without the Tailscale client reached services over HTTP through a subnet router even though ping did not pass ⚠️ our observation.

    Not every device can run Tailscale. A subnet router is an ordinary node of your network that advertises a LAN subnet so the other nodes can reach the devices in it. Three separate things are needed: packet forwarding in the kernel, an approved route, and a rule that allows the traffic.

    bash · on the router
    # 1) packet forwarding (if /etc/sysctl.d exists)
    echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
    echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
    sudo sysctl -p /etc/sysctl.d/99-tailscale.conf
    # 2) advertise the route (use your own subnet)
    sudo tailscale set --advertise-routes=<lan-subnet>

    Then in the console: Machines → the router → Subnets → Edit, tick the route and save (not needed if you set up autoApprovers in the policy). Finally add an access rule (step 4). On Linux clients the routes are only used with this option on:

    bash · on the Linux client
    sudo tailscale set --accept-routes
    ⚠️
    Approval is not an access rule
    Approval puts the route into the clients' tables; the rule decides who may send what. Miss either and packets do not pass. The subnet is behind SNAT by default — devices behind the router see the router as the source of the traffic.
    ℹ️
    If the client has the same subnet
    When the client's own LAN uses the same address range, the local network wins and the route breaks. In our trials a narrower route (/32) for just the target device helped; the documentation confirms that overlapping routes pick the most specific prefix, but the local-collision case itself is ⚠️ not described there — test it in your own setup.
    bash · on the Linux client · /32 route on a collision
    # for the target device only; does not survive a reboot — make it persistent (e.g. a systemd service)
    sudo ip route add <target-address>/32 dev tailscale0
    ⚠️
    A single point of failure
    If one node is subnet router, exit node and connector at once, its failure cuts everything through it. Plan a second router (high availability is described in the documentation) — and with two routers on the same routes, do not turn on --accept-routes on the standby.
  2. Sharing across accounts — and its limit

    You can give a user from another network access to one machine without exposing it to the internet. A shared machine is quarantined: it accepts incoming connections but starts none of its own. Sharing strips tags, groups and subnet information from the recipient's network, and the machine is reached by its full MagicDNS name or address.

    ⛔
    A shared machine does not get your routes
    Neither an access rule nor a narrower route changes this: shared machines do not carry subnets across networks. If a machine from a foreign account must reach a device in your LAN, do not expect a subnet route on it.

    Workaround (our pattern): a relay. Node A in your network sees both the shared machine and the device in the LAN. It calls the shared machine (for example over SSH, a statistics script), reads the result and publishes it to the device or service in the LAN. The foreign machine never needs your routes.

    RoleWhereWhat it does
    Shared machineforeign accountonly answers requests
    Relay (node A)your networkcalls, reads, publishes
    LAN servicebehind the subnet routerreceives the result
    ⚠️
    Tailscale SSH on a foreign machine
    On a machine from a foreign account keep Tailscale SSH off (tailscale set --ssh=false) and use the regular sshd — access to it is decided by the foreign network's policy, not yours.
  3. Keys: leakage, lifetime and rotation

    There are two kinds of keys and they are easy to confuse. The auth key registers a new node without a browser; the node key is how the node identifies itself in the network and by default expires after 180 days.

    KeyLifetimeWhat to watch
    Auth, one-off1–90 days (default 90)Tailscale revokes it after use
    Auth, reusable1–90 daysDangerous if leaked — keep it in a secret store
    Node key180 days by defaultWhen it expires the node looks "offline" though it works locally
    🔑
    If an auth key leaked
    1) Revoke it in the console (Settings → Keys → Revoke). 2) Revoking does not deauthorize nodes already registered with it — delete them from Machines if they are not yours. 3) Issue a new key — tagged, with the shortest workable lifetime; for short-lived jobs tick "Ephemeral". Never put it in a code repository, a chat or a screenshot.
    bash · registering with a key
    # keys are case-sensitive; pass it from a variable, do not type it into shell history
    sudo tailscale up --auth-key="$TS_AUTHKEY"
    ⚠️
    An always-on node that looks "offline"
    If an always-on node is "offline" in the console while its local status is healthy, suspect an expired node key first, not the network. Fix: re-authenticate, or turn off expiry for that node (Machines → the node → key expiry). Tagged devices have expiry off by default; keep phones and laptops with expiry on — a lost device then drops out by itself. In our trial a reboot did not help ⚠️ not confirmed in the documentation. If a connector's key expires (subnet router, exit node), its routes stay but become unreachable — the documentation calls this "fail close".
  4. Access rules: ACLs or grants

    Both are deny by default and live in the network policy. Tailscale recommends grants for all new rules: they give everything ACLs do, and more; ACLs keep working and will not be removed, but get no new features.

    JSON · policy · the same rule
    // grants (recommended) — the group may use port 443 on the subnet
    {"grants":[{"src":["group:<group>"],"dst":["<lan-subnet>"],"ip":["443"]}]}
    
    // ACL (first generation) — the same
    {"acls":[{"action":"accept","src":["group:<group>"],"dst":["<lan-subnet>:443"]}]}
    ℹ️
    Shared users
    For people invited into your network there is a special group, autogroup:shared — you can restrict them (for example to ports 80 and 443 only) without knowing their emails. Be careful with "src": ["*"]: it includes invited users.

04Check

1. Besides advertising the route, what does a Linux subnet router need to work?

2. A machine from a foreign account is shared into your network. Does it receive your subnet routes?

3. An auth key leaked. You revoked it. What else?

4. An always-on node is "offline" in the console but healthy locally. You check first…

05What's next

06Sources

  1. Tailscale: Subnet routers — IP forwarding, tailscale set --advertise-routes, approval, --accept-routes, SNAT, fail close.
  2. Tailscale: Share your machines with other users — quarantine, stripped subnets and tags, autogroup:shared.
  3. Tailscale: Auth keys — one-off and reusable, 1–90 day lifetime, ephemeral, tagged, revoking, 180-day node key.
  4. Tailscale: ACLs — grants as the recommended syntax, ACLs remain.
  5. Tailscale: Grants — grants syntax.
  6. Tailscale: IP addresses — the 100.64.0.0/10 (CGNAT) range and the node's stable address.
  7. KAGAMI's own field notes (the relay pattern, generalised). Commands checked: 01.10.2026.