Tailscale, it was great to link all my machines—laptops, servers, Raspberry Pis, containers, and exit nodes—into one private network. As the number of devices grew, I needed a simple way to keep things secure and easy to manage. That’s why I turned to Tailscale’s Access Control Lists (ACLs) and tags.
By default, Tailscale lets any node talk to any other node. That works for a few devices, but when I added services and shared with others, I wanted:
Tags let me label devices—server
, container
, exit-node
, rdp-client
, and so on—so each machine has a clear role. Then ACLs use those tags to allow only the right traffic. This makes it easy to understand exactly what each device is allowed to do, simplifying troubleshooting and improving security auditing.
My policy file lives in a private Git repository. At the top, I set up my admin group, tag owners, and host names:
{
"groups": {
"group:root": [ /* Admin group with full access */ ]
},
"tagOwners": {
/* All device role tags are owned and assigned by the admin group */
},
"hosts": {
/* Static hostnames mapped to internal Tailscale IP addresses */
"arkg15.perch-map.ts.net": "fd7a:115c:a1e0::cafe:babe",
"arklab.perch-map.ts.net": "fd7a:115c:a1e0::dead:beef",
"arklab-wsl.perch-map.ts.net": "fd7a:115c:a1e0::face:feed"
},
"acls": [ /* Access control rules defined below */ ],
"ssh": [ /* SSH access rules using Tailscale SSH */ ],
"nodeAttrs": [
/* Node attributes like Taildrive and Funnel capabilities */
],
"grants": [
/* Permissions for Web UI and Drive access */
]
}
Core Servers
tag:server
Containers & Labs
tag:container
, tag:arklab
Remote Desktop
tag:rdp-client
, tag:rdp-server
Exit Nodes & DNS
tag:exit-node
, tag:dns-server
, tag:dns-client
, tag:doh-client
, tag:dot-client
When I add a new device, I tag it immediately—either via the admin console or an auth key that tags devices automatically. This reduces the risk of misconfiguration and ensures consistent security policies.
Here’s the core of my ACL setup:
"acls": [
{
"action": "accept",
"src": ["group:root"],
"dst": ["*:*"],
},
{
"action": "accept",
"src": ["group:root", "tag:rdp-client"],
"dst": ["tag:rdp-server:3389"],
},
{
"action": "accept",
"src": ["arklab.perch-map.ts.net"],
"dst": ["arkg15.perch-map.ts.net:3389"],
},
{
"action": "accept",
"src": ["group:root", "tag:exit-node-user"],
"dst": ["autogroup:internet:*"],
},
{
"action": "accept",
"src": ["group:root", "tag:dns-client"],
"dst": ["tag:dns-server:53"],
},
{
"action": "accept",
"src": ["group:root", "tag:doh-client"],
"dst": ["tag:dns-server:443", "tag:dns-server:60443"],
},
{
"action": "accept",
"src": ["group:root", "tag:dot-client"],
"dst": ["tag:dns-server:853", "tag:dns-server:60853"],
},
]
arklab.perch-map.ts.net
) has a special RDP rule for my Windows laptop.Each rule clearly defines allowed interactions. By explicitly stating ports and allowed sources, I can quickly identify unnecessary permissions or diagnose connectivity issues. This granular control minimizes potential vulnerabilities due to overly permissive access.
I use Tailscale SSH instead of manually managing SSH keys. Here’s my SSH setup:
"ssh": [
{
"action": "check",
"src": ["group:root"],
"dst": ["autogroup:self"],
"users": ["autogroup:nonroot", "root"],
"checkPeriod": "72h",
},
{
"action": "accept",
"src": ["group:root"],
"dst": ["tag:device", "tag:server", "tag:container"],
"users": ["autogroup:nonroot"],
},
{
"action": "check",
"src": ["group:root"],
"dst": ["tag:device", "tag:server", "tag:container"],
"users": ["root"],
"checkPeriod": "72h",
},
{
"action": "accept",
"src": ["group:root", "tag:tailscale-ssh-client"],
"dst": ["tag:tailscale-ssh-server"],
"users": ["autogroup:nonroot", "root"],
},
]
Now running ssh [email protected]
just works—Tailscale checks the policy, lets me in, and there is no need for manually configuring a list of authorized_keys
anymore, as the burden of authentication and authorization falls to Tailscale.
Using tags and ACLs has made my tailnet safe and easy to manage. Give it a try—you’ll save time and avoid surprises when you add new devices or share access with others.