diff --git a/Makefile b/Makefile index 694a5eecf..2e0de8a21 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/client/tailscale/localclient.go b/client/tailscale/localclient.go index 4e452f894..b216f16ff 100644 --- a/client/tailscale/localclient.go +++ b/client/tailscale/localclient.go @@ -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 { diff --git a/cmd/tailscale/cli/cli.go b/cmd/tailscale/cli/cli.go index 66961b2e0..e9cf4aba1 100644 --- a/cmd/tailscale/cli/cli.go +++ b/cmd/tailscale/cli/cli.go @@ -206,7 +206,6 @@ change in the future. fileCmd, bugReportCmd, certCmd, - netlockCmd, licensesCmd, exitNodeCmd(), updateCmd, diff --git a/cmd/tailscale/cli/network-lock.go b/cmd/tailscale/cli/network-lock.go index 45f989f10..8bf0f0bd5 100644 --- a/cmd/tailscale/cli/network-lock.go +++ b/cmd/tailscale/cli/network-lock.go @@ -1,6 +1,8 @@ // Copyright (c) Tailscale Inc & AUTHORS // SPDX-License-Identifier: BSD-3-Clause +//go:build !ts_omit_tka + package cli import ( diff --git a/cmd/tailscale/depaware-minlinux.txt b/cmd/tailscale/depaware-minlinux.txt index 4122c35cb..5d41b91a2 100644 --- a/cmd/tailscale/depaware-minlinux.txt +++ b/cmd/tailscale/depaware-minlinux.txt @@ -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 diff --git a/cmd/tailscaled/depaware-minlinux.txt b/cmd/tailscaled/depaware-minlinux.txt index 108f9834a..7baac80f9 100644 --- a/cmd/tailscaled/depaware-minlinux.txt +++ b/cmd/tailscaled/depaware-minlinux.txt @@ -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 diff --git a/control/controlclient/direct.go b/control/controlclient/direct.go index dd361c4a2..4c7981b8a 100644 --- a/control/controlclient/direct.go +++ b/control/controlclient/direct.go @@ -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 == "" { diff --git a/control/controlclient/map.go b/control/controlclient/map.go index 97d49f90d..e3231eea0 100644 --- a/control/controlclient/map.go +++ b/control/controlclient/map.go @@ -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() diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index 73965a3dd..3ad3975b0 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -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 } diff --git a/ipn/ipnlocal/network-lock.go b/ipn/ipnlocal/network-lock.go index bf14d339e..c478d72f0 100644 --- a/ipn/ipnlocal/network-lock.go +++ b/ipn/ipnlocal/network-lock.go @@ -1,6 +1,8 @@ // Copyright (c) Tailscale Inc & AUTHORS // SPDX-License-Identifier: BSD-3-Clause +//go:build !ts_omit_tka + package ipnlocal import ( diff --git a/ipn/ipnstate/ipnstate.go b/ipn/ipnstate/ipnstate.go index 37ab47714..2f80d6767 100644 --- a/ipn/ipnstate/ipnstate.go +++ b/ipn/ipnstate/ipnstate.go @@ -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. diff --git a/ipn/ipnstate/ipnstate_clone.go b/ipn/ipnstate/ipnstate_clone.go deleted file mode 100644 index 20ae43c5f..000000000 --- a/ipn/ipnstate/ipnstate_clone.go +++ /dev/null @@ -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 -}{}) diff --git a/ipn/localapi/localapi.go b/ipn/localapi/localapi.go index 157f72a65..df9993ef0 100644 --- a/ipn/localapi/localapi.go +++ b/ipn/localapi/localapi.go @@ -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) diff --git a/types/netmap/netmap.go b/types/netmap/netmap.go index b1ac612de..ff0bf0e62 100644 --- a/types/netmap/netmap.go +++ b/types/netmap/netmap.go @@ -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