Skip to content

Commit b7ba1c9

Browse files
committed
fix: rewrite url to agent ip in single tailnet
This restores previous behavior of being able to cache connections across agents in single tailnet.
1 parent 8bc91b4 commit b7ba1c9

File tree

2 files changed

+37
-10
lines changed

2 files changed

+37
-10
lines changed

coderd/tailnet.go

+11-10
Original file line numberDiff line numberDiff line change
@@ -103,14 +103,7 @@ func NewServerTailnet(
103103
transport: tailnetTransport.Clone(),
104104
}
105105
tn.transport.DialContext = tn.dialContext
106-
107-
// Bugfix: for some reason all calls to tn.dialContext come from
108-
// "localhost", causing connections to be cached and requests to go to the
109-
// wrong workspaces. This disables keepalives for now until the root cause
110-
// can be found.
111-
tn.transport.MaxIdleConnsPerHost = -1
112-
tn.transport.DisableKeepAlives = true
113-
106+
tn.transport.MaxIdleConnsPerHost = 10
114107
tn.transport.MaxIdleConns = 0
115108
// We intentionally don't verify the certificate chain here.
116109
// The connection to the workspace is already established and most
@@ -312,7 +305,15 @@ type ServerTailnet struct {
312305
}
313306

314307
func (s *ServerTailnet) ReverseProxy(targetURL, dashboardURL *url.URL, agentID uuid.UUID) (_ *httputil.ReverseProxy, release func(), _ error) {
315-
proxy := httputil.NewSingleHostReverseProxy(targetURL)
308+
// Rewrite the targetURL's Host to point to the agent's IP. This is
309+
// necessary because due to TCP connection caching, each agent needs to be
310+
// addressed invidivually. Otherwise, all connections get dialed as
311+
// "localhost:port", causing connections to be shared across agents.
312+
tgt := *targetURL
313+
_, port, _ := net.SplitHostPort(tgt.Host)
314+
tgt.Host = net.JoinHostPort(tailnet.IPFromUUID(agentID).String(), port)
315+
316+
proxy := httputil.NewSingleHostReverseProxy(&tgt)
316317
proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) {
317318
site.RenderStaticErrorPage(w, r, site.ErrorPageData{
318319
Status: http.StatusBadGateway,
@@ -460,7 +461,7 @@ func (s *ServerTailnet) Close() error {
460461
s.cancel()
461462
_ = s.cache.Close()
462463
_ = s.conn.Close()
463-
s.transport.CloseIdleConnections()
464+
// s.transport.CloseIdleConnections()
464465
<-s.derpMapUpdaterClosed
465466
return nil
466467
}

coderd/tailnet_test.go

+26
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,32 @@ func TestServerTailnet_ReverseProxy(t *testing.T) {
9595
assert.Equal(t, http.StatusOK, res.StatusCode)
9696
})
9797

98+
t.Run("HostRewrite", func(t *testing.T) {
99+
t.Parallel()
100+
101+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
102+
defer cancel()
103+
104+
agentID, _, serverTailnet := setupAgent(t, nil)
105+
106+
u, err := url.Parse(fmt.Sprintf("http://127.0.0.1:%d", codersdk.WorkspaceAgentHTTPAPIServerPort))
107+
require.NoError(t, err)
108+
109+
rp, release, err := serverTailnet.ReverseProxy(u, u, agentID)
110+
require.NoError(t, err)
111+
defer release()
112+
113+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
114+
require.NoError(t, err)
115+
116+
// Ensure the reverse proxy director rewrites the url host to the agent's IP.
117+
rp.Director(req)
118+
assert.Equal(t,
119+
fmt.Sprintf("[%s]:%d", tailnet.IPFromUUID(agentID).String(), codersdk.WorkspaceAgentHTTPAPIServerPort),
120+
req.URL.Host,
121+
)
122+
})
123+
98124
t.Run("HTTPSProxy", func(t *testing.T) {
99125
t.Parallel()
100126

0 commit comments

Comments
 (0)