Summary
Commit where this was first implemented: 05c4bff
Examples
Example golang code that sets the audience to something vault expects (audience is bound and configurable on vault side)
# cat tsvault.go
package main
import (
"context"
"fmt"
"log"
"tailscale.com/client/tailscale"
)
func main() {
if jwt, err := tailscale.IDToken(context.Background(), "https://vault.jtcressy.net"); err != nil {
log.Fatalf("error getting id-token:", err)
} else {
fmt.Println(jwt.IDToken)
}
}
Example with untagged user devices:
Details
❯ go run tsvault.go | jq -R 'split(".") | .[1] | @base64d | fromjson'
{
"addresses": [
"100.84.118.56/32",
"fd7a:115c:a1e0:ab12:4843:cd96:6254:7638/128"
],
"aud": [
"https://vault.jtcressy.net"
],
"domain": "jtcressy-home.org.github",
"exp": 1653088083,
"iat": 1653087783,
"iss": "https://login.tailscale.com",
"jti": "OblkJxp_vGICu4Hv6rGjtsq97ONsdVIf7tZdX7y0JDo=",
"key": "nodekey:395ab01fe6922f4ee0ce737f7d45f30bb9bf16f095ccc5fd2f252bc53bb42c3f",
"nbf": 1653087783,
"nid": 5415850319480144,
"node": "jcressy-a01.jtcressy-home.org.github",
"sub": "jcressy-a01.jtcressy-home.org.github",
"uid": 18063865002905160,
"user": "jtcressy-home.org.github:jtcressy@github" <--- the user that originally authorized the machine
}
❯ export VAULT_ADDR=https://vault.jtcressy.net
❯ vault write -field=token auth/tailscale/login jwt=$(go run tsvault.go) role=user > ~/.vault-token
❯ vault token lookup
Key Value
--- -----
accessor FnJdzwqUWL5XEPilPjSx3tx4
creation_time 1653087803
creation_ttl 168h
display_name tailscale-jcressy-a01.jtcressy-home.org.github <--- machine hostname
entity_id c492bcc2-06db-0378-d3f8-2c063855142f
expire_time 2022-05-27T23:03:24.20341713Z
explicit_max_ttl 0s
external_namespace_policies map[]
id [REDACTED]
identity_policies [admin-all default] <--- policies are assigned based on who authorized the machine
issue_time 2022-05-20T23:03:24.203430782Z
meta map[nodekey:nodekey:395ab01fe6922f4ee0ce737f7d45f30bb9bf16f095ccc5fd2f252bc53bb42c3f role:user]
num_uses 0
orphan true
path auth/tailscale/login
policies [default]
renewable true
ttl 167h59m57s
type service
Example with tagged infra devices:
Details
❯ go run tsvault.go | jq -R 'split(".") | .[1] | @base64d | fromjson'
{
"addresses": [
"100.84.118.56/32",
"fd7a:115c:a1e0:ab12:4843:cd96:6254:7638/128"
],
"aud": [
"https://vault.jtcressy.net"
],
"domain": "jtcressy-home.org.github",
"exp": 1653087914,
"iat": 1653087614,
"iss": "https://login.tailscale.com",
"jti": "WrvSTUrS-G6M1irfX9Ih-QvJ0uxE2PMgJVp9D-jMQ4A=",
"key": "nodekey:1b9c422bbe3d94fbb2c0c745e9fdeb796cd10062a350282e0ebd17dd46547637",
"nbf": 1653087614,
"nid": 5415850319480144,
"node": "jcressy-a01.jtcressy-home.org.github",
"sub": "jcressy-a01.jtcressy-home.org.github",
"tags": [
"jtcressy-home.org.github:tag:homeudm" <--- assigning machines with tags changes their authorization in vault, no longer assigned to the original user
]
}
❯ export VAULT_ADDR=https://vault.jtcressy.net
❯ vault write -field=token auth/tailscale/login jwt=$(go run tsvault.go) role=tagged > ~/.vault-token
❯ vault token lookup
Key Value
--- -----
accessor s1K2FxLXlT8bfxdJ7EHuF0q9
creation_time 1653087675
creation_ttl 168h
display_name tailscale-jcressy-a01.jtcressy-home.org.github <--- machine hostname
entity_id c492bcc2-06db-0378-d3f8-2c063855142f
expire_time 2022-05-27T23:01:15.254487487Z
explicit_max_ttl 0s
external_namespace_policies map[]
id [REDACTED]
identity_policies [default home-udm_rw terraform-token] <--- policies are assigned based on ACL tag
issue_time 2022-05-20T23:01:15.254498719Z
meta map[nodekey:nodekey:1b9c422bbe3d94fbb2c0c745e9fdeb796cd10062a350282e0ebd17dd46547637 role:tagged]
num_uses 0
orphan true
path auth/tailscale/login
policies [default]
renewable true
ttl 167h59m56s
type service
Notes:
sub and node claims always contain the machine's hostname. This is set as the entity alias in vault after login.
tags and user
- are mutually exclusive, so they must be split between two roles on the auth method
- both are mapped to vault identity groups via aliases
- multiple machines can map to single identity groups based on either the user who authorized the machine or the ACL tag the machine is assigned
- A machine could receive multiple entitlements if they are assigned multiple ACL tags
metadata
- including metadata such as IP addresses are difficult, as vault cannot translate lists into a single string value
- machine public key (aka
nodekey) can be included in metadata for auditing purposes if the machine hostname changes
Summary
Commit where this was first implemented: 05c4bff
Examples
Example golang code that sets the audience to something vault expects (audience is bound and configurable on vault side)
Example with untagged user devices:
Details
Example with tagged infra devices:
Details
Notes:
subandnodeclaims always contain the machine's hostname. This is set as the entity alias in vault after login.tagsandusermetadatanodekey) can be included in metadata for auditing purposes if the machine hostname changes