From c6c16fdc96ab014d1d49b2613825d14edbb2afa0 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Sat, 11 Jan 2025 08:31:06 -0800 Subject: [PATCH] lanscaping: remove more dnstype, tkatype -rwxr-xr-x@ 1 bradfitz staff 11153842 Jan 11 08:35 /Users/bradfitz/bin/tailscaled.min -rwxr-xr-x@ 1 bradfitz staff 11534488 Jan 11 08:35 /Users/bradfitz/bin/tailscaled.minlinux Change-Id: I2666bb5a8392bbebf00bb48ed1f55367318debe4 Signed-off-by: Brad Fitzpatrick --- client/tailscale/apitype/apitype.go | 3 - cmd/tailscale/depaware-minlinux.txt | 1 - cmd/tailscaled/depaware-minlinux.txt | 4 +- control/controlclient/direct.go | 42 ++--- control/controlclient/map.go | 27 --- ipn/ipnlocal/local.go | 62 ------- ipn/localapi/localapi.go | 62 ------- net/tsdial/dnsmap.go | 52 ------ net/tsdial/tsdial.go | 79 -------- tailcfg/tailcfg.go | 137 +------------- tailcfg/tailcfg_clone.go | 89 ++------- tailcfg/tailcfg_view.go | 102 +++-------- tailcfg/tka.go | 264 --------------------------- types/key/nl.go | 178 ------------------ types/netmap/nodemut.go | 1 - types/persist/persist.go | 2 - types/persist/persist_clone.go | 1 - types/persist/persist_view.go | 2 - 18 files changed, 59 insertions(+), 1049 deletions(-) delete mode 100644 tailcfg/tka.go delete mode 100644 types/key/nl.go diff --git a/client/tailscale/apitype/apitype.go b/client/tailscale/apitype/apitype.go index b1c273a4f..4a39ce2ea 100644 --- a/client/tailscale/apitype/apitype.go +++ b/client/tailscale/apitype/apitype.go @@ -6,7 +6,6 @@ package apitype import ( "tailscale.com/tailcfg" - "tailscale.com/types/dnstype" ) // LocalAPIHost is the Host header value used by the LocalAPI. @@ -73,6 +72,4 @@ type DNSOSConfig struct { type DNSQueryResponse struct { // Bytes is the raw DNS response bytes. Bytes []byte - // Resolvers is the list of resolvers that the forwarder deemed able to resolve the query. - Resolvers []*dnstype.Resolver } diff --git a/cmd/tailscale/depaware-minlinux.txt b/cmd/tailscale/depaware-minlinux.txt index 1ef708f23..bec64be07 100644 --- a/cmd/tailscale/depaware-minlinux.txt +++ b/cmd/tailscale/depaware-minlinux.txt @@ -106,7 +106,6 @@ 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/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/netcheck+ diff --git a/cmd/tailscaled/depaware-minlinux.txt b/cmd/tailscaled/depaware-minlinux.txt index c3aa4e64d..c30475917 100644 --- a/cmd/tailscaled/depaware-minlinux.txt +++ b/cmd/tailscaled/depaware-minlinux.txt @@ -95,7 +95,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de tailscale.com/tstime/mono from tailscale.com/net/tstun+ tailscale.com/tstime/rate from tailscale.com/derp+ tailscale.com/tsweb/varz from tailscale.com/cmd/tailscaled+ - tailscale.com/types/dnstype from tailscale.com/client/tailscale/apitype+ tailscale.com/types/empty from tailscale.com/ipn+ tailscale.com/types/flagtype from tailscale.com/cmd/tailscaled tailscale.com/types/ipproto from tailscale.com/ipn+ @@ -111,7 +110,6 @@ 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/control/controlclient+ tailscale.com/types/views from tailscale.com/control/controlclient+ tailscale.com/util/cibuild from tailscale.com/health tailscale.com/util/clientmetric from tailscale.com/cmd/tailscaled+ @@ -177,7 +175,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de golang.org/x/crypto/sha3 from crypto/internal/mlkem768+ golang.org/x/exp/maps from tailscale.com/ipn/store/mem+ golang.org/x/net/bpf from github.com/mdlayher/genetlink+ - golang.org/x/net/dns/dnsmessage from net+ + golang.org/x/net/dns/dnsmessage from net golang.org/x/net/http/httpguts from golang.org/x/net/http2+ golang.org/x/net/http/httpproxy from net/http golang.org/x/net/http2 from tailscale.com/control/controlclient+ diff --git a/control/controlclient/direct.go b/control/controlclient/direct.go index 232cd5f9f..fe28eb7ac 100644 --- a/control/controlclient/direct.go +++ b/control/controlclient/direct.go @@ -44,7 +44,6 @@ import ( "tailscale.com/types/netmap" "tailscale.com/types/persist" "tailscale.com/types/ptr" - "tailscale.com/types/tkatype" "tailscale.com/util/clientmetric" "tailscale.com/util/multierr" "tailscale.com/util/singleflight" @@ -412,13 +411,12 @@ func (c *Direct) WaitLoginURL(ctx context.Context, url string) (newURL string, e } func (c *Direct) doLoginOrRegen(ctx context.Context, opt loginOpt) (newURL string, err error) { - mustRegen, url, oldNodeKeySignature, err := c.doLogin(ctx, opt) + mustRegen, url, _, err := c.doLogin(ctx, opt) if err != nil { return url, err } if mustRegen { opt.Regen = true - opt.OldNodeKeySignature = oldNodeKeySignature _, url, _, err = c.doLogin(ctx, opt) } return url, err @@ -444,10 +442,6 @@ type loginOpt struct { // It is ignored if Logout is set since Logout works by setting a // expiry time in the far past. Expiry *time.Time - - // OldNodeKeySignature indicates the former NodeKeySignature - // that must be resigned for the new node-key. - OldNodeKeySignature tkatype.MarshaledSignature } // hostInfoLocked returns a Clone of c.hostinfo and c.netinfo. @@ -468,7 +462,7 @@ var macOSScreenTime = health.Register(&health.Warnable{ ImpactsConnectivity: true, }) -func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, newURL string, nks tkatype.MarshaledSignature, err error) { +func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, newURL string, Old any, err error) { if c.panicOnUse { panic("tainted client") } @@ -552,10 +546,6 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new if !persist.OldPrivateNodeKey.IsZero() { oldNodeKey = persist.OldPrivateNodeKey.Public() } - if persist.NetworkLockKey.IsZero() { - persist.NetworkLockKey = key.NewNLPrivate() - } - nlPub := persist.NetworkLockKey.Public() if tryingNewKey.IsZero() { if opt.Logout { @@ -564,11 +554,6 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new log.Fatalf("tryingNewKey is empty, give up") } - var nodeKeySignature tkatype.MarshaledSignature - if !oldNodeKey.IsZero() && opt.OldNodeKeySignature != nil { - // lanscaping - } - if backendLogID == "" { err = errors.New("hostinfo: BackendLogID missing") return regen, opt.URL, nil, err @@ -580,16 +565,14 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new } now := c.clock.Now().Round(time.Second) request := tailcfg.RegisterRequest{ - Version: 1, - OldNodeKey: oldNodeKey, - NodeKey: tryingNewKey.Public(), - NLKey: nlPub, - Hostinfo: hi, - Followup: opt.URL, - Timestamp: &now, - Ephemeral: (opt.Flags & LoginEphemeral) != 0, - NodeKeySignature: nodeKeySignature, - Tailnet: tailnet, + Version: 1, + OldNodeKey: oldNodeKey, + NodeKey: tryingNewKey.Public(), + Hostinfo: hi, + Followup: opt.URL, + Timestamp: &now, + Ephemeral: (opt.Flags & LoginEphemeral) != 0, + Tailnet: tailnet, } if opt.Logout { request.Expiry = time.Unix(123, 0) // far in the past @@ -598,7 +581,7 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new } c.logf("RegisterReq: onode=%v node=%v fup=%v nks=%v", request.OldNodeKey.ShortString(), - request.NodeKey.ShortString(), opt.URL != "", len(nodeKeySignature) > 0) + request.NodeKey.ShortString(), opt.URL != "", false) if authKey != "" { request.Auth = &tailcfg.RegisterResponseAuth{ AuthKey: authKey, @@ -671,9 +654,6 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new if resp.Error != "" { return false, "", nil, UserVisibleError(resp.Error) } - if len(resp.NodeKeySignature) > 0 { - return true, "", resp.NodeKeySignature, nil - } if resp.NodeKeyExpired { if regen { diff --git a/control/controlclient/map.go b/control/controlclient/map.go index e3231eea0..5ea765f4c 100644 --- a/control/controlclient/map.go +++ b/control/controlclient/map.go @@ -88,7 +88,6 @@ type mapSession struct { lastDomainAuditLogID string lastHealth []string lastPopBrowserURL string - lastTKAInfo *tailcfg.TKAInfo lastNetmapSummary string // from NetworkMap.VeryConcise lastMaxExpiry time.Duration } @@ -341,9 +340,6 @@ func (ms *mapSession) updateStateFromResponse(resp *tailcfg.MapResponse) { if resp.Health != nil { ms.lastHealth = resp.Health } - if resp.TKAInfo != nil { - ms.lastTKAInfo = resp.TKAInfo - } if resp.MaxKeyDuration > 0 { ms.lastMaxExpiry = resp.MaxKeyDuration } @@ -474,10 +470,6 @@ func (ms *mapSession) updatePeersStateFromResponse(resp *tailcfg.MapResponse) (s mut.KeyExpiry = *v patchKeyExpiry.Add(1) } - if v := pc.KeySignature; v != nil { - mut.KeySignature = v - patchKeySignature.Add(1) - } if v := pc.CapMap; v != nil { mut.CapMap = v patchCapMap.Add(1) @@ -607,10 +599,6 @@ func peerChangeDiff(was tailcfg.NodeView, n *tailcfg.Node) (_ *tailcfg.PeerChang if !was.KeyExpiry().Equal(n.KeyExpiry) { pc().KeyExpiry = ptr.To(n.KeyExpiry) } - case "KeySignature": - if !was.KeySignature().Equal(n.KeySignature) { - pc().KeySignature = slices.Clone(n.KeySignature) - } case "Machine": if was.Machine() != n.Machine { return nil, false @@ -734,18 +722,6 @@ func peerChangeDiff(was tailcfg.NodeView, n *tailcfg.Node) (_ *tailcfg.PeerChang if va == nil || vb == nil || *va != *vb { return nil, false } - case "ExitNodeDNSResolvers": - va, vb := was.ExitNodeDNSResolvers(), views.SliceOfViews(n.ExitNodeDNSResolvers) - - if va.Len() != vb.Len() { - return nil, false - } - - for i := range va.Len() { - if !va.At(i).Equal(vb.At(i)) { - return nil, false - } - } } } @@ -799,8 +775,5 @@ func (ms *mapSession) netmap() *netmap.NetworkMap { ms.addUserProfile(nm, peer.Sharer()) ms.addUserProfile(nm, peer.User()) } - if DevKnob.ForceProxyDNS() { - nm.DNS.Proxied = true - } return nm } diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index b7cb63a51..90bad3122 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -59,7 +59,6 @@ import ( "tailscale.com/tailcfg" "tailscale.com/tsd" "tailscale.com/tstime" - "tailscale.com/types/dnstype" "tailscale.com/types/empty" "tailscale.com/types/key" "tailscale.com/types/logger" @@ -72,7 +71,6 @@ import ( "tailscale.com/types/views" "tailscale.com/util/clientmetric" "tailscale.com/util/deephash" - "tailscale.com/util/dnsname" "tailscale.com/util/goroutines" "tailscale.com/util/mak" "tailscale.com/util/multierr" @@ -888,7 +886,6 @@ func stripKeysFromPrefs(p ipn.PrefsView) ipn.PrefsView { p2.Persist.LegacyFrontendPrivateMachineKey = key.MachinePrivate{} p2.Persist.PrivateNodeKey = key.NodePrivate{} p2.Persist.OldPrivateNodeKey = key.NodePrivate{} - p2.Persist.NetworkLockKey = key.NLPrivate{} return p2.View() } @@ -943,13 +940,11 @@ func (b *LocalBackend) UpdateStatus(sb *ipnstate.StatusBuilder) { s.Health = append(s.Health, m) } if b.netMap != nil { - s.CertDomains = append([]string(nil), b.netMap.DNS.CertDomains...) s.MagicDNSSuffix = b.netMap.MagicDNSSuffix() if s.CurrentTailnet == nil { s.CurrentTailnet = &ipnstate.TailnetStatus{} } s.CurrentTailnet.MagicDNSSuffix = b.netMap.MagicDNSSuffix() - s.CurrentTailnet.MagicDNSEnabled = b.netMap.DNS.Proxied s.CurrentTailnet.Name = b.netMap.Domain if prefs := b.pm.CurrentPrefs(); prefs.Valid() { if !prefs.RouteAll() && b.netMap.AnyPeersAdvertiseRoutes() { @@ -4943,37 +4938,6 @@ func (b *LocalBackend) OfferingExitNode() bool { return def4 && def6 } -// allowExitNodeDNSProxyToServeName reports whether the Exit Node DNS -// proxy is allowed to serve responses for the provided DNS name. -func (b *LocalBackend) allowExitNodeDNSProxyToServeName(name string) bool { - b.mu.Lock() - defer b.mu.Unlock() - nm := b.netMap - if nm == nil { - return false - } - name = strings.ToLower(name) - for _, bad := range nm.DNS.ExitNodeFilteredSet { - if bad == "" { - // Invalid, ignore. - continue - } - if bad[0] == '.' { - // Entries beginning with a dot are suffix matches. - if dnsname.HasSuffix(name, bad) { - return false - } - continue - } - // Otherwise entries are exact matches. They're - // guaranteed to be lowercase already. - if name == bad { - return false - } - } - return true -} - // SetExpiry updates the expiry of the current node key to t, as long as it's // only sooner than the old expiry. // @@ -5019,32 +4983,6 @@ func exitNodeCanProxyDNS(nm *netmap.NetworkMap, peers map[tailcfg.NodeID]tailcfg return "", false } -// wireguardExitNodeDNSResolvers returns the DNS resolvers to use for a -// WireGuard-only exit node, if it has resolver addresses. -func wireguardExitNodeDNSResolvers(nm *netmap.NetworkMap, peers map[tailcfg.NodeID]tailcfg.NodeView, exitNodeID tailcfg.StableNodeID) ([]*dnstype.Resolver, bool) { - if exitNodeID.IsZero() { - return nil, false - } - - for _, p := range peers { - if p.StableID() == exitNodeID { - if p.IsWireGuardOnly() { - resolvers := p.ExitNodeDNSResolvers() - if !resolvers.IsNil() && resolvers.Len() > 0 { - copies := make([]*dnstype.Resolver, resolvers.Len()) - for i, r := range resolvers.All() { - copies[i] = r.AsStruct() - } - return copies, true - } - } - return nil, false - } - } - - return nil, false -} - func peerCanProxyDNS(p tailcfg.NodeView) bool { if p.Cap() >= 26 { // Actually added at 25 diff --git a/ipn/localapi/localapi.go b/ipn/localapi/localapi.go index 61ab38b8d..48568a6ee 100644 --- a/ipn/localapi/localapi.go +++ b/ipn/localapi/localapi.go @@ -6,7 +6,6 @@ package localapi import ( "bytes" - "cmp" "encoding/json" "errors" "fmt" @@ -29,7 +28,6 @@ import ( "tailscale.com/ipn/ipnauth" "tailscale.com/ipn/ipnlocal" "tailscale.com/ipn/ipnstate" - "tailscale.com/net/netutil" "tailscale.com/tailcfg" "tailscale.com/tstime" "tailscale.com/types/key" @@ -62,7 +60,6 @@ var handler = map[string]localAPIHandler{ "bugreport": (*Handler).serveBugReport, "check-ip-forwarding": (*Handler).serveCheckIPForwarding, "check-prefs": (*Handler).serveCheckPrefs, - "dial": (*Handler).serveDial, "disconnect-control": (*Handler).disconnectControl, "goroutines": (*Handler).serveGoroutines, "handle-push-message": (*Handler).serveHandlePushMessage, @@ -953,65 +950,6 @@ func (h *Handler) servePing(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(res) } -func (h *Handler) serveDial(w http.ResponseWriter, r *http.Request) { - if r.Method != "POST" { - http.Error(w, "POST required", http.StatusMethodNotAllowed) - return - } - const upgradeProto = "ts-dial" - if !strings.Contains(r.Header.Get("Connection"), "upgrade") || - r.Header.Get("Upgrade") != upgradeProto { - http.Error(w, "bad ts-dial upgrade", http.StatusBadRequest) - return - } - hostStr, portStr := r.Header.Get("Dial-Host"), r.Header.Get("Dial-Port") - if hostStr == "" || portStr == "" { - http.Error(w, "missing Dial-Host or Dial-Port header", http.StatusBadRequest) - return - } - hijacker, ok := w.(http.Hijacker) - if !ok { - http.Error(w, "make request over HTTP/1", http.StatusBadRequest) - return - } - - network := cmp.Or(r.Header.Get("Dial-Network"), "tcp") - - addr := net.JoinHostPort(hostStr, portStr) - outConn, err := h.b.Dialer().UserDial(r.Context(), network, addr) - if err != nil { - http.Error(w, "dial failure: "+err.Error(), http.StatusBadGateway) - return - } - defer outConn.Close() - - w.Header().Set("Upgrade", upgradeProto) - w.Header().Set("Connection", "upgrade") - w.WriteHeader(http.StatusSwitchingProtocols) - - reqConn, brw, err := hijacker.Hijack() - if err != nil { - h.logf("localapi dial Hijack error: %v", err) - return - } - defer reqConn.Close() - if err := brw.Flush(); err != nil { - return - } - reqConn = netutil.NewDrainBufConn(reqConn, brw.Reader) - - errc := make(chan error, 1) - go func() { - _, err := io.Copy(reqConn, outConn) - errc <- err - }() - go func() { - _, err := io.Copy(outConn, reqConn) - errc <- err - }() - <-errc -} - func (h *Handler) serveSetPushDeviceToken(w http.ResponseWriter, r *http.Request) { if !h.PermitWrite { http.Error(w, "set push device token access denied", http.StatusForbidden) diff --git a/net/tsdial/dnsmap.go b/net/tsdial/dnsmap.go index 2ef1cb1f1..1581eb1c9 100644 --- a/net/tsdial/dnsmap.go +++ b/net/tsdial/dnsmap.go @@ -11,9 +11,6 @@ import ( "net/netip" "strconv" "strings" - - "tailscale.com/types/netmap" - "tailscale.com/util/dnsname" ) // dnsMap maps MagicDNS names (both base + FQDN) to their first IP. @@ -28,55 +25,6 @@ func canonMapKey(s string) string { return strings.ToLower(strings.TrimSuffix(s, ".")) } -func dnsMapFromNetworkMap(nm *netmap.NetworkMap) dnsMap { - if nm == nil { - return nil - } - ret := make(dnsMap) - suffix := nm.MagicDNSSuffix() - have4 := false - addrs := nm.GetAddresses() - if nm.Name != "" && addrs.Len() > 0 { - ip := addrs.At(0).Addr() - ret[canonMapKey(nm.Name)] = ip - if dnsname.HasSuffix(nm.Name, suffix) { - ret[canonMapKey(dnsname.TrimSuffix(nm.Name, suffix))] = ip - } - for _, p := range addrs.All() { - if p.Addr().Is4() { - have4 = true - } - } - } - for _, p := range nm.Peers { - if p.Name() == "" { - continue - } - for _, pfx := range p.Addresses().All() { - ip := pfx.Addr() - if ip.Is4() && !have4 { - continue - } - ret[canonMapKey(p.Name())] = ip - if dnsname.HasSuffix(p.Name(), suffix) { - ret[canonMapKey(dnsname.TrimSuffix(p.Name(), suffix))] = ip - } - break - } - } - for _, rec := range nm.DNS.ExtraRecords { - if rec.Type != "" { - continue - } - ip, err := netip.ParseAddr(rec.Value) - if err != nil { - continue - } - ret[canonMapKey(rec.Name)] = ip - } - return ret -} - // errUnresolved is a sentinel error returned by dnsMap.resolveMemory. var errUnresolved = errors.New("address well formed but not resolved") diff --git a/net/tsdial/tsdial.go b/net/tsdial/tsdial.go index 4e5a0e545..ab9e614b5 100644 --- a/net/tsdial/tsdial.go +++ b/net/tsdial/tsdial.go @@ -21,13 +21,11 @@ import ( "tailscale.com/net/netknob" "tailscale.com/net/netmon" "tailscale.com/net/netns" - "tailscale.com/net/tsaddr" "tailscale.com/types/logger" "tailscale.com/types/netmap" "tailscale.com/util/clientmetric" "tailscale.com/util/mak" "tailscale.com/util/testenv" - "tailscale.com/version" ) // NewDialer returns a new Dialer that can dial out of tailscaled. @@ -74,7 +72,6 @@ type Dialer struct { mu sync.Mutex closed bool - dns dnsMap tunName string // tun device name netMon *netmon.Monitor netMonUnregister func() @@ -264,47 +261,9 @@ func (d *Dialer) PeerDialControlFunc() func(network, address string, c syscall.R // SetNetMap sets the current network map and notably, the DNS names // in its DNS configuration. func (d *Dialer) SetNetMap(nm *netmap.NetworkMap) { - m := dnsMapFromNetworkMap(nm) d.mu.Lock() defer d.mu.Unlock() - d.dns = m -} - -// userDialResolve resolves addr as if a user initiating the dial. (e.g. from a -// SOCKS or HTTP outbound proxy) -func (d *Dialer) userDialResolve(ctx context.Context, network, addr string) (netip.AddrPort, error) { - d.mu.Lock() - dns := d.dns - d.mu.Unlock() - - // MagicDNS or otherwise baked into the NetworkMap? Try that first. - ipp, err := dns.resolveMemory(ctx, network, addr) - if err != errUnresolved { - return ipp, err - } - - // Otherwise, hit the network. - - // TODO(bradfitz): wire up net/dnscache too. - - host, port, err := splitHostPort(addr) - if err != nil { - // addr is malformed. - return netip.AddrPort{}, err - } - - var r net.Resolver - - ips, err := r.LookupIP(ctx, ipNetOfNetwork(network), host) - if err != nil { - return netip.AddrPort{}, err - } - if len(ips) == 0 { - return netip.AddrPort{}, fmt.Errorf("DNS lookup returned no results for %q", host) - } - ip, _ := netip.AddrFromSlice(ips[0]) - return netip.AddrPortFrom(ip.Unmap(), port), nil } // ipNetOfNetwork returns "ip", "ip4", or "ip6" corresponding @@ -365,44 +324,6 @@ func (d *Dialer) SystemDial(ctx context.Context, network, addr string) (net.Conn }, nil } -// UserDial connects to the provided network address as if a user were -// initiating the dial. (e.g. from a SOCKS or HTTP outbound proxy) -func (d *Dialer) UserDial(ctx context.Context, network, addr string) (net.Conn, error) { - ipp, err := d.userDialResolve(ctx, network, addr) - if err != nil { - return nil, err - } - if d.UseNetstackForIP != nil && d.UseNetstackForIP(ipp.Addr()) { - if d.NetstackDialTCP == nil || d.NetstackDialUDP == nil { - return nil, errors.New("Dialer not initialized correctly") - } - if strings.HasPrefix(network, "udp") { - return d.NetstackDialUDP(ctx, ipp) - } - return d.NetstackDialTCP(ctx, ipp) - } - - if routes := d.routes.Load(); routes != nil { - if isTailscaleRoute, _ := routes.Lookup(ipp.Addr()); isTailscaleRoute { - return d.getPeerDialer().DialContext(ctx, network, ipp.String()) - } - - return d.SystemDial(ctx, network, ipp.String()) - } - - // Workaround for macOS for now: dial Tailscale IPs with peer dialer. - // TODO(bradfitz): fix dialing subnet routers, public IPs via exit nodes, - // etc. This is a temporary partial for macOS. We need to plumb ART tables & - // prefs & host routing table updates around in more places. We just don't - // know from the limited context here how to dial properly. - if version.IsMacGUIVariant() && tsaddr.IsTailscaleIP(ipp.Addr()) { - return d.getPeerDialer().DialContext(ctx, network, ipp.String()) - } - // TODO(bradfitz): netns, etc - var stdDialer net.Dialer - return stdDialer.DialContext(ctx, network, ipp.String()) -} - // dialPeerAPI connects to a Tailscale peer's peerapi over TCP. // // network must a "tcp" type, and addr must be an ip:port. Name resolution diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index 1ede0bd9b..6d5a2707a 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -8,7 +8,6 @@ package tailcfg //go:generate go run tailscale.com/cmd/viewer --type=User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,RegisterResponseAuth,RegisterRequest,DERPHomeParams,DERPRegion,DERPMap,DERPNode,SSHRule,SSHAction,SSHPrincipal,ControlDialPlan,Location,UserProfile --clonefunc import ( - "bytes" "cmp" "encoding/json" "errors" @@ -20,11 +19,9 @@ import ( "strings" "time" - "tailscale.com/types/dnstype" "tailscale.com/types/key" "tailscale.com/types/opt" "tailscale.com/types/structs" - "tailscale.com/types/tkatype" "tailscale.com/util/dnsname" "tailscale.com/util/slicesx" ) @@ -337,14 +334,13 @@ type Node struct { // Sharer, if non-zero, is the user who shared this node, if different than User. Sharer UserID `json:",omitempty"` - Key key.NodePublic - KeyExpiry time.Time // the zero value if this node does not expire - KeySignature tkatype.MarshaledSignature `json:",omitempty"` - Machine key.MachinePublic - DiscoKey key.DiscoPublic - Addresses []netip.Prefix // IP addresses of this Node directly - AllowedIPs []netip.Prefix // range of IP addresses to route to this node - Endpoints []netip.AddrPort `json:",omitempty"` // IP+port (public via STUN, and local LANs) + Key key.NodePublic + KeyExpiry time.Time // the zero value if this node does not expire + Machine key.MachinePublic + DiscoKey key.DiscoPublic + Addresses []netip.Prefix // IP addresses of this Node directly + AllowedIPs []netip.Prefix // range of IP addresses to route to this node + Endpoints []netip.AddrPort `json:",omitempty"` // IP+port (public via STUN, and local LANs) // DERP is this node's home DERP region ID integer, but shoved into an // IP:port string for legacy reasons. The IP address is always "127.3.3.40" @@ -484,10 +480,6 @@ type Node struct { // initiate connections, however outbound connections to it should still be // allowed. IsJailed bool `json:",omitempty"` - - // ExitNodeDNSResolvers is the list of DNS servers that should be used when this - // node is marked IsWireGuardOnly and being used as an exit node. - ExitNodeDNSResolvers []*dnstype.Resolver `json:",omitempty"` } // HasCap reports whether the node has the given capability. @@ -1215,7 +1207,6 @@ type RegisterRequest struct { NodeKey key.NodePublic OldNodeKey key.NodePublic - NLKey key.NLPublic Auth *RegisterResponseAuth `json:",omitempty"` // Expiry optionally specifies the requested key expiry. // The server policy may override. @@ -1230,13 +1221,6 @@ type RegisterRequest struct { // when it stops being active. Ephemeral bool `json:",omitempty"` - // NodeKeySignature is the node's own node-key signature, re-signed - // for its new node key using its network-lock key. - // - // This field is set when the client retries registration after learning - // its NodeKeySignature (which is in need of rotation). - NodeKeySignature tkatype.MarshaledSignature - // The following fields are not used for SignatureNone and are required for // SignatureV1: SignatureType SignatureType `json:",omitempty"` @@ -1264,10 +1248,6 @@ type RegisterResponse struct { MachineAuthorized bool // TODO(crawshaw): move to using MachineStatus AuthURL string // if set, authorization pending - // If set, this is the current node-key signature that needs to be - // re-signed for the node's new node-key. - NodeKeySignature tkatype.MarshaledSignature - // Error indicates that authorization failed. If this is non-empty, // other status fields should be ignored. Error string @@ -1643,93 +1623,7 @@ var FilterAllowAll = []FilterRule{ }, } -// DNSConfig is the DNS configuration. -type DNSConfig struct { - // Resolvers are the DNS resolvers to use, in order of preference. - Resolvers []*dnstype.Resolver `json:",omitempty"` - - // Routes maps DNS name suffixes to a set of DNS resolvers to - // use. It is used to implement "split DNS" and other advanced DNS - // routing overlays. - // - // Map keys are fully-qualified DNS name suffixes; they may - // optionally contain a trailing dot but no leading dot. - // - // If the value is an empty slice, that means the suffix should still - // be handled by Tailscale's built-in resolver (100.100.100.100), such - // as for the purpose of handling ExtraRecords. - Routes map[string][]*dnstype.Resolver `json:",omitempty"` - - // FallbackResolvers is like Resolvers, but is only used if a - // split DNS configuration is requested in a configuration that - // doesn't work yet without explicit default resolvers. - // https://github.com/tailscale/tailscale/issues/1743 - FallbackResolvers []*dnstype.Resolver `json:",omitempty"` - // Domains are the search domains to use. - // Search domains must be FQDNs, but *without* the trailing dot. - Domains []string `json:",omitempty"` - // Proxied turns on automatic resolution of hostnames for devices - // in the network map, aka MagicDNS. - // Despite the (legacy) name, does not necessarily cause request - // proxying to be enabled. - Proxied bool `json:",omitempty"` - - // The following fields are only set and used by - // MapRequest.Version >=9 and <14. - - // Nameservers are the IP addresses of the nameservers to use. - Nameservers []netip.Addr `json:",omitempty"` - - // CertDomains are the set of DNS names for which the control - // plane server will assist with provisioning TLS - // certificates. See SetDNSRequest, which can be used to - // answer dns-01 ACME challenges for e.g. LetsEncrypt. - // These names are FQDNs without trailing periods, and without - // any "_acme-challenge." prefix. - CertDomains []string `json:",omitempty"` - - // ExtraRecords contains extra DNS records to add to the - // MagicDNS config. - ExtraRecords []DNSRecord `json:",omitempty"` - - // ExitNodeFilteredSuffixes are the DNS suffixes that the - // node, when being an exit node DNS proxy, should not answer. - // - // The entries do not contain trailing periods and are always - // all lowercase. - // - // If an entry starts with a period, it's a suffix match (but - // suffix ".a.b" doesn't match "a.b"; a prefix is required). - // - // If an entry does not start with a period, it's an exact - // match. - // - // Matches are case insensitive. - ExitNodeFilteredSet []string `json:",omitempty"` - - // TempCorpIssue13969 is a temporary (2023-08-16) field for an internal hack day prototype. - // It contains a user inputed URL that should have a list of domains to be blocked. - // See https://github.com/tailscale/corp/issues/13969. - TempCorpIssue13969 string `json:",omitempty"` -} - -// DNSRecord is an extra DNS record to add to MagicDNS. -type DNSRecord struct { - // Name is the fully qualified domain name of - // the record to add. The trailing dot is optional. - Name string - - // Type is the DNS record type. - // Empty means A or AAAA, depending on value. - // Other values are currently ignored. - Type string `json:",omitempty"` - - // Value is the IP address in string form. - // TODO(bradfitz): if we ever add support for record types - // with non-UTF8 binary data, add ValueBytes []byte that - // would take precedence. - Value string -} +type DNSConfig struct{} // PingType is a string representing the kind of ping to perform. type PingType string @@ -2009,16 +1903,6 @@ type MapResponse struct { // ControlTime, if non-zero, is the current timestamp according to the control server. ControlTime *time.Time `json:",omitempty"` - // TKAInfo describes the control plane's view of tailnet - // key authority (TKA) state. - // - // An initial nil TKAInfo indicates that the control plane - // believes TKA should not be enabled. An initial non-nil TKAInfo - // indicates the control plane believes TKA should be enabled. - // A nil TKAInfo in a mapresponse stream (i.e. a 'delta' mapresponse) - // indicates no change from the value sent earlier. - TKAInfo *TKAInfo `json:",omitempty"` - // DomainDataPlaneAuditLogID, if non-empty, is the per-tailnet log ID to be // used when writing data plane audit logs. DomainDataPlaneAuditLogID string `json:",omitempty"` @@ -2154,7 +2038,6 @@ func (n *Node) Equal(n2 *Node) bool { n.UnsignedPeerAPIOnly == n2.UnsignedPeerAPIOnly && n.Key == n2.Key && n.KeyExpiry.Equal(n2.KeyExpiry) && - bytes.Equal(n.KeySignature, n2.KeySignature) && n.Machine == n2.Machine && n.DiscoKey == n2.DiscoKey && eqPtr(n.Online, n2.Online) && @@ -2901,10 +2784,6 @@ type PeerChange struct { // Key, if non-nil, means that the NodeID's wireguard public key changed. Key *key.NodePublic `json:",omitempty"` - // KeySignature, if non-nil, means that the signature of the wireguard - // public key has changed. - KeySignature tkatype.MarshaledSignature `json:",omitempty"` - // DiscoKey, if non-nil, means that the NodeID's discokey changed. DiscoKey *key.DiscoPublic `json:",omitempty"` diff --git a/tailcfg/tailcfg_clone.go b/tailcfg/tailcfg_clone.go index d282719b7..c3f568383 100644 --- a/tailcfg/tailcfg_clone.go +++ b/tailcfg/tailcfg_clone.go @@ -10,12 +10,10 @@ import ( "net/netip" "time" - "tailscale.com/types/dnstype" "tailscale.com/types/key" "tailscale.com/types/opt" "tailscale.com/types/ptr" "tailscale.com/types/structs" - "tailscale.com/types/tkatype" ) // Clone makes a deep copy of User. @@ -45,7 +43,6 @@ func (src *Node) Clone() *Node { } dst := new(Node) *dst = *src - dst.KeySignature = append(src.KeySignature[:0:0], src.KeySignature...) dst.Addresses = append(src.Addresses[:0:0], src.Addresses...) dst.AllowedIPs = append(src.AllowedIPs[:0:0], src.AllowedIPs...) dst.Endpoints = append(src.Endpoints[:0:0], src.Endpoints...) @@ -71,16 +68,6 @@ func (src *Node) Clone() *Node { if dst.SelfNodeV6MasqAddrForThisPeer != nil { dst.SelfNodeV6MasqAddrForThisPeer = ptr.To(*src.SelfNodeV6MasqAddrForThisPeer) } - if src.ExitNodeDNSResolvers != nil { - dst.ExitNodeDNSResolvers = make([]*dnstype.Resolver, len(src.ExitNodeDNSResolvers)) - for i := range dst.ExitNodeDNSResolvers { - if src.ExitNodeDNSResolvers[i] == nil { - dst.ExitNodeDNSResolvers[i] = nil - } else { - dst.ExitNodeDNSResolvers[i] = src.ExitNodeDNSResolvers[i].Clone() - } - } - } return dst } @@ -93,7 +80,6 @@ var _NodeCloneNeedsRegeneration = Node(struct { Sharer UserID Key key.NodePublic KeyExpiry time.Time - KeySignature tkatype.MarshaledSignature Machine key.MachinePublic DiscoKey key.DiscoPublic Addresses []netip.Prefix @@ -120,7 +106,6 @@ var _NodeCloneNeedsRegeneration = Node(struct { SelfNodeV6MasqAddrForThisPeer *netip.Addr IsWireGuardOnly bool IsJailed bool - ExitNodeDNSResolvers []*dnstype.Resolver }{}) // Clone makes a deep copy of Hostinfo. @@ -243,52 +228,11 @@ func (src *DNSConfig) Clone() *DNSConfig { } dst := new(DNSConfig) *dst = *src - if src.Resolvers != nil { - dst.Resolvers = make([]*dnstype.Resolver, len(src.Resolvers)) - for i := range dst.Resolvers { - if src.Resolvers[i] == nil { - dst.Resolvers[i] = nil - } else { - dst.Resolvers[i] = src.Resolvers[i].Clone() - } - } - } - if dst.Routes != nil { - dst.Routes = map[string][]*dnstype.Resolver{} - for k := range src.Routes { - dst.Routes[k] = append([]*dnstype.Resolver{}, src.Routes[k]...) - } - } - if src.FallbackResolvers != nil { - dst.FallbackResolvers = make([]*dnstype.Resolver, len(src.FallbackResolvers)) - for i := range dst.FallbackResolvers { - if src.FallbackResolvers[i] == nil { - dst.FallbackResolvers[i] = nil - } else { - dst.FallbackResolvers[i] = src.FallbackResolvers[i].Clone() - } - } - } - dst.Domains = append(src.Domains[:0:0], src.Domains...) - dst.Nameservers = append(src.Nameservers[:0:0], src.Nameservers...) - dst.CertDomains = append(src.CertDomains[:0:0], src.CertDomains...) - dst.ExtraRecords = append(src.ExtraRecords[:0:0], src.ExtraRecords...) - dst.ExitNodeFilteredSet = append(src.ExitNodeFilteredSet[:0:0], src.ExitNodeFilteredSet...) return dst } // A compilation failure here means this code must be regenerated, with the command at the top of this file. var _DNSConfigCloneNeedsRegeneration = DNSConfig(struct { - Resolvers []*dnstype.Resolver - Routes map[string][]*dnstype.Resolver - FallbackResolvers []*dnstype.Resolver - Domains []string - Proxied bool - Nameservers []netip.Addr - CertDomains []string - ExtraRecords []DNSRecord - ExitNodeFilteredSet []string - TempCorpIssue13969 string }{}) // Clone makes a deep copy of RegisterResponse. @@ -299,7 +243,6 @@ func (src *RegisterResponse) Clone() *RegisterResponse { } dst := new(RegisterResponse) *dst = *src - dst.NodeKeySignature = append(src.NodeKeySignature[:0:0], src.NodeKeySignature...) return dst } @@ -310,7 +253,6 @@ var _RegisterResponseCloneNeedsRegeneration = RegisterResponse(struct { NodeKeyExpired bool MachineAuthorized bool AuthURL string - NodeKeySignature tkatype.MarshaledSignature Error string }{}) @@ -345,7 +287,6 @@ func (src *RegisterRequest) Clone() *RegisterRequest { *dst = *src dst.Auth = src.Auth.Clone() dst.Hostinfo = src.Hostinfo.Clone() - dst.NodeKeySignature = append(src.NodeKeySignature[:0:0], src.NodeKeySignature...) if dst.Timestamp != nil { dst.Timestamp = ptr.To(*src.Timestamp) } @@ -356,22 +297,20 @@ func (src *RegisterRequest) Clone() *RegisterRequest { // A compilation failure here means this code must be regenerated, with the command at the top of this file. var _RegisterRequestCloneNeedsRegeneration = RegisterRequest(struct { - _ structs.Incomparable - Version CapabilityVersion - NodeKey key.NodePublic - OldNodeKey key.NodePublic - NLKey key.NLPublic - Auth *RegisterResponseAuth - Expiry time.Time - Followup string - Hostinfo *Hostinfo - Ephemeral bool - NodeKeySignature tkatype.MarshaledSignature - SignatureType SignatureType - Timestamp *time.Time - DeviceCert []byte - Signature []byte - Tailnet string + _ structs.Incomparable + Version CapabilityVersion + NodeKey key.NodePublic + OldNodeKey key.NodePublic + Auth *RegisterResponseAuth + Expiry time.Time + Followup string + Hostinfo *Hostinfo + Ephemeral bool + SignatureType SignatureType + Timestamp *time.Time + DeviceCert []byte + Signature []byte + Tailnet string }{}) // Clone makes a deep copy of DERPHomeParams. diff --git a/tailcfg/tailcfg_view.go b/tailcfg/tailcfg_view.go index 774a18258..5860ab783 100644 --- a/tailcfg/tailcfg_view.go +++ b/tailcfg/tailcfg_view.go @@ -11,11 +11,9 @@ import ( "net/netip" "time" - "tailscale.com/types/dnstype" "tailscale.com/types/key" "tailscale.com/types/opt" "tailscale.com/types/structs" - "tailscale.com/types/tkatype" "tailscale.com/types/views" ) @@ -124,16 +122,13 @@ func (v *NodeView) UnmarshalJSON(b []byte) error { return nil } -func (v NodeView) ID() NodeID { return v.ж.ID } -func (v NodeView) StableID() StableNodeID { return v.ж.StableID } -func (v NodeView) Name() string { return v.ж.Name } -func (v NodeView) User() UserID { return v.ж.User } -func (v NodeView) Sharer() UserID { return v.ж.Sharer } -func (v NodeView) Key() key.NodePublic { return v.ж.Key } -func (v NodeView) KeyExpiry() time.Time { return v.ж.KeyExpiry } -func (v NodeView) KeySignature() views.ByteSlice[tkatype.MarshaledSignature] { - return views.ByteSliceOf(v.ж.KeySignature) -} +func (v NodeView) ID() NodeID { return v.ж.ID } +func (v NodeView) StableID() StableNodeID { return v.ж.StableID } +func (v NodeView) Name() string { return v.ж.Name } +func (v NodeView) User() UserID { return v.ж.User } +func (v NodeView) Sharer() UserID { return v.ж.Sharer } +func (v NodeView) Key() key.NodePublic { return v.ж.Key } +func (v NodeView) KeyExpiry() time.Time { return v.ж.KeyExpiry } func (v NodeView) Machine() key.MachinePublic { return v.ж.Machine } func (v NodeView) DiscoKey() key.DiscoPublic { return v.ж.DiscoKey } func (v NodeView) Addresses() views.Slice[netip.Prefix] { return views.SliceOf(v.ж.Addresses) } @@ -188,11 +183,8 @@ func (v NodeView) SelfNodeV6MasqAddrForThisPeer() *netip.Addr { return &x } -func (v NodeView) IsWireGuardOnly() bool { return v.ж.IsWireGuardOnly } -func (v NodeView) IsJailed() bool { return v.ж.IsJailed } -func (v NodeView) ExitNodeDNSResolvers() views.SliceView[*dnstype.Resolver, dnstype.ResolverView] { - return views.SliceOfViews[*dnstype.Resolver, dnstype.ResolverView](v.ж.ExitNodeDNSResolvers) -} +func (v NodeView) IsWireGuardOnly() bool { return v.ж.IsWireGuardOnly } +func (v NodeView) IsJailed() bool { return v.ж.IsJailed } func (v NodeView) Equal(v2 NodeView) bool { return v.ж.Equal(v2.ж) } // A compilation failure here means this code must be regenerated, with the command at the top of this file. @@ -204,7 +196,6 @@ var _NodeViewNeedsRegeneration = Node(struct { Sharer UserID Key key.NodePublic KeyExpiry time.Time - KeySignature tkatype.MarshaledSignature Machine key.MachinePublic DiscoKey key.DiscoPublic Addresses []netip.Prefix @@ -231,7 +222,6 @@ var _NodeViewNeedsRegeneration = Node(struct { SelfNodeV6MasqAddrForThisPeer *netip.Addr IsWireGuardOnly bool IsJailed bool - ExitNodeDNSResolvers []*dnstype.Resolver }{}) // View returns a readonly view of Hostinfo. @@ -552,40 +542,8 @@ func (v *DNSConfigView) UnmarshalJSON(b []byte) error { return nil } -func (v DNSConfigView) Resolvers() views.SliceView[*dnstype.Resolver, dnstype.ResolverView] { - return views.SliceOfViews[*dnstype.Resolver, dnstype.ResolverView](v.ж.Resolvers) -} - -func (v DNSConfigView) Routes() views.MapFn[string, []*dnstype.Resolver, views.SliceView[*dnstype.Resolver, dnstype.ResolverView]] { - return views.MapFnOf(v.ж.Routes, func(t []*dnstype.Resolver) views.SliceView[*dnstype.Resolver, dnstype.ResolverView] { - return views.SliceOfViews[*dnstype.Resolver, dnstype.ResolverView](t) - }) -} -func (v DNSConfigView) FallbackResolvers() views.SliceView[*dnstype.Resolver, dnstype.ResolverView] { - return views.SliceOfViews[*dnstype.Resolver, dnstype.ResolverView](v.ж.FallbackResolvers) -} -func (v DNSConfigView) Domains() views.Slice[string] { return views.SliceOf(v.ж.Domains) } -func (v DNSConfigView) Proxied() bool { return v.ж.Proxied } -func (v DNSConfigView) Nameservers() views.Slice[netip.Addr] { return views.SliceOf(v.ж.Nameservers) } -func (v DNSConfigView) CertDomains() views.Slice[string] { return views.SliceOf(v.ж.CertDomains) } -func (v DNSConfigView) ExtraRecords() views.Slice[DNSRecord] { return views.SliceOf(v.ж.ExtraRecords) } -func (v DNSConfigView) ExitNodeFilteredSet() views.Slice[string] { - return views.SliceOf(v.ж.ExitNodeFilteredSet) -} -func (v DNSConfigView) TempCorpIssue13969() string { return v.ж.TempCorpIssue13969 } - // A compilation failure here means this code must be regenerated, with the command at the top of this file. var _DNSConfigViewNeedsRegeneration = DNSConfig(struct { - Resolvers []*dnstype.Resolver - Routes map[string][]*dnstype.Resolver - FallbackResolvers []*dnstype.Resolver - Domains []string - Proxied bool - Nameservers []netip.Addr - CertDomains []string - ExtraRecords []DNSRecord - ExitNodeFilteredSet []string - TempCorpIssue13969 string }{}) // View returns a readonly view of RegisterResponse. @@ -638,10 +596,7 @@ func (v RegisterResponseView) Login() Login { return v.ж.Login } func (v RegisterResponseView) NodeKeyExpired() bool { return v.ж.NodeKeyExpired } func (v RegisterResponseView) MachineAuthorized() bool { return v.ж.MachineAuthorized } func (v RegisterResponseView) AuthURL() string { return v.ж.AuthURL } -func (v RegisterResponseView) NodeKeySignature() views.ByteSlice[tkatype.MarshaledSignature] { - return views.ByteSliceOf(v.ж.NodeKeySignature) -} -func (v RegisterResponseView) Error() string { return v.ж.Error } +func (v RegisterResponseView) Error() string { return v.ж.Error } // A compilation failure here means this code must be regenerated, with the command at the top of this file. var _RegisterResponseViewNeedsRegeneration = RegisterResponse(struct { @@ -650,7 +605,6 @@ var _RegisterResponseViewNeedsRegeneration = RegisterResponse(struct { NodeKeyExpired bool MachineAuthorized bool AuthURL string - NodeKeySignature tkatype.MarshaledSignature Error string }{}) @@ -764,16 +718,12 @@ func (v *RegisterRequestView) UnmarshalJSON(b []byte) error { func (v RegisterRequestView) Version() CapabilityVersion { return v.ж.Version } func (v RegisterRequestView) NodeKey() key.NodePublic { return v.ж.NodeKey } func (v RegisterRequestView) OldNodeKey() key.NodePublic { return v.ж.OldNodeKey } -func (v RegisterRequestView) NLKey() key.NLPublic { return v.ж.NLKey } func (v RegisterRequestView) Auth() RegisterResponseAuthView { return v.ж.Auth.View() } func (v RegisterRequestView) Expiry() time.Time { return v.ж.Expiry } func (v RegisterRequestView) Followup() string { return v.ж.Followup } func (v RegisterRequestView) Hostinfo() HostinfoView { return v.ж.Hostinfo.View() } func (v RegisterRequestView) Ephemeral() bool { return v.ж.Ephemeral } -func (v RegisterRequestView) NodeKeySignature() views.ByteSlice[tkatype.MarshaledSignature] { - return views.ByteSliceOf(v.ж.NodeKeySignature) -} -func (v RegisterRequestView) SignatureType() SignatureType { return v.ж.SignatureType } +func (v RegisterRequestView) SignatureType() SignatureType { return v.ж.SignatureType } func (v RegisterRequestView) Timestamp() *time.Time { if v.ж.Timestamp == nil { return nil @@ -792,22 +742,20 @@ func (v RegisterRequestView) Tailnet() string { return v.ж.Tailnet } // A compilation failure here means this code must be regenerated, with the command at the top of this file. var _RegisterRequestViewNeedsRegeneration = RegisterRequest(struct { - _ structs.Incomparable - Version CapabilityVersion - NodeKey key.NodePublic - OldNodeKey key.NodePublic - NLKey key.NLPublic - Auth *RegisterResponseAuth - Expiry time.Time - Followup string - Hostinfo *Hostinfo - Ephemeral bool - NodeKeySignature tkatype.MarshaledSignature - SignatureType SignatureType - Timestamp *time.Time - DeviceCert []byte - Signature []byte - Tailnet string + _ structs.Incomparable + Version CapabilityVersion + NodeKey key.NodePublic + OldNodeKey key.NodePublic + Auth *RegisterResponseAuth + Expiry time.Time + Followup string + Hostinfo *Hostinfo + Ephemeral bool + SignatureType SignatureType + Timestamp *time.Time + DeviceCert []byte + Signature []byte + Tailnet string }{}) // View returns a readonly view of DERPHomeParams. diff --git a/tailcfg/tka.go b/tailcfg/tka.go deleted file mode 100644 index 97fdcc0db..000000000 --- a/tailcfg/tka.go +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright (c) Tailscale Inc & AUTHORS -// SPDX-License-Identifier: BSD-3-Clause - -package tailcfg - -import ( - "tailscale.com/types/key" - "tailscale.com/types/tkatype" -) - -// TKAInitBeginRequest submits a genesis AUM to seed the creation of the -// tailnet's key authority. -type TKAInitBeginRequest struct { - // Version is the client's capabilities. - Version CapabilityVersion - - // NodeKey is the client's current node key. - NodeKey key.NodePublic - - // GenesisAUM is the initial (genesis) AUM that the node generated - // to bootstrap tailnet key authority state. - GenesisAUM tkatype.MarshaledAUM -} - -// TKASignInfo describes information about an existing node that needs -// to be signed into a node-key signature. -type TKASignInfo struct { - // NodeID is the ID of the node which needs a signature. It must - // correspond to NodePublic. - NodeID NodeID - // NodePublic is the node (Wireguard) public key which is being - // signed. - NodePublic key.NodePublic - - // RotationPubkey specifies the public key which may sign - // a NodeKeySignature (NKS), which rotates the node key. - // - // This is necessary so the node can rotate its node-key without - // talking to a node which holds a trusted network-lock key. - // It does this by nesting the original NKS in a 'rotation' NKS, - // which it then signs with the key corresponding to RotationPubkey. - // - // This field expects a raw ed25519 public key. - RotationPubkey []byte -} - -// TKAInitBeginResponse is the JSON response from a /tka/init/begin RPC. -// This structure describes node information which must be signed to -// complete initialization of the tailnets' key authority. -type TKAInitBeginResponse struct { - // NeedSignatures specify information about the nodes in your tailnet - // which need initial signatures to function once the tailnet key - // authority is in use. The generated signatures should then be - // submitted in a /tka/init/finish RPC. - NeedSignatures []TKASignInfo -} - -// TKAInitFinishRequest is the JSON request of a /tka/init/finish RPC. -// This RPC finalizes initialization of the tailnet key authority -// by submitting node-key signatures for all existing nodes. -type TKAInitFinishRequest struct { - // Version is the client's capabilities. - Version CapabilityVersion - - // NodeKey is the client's current node key. - NodeKey key.NodePublic - - // Signatures are serialized tka.NodeKeySignatures for all nodes - // in the tailnet. - Signatures map[NodeID]tkatype.MarshaledSignature - - // SupportDisablement is a disablement secret for Tailscale support. - // This is only generated if --gen-disablement-for-support is specified - // in an invocation to 'tailscale lock init'. - SupportDisablement []byte `json:",omitempty"` -} - -// TKAInitFinishResponse is the JSON response from a /tka/init/finish RPC. -// This schema describes the successful enablement of the tailnet's -// key authority. -type TKAInitFinishResponse struct { - // Nothing. (yet?) -} - -// TKAInfo encodes the control plane's view of tailnet key authority (TKA) -// state. This information is transmitted as part of the MapResponse. -type TKAInfo struct { - // Head describes the hash of the latest AUM applied to the authority. - // Head is encoded as tka.AUMHash.MarshalText. - // - // If the Head state differs to that known locally, the node should perform - // synchronization via a separate RPC. - Head string `json:",omitempty"` - - // Disabled indicates the control plane believes TKA should be disabled, - // and the node should reach out to fetch a disablement - // secret. If the disablement secret verifies, then the node should then - // disable TKA locally. - // This field exists to disambiguate a nil TKAInfo in a delta mapresponse - // from a nil TKAInfo indicating TKA should be disabled. - Disabled bool `json:",omitempty"` -} - -// TKABootstrapRequest is sent by a node to get information necessary for -// enabling or disabling the tailnet key authority. -type TKABootstrapRequest struct { - // Version is the client's capabilities. - Version CapabilityVersion - - // NodeKey is the client's current node key. - NodeKey key.NodePublic - - // Head represents the node's head AUMHash (tka.Authority.Head), if - // network lock is enabled. - Head string -} - -// TKABootstrapResponse encodes values necessary to enable or disable -// the tailnet key authority (TKA). -type TKABootstrapResponse struct { - // GenesisAUM returns the initial AUM necessary to initialize TKA. - GenesisAUM tkatype.MarshaledAUM `json:",omitempty"` - - // DisablementSecret encodes a secret necessary to disable TKA. - DisablementSecret []byte `json:",omitempty"` -} - -// TKASyncOfferRequest encodes a request to synchronize tailnet key authority -// state (TKA). Values of type tka.AUMHash are encoded as strings in their -// MarshalText form. -type TKASyncOfferRequest struct { - // Version is the client's capabilities. - Version CapabilityVersion - - // NodeKey is the client's current node key. - NodeKey key.NodePublic - - // Head represents the node's head AUMHash (tka.Authority.Head). This - // corresponds to tka.SyncOffer.Head. - Head string - // Ancestors represents a selection of ancestor AUMHash values ascending - // from the current head. This corresponds to tka.SyncOffer.Ancestors. - Ancestors []string -} - -// TKASyncOfferResponse encodes a response in synchronizing a node's -// tailnet key authority state. Values of type tka.AUMHash are encoded as -// strings in their MarshalText form. -type TKASyncOfferResponse struct { - // Head represents the control plane's head AUMHash (tka.Authority.Head). - // This corresponds to tka.SyncOffer.Head. - Head string - // Ancestors represents a selection of ancestor AUMHash values ascending - // from the control plane's head. This corresponds to - // tka.SyncOffer.Ancestors. - Ancestors []string - // MissingAUMs encodes AUMs that the control plane believes the node - // is missing. - MissingAUMs []tkatype.MarshaledAUM -} - -// TKASyncSendRequest encodes AUMs that a node believes the control plane -// is missing, and notifies control of its local TKA state (specifically -// the head hash). -type TKASyncSendRequest struct { - // Version is the client's capabilities. - Version CapabilityVersion - - // NodeKey is the client's current node key. - NodeKey key.NodePublic - - // Head represents the node's head AUMHash (tka.Authority.Head) after - // applying any AUMs from the sync-offer response. - // It is encoded as tka.AUMHash.MarshalText. - Head string - - // MissingAUMs encodes AUMs that the node believes the control plane - // is missing. - MissingAUMs []tkatype.MarshaledAUM - - // Interactive is true if additional error checking should be performed as - // the request is on behalf of an interactive operation (e.g., an - // administrator publishing new changes) as opposed to an automatic - // synchronization that may be reporting lost data. - Interactive bool -} - -// TKASyncSendResponse encodes the control plane's response to a node -// submitting AUMs during AUM synchronization. -type TKASyncSendResponse struct { - // Head represents the control plane's head AUMHash (tka.Authority.Head), - // after applying the missing AUMs. - Head string -} - -// TKADisableRequest disables network-lock across the tailnet using the -// provided disablement secret. -// -// This is the request schema for a /tka/disable noise RPC. -type TKADisableRequest struct { - // Version is the client's capabilities. - Version CapabilityVersion - - // NodeKey is the client's current node key. - NodeKey key.NodePublic - - // Head represents the node's head AUMHash (tka.Authority.Head). - // It is encoded as tka.AUMHash.MarshalText. - Head string - - // DisablementSecret encodes the secret necessary to disable TKA. - DisablementSecret []byte -} - -// TKADisableResponse is the JSON response from a /tka/disable RPC. -// This schema describes the successful disablement of the tailnet's -// key authority. -type TKADisableResponse struct { - // Nothing. (yet?) -} - -// TKASubmitSignatureRequest transmits a node-key signature to the control plane. -// -// This is the request schema for a /tka/sign noise RPC. -type TKASubmitSignatureRequest struct { - // Version is the client's capabilities. - Version CapabilityVersion - - // NodeKey is the client's current node key. The node-key which - // is being signed is embedded in Signature. - NodeKey key.NodePublic - - // Signature encodes the node-key signature being submitted. - Signature tkatype.MarshaledSignature -} - -// TKASubmitSignatureResponse is the JSON response from a /tka/sign RPC. -type TKASubmitSignatureResponse struct { - // Nothing. (yet?) -} - -// TKASignaturesUsingKeyRequest asks the control plane for -// all signatures which are signed by the provided keyID. -// -// This is the request schema for a /tka/affected-sigs RPC. -type TKASignaturesUsingKeyRequest struct { - // Version is the client's capabilities. - Version CapabilityVersion - - // NodeKey is the client's current node key. - NodeKey key.NodePublic - - // KeyID is the key we are querying using. - KeyID tkatype.KeyID -} - -// TKASignaturesUsingKeyResponse is the JSON response to -// a /tka/affected-sigs RPC. -// -// It enumerates all signatures which are signed by the -// queried keyID. -type TKASignaturesUsingKeyResponse struct { - Signatures []tkatype.MarshaledSignature -} diff --git a/types/key/nl.go b/types/key/nl.go deleted file mode 100644 index 50caed98c..000000000 --- a/types/key/nl.go +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright (c) Tailscale Inc & AUTHORS -// SPDX-License-Identifier: BSD-3-Clause - -package key - -import ( - "crypto/ed25519" - "crypto/subtle" - - "go4.org/mem" - "tailscale.com/types/structs" - "tailscale.com/types/tkatype" -) - -const ( - // nlPrivateHexPrefix is the prefix used to identify a - // hex-encoded tailnet-lock key. - nlPrivateHexPrefix = "nlpriv:" - - // nlPublicHexPrefix is the prefix used to identify the public - // side of a hex-encoded tailnet-lock key. - nlPublicHexPrefix = "nlpub:" - - // nlPublicHexPrefixCLI is the prefix used for tailnet-lock keys - // when shown on the CLI. - // It's not practical for us to change the prefix everywhere due to - // compatibility with existing clients, but we can support both prefixes - // as well as use the CLI form when presenting to the user. - nlPublicHexPrefixCLI = "tlpub:" -) - -// NLPrivate is a node-managed network-lock key, used for signing -// node-key signatures and authority update messages. -type NLPrivate struct { - _ structs.Incomparable // because == isn't constant-time - k [ed25519.PrivateKeySize]byte -} - -// IsZero reports whether k is the zero value. -func (k NLPrivate) IsZero() bool { - empty := NLPrivate{} - return subtle.ConstantTimeCompare(k.k[:], empty.k[:]) == 1 -} - -// NewNLPrivate creates and returns a new network-lock key. -func NewNLPrivate() NLPrivate { - // ed25519.GenerateKey 'clamps' the key, not that it - // matters given we don't do Diffie-Hellman. - _, priv, err := ed25519.GenerateKey(nil) // nil == crypto/rand - if err != nil { - panic(err) - } - - var out NLPrivate - copy(out.k[:], priv) - return out -} - -// MarshalText implements encoding.TextUnmarshaler. -func (k *NLPrivate) UnmarshalText(b []byte) error { - return parseHex(k.k[:], mem.B(b), mem.S(nlPrivateHexPrefix)) -} - -// AppendText implements encoding.TextAppender. -func (k NLPrivate) AppendText(b []byte) ([]byte, error) { - return appendHexKey(b, nlPrivateHexPrefix, k.k[:]), nil -} - -// MarshalText implements encoding.TextMarshaler. -func (k NLPrivate) MarshalText() ([]byte, error) { - return k.AppendText(nil) -} - -// Equal reports whether k and other are the same key. -func (k NLPrivate) Equal(other NLPrivate) bool { - return subtle.ConstantTimeCompare(k.k[:], other.k[:]) == 1 -} - -// Public returns the public component of this key. -func (k NLPrivate) Public() NLPublic { - var out NLPublic - copy(out.k[:], ed25519.PrivateKey(k.k[:]).Public().(ed25519.PublicKey)) - return out -} - -// KeyID returns an identifier for this key. -func (k NLPrivate) KeyID() tkatype.KeyID { - // The correct way to compute this is: - // return tka.Key{ - // Kind: tka.Key25519, - // Public: pub.k[:], - // }.ID() - // - // However, under the hood the key id for a 25519 - // key is just the public key, so we avoid the - // dependency on tka by just doing this ourselves. - pub := k.Public().k - return pub[:] -} - -// SignAUM implements tka.Signer. -func (k NLPrivate) SignAUM(sigHash tkatype.AUMSigHash) ([]tkatype.Signature, error) { - return []tkatype.Signature{{ - KeyID: k.KeyID(), - Signature: ed25519.Sign(ed25519.PrivateKey(k.k[:]), sigHash[:]), - }}, nil -} - -// SignNKS signs the tka.NodeKeySignature identified by sigHash. -func (k NLPrivate) SignNKS(sigHash tkatype.NKSSigHash) ([]byte, error) { - return ed25519.Sign(ed25519.PrivateKey(k.k[:]), sigHash[:]), nil -} - -// NLPublic is the public portion of a a NLPrivate. -type NLPublic struct { - k [ed25519.PublicKeySize]byte -} - -// NLPublicFromEd25519Unsafe converts an ed25519 public key into -// a type of NLPublic. -// -// New uses of this function should be avoided, as its possible to -// accidentally construct an NLPublic from a non network-lock key. -func NLPublicFromEd25519Unsafe(public ed25519.PublicKey) NLPublic { - var out NLPublic - copy(out.k[:], public) - return out -} - -// UnmarshalText implements encoding.TextUnmarshaler. This function -// is able to decode both the CLI form (tlpub:) & the -// regular form (nlpub:). -func (k *NLPublic) UnmarshalText(b []byte) error { - if mem.HasPrefix(mem.B(b), mem.S(nlPublicHexPrefix)) { - return parseHex(k.k[:], mem.B(b), mem.S(nlPublicHexPrefix)) - } - return parseHex(k.k[:], mem.B(b), mem.S(nlPublicHexPrefixCLI)) -} - -// AppendText implements encoding.TextAppender. -func (k NLPublic) AppendText(b []byte) ([]byte, error) { - return appendHexKey(b, nlPublicHexPrefix, k.k[:]), nil -} - -// MarshalText implements encoding.TextMarshaler, emitting a -// representation of the form nlpub:. -func (k NLPublic) MarshalText() ([]byte, error) { - return k.AppendText(nil) -} - -// CLIString returns a marshalled representation suitable for use -// with tailnet lock commands, of the form tlpub: instead of -// the nlpub: form emitted by MarshalText. Both forms can -// be decoded by UnmarshalText. -func (k NLPublic) CLIString() string { - return string(appendHexKey(nil, nlPublicHexPrefixCLI, k.k[:])) -} - -// Verifier returns a ed25519.PublicKey that can be used to -// verify signatures. -func (k NLPublic) Verifier() ed25519.PublicKey { - return ed25519.PublicKey(k.k[:]) -} - -// IsZero reports whether k is the zero value. -func (k NLPublic) IsZero() bool { - return k.Equal(NLPublic{}) -} - -// Equal reports whether k and other are the same key. -func (k NLPublic) Equal(other NLPublic) bool { - return subtle.ConstantTimeCompare(k.k[:], other.k[:]) == 1 -} - -// KeyID returns a tkatype.KeyID that can be used with a tka.Authority. -func (k NLPublic) KeyID() tkatype.KeyID { - return k.k[:] -} diff --git a/types/netmap/nodemut.go b/types/netmap/nodemut.go index 46fbaefc6..1096d17bf 100644 --- a/types/netmap/nodemut.go +++ b/types/netmap/nodemut.go @@ -165,7 +165,6 @@ func mapResponseContainsNonPatchFields(res *tailcfg.MapResponse) bool { res.UserProfiles != nil || res.Health != nil || res.SSHPolicy != nil || - res.TKAInfo != nil || res.DomainDataPlaneAuditLogID != "" || res.Debug != nil || res.ControlDialPlan != nil || diff --git a/types/persist/persist.go b/types/persist/persist.go index 8b555abd4..91fb62508 100644 --- a/types/persist/persist.go +++ b/types/persist/persist.go @@ -35,7 +35,6 @@ type Persist struct { PrivateNodeKey key.NodePrivate OldPrivateNodeKey key.NodePrivate // needed to request key rotation UserProfile tailcfg.UserProfile - NetworkLockKey key.NLPrivate NodeID tailcfg.StableNodeID // DisallowedTKAStateIDs stores the tka.State.StateID values which @@ -99,7 +98,6 @@ func (p *Persist) Equals(p2 *Persist) bool { p.PrivateNodeKey.Equal(p2.PrivateNodeKey) && p.OldPrivateNodeKey.Equal(p2.OldPrivateNodeKey) && p.UserProfile.Equal(&p2.UserProfile) && - p.NetworkLockKey.Equal(p2.NetworkLockKey) && p.NodeID == p2.NodeID && reflect.DeepEqual(nilIfEmpty(p.DisallowedTKAStateIDs), nilIfEmpty(p2.DisallowedTKAStateIDs)) } diff --git a/types/persist/persist_clone.go b/types/persist/persist_clone.go index 95dd65ac1..1daa01ec3 100644 --- a/types/persist/persist_clone.go +++ b/types/persist/persist_clone.go @@ -30,7 +30,6 @@ var _PersistCloneNeedsRegeneration = Persist(struct { PrivateNodeKey key.NodePrivate OldPrivateNodeKey key.NodePrivate UserProfile tailcfg.UserProfile - NetworkLockKey key.NLPrivate NodeID tailcfg.StableNodeID DisallowedTKAStateIDs []string }{}) diff --git a/types/persist/persist_view.go b/types/persist/persist_view.go index 1d479b3bf..ec1c78325 100644 --- a/types/persist/persist_view.go +++ b/types/persist/persist_view.go @@ -68,7 +68,6 @@ func (v PersistView) LegacyFrontendPrivateMachineKey() key.MachinePrivate { func (v PersistView) PrivateNodeKey() key.NodePrivate { return v.ж.PrivateNodeKey } func (v PersistView) OldPrivateNodeKey() key.NodePrivate { return v.ж.OldPrivateNodeKey } func (v PersistView) UserProfile() tailcfg.UserProfile { return v.ж.UserProfile } -func (v PersistView) NetworkLockKey() key.NLPrivate { return v.ж.NetworkLockKey } func (v PersistView) NodeID() tailcfg.StableNodeID { return v.ж.NodeID } func (v PersistView) DisallowedTKAStateIDs() views.Slice[string] { return views.SliceOf(v.ж.DisallowedTKAStateIDs) @@ -81,7 +80,6 @@ var _PersistViewNeedsRegeneration = Persist(struct { PrivateNodeKey key.NodePrivate OldPrivateNodeKey key.NodePrivate UserProfile tailcfg.UserProfile - NetworkLockKey key.NLPrivate NodeID tailcfg.StableNodeID DisallowedTKAStateIDs []string }{})