cmd/tailscale: add id-token subcommand

RELNOTE=Initial support for getting OIDC ID Tokens

Updates tailscale/corp#4347

Signed-off-by: Maisem Ali <maisem@tailscale.com>
This commit is contained in:
Maisem Ali 2022-04-12 11:57:46 -07:00 committed by Maisem Ali
parent 3ae701f0eb
commit c87ed52ad4
4 changed files with 54 additions and 5 deletions

View File

@ -275,6 +275,21 @@ func status(ctx context.Context, queryString string) (*ipnstate.Status, error) {
return st, nil return st, nil
} }
// IDToken is a request to get an OIDC ID token for an audience.
// The token can be presented to any resource provider which offers OIDC
// Federation.
func IDToken(ctx context.Context, aud string) (*tailcfg.TokenResponse, error) {
body, err := get200(ctx, "/localapi/v0/id-token?aud="+url.QueryEscape(aud))
if err != nil {
return nil, err
}
tr := new(tailcfg.TokenResponse)
if err := json.Unmarshal(body, tr); err != nil {
return nil, err
}
return tr, nil
}
func WaitingFiles(ctx context.Context) ([]apitype.WaitingFile, error) { func WaitingFiles(ctx context.Context) ([]apitype.WaitingFile, error) {
body, err := get200(ctx, "/localapi/v0/files/") body, err := get200(ctx, "/localapi/v0/files/")
if err != nil { if err != nil {

View File

@ -25,6 +25,7 @@
"github.com/peterbourgon/ff/v3/ffcli" "github.com/peterbourgon/ff/v3/ffcli"
"tailscale.com/client/tailscale" "tailscale.com/client/tailscale"
"tailscale.com/envknob"
"tailscale.com/ipn" "tailscale.com/ipn"
"tailscale.com/paths" "tailscale.com/paths"
"tailscale.com/safesocket" "tailscale.com/safesocket"
@ -173,6 +174,9 @@ func Run(args []string) (err error) {
for _, c := range rootCmd.Subcommands { for _, c := range rootCmd.Subcommands {
c.UsageFunc = usageFunc c.UsageFunc = usageFunc
} }
if envknob.UseWIPCode() {
rootCmd.Subcommands = append(rootCmd.Subcommands, idTokenCmd)
}
// Don't advertise the debug command, but it exists. // Don't advertise the debug command, but it exists.
if strSliceContains(args, "debug") { if strSliceContains(args, "debug") {

View File

@ -0,0 +1,35 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cli
import (
"context"
"errors"
"fmt"
"github.com/peterbourgon/ff/v3/ffcli"
"tailscale.com/client/tailscale"
)
var idTokenCmd = &ffcli.Command{
Name: "id-token",
ShortUsage: "id-token <aud>",
ShortHelp: "fetch an OIDC id-token for the Tailscale machine",
Exec: runIDToken,
}
func runIDToken(ctx context.Context, args []string) error {
if len(args) != 1 {
return errors.New("usage: id-token <aud>")
}
tr, err := tailscale.IDToken(ctx, args[0])
if err != nil {
return err
}
fmt.Println(tr.IDToken)
return nil
}

View File

@ -25,7 +25,6 @@
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/client/tailscale/apitype" "tailscale.com/client/tailscale/apitype"
"tailscale.com/envknob"
"tailscale.com/ipn" "tailscale.com/ipn"
"tailscale.com/ipn/ipnlocal" "tailscale.com/ipn/ipnlocal"
"tailscale.com/ipn/ipnstate" "tailscale.com/ipn/ipnstate"
@ -145,10 +144,6 @@ func (h *Handler) serveIDToken(w http.ResponseWriter, r *http.Request) {
http.Error(w, "id-token access denied", http.StatusForbidden) http.Error(w, "id-token access denied", http.StatusForbidden)
return return
} }
if !envknob.UseWIPCode() {
http.Error(w, "id-token access denied", http.StatusServiceUnavailable)
return
}
nm := h.b.NetMap() nm := h.b.NetMap()
if nm == nil { if nm == nil {
http.Error(w, "no netmap", http.StatusServiceUnavailable) http.Error(w, "no netmap", http.StatusServiceUnavailable)