Skip to content

Doc: tailscale machine auth via id-token #14

Description

@jtcressy

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions