mirror of
https://github.com/tailscale/tailscale.git
synced 2026-05-08 21:56:48 +02:00
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 <bradfitz@tailscale.com>
This commit is contained in:
parent
e2d7f94f46
commit
c6c16fdc96
@ -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
|
||||
}
|
||||
|
||||
@ -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+
|
||||
|
||||
@ -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+
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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")
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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"`
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
264
tailcfg/tka.go
264
tailcfg/tka.go
@ -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
|
||||
}
|
||||
178
types/key/nl.go
178
types/key/nl.go
@ -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:<hex>) & the
|
||||
// regular form (nlpub:<hex>).
|
||||
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:<hex>.
|
||||
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:<hex> instead of
|
||||
// the nlpub:<hex> 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[:]
|
||||
}
|
||||
@ -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 ||
|
||||
|
||||
@ -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))
|
||||
}
|
||||
|
||||
@ -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
|
||||
}{})
|
||||
|
||||
@ -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
|
||||
}{})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user