mirror of
https://github.com/tailscale/tailscale.git
synced 2025-08-12 13:48:01 +00:00
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:
@@ -647,6 +647,43 @@ func (b *LocalBackend) NetworkLockDisable(secret []byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// NetworkLockLog returns the changelog of TKA state up to maxEntries in size.
|
||||
func (b *LocalBackend) NetworkLockLog(maxEntries int) ([]ipnstate.NetworkLockUpdate, error) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
if b.tka == nil {
|
||||
return nil, errNetworkLockNotActive
|
||||
}
|
||||
|
||||
var out []ipnstate.NetworkLockUpdate
|
||||
cursor := b.tka.authority.Head()
|
||||
for i := 0; i < maxEntries; i++ {
|
||||
aum, err := b.tka.storage.AUM(cursor)
|
||||
if err != nil {
|
||||
if err == os.ErrNotExist {
|
||||
break
|
||||
}
|
||||
return out, fmt.Errorf("reading AUM: %w", err)
|
||||
}
|
||||
|
||||
update := ipnstate.NetworkLockUpdate{
|
||||
Hash: cursor,
|
||||
Change: aum.MessageKind.String(),
|
||||
Raw: aum.Serialize(),
|
||||
}
|
||||
out = append(out, update)
|
||||
|
||||
parent, hasParent := aum.Parent()
|
||||
if !hasParent {
|
||||
break
|
||||
}
|
||||
cursor = parent
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func signNodeKey(nodeInfo tailcfg.TKASignInfo, signer key.NLPrivate) (*tka.NodeKeySignature, error) {
|
||||
p, err := nodeInfo.NodePublic.MarshalBinary()
|
||||
if err != nil {
|
||||
|
@@ -101,6 +101,16 @@ type NetworkLockStatus struct {
|
||||
TrustedKeys []TKAKey
|
||||
}
|
||||
|
||||
// NetworkLockUpdate describes a change to network-lock state.
|
||||
type NetworkLockUpdate struct {
|
||||
Hash [32]byte
|
||||
Change string // values of tka.AUMKind.String()
|
||||
|
||||
// Raw contains the serialized AUM. The AUM is sent in serialized
|
||||
// form to avoid transitive dependences bloating this package.
|
||||
Raw []byte
|
||||
}
|
||||
|
||||
// TailnetStatus is information about a Tailscale network ("tailnet").
|
||||
type TailnetStatus struct {
|
||||
// Name is the name of the network that's currently in use.
|
||||
|
@@ -82,6 +82,7 @@ var handler = map[string]localAPIHandler{
|
||||
"set-expiry-sooner": (*Handler).serveSetExpirySooner,
|
||||
"status": (*Handler).serveStatus,
|
||||
"tka/init": (*Handler).serveTKAInit,
|
||||
"tka/log": (*Handler).serveTKALog,
|
||||
"tka/modify": (*Handler).serveTKAModify,
|
||||
"tka/sign": (*Handler).serveTKASign,
|
||||
"tka/status": (*Handler).serveTKAStatus,
|
||||
@@ -1164,6 +1165,37 @@ func (h *Handler) serveTKADisable(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(200)
|
||||
}
|
||||
|
||||
func (h *Handler) serveTKALog(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
http.Error(w, "use GET", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
limit := 50
|
||||
if limitStr := r.FormValue("limit"); limitStr != "" {
|
||||
l, err := strconv.Atoi(limitStr)
|
||||
if err != nil {
|
||||
http.Error(w, "parsing 'limit' parameter: "+err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
limit = int(l)
|
||||
}
|
||||
|
||||
updates, err := h.b.NetworkLockLog(limit)
|
||||
if err != nil {
|
||||
http.Error(w, "reading log failed: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
j, err := json.MarshalIndent(updates, "", "\t")
|
||||
if err != nil {
|
||||
http.Error(w, "JSON encoding error", 500)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write(j)
|
||||
}
|
||||
|
||||
// serveProfiles serves profile switching-related endpoints. Supported methods
|
||||
// and paths are:
|
||||
// - GET /profiles/: list all profiles (JSON-encoded array of ipn.LoginProfiles)
|
||||
|
Reference in New Issue
Block a user