Skip to content

fix(coderd): pass block endpoints into servertailnet #12149

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions coderd/coderd.go
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,7 @@ func New(options *Options) *API {
func(context.Context) (tailnet.MultiAgentConn, error) {
return (*api.TailnetCoordinator.Load()).ServeMultiAgent(uuid.New()), nil
},
options.DeploymentValues.DERP.Config.BlockDirect.Value(),
api.TracerProvider,
)
if err != nil {
Expand Down
8 changes: 8 additions & 0 deletions coderd/tailnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,15 @@ func NewServerTailnet(
derpMapFn func() *tailcfg.DERPMap,
derpForceWebSockets bool,
getMultiAgent func(context.Context) (tailnet.MultiAgentConn, error),
blockEndpoints bool,
traceProvider trace.TracerProvider,
) (*ServerTailnet, error) {
logger = logger.Named("servertailnet")
conn, err := tailnet.NewConn(&tailnet.Options{
Addresses: []netip.Prefix{netip.PrefixFrom(tailnet.IP(), 128)},
DERPForceWebSockets: derpForceWebSockets,
Logger: logger,
BlockEndpoints: blockEndpoints,
})
if err != nil {
return nil, xerrors.Errorf("create tailnet conn: %w", err)
Expand Down Expand Up @@ -166,6 +168,12 @@ func NewServerTailnet(
return tn, nil
}

// Conn is used to access the underlying tailnet conn of the ServerTailnet. It
// should only be used for read-only purposes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding this plus the multiple levels of Get methods seems like a lot just to test that we successfully set a flag.

Can we call the coordinator.Node() method and directly verify that endpoints are not set?

Copy link
Contributor Author

@coadler coadler Feb 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's definitely true. What I was trying to get at was to only test the specific logic of the servertailnet, which is only responsible for setting the two fields on the configmap and nodeupdater. By checking the node I'm essentially testing logic in tailnet, but that might be a fair tradeoff.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The more I think about this I'm not sure that checking the endpoints is a solid way to go, since it seems very racey. How do I know that the agent just hasn't finished the STUN dance before I check? Should I just wait x seconds and verify that we never get a STUN addr?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't want racy tests! If this plumbing is the cost, then so be it, I guess.

func (s *ServerTailnet) Conn() *tailnet.Conn {
return s.conn
}

func (s *ServerTailnet) nodeCallback(node *tailnet.Node) {
pn, err := tailnet.NodeToProto(node)
if err != nil {
Expand Down
31 changes: 31 additions & 0 deletions coderd/tailnet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,36 @@ func TestServerTailnet_ReverseProxy(t *testing.T) {

assert.Equal(t, expectedResponseCode, res.StatusCode)
})

t.Run("BlockEndpoints", func(t *testing.T) {
t.Parallel()

ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()

agents, serverTailnet := setupServerTailnetAgent(t, 1, tailnettest.DisableSTUN)
a := agents[0]

require.True(t, serverTailnet.Conn().GetBlockEndpoints(), "expected BlockEndpoints to be set")

u, err := url.Parse(fmt.Sprintf("http://127.0.0.1:%d", codersdk.WorkspaceAgentHTTPAPIServerPort))
require.NoError(t, err)

rp := serverTailnet.ReverseProxy(u, u, a.id)

rw := httptest.NewRecorder()
req := httptest.NewRequest(
http.MethodGet,
u.String(),
nil,
).WithContext(ctx)

rp.ServeHTTP(rw, req)
res := rw.Result()
defer res.Body.Close()

assert.Equal(t, http.StatusOK, res.StatusCode)
})
}

type wrappedListener struct {
Expand Down Expand Up @@ -375,6 +405,7 @@ func setupServerTailnetAgent(t *testing.T, agentNum int, opts ...tailnettest.DER
func() *tailcfg.DERPMap { return derpMap },
false,
func(context.Context) (tailnet.MultiAgentConn, error) { return coord.ServeMultiAgent(uuid.New()), nil },
!derpMap.HasSTUN(),
trace.NewNoopTracerProvider(),
)
require.NoError(t, err)
Expand Down
3 changes: 1 addition & 2 deletions enterprise/derpmesh/derpmesh.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ import (
"tailscale.com/derp/derphttp"
"tailscale.com/types/key"

"github.com/coder/coder/v2/tailnet"

"cdr.dev/slog"
"github.com/coder/coder/v2/tailnet"
)

// New constructs a new mesh for DERP servers.
Expand Down
1 change: 1 addition & 0 deletions enterprise/wsproxy/wsproxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ func New(ctx context.Context, opts *Options) (*Server, error) {
},
regResp.DERPForceWebSockets,
s.DialCoordinator,
false, // TODO: this will be covered in a subsequent pr.
s.TracerProvider,
)
if err != nil {
Expand Down
8 changes: 8 additions & 0 deletions tailnet/configmaps.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,14 @@ func (c *configMaps) setBlockEndpoints(blockEndpoints bool) {
c.Broadcast()
}

// getBlockEndpoints returns the value of the most recent setBlockEndpoints
// call.
func (c *configMaps) getBlockEndpoints() bool {
c.L.Lock()
defer c.L.Unlock()
return c.blockEndpoints
}

// setDERPMap sets the DERP map, triggering a configuration of the engine if it has changed.
// c.L MUST NOT be held.
func (c *configMaps) setDERPMap(derpMap *tailcfg.DERPMap) {
Expand Down
4 changes: 4 additions & 0 deletions tailnet/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,10 @@ type Conn struct {
trafficStats *connstats.Statistics
}

func (c *Conn) GetBlockEndpoints() bool {
return c.configMaps.getBlockEndpoints() && c.nodeUpdater.getBlockEndpoints()
}

func (c *Conn) InstallCaptureHook(f capture.Callback) {
c.mutex.Lock()
defer c.mutex.Unlock()
Expand Down
8 changes: 8 additions & 0 deletions tailnet/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,3 +239,11 @@ func (u *nodeUpdater) fillPeerDiagnostics(d *PeerDiagnostics) {
d.PreferredDERP = u.preferredDERP
d.SentNode = u.sentNode
}

// getBlockEndpoints returns the value of the most recent setBlockEndpoints
// call.
func (u *nodeUpdater) getBlockEndpoints() bool {
u.L.Lock()
defer u.L.Unlock()
return u.blockEndpoints
}