Skip to content

Commit

Permalink
feat: add magicsock opt to block direct endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
deansheather committed Nov 21, 2023
1 parent ba3acaa commit 7258b07
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 0 deletions.
34 changes: 34 additions & 0 deletions wgengine/magicsock/magicsock.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,10 @@ type Conn struct {
// that will call Conn.doPeriodicSTUN.
periodicReSTUNTimer *time.Timer

// blockEndpoints is whether to avoid capturing, storing and sending
// endpoints gathered from local interfaces or STUN. Only DERP endpoints
// will be sent.
blockEndpoints bool
// endpointsUpdateActive indicates that updateEndpoints is
// currently running. It's used to deduplicate concurrent endpoint
// update requests.
Expand Down Expand Up @@ -330,6 +334,13 @@ type Options struct {
// endpoints change. The called func does not own the slice.
EndpointsFunc func([]tailcfg.Endpoint)

// BlockEndpoints is whether to avoid capturing, storing and sending
// endpoints gathered from local interfaces or STUN. Only DERP endpoints
// will be sent.
// This does not disable the UDP socket or portmapping attempts as this
// setting can be toggled at runtime.
BlockEndpoints bool

// DERPActiveFunc optionally provides a func to be called when
// a connection is made to a DERP server.
DERPActiveFunc func()
Expand Down Expand Up @@ -580,6 +591,11 @@ func (c *Conn) setEndpoints(endpoints []tailcfg.Endpoint) (changed bool) {
c.mu.Lock()
defer c.mu.Unlock()

if c.blockEndpoints {
anySTUN = false
endpoints = []tailcfg.Endpoint{}
}

if !anySTUN && c.derpMap == nil && !inTest() {
// Don't bother storing or reporting this yet. We
// don't have a DERP map or any STUN entries, so we're
Expand Down Expand Up @@ -826,6 +842,21 @@ func (c *Conn) DiscoPublicKey() key.DiscoPublic {
return c.discoPublic
}

// SetBlockEndpoints sets the blockEndpoints field. If changed, endpoints will
// be updated to apply the new settings. Existing connections may continue to
// use the old setting until they are reestablished. Disabling endpoints does
// not affect the UDP socket or portmapper.
func (c *Conn) SetBlockEndpoints(block bool) {
c.mu.Lock()
didChange := c.blockEndpoints != block
c.blockEndpoints = block
c.mu.Unlock()

if didChange {
go c.updateEndpoints("SetBlockEndpoints")
}
}

// determineEndpoints returns the machine's endpoint addresses. It
// does a STUN lookup (via netcheck) to determine its public address.
//
Expand Down Expand Up @@ -1648,6 +1679,9 @@ func (c *Conn) enqueueCallMeMaybe(derpAddr netip.AddrPort, de *endpoint) {
for _, ep := range c.lastEndpoints {
eps = append(eps, ep.Addr)
}
// NOTE: sending an empty call-me-maybe (e.g. when BlockEndpoints is true)
// is still valid and results in the other side forgetting all the endpoints
// it knows of ours.
go de.c.sendDiscoMessage(derpAddr, de.publicKey, epDisco.key, &disco.CallMeMaybe{MyNumber: eps}, discoLog)
if debugSendCallMeUnknownPeer() {
// Send a callMeMaybe packet to a non-existent peer
Expand Down
51 changes: 51 additions & 0 deletions wgengine/magicsock/magicsock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3000,3 +3000,54 @@ func TestDERPForceWebsockets(t *testing.T) {
t.Errorf("no websocket upgrade requests seen")
}
}

func TestBlockEndpoints(t *testing.T) {
logf, closeLogf := logger.LogfCloser(t.Logf)
defer closeLogf()

derpMap, cleanup := runDERPAndStun(t, t.Logf, localhostListener{}, netaddr.IPv4(127, 0, 0, 1))
defer cleanup()

m := &natlab.Machine{Name: "m1"}
ms := newMagicStackFunc(t, logger.WithPrefix(logf, "conn1: "), m, derpMap, nil)
defer ms.Close()

// Check that some endpoints exist. This should be the case as we should use
// interface addressess as endpoints instantly on startup, and we already
// have a DERP connection due to newMagicStackFunc.
ms.conn.mu.Lock()
haveEndpoint := false
for _, ep := range ms.conn.lastEndpoints {
if ep.Addr.Addr() == tailcfg.DerpMagicIPAddr {
t.Fatal("DERP IP in endpoints list?", ep.Addr)
}
haveEndpoint = true
break
}
ms.conn.mu.Unlock()
if !haveEndpoint {
t.Fatal("no endpoints found")
}

// Block endpoints, should result in an update.
ms.conn.SetBlockEndpoints(true)

// Wait for endpoints to finish updating.
ok := false
parentLoop:
for i := 0; i < 50; i++ {
time.Sleep(100 * time.Millisecond)
ms.conn.mu.Lock()
for _, ep := range ms.conn.lastEndpoints {
t.Errorf("endpoint %v was not blocked", ep.Addr)
ms.conn.mu.Unlock()
continue parentLoop
}
ms.conn.mu.Unlock()
ok = true
break
}
if !ok {
t.Fatal("endpoints were not blocked after 50 attempts")
}
}

0 comments on commit 7258b07

Please sign in to comment.