cmd/tailscale,ipn: implement lock log command

This commit implements `tailscale lock log [--limit N]`, which displays an ordered list
of changes to network-lock state in a manner familiar to `git log`.

Signed-off-by: Tom DNetto <tom@tailscale.com>
This commit is contained in:
Tom DNetto
2022-11-14 15:04:10 -08:00
committed by Tom
parent ed1fae6c73
commit 6708f9a93f
7 changed files with 198 additions and 2 deletions

View File

@@ -7,12 +7,18 @@ package cli
import (
"context"
"encoding/hex"
"encoding/json"
"errors"
"flag"
"fmt"
"os"
"strconv"
"strings"
"github.com/mattn/go-colorable"
"github.com/mattn/go-isatty"
"github.com/peterbourgon/ff/v3/ffcli"
"tailscale.com/ipn/ipnstate"
"tailscale.com/tka"
"tailscale.com/types/key"
)
@@ -29,6 +35,7 @@ var netlockCmd = &ffcli.Command{
nlSignCmd,
nlDisableCmd,
nlDisablementKDFCmd,
nlLogCmd,
},
Exec: runNetworkLockStatus,
}
@@ -282,3 +289,100 @@ func runNetworkLockDisablementKDF(ctx context.Context, args []string) error {
fmt.Printf("disablement:%x\n", tka.DisablementKDF(secret))
return nil
}
var nlLogArgs struct {
limit int
}
var nlLogCmd = &ffcli.Command{
Name: "log",
ShortUsage: "log [--limit N]",
ShortHelp: "List changes applied to network-lock",
Exec: runNetworkLockLog,
FlagSet: (func() *flag.FlagSet {
fs := newFlagSet("lock log")
fs.IntVar(&nlLogArgs.limit, "limit", 50, "max number of updates to list")
return fs
})(),
}
func nlDescribeUpdate(update ipnstate.NetworkLockUpdate, color bool) (string, error) {
terminalYellow := ""
terminalClear := ""
if color {
terminalYellow = "\x1b[33m"
terminalClear = "\x1b[0m"
}
var stanza strings.Builder
printKey := func(key *tka.Key, prefix string) {
fmt.Fprintf(&stanza, "%sType: %s\n", prefix, key.Kind.String())
fmt.Fprintf(&stanza, "%sKeyID: %x\n", prefix, key.ID())
fmt.Fprintf(&stanza, "%sVotes: %d\n", prefix, key.Votes)
if key.Meta != nil {
fmt.Fprintf(&stanza, "%sMetadata: %+v\n", prefix, key.Meta)
}
}
var aum tka.AUM
if err := aum.Unserialize(update.Raw); err != nil {
return "", fmt.Errorf("decoding: %w", err)
}
fmt.Fprintf(&stanza, "%supdate %x (%s)%s\n", terminalYellow, update.Hash, update.Change, terminalClear)
switch update.Change {
case tka.AUMAddKey.String():
printKey(aum.Key, "")
case tka.AUMRemoveKey.String():
fmt.Fprintf(&stanza, "KeyID: %x\n", aum.KeyID)
case tka.AUMUpdateKey.String():
fmt.Fprintf(&stanza, "KeyID: %x\n", aum.KeyID)
if aum.Votes != nil {
fmt.Fprintf(&stanza, "Votes: %d\n", aum.Votes)
}
if aum.Meta != nil {
fmt.Fprintf(&stanza, "Metadata: %+v\n", aum.Meta)
}
case tka.AUMCheckpoint.String():
fmt.Fprintln(&stanza, "Disablement values:")
for _, v := range aum.State.DisablementSecrets {
fmt.Fprintf(&stanza, " - %x\n", v)
}
fmt.Fprintln(&stanza, "Keys:")
for _, k := range aum.State.Keys {
printKey(&k, " ")
}
default:
// Print a JSON encoding of the AUM as a fallback.
e := json.NewEncoder(&stanza)
e.SetIndent("", "\t")
if err := e.Encode(aum); err != nil {
return "", err
}
stanza.WriteRune('\n')
}
return stanza.String(), nil
}
func runNetworkLockLog(ctx context.Context, args []string) error {
updates, err := localClient.NetworkLockLog(ctx, nlLogArgs.limit)
if err != nil {
return fixTailscaledConnectError(err)
}
useColor := isatty.IsTerminal(os.Stdout.Fd())
stdOut := colorable.NewColorableStdout()
for _, update := range updates {
stanza, err := nlDescribeUpdate(update, useColor)
if err != nil {
return err
}
fmt.Fprintln(stdOut, stanza)
}
return nil
}

View File

@@ -14,6 +14,8 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
L github.com/jsimonetti/rtnetlink/internal/unix from github.com/jsimonetti/rtnetlink
github.com/kballard/go-shellquote from tailscale.com/cmd/tailscale/cli
github.com/klauspost/compress/flate from nhooyr.io/websocket
💣 github.com/mattn/go-colorable from tailscale.com/cmd/tailscale/cli
💣 github.com/mattn/go-isatty from github.com/mattn/go-colorable+
L 💣 github.com/mdlayher/netlink from github.com/jsimonetti/rtnetlink+
L 💣 github.com/mdlayher/netlink/nlenc from github.com/jsimonetti/rtnetlink+
L 💣 github.com/mdlayher/socket from github.com/mdlayher/netlink