|
@@ -668,7 +668,7 @@ func runUp(ctx context.Context, cmd string, args []string, upArgs upArgsT) (retE
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return err
|
|
return err
|
|
|
}
|
|
}
|
|
|
- authKey, err = resolveAuthKey(ctx, authKey)
|
|
|
|
|
|
|
+ authKey, err = resolveAuthKey(ctx, authKey, upArgs.advertiseTags)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return err
|
|
return err
|
|
|
}
|
|
}
|
|
@@ -1121,19 +1121,23 @@ func init() {
|
|
|
// resolveAuthKey either returns v unchanged (in the common case) or, if it
|
|
// resolveAuthKey either returns v unchanged (in the common case) or, if it
|
|
|
// starts with "tskey-client-" (as Tailscale OAuth secrets do) parses it like
|
|
// starts with "tskey-client-" (as Tailscale OAuth secrets do) parses it like
|
|
|
//
|
|
//
|
|
|
-// tskey-client-xxxx[?ephemeral=false&tags=foo,bar&preauthorized=BOOL&baseURL=...]
|
|
|
|
|
|
|
+// tskey-client-xxxx[?ephemeral=false&bar&preauthorized=BOOL&baseURL=...]
|
|
|
//
|
|
//
|
|
|
// and does the OAuth2 dance to get and return an authkey. The "ephemeral"
|
|
// and does the OAuth2 dance to get and return an authkey. The "ephemeral"
|
|
|
// property defaults to true if unspecified. The "preauthorized" defaults to
|
|
// property defaults to true if unspecified. The "preauthorized" defaults to
|
|
|
-// false. The "baseURL" defaults to
|
|
|
|
|
-// https://api.tailscale.com.
|
|
|
|
|
-func resolveAuthKey(ctx context.Context, v string) (string, error) {
|
|
|
|
|
|
|
+// false. The "baseURL" defaults to https://api.tailscale.com.
|
|
|
|
|
+// The passed in tags are required, and must be non-empty. These will be
|
|
|
|
|
+// set on the authkey generated by the OAuth2 dance.
|
|
|
|
|
+func resolveAuthKey(ctx context.Context, v, tags string) (string, error) {
|
|
|
if !strings.HasPrefix(v, "tskey-client-") {
|
|
if !strings.HasPrefix(v, "tskey-client-") {
|
|
|
return v, nil
|
|
return v, nil
|
|
|
}
|
|
}
|
|
|
if !envknob.Bool("TS_EXPERIMENT_OAUTH_AUTHKEY") {
|
|
if !envknob.Bool("TS_EXPERIMENT_OAUTH_AUTHKEY") {
|
|
|
return "", errors.New("oauth authkeys are in experimental status")
|
|
return "", errors.New("oauth authkeys are in experimental status")
|
|
|
}
|
|
}
|
|
|
|
|
+ if tags == "" {
|
|
|
|
|
+ return "", errors.New("oauth authkeys require --advertise-tags")
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
clientSecret, named, _ := strings.Cut(v, "?")
|
|
clientSecret, named, _ := strings.Cut(v, "?")
|
|
|
attrs, err := url.ParseQuery(named)
|
|
attrs, err := url.ParseQuery(named)
|
|
@@ -1142,7 +1146,7 @@ func resolveAuthKey(ctx context.Context, v string) (string, error) {
|
|
|
}
|
|
}
|
|
|
for k := range attrs {
|
|
for k := range attrs {
|
|
|
switch k {
|
|
switch k {
|
|
|
- case "ephemeral", "preauthorized", "tags", "baseURL":
|
|
|
|
|
|
|
+ case "ephemeral", "preauthorized", "baseURL":
|
|
|
default:
|
|
default:
|
|
|
return "", fmt.Errorf("unknown attribute %q", k)
|
|
return "", fmt.Errorf("unknown attribute %q", k)
|
|
|
}
|
|
}
|
|
@@ -1166,10 +1170,6 @@ func resolveAuthKey(ctx context.Context, v string) (string, error) {
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return "", err
|
|
return "", err
|
|
|
}
|
|
}
|
|
|
- var tags []string
|
|
|
|
|
- if v := attrs.Get("tags"); v != "" {
|
|
|
|
|
- tags = strings.Split(v, ",")
|
|
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
baseURL := "https://api.tailscale.com"
|
|
baseURL := "https://api.tailscale.com"
|
|
|
if v := attrs.Get("baseURL"); v != "" {
|
|
if v := attrs.Get("baseURL"); v != "" {
|
|
@@ -1193,7 +1193,7 @@ func resolveAuthKey(ctx context.Context, v string) (string, error) {
|
|
|
Reusable: false,
|
|
Reusable: false,
|
|
|
Ephemeral: ephemeral,
|
|
Ephemeral: ephemeral,
|
|
|
Preauthorized: preauth,
|
|
Preauthorized: preauth,
|
|
|
- Tags: tags,
|
|
|
|
|
|
|
+ Tags: strings.Split(tags, ","),
|
|
|
},
|
|
},
|
|
|
},
|
|
},
|
|
|
}
|
|
}
|