Group visibility on Tailscale clients
Last validated:
You can configure devices on your Tailscale network (known as a tailnet) to receive group membership information from the Tailscale control plane. The group information includes both groups defined in the tailnet policy file and groups synchronized from identity providers by using System for Cross-domain Identity Management (SCIM). Once a Tailscale client has been configured to receive group information, that information is available by using the Tailscale CLI, the tsnet library, and LocalAPI.
Requirements
Before you can receive group information in clients, ensure you meet these requirements.
- The
node-attr-visible-groupsfeature flag is enabled for your tailnet. To enable this flag, contact Tailscale Support. - You can edit your tailnet policy file. You need to be an Owner, Admin, or Network admin to edit a tailnet policy file.
- The devices that you want to receive group membership information are running Tailscale v1.97.8 or later.
- (Optional) You can assign a tag to devices that need to receive group membership information.
Use cases
-
Group-specific functionality
Applications can use group membership information to enable or disable features without requiring additional user authorization. For example, you can turn on additional features if a user is in a group for beta testers or in a group for a paid plan.
-
Visibility into group usage
Applications can log group membership as part of their logs and audit trails. This might be useful for adoption analysis, cost tracking, and incident investigation.
How it works
Some applications need information based on Tailscale identity for the connecting user so the application can communicate that information to a user interface (UI) or its own clients. To support this, the Tailscale control plane sends each device the following information if the user has access to the device:
- user name
- user login
- user profile picture URL
By default, the Tailscale control plane does not send group membership information to devices. To enable this, use the node-attr-visible-groups node attribute as an application capability, apply it to a set of devices, and specify which groups those devices have access to.
An application running on a device that contains the node-attr-visible-groups attribute can then retrieve the group membership information from the device by using the tsnet library or LocalAPI.
Update your tailnet policy file
Configure your tailnet policy file to update the Tailscale control plane with which devices can receive group information. You need to make the following updates to your tailnet policy file:
- (Optional) Create a tag to identify which devices will have the capability to receive group membership information.
- Create a node attribute to identity which groups will be visible to the devices.
You need to be an Owner, Admin, or Network admin to edit a tailnet policy file.
(Optional) Create a tag
Create a tag so that you can apply the tag to the devices for which you want to receive group information. For purposes of this example, name the tag prod.
-
Go to the Access controls page of the admin console.
-
If you don't already have a
tagOwnersfield in your tailnet policy file, create it.{"tagOwners": {},} -
Within the
tagOwnersfield, create atagnamedprodand assignautogroup:adminas the tag owner.{ "tagOwners": {"tag:prod": ["autogroup:admin"],}, }This lets any admin of your tailnet assign the
prodtag to devices.
Create the node attribute
Add a node attribute (nodeAttrs) that grants application layer access of the tailscale.com/visible-groups capability for devices that your target.
-
Create a
nodeAttrfield. -
Within the
nodeAttrfield, create atargetfield, and assign its array value. If you created a tag, assign that as the value. Otherwise, assign a set of users, hosts, or IP addresses. -
Also within the
nodeAttrfield, create anappfield. -
Within the
appfield, create atailscale.com/visible-groupsfield, which is the app capability you are going to assign to your devices. -
Within the
tailscale.com/visible-groupsfield, create agroupsarray and assign it the set of groups that you want to make visible to your devices. The set of groups can be a single wildcard entry ["*"] to make all groups visible, or can be a list of groups.The tailnet policy file validation does not check whether any of the groups exist in your tailnet.
Your entry for the node attribute should be similar to:
"nodeAttrs": [
{
"target": ["tag:prod"],
// If not using a tag, use the following format for the target instead:
// "target": ["my-prod-app", "100.123.123.123", ...],
"app": {
"tailscale.com/visible-groups": [
{
// "groups": ["*"] // to match all groups
"groups": [
"group:engineering", # group defined in policy file
"group:sales@example.com", # synced group
"group:support@example.com" # synced group
]
}
]
}
}
]
Verify group visibility using the Tailscale CLI
On a device that has the node-attr-visible-groups node attribute, you can verify group visibility by running the tailscale status command:
tailscale status -json
The output contains a Groups field that lists a user's group membership:
{
[...]
"User": {
"1234567890": {
"ID": 1234567890,
"LoginName": "amelie@example.com",
"DisplayName": "Amelie Pangolin",
"ProfilePicURL": "https://example.com/a/1122334455",
"Groups": [
"group:engineering",
"group:sales@example.com"
]
},
[...]
},
[...]
}
Verify group visibility using tsnet
tsnet is a library that lets you embed Tailscale inside a Go program. You can modify the Hello tsnet example to also report group membership.
[...]
log.Fatal(http.Serve(ln, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
who, err := lc.WhoIs(r.Context(), r.RemoteAddr)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
fmt.Fprintf(w, "<html><body><h1>Hello, world!</h1>\n")
fmt.Fprintf(w, "<p>You are <b>%s</b> from <b>%s</b> (%s)</p>",
html.EscapeString(who.UserProfile.LoginName),
html.EscapeString(who.UserProfile.LoginName),
html.EscapeString(firstLabel(who.Node.ComputedName)),
r.RemoteAddr)
fmt.Fprintf(w, "<p><strong>Your Groups:</strong> %s</p>", strings.Join(who.UserProfile.Groups, ", "))
[...]