lanscaping: remove TKA

-rwxr-xr-x@ 1 bradfitz  staff  15297778 Jan 10 21:03 /Users/bradfitz/bin/tailscaled.min
-rwxr-xr-x@ 1 bradfitz  staff  16515224 Jan 10 21:03 /Users/bradfitz/bin/tailscaled.minlinux

Change-Id: I297e3d82d5763019ab6fb1f2a1bc3f3369d10d9c
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick 2025-01-10 21:03:14 -08:00
parent 2348da8980
commit 5b4d3207b0
14 changed files with 22 additions and 802 deletions

View File

@ -24,7 +24,7 @@ updatedeps: ## Update depaware deps
tailscale.com/cmd/k8s-operator \
tailscale.com/cmd/stund
MIN_OMITS ?= ts_omit_aws,ts_omit_bird,ts_omit_tap,ts_omit_kube,ts_omit_completion,ts_omit_netstack,ts_omit_nftables,ts_omit_ssh
MIN_OMITS ?= ts_omit_aws,ts_omit_bird,ts_omit_tap,ts_omit_kube,ts_omit_completion,ts_omit_netstack,ts_omit_nftables,ts_omit_ssh,ts_omit_tka
min:
./tool/go build -o $$HOME/bin/tailscaled.min -ldflags "-w -s" --tags=${MIN_OMITS} ./cmd/tailscaled

View File

@ -36,10 +36,8 @@ import (
"tailscale.com/paths"
"tailscale.com/safesocket"
"tailscale.com/tailcfg"
"tailscale.com/tka"
"tailscale.com/types/dnstype"
"tailscale.com/types/key"
"tailscale.com/types/tkatype"
"tailscale.com/util/syspolicy/setting"
)
@ -1147,183 +1145,6 @@ func (lc *LocalClient) Ping(ctx context.Context, ip netip.Addr, pingtype tailcfg
return lc.PingWithOpts(ctx, ip, pingtype, PingOpts{})
}
// NetworkLockStatus fetches information about the tailnet key authority, if one is configured.
func (lc *LocalClient) NetworkLockStatus(ctx context.Context) (*ipnstate.NetworkLockStatus, error) {
body, err := lc.send(ctx, "GET", "/localapi/v0/tka/status", 200, nil)
if err != nil {
return nil, fmt.Errorf("error: %w", err)
}
return decodeJSON[*ipnstate.NetworkLockStatus](body)
}
// NetworkLockInit initializes the tailnet key authority.
//
// TODO(tom): Plumb through disablement secrets.
func (lc *LocalClient) NetworkLockInit(ctx context.Context, keys []tka.Key, disablementValues [][]byte, supportDisablement []byte) (*ipnstate.NetworkLockStatus, error) {
var b bytes.Buffer
type initRequest struct {
Keys []tka.Key
DisablementValues [][]byte
SupportDisablement []byte
}
if err := json.NewEncoder(&b).Encode(initRequest{Keys: keys, DisablementValues: disablementValues, SupportDisablement: supportDisablement}); err != nil {
return nil, err
}
body, err := lc.send(ctx, "POST", "/localapi/v0/tka/init", 200, &b)
if err != nil {
return nil, fmt.Errorf("error: %w", err)
}
return decodeJSON[*ipnstate.NetworkLockStatus](body)
}
// NetworkLockWrapPreauthKey wraps a pre-auth key with information to
// enable unattended bringup in the locked tailnet.
func (lc *LocalClient) NetworkLockWrapPreauthKey(ctx context.Context, preauthKey string, tkaKey key.NLPrivate) (string, error) {
encodedPrivate, err := tkaKey.MarshalText()
if err != nil {
return "", err
}
var b bytes.Buffer
type wrapRequest struct {
TSKey string
TKAKey string // key.NLPrivate.MarshalText
}
if err := json.NewEncoder(&b).Encode(wrapRequest{TSKey: preauthKey, TKAKey: string(encodedPrivate)}); err != nil {
return "", err
}
body, err := lc.send(ctx, "POST", "/localapi/v0/tka/wrap-preauth-key", 200, &b)
if err != nil {
return "", fmt.Errorf("error: %w", err)
}
return string(body), nil
}
// NetworkLockModify adds and/or removes key(s) to the tailnet key authority.
func (lc *LocalClient) NetworkLockModify(ctx context.Context, addKeys, removeKeys []tka.Key) error {
var b bytes.Buffer
type modifyRequest struct {
AddKeys []tka.Key
RemoveKeys []tka.Key
}
if err := json.NewEncoder(&b).Encode(modifyRequest{AddKeys: addKeys, RemoveKeys: removeKeys}); err != nil {
return err
}
if _, err := lc.send(ctx, "POST", "/localapi/v0/tka/modify", 204, &b); err != nil {
return fmt.Errorf("error: %w", err)
}
return nil
}
// NetworkLockSign signs the specified node-key and transmits that signature to the control plane.
// rotationPublic, if specified, must be an ed25519 public key.
func (lc *LocalClient) NetworkLockSign(ctx context.Context, nodeKey key.NodePublic, rotationPublic []byte) error {
var b bytes.Buffer
type signRequest struct {
NodeKey key.NodePublic
RotationPublic []byte
}
if err := json.NewEncoder(&b).Encode(signRequest{NodeKey: nodeKey, RotationPublic: rotationPublic}); err != nil {
return err
}
if _, err := lc.send(ctx, "POST", "/localapi/v0/tka/sign", 200, &b); err != nil {
return fmt.Errorf("error: %w", err)
}
return nil
}
// NetworkLockAffectedSigs returns all signatures signed by the specified keyID.
func (lc *LocalClient) NetworkLockAffectedSigs(ctx context.Context, keyID tkatype.KeyID) ([]tkatype.MarshaledSignature, error) {
body, err := lc.send(ctx, "POST", "/localapi/v0/tka/affected-sigs", 200, bytes.NewReader(keyID))
if err != nil {
return nil, fmt.Errorf("error: %w", err)
}
return decodeJSON[[]tkatype.MarshaledSignature](body)
}
// NetworkLockLog returns up to maxEntries number of changes to network-lock state.
func (lc *LocalClient) NetworkLockLog(ctx context.Context, maxEntries int) ([]ipnstate.NetworkLockUpdate, error) {
v := url.Values{}
v.Set("limit", fmt.Sprint(maxEntries))
body, err := lc.send(ctx, "GET", "/localapi/v0/tka/log?"+v.Encode(), 200, nil)
if err != nil {
return nil, fmt.Errorf("error %w: %s", err, body)
}
return decodeJSON[[]ipnstate.NetworkLockUpdate](body)
}
// NetworkLockForceLocalDisable forcibly shuts down network lock on this node.
func (lc *LocalClient) NetworkLockForceLocalDisable(ctx context.Context) error {
// This endpoint expects an empty JSON stanza as the payload.
var b bytes.Buffer
if err := json.NewEncoder(&b).Encode(struct{}{}); err != nil {
return err
}
if _, err := lc.send(ctx, "POST", "/localapi/v0/tka/force-local-disable", 200, &b); err != nil {
return fmt.Errorf("error: %w", err)
}
return nil
}
// NetworkLockVerifySigningDeeplink verifies the network lock deeplink contained
// in url and returns information extracted from it.
func (lc *LocalClient) NetworkLockVerifySigningDeeplink(ctx context.Context, url string) (*tka.DeeplinkValidationResult, error) {
vr := struct {
URL string
}{url}
body, err := lc.send(ctx, "POST", "/localapi/v0/tka/verify-deeplink", 200, jsonBody(vr))
if err != nil {
return nil, fmt.Errorf("sending verify-deeplink: %w", err)
}
return decodeJSON[*tka.DeeplinkValidationResult](body)
}
// NetworkLockGenRecoveryAUM generates an AUM for recovering from a tailnet-lock key compromise.
func (lc *LocalClient) NetworkLockGenRecoveryAUM(ctx context.Context, removeKeys []tkatype.KeyID, forkFrom tka.AUMHash) ([]byte, error) {
vr := struct {
Keys []tkatype.KeyID
ForkFrom string
}{removeKeys, forkFrom.String()}
body, err := lc.send(ctx, "POST", "/localapi/v0/tka/generate-recovery-aum", 200, jsonBody(vr))
if err != nil {
return nil, fmt.Errorf("sending generate-recovery-aum: %w", err)
}
return body, nil
}
// NetworkLockCosignRecoveryAUM co-signs a recovery AUM using the node's tailnet lock key.
func (lc *LocalClient) NetworkLockCosignRecoveryAUM(ctx context.Context, aum tka.AUM) ([]byte, error) {
r := bytes.NewReader(aum.Serialize())
body, err := lc.send(ctx, "POST", "/localapi/v0/tka/cosign-recovery-aum", 200, r)
if err != nil {
return nil, fmt.Errorf("sending cosign-recovery-aum: %w", err)
}
return body, nil
}
// NetworkLockSubmitRecoveryAUM submits a recovery AUM to the control plane.
func (lc *LocalClient) NetworkLockSubmitRecoveryAUM(ctx context.Context, aum tka.AUM) error {
r := bytes.NewReader(aum.Serialize())
_, err := lc.send(ctx, "POST", "/localapi/v0/tka/submit-recovery-aum", 200, r)
if err != nil {
return fmt.Errorf("sending cosign-recovery-aum: %w", err)
}
return nil
}
// SetServeConfig sets or replaces the serving settings.
// If config is nil, settings are cleared and serving is disabled.
func (lc *LocalClient) SetServeConfig(ctx context.Context, config *ipn.ServeConfig) error {

View File

@ -206,7 +206,6 @@ change in the future.
fileCmd,
bugReportCmd,
certCmd,
netlockCmd,
licensesCmd,
exitNodeCmd(),
updateCmd,

View File

@ -1,6 +1,8 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build !ts_omit_tka
package cli
import (

View File

@ -1,9 +1,8 @@
tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/depaware)
filippo.io/edwards25519 from github.com/hdevalence/ed25519consensus
filippo.io/edwards25519/field from filippo.io/edwards25519
L filippo.io/edwards25519 from github.com/hdevalence/ed25519consensus
L filippo.io/edwards25519/field from filippo.io/edwards25519
L github.com/coreos/go-iptables/iptables from tailscale.com/util/linuxfw
github.com/fxamacker/cbor/v2 from tailscale.com/tka
github.com/go-json-experiment/json from tailscale.com/types/opt+
github.com/go-json-experiment/json/internal from github.com/go-json-experiment/json+
github.com/go-json-experiment/json/internal/jsonflags from github.com/go-json-experiment/json+
@ -14,7 +13,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
D github.com/google/uuid from tailscale.com/util/quarantine
github.com/gorilla/csrf from tailscale.com/client/web
github.com/gorilla/securecookie from github.com/gorilla/csrf
github.com/hdevalence/ed25519consensus from tailscale.com/clientupdate/distsign+
L github.com/hdevalence/ed25519consensus from tailscale.com/clientupdate/distsign
L github.com/josharian/native from github.com/mdlayher/netlink+
L 💣 github.com/jsimonetti/rtnetlink from tailscale.com/net/netmon
L github.com/jsimonetti/rtnetlink/internal/unix from github.com/jsimonetti/rtnetlink
@ -42,7 +41,6 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
github.com/tailscale/web-client-prebuilt from tailscale.com/client/web
github.com/toqueteos/webbrowser from tailscale.com/cmd/tailscale/cli
L github.com/vishvananda/netns from github.com/tailscale/netlink+
github.com/x448/float16 from github.com/fxamacker/cbor/v2
💣 go4.org/mem from tailscale.com/client/tailscale+
go4.org/netipx from tailscale.com/net/tsaddr
software.sslmate.com/src/go-pkcs12 from tailscale.com/cmd/tailscale/cli
@ -101,8 +99,6 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
tailscale.com/syncs from tailscale.com/cmd/tailscale/cli+
tailscale.com/tailcfg from tailscale.com/client/tailscale+
tailscale.com/tempfork/spf13/cobra from tailscale.com/cmd/tailscale/cli/ffcomplete+
tailscale.com/tka from tailscale.com/client/tailscale+
tailscale.com/tsconst from tailscale.com/cmd/tailscale/cli
tailscale.com/tstime from tailscale.com/control/controlhttp+
tailscale.com/tstime/mono from tailscale.com/tstime/rate
tailscale.com/tstime/rate from tailscale.com/cmd/tailscale/cli+
@ -121,7 +117,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
tailscale.com/types/ptr from tailscale.com/hostinfo+
tailscale.com/types/result from tailscale.com/util/lineiter
tailscale.com/types/structs from tailscale.com/ipn+
tailscale.com/types/tkatype from tailscale.com/client/tailscale+
tailscale.com/types/tkatype from tailscale.com/tailcfg+
tailscale.com/types/views from tailscale.com/client/web+
tailscale.com/util/cibuild from tailscale.com/health
tailscale.com/util/clientmetric from tailscale.com/net/dnsfallback+
@ -159,8 +155,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
tailscale.com/version/distro from tailscale.com/client/web+
tailscale.com/wgengine/capture from tailscale.com/cmd/tailscale/cli
tailscale.com/wgengine/filter/filtertype from tailscale.com/types/netmap
golang.org/x/crypto/argon2 from tailscale.com/tka
golang.org/x/crypto/blake2b from golang.org/x/crypto/argon2+
golang.org/x/crypto/blake2b from golang.org/x/crypto/nacl/box
golang.org/x/crypto/blake2s from tailscale.com/clientupdate/distsign+
golang.org/x/crypto/chacha20 from golang.org/x/crypto/chacha20poly1305
golang.org/x/crypto/chacha20poly1305 from crypto/internal/hpke+
@ -231,7 +226,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
embed from crypto/internal/nistec+
encoding from encoding/gob+
encoding/asn1 from crypto/x509+
encoding/base32 from github.com/fxamacker/cbor/v2+
encoding/base32 from github.com/go-json-experiment/json
encoding/base64 from encoding/json+
encoding/binary from compress/gzip+
encoding/gob from github.com/gorilla/securecookie

View File

@ -1,11 +1,10 @@
tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/depaware)
filippo.io/edwards25519 from github.com/hdevalence/ed25519consensus
filippo.io/edwards25519/field from filippo.io/edwards25519
L filippo.io/edwards25519 from github.com/hdevalence/ed25519consensus
L filippo.io/edwards25519/field from filippo.io/edwards25519
github.com/bits-and-blooms/bitset from github.com/gaissmai/bart
L github.com/coreos/go-iptables/iptables from tailscale.com/util/linuxfw
L github.com/digitalocean/go-smbios/smbios from tailscale.com/posture
github.com/fxamacker/cbor/v2 from tailscale.com/tka
github.com/gaissmai/bart from tailscale.com/net/ipset+
github.com/go-json-experiment/json from tailscale.com/types/opt+
github.com/go-json-experiment/json/internal from github.com/go-json-experiment/json+
@ -17,7 +16,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
github.com/golang/groupcache/lru from tailscale.com/net/dnscache
github.com/gorilla/csrf from tailscale.com/client/web
github.com/gorilla/securecookie from github.com/gorilla/csrf
github.com/hdevalence/ed25519consensus from tailscale.com/clientupdate/distsign+
L github.com/hdevalence/ed25519consensus from tailscale.com/clientupdate/distsign
L 💣 github.com/illarion/gonotify/v2 from tailscale.com/net/dns
L github.com/josharian/native from github.com/mdlayher/netlink+
L 💣 github.com/jsimonetti/rtnetlink from tailscale.com/net/netmon
@ -61,7 +60,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
github.com/tailscale/wireguard-go/tai64n from github.com/tailscale/wireguard-go/device
💣 github.com/tailscale/wireguard-go/tun from github.com/tailscale/wireguard-go/device+
L github.com/vishvananda/netns from github.com/tailscale/netlink+
github.com/x448/float16 from github.com/fxamacker/cbor/v2
💣 go4.org/mem from tailscale.com/client/tailscale+
go4.org/netipx from tailscale.com/ipn/ipnlocal+
tailscale.com from tailscale.com/version
@ -154,8 +152,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/tailcfg from tailscale.com/client/tailscale+
tailscale.com/taildrop from tailscale.com/ipn/ipnlocal+
tailscale.com/tempfork/heap from tailscale.com/wgengine/magicsock
tailscale.com/tka from tailscale.com/client/tailscale+
tailscale.com/tsconst from tailscale.com/ipn/ipnlocal
tailscale.com/tsd from tailscale.com/cmd/tailscaled+
tailscale.com/tstime from tailscale.com/control/controlclient+
tailscale.com/tstime/mono from tailscale.com/net/tstun+
@ -179,7 +175,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/types/ptr from tailscale.com/control/controlclient+
tailscale.com/types/result from tailscale.com/util/lineiter
tailscale.com/types/structs from tailscale.com/control/controlclient+
tailscale.com/types/tkatype from tailscale.com/client/tailscale+
tailscale.com/types/tkatype from tailscale.com/control/controlclient+
tailscale.com/types/views from tailscale.com/appc+
tailscale.com/util/cibuild from tailscale.com/health
tailscale.com/util/clientmetric from tailscale.com/appc+
@ -241,8 +237,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/wgengine/wgcfg/nmcfg from tailscale.com/ipn/ipnlocal
💣 tailscale.com/wgengine/wgint from tailscale.com/wgengine+
tailscale.com/wgengine/wglog from tailscale.com/wgengine
golang.org/x/crypto/argon2 from tailscale.com/tka
golang.org/x/crypto/blake2b from golang.org/x/crypto/argon2+
golang.org/x/crypto/blake2b from golang.org/x/crypto/nacl/box
golang.org/x/crypto/blake2s from github.com/tailscale/wireguard-go/device+
golang.org/x/crypto/blowfish from github.com/tailscale/golang-x-crypto/ssh/internal/bcrypt_pbkdf
golang.org/x/crypto/chacha20 from github.com/tailscale/golang-x-crypto/ssh+
@ -312,7 +307,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
embed from crypto/internal/nistec+
encoding from encoding/gob+
encoding/asn1 from crypto/x509+
encoding/base32 from github.com/fxamacker/cbor/v2+
encoding/base32 from github.com/go-json-experiment/json
encoding/base64 from encoding/json+
encoding/binary from compress/gzip+
encoding/gob from github.com/gorilla/securecookie

View File

@ -42,7 +42,6 @@ import (
"tailscale.com/net/tsdial"
"tailscale.com/net/tshttpproxy"
"tailscale.com/tailcfg"
"tailscale.com/tka"
"tailscale.com/tstime"
"tailscale.com/types/key"
"tailscale.com/types/logger"
@ -498,7 +497,7 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new
tryingNewKey := c.tryingNewKey
serverKey := c.serverLegacyKey
serverNoiseKey := c.serverNoiseKey
authKey, isWrapped, wrappedSig, wrappedKey := tka.DecodeWrappedAuthkey(c.authKey, c.logf)
authKey := c.authKey
hi := c.hostInfoLocked()
backendLogID := hi.BackendLogID
expired := !c.expiry.IsZero() && c.expiry.Before(c.clock.Now())
@ -588,17 +587,7 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new
var nodeKeySignature tkatype.MarshaledSignature
if !oldNodeKey.IsZero() && opt.OldNodeKeySignature != nil {
if nodeKeySignature, err = tka.ResignNKS(persist.NetworkLockKey, tryingNewKey.Public(), opt.OldNodeKeySignature); err != nil {
c.logf("Failed re-signing node-key signature: %v", err)
}
} else if isWrapped {
// We were given a wrapped pre-auth key, which means that in addition
// to being a regular pre-auth key there was a suffix with information to
// generate a tailnet-lock signature.
nodeKeySignature, err = tka.SignByCredential(wrappedKey, wrappedSig, tryingNewKey.Public())
if err != nil {
return false, "", nil, err
}
// lanscaping
}
if backendLogID == "" {

View File

@ -784,17 +784,9 @@ func (ms *mapSession) netmap() *netmap.NetworkMap {
CollectServices: ms.collectServices,
DERPMap: ms.lastDERPMap,
ControlHealth: ms.lastHealth,
TKAEnabled: ms.lastTKAInfo != nil && !ms.lastTKAInfo.Disabled,
MaxKeyDuration: ms.lastMaxExpiry,
}
if ms.lastTKAInfo != nil && ms.lastTKAInfo.Head != "" {
if err := nm.TKAHead.UnmarshalText([]byte(ms.lastTKAInfo.Head)); err != nil {
ms.logf("error unmarshalling TKAHead: %v", err)
nm.TKAEnabled = false
}
}
if node := ms.lastNode; node.Valid() {
nm.SelfNode = node
nm.Expiry = node.KeyExpiry()

View File

@ -78,7 +78,6 @@ import (
"tailscale.com/syncs"
"tailscale.com/tailcfg"
"tailscale.com/taildrop"
"tailscale.com/tka"
"tailscale.com/tsd"
"tailscale.com/tstime"
"tailscale.com/types/appctype"
@ -250,7 +249,7 @@ type LocalBackend struct {
cc controlclient.Client
ccAuto *controlclient.Auto // if cc is of type *controlclient.Auto
machinePrivKey key.MachinePrivate
tka *tkaState
tka *int
state ipn.State
capFileSharing bool // whether netMap contains the file sharing capability
capTailnetLock bool // whether netMap contains the tailnet lock capability
@ -1570,29 +1569,12 @@ func (b *LocalBackend) SetControlClientStatus(c controlclient.Client, st control
b.capTailnetLock = st.NetMap.HasCap(tailcfg.CapabilityTailnetLock)
b.setWebClientAtomicBoolLocked(st.NetMap)
b.mu.Unlock() // respect locking rules for tkaSyncIfNeeded
if err := b.tkaSyncIfNeeded(st.NetMap, prefs.View()); err != nil {
b.logf("[v1] TKA sync error: %v", err)
}
b.mu.Lock()
// As we stepped outside of the lock, it's possible for b.cc
// to now be nil.
if b.cc != nil {
if b.tka != nil {
head, err := b.tka.authority.Head().MarshalText()
if err != nil {
b.logf("[v1] error marshalling tka head: %v", err)
} else {
b.cc.SetTKAHead(string(head))
}
} else {
b.cc.SetTKAHead("")
}
b.cc.SetTKAHead("")
}
if !envknob.TKASkipSignatureCheck() {
b.tkaFilterNetmapLocked(st.NetMap)
}
b.setNetMapLocked(st.NetMap)
b.updateFilterLocked(st.NetMap, prefs.View())
}
@ -2254,13 +2236,6 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
b.logf("initTKALocked: %v", err)
}
var tkaHead string
if b.tka != nil {
head, err := b.tka.authority.Head().MarshalText()
if err != nil {
return fmt.Errorf("marshalling tka head: %w", err)
}
tkaHead = string(head)
}
confWantRunning := b.conf != nil && wantRunning
if endpoints != nil {
@ -2498,7 +2473,7 @@ func (b *LocalBackend) checkCaptivePortalLoop(ctx context.Context) {
func (b *LocalBackend) performCaptiveDetection() {
}
// shouldRunCaptivePortalDetection reports whether captive portal detection
// shouldRunCaptivePortalDetection reports whether capgtive portal detection
// should be run. It is enabled by default, but can be disabled via a control
// knob. It is also only run when the user explicitly wants the backend to be
// running.
@ -6739,48 +6714,6 @@ func (b *LocalBackend) SwitchProfile(profile ipn.ProfileID) error {
}
func (b *LocalBackend) initTKALocked() error {
cp := b.pm.CurrentProfile()
if cp.ID == "" {
b.tka = nil
return nil
}
if b.tka != nil {
if b.tka.profile == cp.ID {
// Already initialized.
return nil
}
// As we're switching profiles, we need to reset the TKA to nil.
b.tka = nil
}
root := b.TailscaleVarRoot()
if root == "" {
b.tka = nil
b.logf("network-lock unavailable; no state directory")
return nil
}
chonkDir := b.chonkPathLocked()
if _, err := os.Stat(chonkDir); err == nil {
// The directory exists, which means network-lock has been initialized.
storage, err := tka.ChonkDir(chonkDir)
if err != nil {
return fmt.Errorf("opening tailchonk: %v", err)
}
authority, err := tka.Open(storage)
if err != nil {
return fmt.Errorf("initializing tka: %v", err)
}
if err := authority.Compact(storage, tkaCompactionDefaults); err != nil {
b.logf("tka compaction failed: %v", err)
}
b.tka = &tkaState{
profile: cp.ID,
authority: authority,
storage: storage,
}
b.logf("tka initialized at head %x", authority.Head())
}
return nil
}

View File

@ -1,6 +1,8 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build !ts_omit_tka
package ipnlocal
import (

View File

@ -18,7 +18,6 @@ import (
"time"
"tailscale.com/tailcfg"
"tailscale.com/tka"
"tailscale.com/types/key"
"tailscale.com/types/ptr"
"tailscale.com/types/views"
@ -26,8 +25,6 @@ import (
"tailscale.com/version"
)
//go:generate go run tailscale.com/cmd/cloner -clonefunc=false -type=TKAPeer
// Status represents the entire state of the IPN network.
type Status struct {
// Version is the daemon's long version (see version.Long).
@ -87,77 +84,6 @@ type Status struct {
ClientVersion *tailcfg.ClientVersion
}
// TKAKey describes a key trusted by network lock.
type TKAKey struct {
Key key.NLPublic
Metadata map[string]string
Votes uint
}
// TKAPeer describes a peer and its network lock details.
type TKAPeer struct {
Name string // DNS
ID tailcfg.NodeID
StableID tailcfg.StableNodeID
TailscaleIPs []netip.Addr // Tailscale IP(s) assigned to this node
NodeKey key.NodePublic
NodeKeySignature tka.NodeKeySignature
}
// NetworkLockStatus represents whether network-lock is enabled,
// along with details about the locally-known state of the tailnet
// key authority.
type NetworkLockStatus struct {
// Enabled is true if network lock is enabled.
Enabled bool
// Head describes the AUM hash of the leaf AUM. Head is nil
// if network lock is not enabled.
Head *[32]byte
// PublicKey describes the node's network-lock public key.
// It may be zero if the node has not logged in.
PublicKey key.NLPublic
// NodeKey describes the node's current node-key. This field is not
// populated if the node is not operating (i.e. waiting for a login).
NodeKey *key.NodePublic
// NodeKeySigned is true if our node is authorized by network-lock.
NodeKeySigned bool
// NodeKeySignature is the current signature of this node's key.
NodeKeySignature *tka.NodeKeySignature
// TrustedKeys describes the keys currently trusted to make changes
// to network-lock.
TrustedKeys []TKAKey
// VisiblePeers describes peers which are visible in the netmap that
// have valid Tailnet Lock signatures signatures.
VisiblePeers []*TKAPeer
// FilteredPeers describes peers which were removed from the netmap
// (i.e. no connectivity) because they failed tailnet lock
// checks.
FilteredPeers []*TKAPeer
// StateID is a nonce associated with the network lock authority,
// generated upon enablement. This field is not populated if the
// network lock is disabled.
StateID uint64
}
// 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.

View File

@ -1,37 +0,0 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
// Code generated by tailscale.com/cmd/cloner; DO NOT EDIT.
package ipnstate
import (
"net/netip"
"tailscale.com/tailcfg"
"tailscale.com/tka"
"tailscale.com/types/key"
)
// Clone makes a deep copy of TKAPeer.
// The result aliases no memory with the original.
func (src *TKAPeer) Clone() *TKAPeer {
if src == nil {
return nil
}
dst := new(TKAPeer)
*dst = *src
dst.TailscaleIPs = append(src.TailscaleIPs[:0:0], src.TailscaleIPs...)
dst.NodeKeySignature = *src.NodeKeySignature.Clone()
return dst
}
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _TKAPeerCloneNeedsRegeneration = TKAPeer(struct {
Name string
ID tailcfg.NodeID
StableID tailcfg.StableNodeID
TailscaleIPs []netip.Addr
NodeKey key.NodePublic
NodeKeySignature tka.NodeKeySignature
}{})

View File

@ -47,14 +47,12 @@ import (
"tailscale.com/net/portmapper"
"tailscale.com/tailcfg"
"tailscale.com/taildrop"
"tailscale.com/tka"
"tailscale.com/tstime"
"tailscale.com/types/dnstype"
"tailscale.com/types/key"
"tailscale.com/types/logger"
"tailscale.com/types/logid"
"tailscale.com/types/ptr"
"tailscale.com/types/tkatype"
"tailscale.com/util/clientmetric"
"tailscale.com/util/httphdr"
"tailscale.com/util/httpm"
@ -130,19 +128,6 @@ var handler = map[string]localAPIHandler{
"start": (*Handler).serveStart,
"status": (*Handler).serveStatus,
"suggest-exit-node": (*Handler).serveSuggestExitNode,
"tka/affected-sigs": (*Handler).serveTKAAffectedSigs,
"tka/cosign-recovery-aum": (*Handler).serveTKACosignRecoveryAUM,
"tka/disable": (*Handler).serveTKADisable,
"tka/force-local-disable": (*Handler).serveTKALocalDisable,
"tka/generate-recovery-aum": (*Handler).serveTKAGenerateRecoveryAUM,
"tka/init": (*Handler).serveTKAInit,
"tka/log": (*Handler).serveTKALog,
"tka/modify": (*Handler).serveTKAModify,
"tka/sign": (*Handler).serveTKASign,
"tka/status": (*Handler).serveTKAStatus,
"tka/submit-recovery-aum": (*Handler).serveTKASubmitRecoveryAUM,
"tka/verify-deeplink": (*Handler).serveTKAVerifySigningDeeplink,
"tka/wrap-preauth-key": (*Handler).serveTKAWrapPreauthKey,
"update/check": (*Handler).serveUpdateCheck,
"update/install": (*Handler).serveUpdateInstall,
"update/progress": (*Handler).serveUpdateProgress,
@ -2113,25 +2098,6 @@ func (h *Handler) serveUploadClientMetrics(w http.ResponseWriter, r *http.Reques
json.NewEncoder(w).Encode(struct{}{})
}
func (h *Handler) serveTKAStatus(w http.ResponseWriter, r *http.Request) {
if !h.PermitRead {
http.Error(w, "lock status access denied", http.StatusForbidden)
return
}
if r.Method != httpm.GET {
http.Error(w, "use GET", http.StatusMethodNotAllowed)
return
}
j, err := json.MarshalIndent(h.b.NetworkLockStatus(), "", "\t")
if err != nil {
http.Error(w, "JSON encoding error", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(j)
}
func (h *Handler) serveSetGUIVisible(w http.ResponseWriter, r *http.Request) {
if r.Method != httpm.POST {
http.Error(w, "use POST", http.StatusMethodNotAllowed)
@ -2179,361 +2145,6 @@ func (h *Handler) serveSetUseExitNodeEnabled(w http.ResponseWriter, r *http.Requ
e.Encode(prefs)
}
func (h *Handler) serveTKASign(w http.ResponseWriter, r *http.Request) {
if !h.PermitWrite {
http.Error(w, "lock sign access denied", http.StatusForbidden)
return
}
if r.Method != httpm.POST {
http.Error(w, "use POST", http.StatusMethodNotAllowed)
return
}
type signRequest struct {
NodeKey key.NodePublic
RotationPublic []byte
}
var req signRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "invalid JSON body", http.StatusBadRequest)
return
}
if err := h.b.NetworkLockSign(req.NodeKey, req.RotationPublic); err != nil {
http.Error(w, "signing failed: "+err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}
func (h *Handler) serveTKAInit(w http.ResponseWriter, r *http.Request) {
if !h.PermitWrite {
http.Error(w, "lock init access denied", http.StatusForbidden)
return
}
if r.Method != httpm.POST {
http.Error(w, "use POST", http.StatusMethodNotAllowed)
return
}
type initRequest struct {
Keys []tka.Key
DisablementValues [][]byte
SupportDisablement []byte
}
var req initRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "invalid JSON body", http.StatusBadRequest)
return
}
if err := h.b.NetworkLockInit(req.Keys, req.DisablementValues, req.SupportDisablement); err != nil {
http.Error(w, "initialization failed: "+err.Error(), http.StatusInternalServerError)
return
}
j, err := json.MarshalIndent(h.b.NetworkLockStatus(), "", "\t")
if err != nil {
http.Error(w, "JSON encoding error", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(j)
}
func (h *Handler) serveTKAModify(w http.ResponseWriter, r *http.Request) {
if !h.PermitWrite {
http.Error(w, "network-lock modify access denied", http.StatusForbidden)
return
}
if r.Method != httpm.POST {
http.Error(w, "use POST", http.StatusMethodNotAllowed)
return
}
type modifyRequest struct {
AddKeys []tka.Key
RemoveKeys []tka.Key
}
var req modifyRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "invalid JSON body", http.StatusBadRequest)
return
}
if err := h.b.NetworkLockModify(req.AddKeys, req.RemoveKeys); err != nil {
http.Error(w, "network-lock modify failed: "+err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(204)
}
func (h *Handler) serveTKAWrapPreauthKey(w http.ResponseWriter, r *http.Request) {
if !h.PermitWrite {
http.Error(w, "network-lock modify access denied", http.StatusForbidden)
return
}
if r.Method != httpm.POST {
http.Error(w, "use POST", http.StatusMethodNotAllowed)
return
}
type wrapRequest struct {
TSKey string
TKAKey string // key.NLPrivate.MarshalText
}
var req wrapRequest
if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 12*1024)).Decode(&req); err != nil {
http.Error(w, "invalid JSON body", http.StatusBadRequest)
return
}
var priv key.NLPrivate
if err := priv.UnmarshalText([]byte(req.TKAKey)); err != nil {
http.Error(w, "invalid JSON body", http.StatusBadRequest)
return
}
wrappedKey, err := h.b.NetworkLockWrapPreauthKey(req.TSKey, priv)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
w.Write([]byte(wrappedKey))
}
func (h *Handler) serveTKAVerifySigningDeeplink(w http.ResponseWriter, r *http.Request) {
if !h.PermitRead {
http.Error(w, "signing deeplink verification access denied", http.StatusForbidden)
return
}
if r.Method != httpm.POST {
http.Error(w, "use POST", http.StatusMethodNotAllowed)
return
}
type verifyRequest struct {
URL string
}
var req verifyRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "invalid JSON for verifyRequest body", http.StatusBadRequest)
return
}
res := h.b.NetworkLockVerifySigningDeeplink(req.URL)
j, err := json.MarshalIndent(res, "", "\t")
if err != nil {
http.Error(w, "JSON encoding error", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(j)
}
func (h *Handler) serveTKADisable(w http.ResponseWriter, r *http.Request) {
if !h.PermitWrite {
http.Error(w, "network-lock modify access denied", http.StatusForbidden)
return
}
if r.Method != httpm.POST {
http.Error(w, "use POST", http.StatusMethodNotAllowed)
return
}
body := io.LimitReader(r.Body, 1024*1024)
secret, err := io.ReadAll(body)
if err != nil {
http.Error(w, "reading secret", http.StatusBadRequest)
return
}
if err := h.b.NetworkLockDisable(secret); err != nil {
http.Error(w, "network-lock disable failed: "+err.Error(), http.StatusBadRequest)
return
}
w.WriteHeader(http.StatusOK)
}
func (h *Handler) serveTKALocalDisable(w http.ResponseWriter, r *http.Request) {
if !h.PermitWrite {
http.Error(w, "network-lock modify access denied", http.StatusForbidden)
return
}
if r.Method != httpm.POST {
http.Error(w, "use POST", http.StatusMethodNotAllowed)
return
}
// Require a JSON stanza for the body as an additional CSRF protection.
var req struct{}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "invalid JSON body", http.StatusBadRequest)
return
}
if err := h.b.NetworkLockForceLocalDisable(); err != nil {
http.Error(w, "network-lock local disable failed: "+err.Error(), http.StatusBadRequest)
return
}
w.WriteHeader(http.StatusOK)
}
func (h *Handler) serveTKALog(w http.ResponseWriter, r *http.Request) {
if r.Method != httpm.GET {
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", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(j)
}
func (h *Handler) serveTKAAffectedSigs(w http.ResponseWriter, r *http.Request) {
if r.Method != httpm.POST {
http.Error(w, "use POST", http.StatusMethodNotAllowed)
return
}
keyID, err := io.ReadAll(http.MaxBytesReader(w, r.Body, 2048))
if err != nil {
http.Error(w, "reading body", http.StatusBadRequest)
return
}
sigs, err := h.b.NetworkLockAffectedSigs(keyID)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
j, err := json.MarshalIndent(sigs, "", "\t")
if err != nil {
http.Error(w, "JSON encoding error", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(j)
}
func (h *Handler) serveTKAGenerateRecoveryAUM(w http.ResponseWriter, r *http.Request) {
if !h.PermitWrite {
http.Error(w, "access denied", http.StatusForbidden)
return
}
if r.Method != httpm.POST {
http.Error(w, "use POST", http.StatusMethodNotAllowed)
return
}
type verifyRequest struct {
Keys []tkatype.KeyID
ForkFrom string
}
var req verifyRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "invalid JSON for verifyRequest body", http.StatusBadRequest)
return
}
var forkFrom tka.AUMHash
if req.ForkFrom != "" {
if err := forkFrom.UnmarshalText([]byte(req.ForkFrom)); err != nil {
http.Error(w, "decoding fork-from: "+err.Error(), http.StatusBadRequest)
return
}
}
res, err := h.b.NetworkLockGenerateRecoveryAUM(req.Keys, forkFrom)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/octet-stream")
w.Write(res.Serialize())
}
func (h *Handler) serveTKACosignRecoveryAUM(w http.ResponseWriter, r *http.Request) {
if !h.PermitWrite {
http.Error(w, "access denied", http.StatusForbidden)
return
}
if r.Method != httpm.POST {
http.Error(w, "use POST", http.StatusMethodNotAllowed)
return
}
body := io.LimitReader(r.Body, 1024*1024)
aumBytes, err := io.ReadAll(body)
if err != nil {
http.Error(w, "reading AUM", http.StatusBadRequest)
return
}
var aum tka.AUM
if err := aum.Unserialize(aumBytes); err != nil {
http.Error(w, "decoding AUM", http.StatusBadRequest)
return
}
res, err := h.b.NetworkLockCosignRecoveryAUM(&aum)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/octet-stream")
w.Write(res.Serialize())
}
func (h *Handler) serveTKASubmitRecoveryAUM(w http.ResponseWriter, r *http.Request) {
if !h.PermitWrite {
http.Error(w, "access denied", http.StatusForbidden)
return
}
if r.Method != httpm.POST {
http.Error(w, "use POST", http.StatusMethodNotAllowed)
return
}
body := io.LimitReader(r.Body, 1024*1024)
aumBytes, err := io.ReadAll(body)
if err != nil {
http.Error(w, "reading AUM", http.StatusBadRequest)
return
}
var aum tka.AUM
if err := aum.Unserialize(aumBytes); err != nil {
http.Error(w, "decoding AUM", http.StatusBadRequest)
return
}
if err := h.b.NetworkLockSubmitRecoveryAUM(&aum); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}
// serveProfiles serves profile switching-related endpoints. Supported methods
// and paths are:
// - GET /profiles/: list all profiles (JSON-encoded array of ipn.LoginProfiles)

View File

@ -14,7 +14,6 @@ import (
"time"
"tailscale.com/tailcfg"
"tailscale.com/tka"
"tailscale.com/types/key"
"tailscale.com/types/views"
"tailscale.com/util/set"
@ -61,13 +60,6 @@ type NetworkMap struct {
// check problems.
ControlHealth []string
// TKAEnabled indicates whether the tailnet key authority should be
// enabled, from the perspective of the control plane.
TKAEnabled bool
// TKAHead indicates the control plane's understanding of 'head' (the
// hash of the latest update message to tick through TKA).
TKAHead tka.AUMHash
// Domain is the current Tailnet name.
Domain string