|
|
@ -20,6 +20,7 @@ import (
|
|
|
|
"github.com/peterbourgon/ff/v3/ffcli"
|
|
|
|
"github.com/peterbourgon/ff/v3/ffcli"
|
|
|
|
"tailscale.com/ipn/ipnstate"
|
|
|
|
"tailscale.com/ipn/ipnstate"
|
|
|
|
"tailscale.com/tka"
|
|
|
|
"tailscale.com/tka"
|
|
|
|
|
|
|
|
"tailscale.com/tsconst"
|
|
|
|
"tailscale.com/types/key"
|
|
|
|
"tailscale.com/types/key"
|
|
|
|
"tailscale.com/types/tkatype"
|
|
|
|
"tailscale.com/types/tkatype"
|
|
|
|
)
|
|
|
|
)
|
|
|
@ -443,15 +444,33 @@ func runNetworkLockModify(ctx context.Context, addArgs, removeArgs []string) err
|
|
|
|
|
|
|
|
|
|
|
|
var nlSignCmd = &ffcli.Command{
|
|
|
|
var nlSignCmd = &ffcli.Command{
|
|
|
|
Name: "sign",
|
|
|
|
Name: "sign",
|
|
|
|
ShortUsage: "tailscale lock sign <node-key> [<rotation-key>] or sign <auth-key>",
|
|
|
|
ShortUsage: "tailscale lock sign <node-key> [<rotation-key>]\ntailscale lock sign <auth-key>",
|
|
|
|
ShortHelp: "Signs a node or pre-approved auth key",
|
|
|
|
ShortHelp: "Signs a node or pre-approved auth key",
|
|
|
|
LongHelp: `Either:
|
|
|
|
LongHelp: `Either:
|
|
|
|
- signs a node key and transmits the signature to the coordination server, or
|
|
|
|
- signs a node key and transmits the signature to the coordination
|
|
|
|
- signs a pre-approved auth key, printing it in a form that can be used to bring up nodes under tailnet lock`,
|
|
|
|
server, or
|
|
|
|
|
|
|
|
- signs a pre-approved auth key, printing it in a form that can be
|
|
|
|
|
|
|
|
used to bring up nodes under tailnet lock
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
If any of the key arguments begin with "file:", the key is retrieved from
|
|
|
|
|
|
|
|
the file at the path specified in the argument suffix.`,
|
|
|
|
Exec: runNetworkLockSign,
|
|
|
|
Exec: runNetworkLockSign,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func runNetworkLockSign(ctx context.Context, args []string) error {
|
|
|
|
func runNetworkLockSign(ctx context.Context, args []string) error {
|
|
|
|
|
|
|
|
// If any of the arguments start with "file:", replace that argument
|
|
|
|
|
|
|
|
// with the contents of the file. We do this early, before the check
|
|
|
|
|
|
|
|
// to see if the first argument is an auth key.
|
|
|
|
|
|
|
|
for i, arg := range args {
|
|
|
|
|
|
|
|
if filename, ok := strings.CutPrefix(arg, "file:"); ok {
|
|
|
|
|
|
|
|
b, err := os.ReadFile(filename)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
args[i] = strings.TrimSpace(string(b))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if len(args) > 0 && strings.HasPrefix(args[0], "tskey-auth-") {
|
|
|
|
if len(args) > 0 && strings.HasPrefix(args[0], "tskey-auth-") {
|
|
|
|
return runTskeyWrapCmd(ctx, args)
|
|
|
|
return runTskeyWrapCmd(ctx, args)
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -476,7 +495,7 @@ func runNetworkLockSign(ctx context.Context, args []string) error {
|
|
|
|
err := localClient.NetworkLockSign(ctx, nodeKey, []byte(rotationKey.Verifier()))
|
|
|
|
err := localClient.NetworkLockSign(ctx, nodeKey, []byte(rotationKey.Verifier()))
|
|
|
|
// Provide a better help message for when someone clicks through the signing flow
|
|
|
|
// Provide a better help message for when someone clicks through the signing flow
|
|
|
|
// on the wrong device.
|
|
|
|
// on the wrong device.
|
|
|
|
if err != nil && strings.Contains(err.Error(), "this node is not trusted by network lock") {
|
|
|
|
if err != nil && strings.Contains(err.Error(), tsconst.TailnetLockNotTrustedMsg) {
|
|
|
|
fmt.Fprintln(Stderr, "Error: Signing is not available on this device because it does not have a trusted tailnet lock key.")
|
|
|
|
fmt.Fprintln(Stderr, "Error: Signing is not available on this device because it does not have a trusted tailnet lock key.")
|
|
|
|
fmt.Fprintln(Stderr)
|
|
|
|
fmt.Fprintln(Stderr)
|
|
|
|
fmt.Fprintln(Stderr, "Try again on a signing device instead. Tailnet admins can see signing devices on the admin panel.")
|
|
|
|
fmt.Fprintln(Stderr, "Try again on a signing device instead. Tailnet admins can see signing devices on the admin panel.")
|
|
|
|