Compare commits
14 Commits
Author | SHA1 | Date |
---|---|---|
|
e02da8ea72 | |
|
945a6dc677 | |
|
d0a385c4ea | |
|
6ee1ad3488 | |
|
d186897551 | |
|
d4d7eb0ac2 | |
|
b27de27db3 | |
|
76df8def85 | |
|
1bc9e46c17 | |
![]() |
ffaa6be8a4 | |
![]() |
7b1c3dfd28 | |
![]() |
f05a9f3e7f | |
![]() |
339397ab74 | |
![]() |
9d1a3a995c |
|
@ -72,7 +72,7 @@ func NewManualCertManager(certdir, hostname string) (certProvider, error) {
|
||||||
return nil, fmt.Errorf("can not load cert: %w", err)
|
return nil, fmt.Errorf("can not load cert: %w", err)
|
||||||
}
|
}
|
||||||
if err := x509Cert.VerifyHostname(hostname); err != nil {
|
if err := x509Cert.VerifyHostname(hostname); err != nil {
|
||||||
return nil, fmt.Errorf("cert invalid for hostname %q: %w", hostname, err)
|
// return nil, fmt.Errorf("cert invalid for hostname %q: %w", hostname, err)
|
||||||
}
|
}
|
||||||
return &manualCertManager{cert: &cert, hostname: hostname}, nil
|
return &manualCertManager{cert: &cert, hostname: hostname}, nil
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ func (m *manualCertManager) TLSConfig() *tls.Config {
|
||||||
|
|
||||||
func (m *manualCertManager) getCertificate(hi *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
func (m *manualCertManager) getCertificate(hi *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||||
if hi.ServerName != m.hostname {
|
if hi.ServerName != m.hostname {
|
||||||
return nil, fmt.Errorf("cert mismatch with hostname: %q", hi.ServerName)
|
//return nil, fmt.Errorf("cert mismatch with hostname: %q", hi.ServerName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a shallow copy of the cert so the caller can append to its
|
// Return a shallow copy of the cert so the caller can append to its
|
||||||
|
|
|
@ -287,6 +287,25 @@ func (nc *NoiseClient) GetSingleUseRoundTripper(ctx context.Context) (http.Round
|
||||||
return nil, nil, errors.New("[unexpected] failed to reserve a request on a connection")
|
return nil, nil, errors.New("[unexpected] failed to reserve a request on a connection")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// contextErr is an error that wraps another error and is used to indicate that
|
||||||
|
// the error was because a context expired.
|
||||||
|
type contextErr struct {
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e contextErr) Error() string {
|
||||||
|
return e.err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e contextErr) Unwrap() error {
|
||||||
|
return e.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// getConn returns a noiseConn that can be used to make requests to the
|
||||||
|
// coordination server. It may return a cached connection or create a new one.
|
||||||
|
// Dials are singleflighted, so concurrent calls to getConn may only dial once.
|
||||||
|
// As such, context values may not be respected as there are no guarantees that
|
||||||
|
// the context passed to getConn is the same as the context passed to dial.
|
||||||
func (nc *NoiseClient) getConn(ctx context.Context) (*noiseConn, error) {
|
func (nc *NoiseClient) getConn(ctx context.Context) (*noiseConn, error) {
|
||||||
nc.mu.Lock()
|
nc.mu.Lock()
|
||||||
if last := nc.last; last != nil && last.canTakeNewRequest() {
|
if last := nc.last; last != nil && last.canTakeNewRequest() {
|
||||||
|
@ -295,11 +314,35 @@ func (nc *NoiseClient) getConn(ctx context.Context) (*noiseConn, error) {
|
||||||
}
|
}
|
||||||
nc.mu.Unlock()
|
nc.mu.Unlock()
|
||||||
|
|
||||||
conn, err, _ := nc.sfDial.Do(struct{}{}, nc.dial)
|
for {
|
||||||
|
// We singeflight the dial to avoid making multiple connections, however
|
||||||
|
// that means that we can't simply cancel the dial if the context is
|
||||||
|
// canceled. Instead, we have to additionally check that the context
|
||||||
|
// which was canceled is our context and retry if our context is still
|
||||||
|
// valid.
|
||||||
|
conn, err, _ := nc.sfDial.Do(struct{}{}, func() (*noiseConn, error) {
|
||||||
|
c, err := nc.dial(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if ctx.Err() != nil {
|
||||||
|
return nil, contextErr{ctx.Err()}
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return conn, nil
|
return c, nil
|
||||||
|
})
|
||||||
|
var ce contextErr
|
||||||
|
if err == nil || !errors.As(err, &ce) {
|
||||||
|
return conn, err
|
||||||
|
}
|
||||||
|
if ctx.Err() == nil {
|
||||||
|
// The dial failed because of a context error, but our context
|
||||||
|
// is still valid. Retry.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// The dial failed because our context was canceled. Return the
|
||||||
|
// underlying error.
|
||||||
|
return nil, ce.Unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (nc *NoiseClient) RoundTrip(req *http.Request) (*http.Response, error) {
|
func (nc *NoiseClient) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
|
@ -344,7 +387,7 @@ func (nc *NoiseClient) Close() error {
|
||||||
|
|
||||||
// dial opens a new connection to tailcontrol, fetching the server noise key
|
// dial opens a new connection to tailcontrol, fetching the server noise key
|
||||||
// if not cached.
|
// if not cached.
|
||||||
func (nc *NoiseClient) dial() (*noiseConn, error) {
|
func (nc *NoiseClient) dial(ctx context.Context) (*noiseConn, error) {
|
||||||
nc.mu.Lock()
|
nc.mu.Lock()
|
||||||
connID := nc.nextID
|
connID := nc.nextID
|
||||||
nc.nextID++
|
nc.nextID++
|
||||||
|
@ -392,7 +435,7 @@ func (nc *NoiseClient) dial() (*noiseConn, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
timeout := time.Duration(timeoutSec * float64(time.Second))
|
timeout := time.Duration(timeoutSec * float64(time.Second))
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
clientConn, err := (&controlhttp.Dialer{
|
clientConn, err := (&controlhttp.Dialer{
|
||||||
|
|
|
@ -742,7 +742,6 @@ func (b *LocalBackend) populatePeerStatusLocked(sb *ipnstate.StatusBuilder) {
|
||||||
HostName: p.Hostinfo.Hostname(),
|
HostName: p.Hostinfo.Hostname(),
|
||||||
DNSName: p.Name,
|
DNSName: p.Name,
|
||||||
OS: p.Hostinfo.OS(),
|
OS: p.Hostinfo.OS(),
|
||||||
KeepAlive: p.KeepAlive,
|
|
||||||
LastSeen: lastSeen,
|
LastSeen: lastSeen,
|
||||||
Online: p.Online != nil && *p.Online,
|
Online: p.Online != nil && *p.Online,
|
||||||
ShareeNode: p.Hostinfo.ShareeNode(),
|
ShareeNode: p.Hostinfo.ShareeNode(),
|
||||||
|
@ -1014,7 +1013,7 @@ func (b *LocalBackend) setClientStatus(st controlclient.Status) {
|
||||||
|
|
||||||
// Perform all reconfiguration based on the netmap here.
|
// Perform all reconfiguration based on the netmap here.
|
||||||
if st.NetMap != nil {
|
if st.NetMap != nil {
|
||||||
b.capTailnetLock = hasCapability(st.NetMap, tailcfg.CapabilityTailnetLock)
|
b.capTailnetLock = hasCapability(st.NetMap, tailcfg.CapabilityTailnetLockAlpha)
|
||||||
|
|
||||||
b.mu.Unlock() // respect locking rules for tkaSyncIfNeeded
|
b.mu.Unlock() // respect locking rules for tkaSyncIfNeeded
|
||||||
if err := b.tkaSyncIfNeeded(st.NetMap, prefs.View()); err != nil {
|
if err := b.tkaSyncIfNeeded(st.NetMap, prefs.View()); err != nil {
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"tailscale.com/envknob"
|
||||||
"tailscale.com/health"
|
"tailscale.com/health"
|
||||||
"tailscale.com/ipn"
|
"tailscale.com/ipn"
|
||||||
"tailscale.com/ipn/ipnstate"
|
"tailscale.com/ipn/ipnstate"
|
||||||
|
@ -52,12 +53,20 @@ type tkaState struct {
|
||||||
filtered []ipnstate.TKAFilteredPeer
|
filtered []ipnstate.TKAFilteredPeer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// permitTKAInitLocked returns true if tailnet lock initialization may
|
||||||
|
// occur.
|
||||||
|
// b.mu must be held.
|
||||||
|
func (b *LocalBackend) permitTKAInitLocked() bool {
|
||||||
|
return envknob.UseWIPCode() || b.capTailnetLock
|
||||||
|
}
|
||||||
|
|
||||||
// tkaFilterNetmapLocked checks the signatures on each node key, dropping
|
// tkaFilterNetmapLocked checks the signatures on each node key, dropping
|
||||||
// nodes from the netmap whose signature does not verify.
|
// nodes from the netmap whose signature does not verify.
|
||||||
//
|
//
|
||||||
// b.mu must be held.
|
// b.mu must be held.
|
||||||
func (b *LocalBackend) tkaFilterNetmapLocked(nm *netmap.NetworkMap) {
|
func (b *LocalBackend) tkaFilterNetmapLocked(nm *netmap.NetworkMap) {
|
||||||
if b.tka == nil && !b.capTailnetLock {
|
// TODO(tom): Remove this guard for 1.35 and later.
|
||||||
|
if b.tka == nil && !b.permitTKAInitLocked() {
|
||||||
health.SetTKAHealth(nil)
|
health.SetTKAHealth(nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -144,7 +153,8 @@ func (b *LocalBackend) tkaSyncIfNeeded(nm *netmap.NetworkMap, prefs ipn.PrefsVie
|
||||||
b.mu.Lock() // take mu to protect access to synchronized fields.
|
b.mu.Lock() // take mu to protect access to synchronized fields.
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
if b.tka == nil && !b.capTailnetLock {
|
// TODO(tom): Remove this guard for 1.35 and later.
|
||||||
|
if b.tka == nil && !b.permitTKAInitLocked() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -473,9 +483,10 @@ func (b *LocalBackend) NetworkLockInit(keys []tka.Key, disablementValues [][]byt
|
||||||
var nlPriv key.NLPrivate
|
var nlPriv key.NLPrivate
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
|
|
||||||
if !b.capTailnetLock {
|
// TODO(tom): Remove this guard for 1.35 and later.
|
||||||
|
if !b.permitTKAInitLocked() {
|
||||||
b.mu.Unlock()
|
b.mu.Unlock()
|
||||||
return errors.New("not permitted to enable tailnet lock")
|
return errors.New("this feature is not yet complete, a later release may support this functionality")
|
||||||
}
|
}
|
||||||
|
|
||||||
if p := b.pm.CurrentPrefs(); p.Valid() && p.Persist().Valid() && !p.Persist().PrivateNodeKey().IsZero() {
|
if p := b.pm.CurrentPrefs(); p.Valid() && p.Persist().Valid() && !p.Persist().PrivateNodeKey().IsZero() {
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
"tailscale.com/control/controlclient"
|
"tailscale.com/control/controlclient"
|
||||||
|
"tailscale.com/envknob"
|
||||||
"tailscale.com/hostinfo"
|
"tailscale.com/hostinfo"
|
||||||
"tailscale.com/ipn"
|
"tailscale.com/ipn"
|
||||||
"tailscale.com/ipn/store/mem"
|
"tailscale.com/ipn/store/mem"
|
||||||
|
@ -65,6 +66,8 @@ func fakeNoiseServer(t *testing.T, handler http.HandlerFunc) (*httptest.Server,
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTKAEnablementFlow(t *testing.T) {
|
func TestTKAEnablementFlow(t *testing.T) {
|
||||||
|
envknob.Setenv("TAILSCALE_USE_WIP_CODE", "1")
|
||||||
|
defer envknob.Setenv("TAILSCALE_USE_WIP_CODE", "")
|
||||||
nodePriv := key.NewNode()
|
nodePriv := key.NewNode()
|
||||||
|
|
||||||
// Make a fake TKA authority, getting a usable genesis AUM which
|
// Make a fake TKA authority, getting a usable genesis AUM which
|
||||||
|
@ -147,7 +150,6 @@ func TestTKAEnablementFlow(t *testing.T) {
|
||||||
},
|
},
|
||||||
}).View()))
|
}).View()))
|
||||||
b := LocalBackend{
|
b := LocalBackend{
|
||||||
capTailnetLock: true,
|
|
||||||
varRoot: temp,
|
varRoot: temp,
|
||||||
cc: cc,
|
cc: cc,
|
||||||
ccAuto: cc,
|
ccAuto: cc,
|
||||||
|
@ -172,6 +174,8 @@ func TestTKAEnablementFlow(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTKADisablementFlow(t *testing.T) {
|
func TestTKADisablementFlow(t *testing.T) {
|
||||||
|
envknob.Setenv("TAILSCALE_USE_WIP_CODE", "1")
|
||||||
|
defer envknob.Setenv("TAILSCALE_USE_WIP_CODE", "")
|
||||||
nodePriv := key.NewNode()
|
nodePriv := key.NewNode()
|
||||||
|
|
||||||
// Make a fake TKA authority, to seed local state.
|
// Make a fake TKA authority, to seed local state.
|
||||||
|
@ -293,6 +297,9 @@ func TestTKADisablementFlow(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTKASync(t *testing.T) {
|
func TestTKASync(t *testing.T) {
|
||||||
|
envknob.Setenv("TAILSCALE_USE_WIP_CODE", "1")
|
||||||
|
defer envknob.Setenv("TAILSCALE_USE_WIP_CODE", "")
|
||||||
|
|
||||||
someKeyPriv := key.NewNLPrivate()
|
someKeyPriv := key.NewNLPrivate()
|
||||||
someKey := tka.Key{Kind: tka.Key25519, Public: someKeyPriv.Public().Verifier(), Votes: 1}
|
someKey := tka.Key{Kind: tka.Key25519, Public: someKeyPriv.Public().Verifier(), Votes: 1}
|
||||||
|
|
||||||
|
@ -531,6 +538,9 @@ func TestTKASync(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTKAFilterNetmap(t *testing.T) {
|
func TestTKAFilterNetmap(t *testing.T) {
|
||||||
|
envknob.Setenv("TAILSCALE_USE_WIP_CODE", "1")
|
||||||
|
defer envknob.Setenv("TAILSCALE_USE_WIP_CODE", "")
|
||||||
|
|
||||||
nlPriv := key.NewNLPrivate()
|
nlPriv := key.NewNLPrivate()
|
||||||
nlKey := tka.Key{Kind: tka.Key25519, Public: nlPriv.Public().Verifier(), Votes: 2}
|
nlKey := tka.Key{Kind: tka.Key25519, Public: nlPriv.Public().Verifier(), Votes: 2}
|
||||||
storage := &tka.Mem{}
|
storage := &tka.Mem{}
|
||||||
|
@ -587,6 +597,8 @@ func TestTKAFilterNetmap(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTKADisable(t *testing.T) {
|
func TestTKADisable(t *testing.T) {
|
||||||
|
envknob.Setenv("TAILSCALE_USE_WIP_CODE", "1")
|
||||||
|
defer envknob.Setenv("TAILSCALE_USE_WIP_CODE", "")
|
||||||
nodePriv := key.NewNode()
|
nodePriv := key.NewNode()
|
||||||
|
|
||||||
// Make a fake TKA authority, to seed local state.
|
// Make a fake TKA authority, to seed local state.
|
||||||
|
@ -680,6 +692,8 @@ func TestTKADisable(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTKASign(t *testing.T) {
|
func TestTKASign(t *testing.T) {
|
||||||
|
envknob.Setenv("TAILSCALE_USE_WIP_CODE", "1")
|
||||||
|
defer envknob.Setenv("TAILSCALE_USE_WIP_CODE", "")
|
||||||
nodePriv := key.NewNode()
|
nodePriv := key.NewNode()
|
||||||
toSign := key.NewNode()
|
toSign := key.NewNode()
|
||||||
nlPriv := key.NewNLPrivate()
|
nlPriv := key.NewNLPrivate()
|
||||||
|
@ -766,6 +780,8 @@ func TestTKASign(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTKAForceDisable(t *testing.T) {
|
func TestTKAForceDisable(t *testing.T) {
|
||||||
|
envknob.Setenv("TAILSCALE_USE_WIP_CODE", "1")
|
||||||
|
defer envknob.Setenv("TAILSCALE_USE_WIP_CODE", "")
|
||||||
nodePriv := key.NewNode()
|
nodePriv := key.NewNode()
|
||||||
|
|
||||||
// Make a fake TKA authority, to seed local state.
|
// Make a fake TKA authority, to seed local state.
|
||||||
|
|
|
@ -223,7 +223,6 @@ type PeerStatus struct {
|
||||||
LastSeen time.Time // last seen to tailcontrol; only present if offline
|
LastSeen time.Time // last seen to tailcontrol; only present if offline
|
||||||
LastHandshake time.Time // with local wireguard
|
LastHandshake time.Time // with local wireguard
|
||||||
Online bool // whether node is connected to the control plane
|
Online bool // whether node is connected to the control plane
|
||||||
KeepAlive bool
|
|
||||||
ExitNode bool // true if this is the currently selected exit node.
|
ExitNode bool // true if this is the currently selected exit node.
|
||||||
ExitNodeOption bool // true if this node can be an exit node (offered && approved)
|
ExitNodeOption bool // true if this node can be an exit node (offered && approved)
|
||||||
|
|
||||||
|
@ -437,9 +436,6 @@ func (sb *StatusBuilder) AddPeer(peer key.NodePublic, st *PeerStatus) {
|
||||||
if st.InEngine {
|
if st.InEngine {
|
||||||
e.InEngine = true
|
e.InEngine = true
|
||||||
}
|
}
|
||||||
if st.KeepAlive {
|
|
||||||
e.KeepAlive = true
|
|
||||||
}
|
|
||||||
if st.ExitNode {
|
if st.ExitNode {
|
||||||
e.ExitNode = true
|
e.ExitNode = true
|
||||||
}
|
}
|
||||||
|
|
|
@ -242,8 +242,6 @@ type Node struct {
|
||||||
// current node doesn't have permission to know.
|
// current node doesn't have permission to know.
|
||||||
Online *bool `json:",omitempty"`
|
Online *bool `json:",omitempty"`
|
||||||
|
|
||||||
KeepAlive bool `json:",omitempty"` // open and keep open a connection to this peer
|
|
||||||
|
|
||||||
MachineAuthorized bool `json:",omitempty"` // TODO(crawshaw): replace with MachineStatus
|
MachineAuthorized bool `json:",omitempty"` // TODO(crawshaw): replace with MachineStatus
|
||||||
|
|
||||||
// Capabilities are capabilities that the node has.
|
// Capabilities are capabilities that the node has.
|
||||||
|
@ -1284,7 +1282,7 @@ type DNSConfig struct {
|
||||||
// match.
|
// match.
|
||||||
//
|
//
|
||||||
// Matches are case insensitive.
|
// Matches are case insensitive.
|
||||||
ExitNodeFilteredSet []string
|
ExitNodeFilteredSet []string `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DNSRecord is an extra DNS record to add to MagicDNS.
|
// DNSRecord is an extra DNS record to add to MagicDNS.
|
||||||
|
@ -1852,8 +1850,11 @@ const (
|
||||||
// of connections to the default network interface on Darwin nodes.
|
// of connections to the default network interface on Darwin nodes.
|
||||||
CapabilityDebugDisableBindConnToInterface = "https://tailscale.com/cap/debug-disable-bind-conn-to-interface"
|
CapabilityDebugDisableBindConnToInterface = "https://tailscale.com/cap/debug-disable-bind-conn-to-interface"
|
||||||
|
|
||||||
// CapabilityTailnetLock indicates the node may initialize tailnet lock.
|
// CapabilityTailnetLockAlpha indicates the node is in the tailnet lock alpha,
|
||||||
CapabilityTailnetLock = "https://tailscale.com/cap/tailnet-lock-alpha"
|
// and initialization of tailnet lock may proceed.
|
||||||
|
//
|
||||||
|
// TODO(tom): Remove this for 1.35 and later.
|
||||||
|
CapabilityTailnetLockAlpha = "https://tailscale.com/cap/tailnet-lock-alpha"
|
||||||
|
|
||||||
// Inter-node capabilities as specified in the MapResponse.PacketFilter[].CapGrants.
|
// Inter-node capabilities as specified in the MapResponse.PacketFilter[].CapGrants.
|
||||||
|
|
||||||
|
|
|
@ -93,7 +93,6 @@ var _NodeCloneNeedsRegeneration = Node(struct {
|
||||||
PrimaryRoutes []netip.Prefix
|
PrimaryRoutes []netip.Prefix
|
||||||
LastSeen *time.Time
|
LastSeen *time.Time
|
||||||
Online *bool
|
Online *bool
|
||||||
KeepAlive bool
|
|
||||||
MachineAuthorized bool
|
MachineAuthorized bool
|
||||||
Capabilities []string
|
Capabilities []string
|
||||||
UnsignedPeerAPIOnly bool
|
UnsignedPeerAPIOnly bool
|
||||||
|
|
|
@ -347,7 +347,7 @@ func TestNodeEqual(t *testing.T) {
|
||||||
"Key", "KeyExpiry", "KeySignature", "Machine", "DiscoKey",
|
"Key", "KeyExpiry", "KeySignature", "Machine", "DiscoKey",
|
||||||
"Addresses", "AllowedIPs", "Endpoints", "DERP", "Hostinfo",
|
"Addresses", "AllowedIPs", "Endpoints", "DERP", "Hostinfo",
|
||||||
"Created", "Cap", "Tags", "PrimaryRoutes",
|
"Created", "Cap", "Tags", "PrimaryRoutes",
|
||||||
"LastSeen", "Online", "KeepAlive", "MachineAuthorized",
|
"LastSeen", "Online", "MachineAuthorized",
|
||||||
"Capabilities",
|
"Capabilities",
|
||||||
"UnsignedPeerAPIOnly",
|
"UnsignedPeerAPIOnly",
|
||||||
"ComputedName", "computedHostIfDifferent", "ComputedNameWithHost",
|
"ComputedName", "computedHostIfDifferent", "ComputedNameWithHost",
|
||||||
|
|
|
@ -168,7 +168,6 @@ func (v NodeView) Online() *bool {
|
||||||
return &x
|
return &x
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v NodeView) KeepAlive() bool { return v.ж.KeepAlive }
|
|
||||||
func (v NodeView) MachineAuthorized() bool { return v.ж.MachineAuthorized }
|
func (v NodeView) MachineAuthorized() bool { return v.ж.MachineAuthorized }
|
||||||
func (v NodeView) Capabilities() views.Slice[string] { return views.SliceOf(v.ж.Capabilities) }
|
func (v NodeView) Capabilities() views.Slice[string] { return views.SliceOf(v.ж.Capabilities) }
|
||||||
func (v NodeView) UnsignedPeerAPIOnly() bool { return v.ж.UnsignedPeerAPIOnly }
|
func (v NodeView) UnsignedPeerAPIOnly() bool { return v.ж.UnsignedPeerAPIOnly }
|
||||||
|
@ -210,7 +209,6 @@ var _NodeViewNeedsRegeneration = Node(struct {
|
||||||
PrimaryRoutes []netip.Prefix
|
PrimaryRoutes []netip.Prefix
|
||||||
LastSeen *time.Time
|
LastSeen *time.Time
|
||||||
Online *bool
|
Online *bool
|
||||||
KeepAlive bool
|
|
||||||
MachineAuthorized bool
|
MachineAuthorized bool
|
||||||
Capabilities []string
|
Capabilities []string
|
||||||
UnsignedPeerAPIOnly bool
|
UnsignedPeerAPIOnly bool
|
||||||
|
|
|
@ -283,6 +283,7 @@ func TestConn(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLoopbackLocalAPI(t *testing.T) {
|
func TestLoopbackLocalAPI(t *testing.T) {
|
||||||
|
flakytest.Mark(t, "https://github.com/tailscale/tailscale/issues/8557")
|
||||||
tstest.ResourceCheck(t)
|
tstest.ResourceCheck(t)
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
|
@ -581,8 +581,8 @@ func TestGetTypeHasher(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "tailcfg.Node",
|
name: "tailcfg.Node",
|
||||||
val: &tailcfg.Node{},
|
val: &tailcfg.Node{},
|
||||||
out: "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\tn\x88\xf1\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\tn\x88\xf1\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
out: "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\tn\x88\xf1\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\tn\x88\xf1\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||||
out32: "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\tn\x88\xf1\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\tn\x88\xf1\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
out32: "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\tn\x88\xf1\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\tn\x88\xf1\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|
|
@ -30,6 +30,7 @@ import (
|
||||||
|
|
||||||
"github.com/tailscale/wireguard-go/conn"
|
"github.com/tailscale/wireguard-go/conn"
|
||||||
"go4.org/mem"
|
"go4.org/mem"
|
||||||
|
"golang.org/x/exp/maps"
|
||||||
"golang.org/x/net/ipv4"
|
"golang.org/x/net/ipv4"
|
||||||
"golang.org/x/net/ipv6"
|
"golang.org/x/net/ipv6"
|
||||||
"tailscale.com/control/controlclient"
|
"tailscale.com/control/controlclient"
|
||||||
|
@ -4409,16 +4410,12 @@ func (de *endpoint) addrForWireGuardSendLocked(now mono.Time) (udpAddr netip.Add
|
||||||
return udpAddr, false
|
return udpAddr, false
|
||||||
}
|
}
|
||||||
|
|
||||||
candidates := make([]netip.AddrPort, 0, len(de.endpointState))
|
candidates := maps.Keys(de.endpointState)
|
||||||
for ipp := range de.endpointState {
|
if len(candidates) == 0 {
|
||||||
if ipp.Addr().Is4() && de.c.noV4.Load() {
|
de.c.logf("magicsock: addrForSendWireguardLocked: [unexpected] no candidates available for endpoint")
|
||||||
continue
|
return udpAddr, false
|
||||||
}
|
|
||||||
if ipp.Addr().Is6() && de.c.noV6.Load() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
candidates = append(candidates, ipp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Randomly select an address to use until we retrieve latency information
|
// Randomly select an address to use until we retrieve latency information
|
||||||
// and give it a short trustBestAddrUntil time so we avoid flapping between
|
// and give it a short trustBestAddrUntil time so we avoid flapping between
|
||||||
// addresses while waiting on latency information to be populated.
|
// addresses while waiting on latency information to be populated.
|
||||||
|
|
|
@ -2809,36 +2809,6 @@ func TestAddrForSendLockedForWireGuardOnly(t *testing.T) {
|
||||||
},
|
},
|
||||||
want: netip.MustParseAddrPort("[2345:0425:2CA1:0000:0000:0567:5673:23b5]:222"),
|
want: netip.MustParseAddrPort("[2345:0425:2CA1:0000:0000:0567:5673:23b5]:222"),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "choose IPv4 when IPv6 is not useable",
|
|
||||||
sendWGPing: false,
|
|
||||||
noV6: true,
|
|
||||||
ep: []endpointDetails{
|
|
||||||
{
|
|
||||||
addrPort: netip.MustParseAddrPort("1.1.1.1:111"),
|
|
||||||
latency: 100 * time.Millisecond,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
addrPort: netip.MustParseAddrPort("[1::1]:567"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
want: netip.MustParseAddrPort("1.1.1.1:111"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "choose IPv6 when IPv4 is not useable",
|
|
||||||
sendWGPing: false,
|
|
||||||
noV4: true,
|
|
||||||
ep: []endpointDetails{
|
|
||||||
{
|
|
||||||
addrPort: netip.MustParseAddrPort("1.1.1.1:111"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
addrPort: netip.MustParseAddrPort("[1::1]:567"),
|
|
||||||
latency: 100 * time.Millisecond,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
want: netip.MustParseAddrPort("[1::1]:567"),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "choose IPv6 address when latency is the same for v4 and v6",
|
name: "choose IPv6 address when latency is the same for v4 and v6",
|
||||||
sendWGPing: true,
|
sendWGPing: true,
|
||||||
|
@ -2865,8 +2835,6 @@ func TestAddrForSendLockedForWireGuardOnly(t *testing.T) {
|
||||||
noV6: atomic.Bool{},
|
noV6: atomic.Bool{},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
endpoint.c.noV4.Store(test.noV4)
|
|
||||||
endpoint.c.noV6.Store(test.noV6)
|
|
||||||
|
|
||||||
for _, epd := range test.ep {
|
for _, epd := range test.ep {
|
||||||
endpoint.endpointState[epd.addrPort] = &endpointState{}
|
endpoint.endpointState[epd.addrPort] = &endpointState{}
|
||||||
|
|
|
@ -96,9 +96,6 @@ func WGCfg(nm *netmap.NetworkMap, logf logger.Logf, flags netmap.WGConfigFlags,
|
||||||
DiscoKey: peer.DiscoKey,
|
DiscoKey: peer.DiscoKey,
|
||||||
})
|
})
|
||||||
cpeer := &cfg.Peers[len(cfg.Peers)-1]
|
cpeer := &cfg.Peers[len(cfg.Peers)-1]
|
||||||
if peer.KeepAlive {
|
|
||||||
cpeer.PersistentKeepalive = 25 // seconds
|
|
||||||
}
|
|
||||||
|
|
||||||
didExitNodeWarn := false
|
didExitNodeWarn := false
|
||||||
cpeer.V4MasqAddr = peer.SelfNodeV4MasqAddrForThisPeer
|
cpeer.V4MasqAddr = peer.SelfNodeV4MasqAddrForThisPeer
|
||||||
|
|
Loading…
Reference in New Issue