Skip to content

Commit 79c666b

Browse files
fix(vpn): avoid setting session token header twice (#18524)
`coderd` currently does not handle a session token header value of the form `token1, token2`. However, it does handle multiple instances of the token header by simply taking the first. This is the default behaviour of `http.Header.Get`. So, setting the token header twice causes issues when Coder is behind a proxy that merges duplicate headers, such as [Apache](https://httpd.apache.org/docs/2.4/mod/mod_headers.html#:~:text=list%20of%20values.-,When%20a%20new%20value%20is%20merged%20onto%20an%20existing%20header,format%20specifiers%20have%20been%20processed). This PR ensures we don't set it twice by not sharing one slice between the `HTTPClient` and the `websocket.DialerOptions`. It also adds a regression test.
1 parent 288ec77 commit 79c666b

File tree

3 files changed

+12
-4
lines changed

3 files changed

+12
-4
lines changed

codersdk/client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ func (c *Client) Dial(ctx context.Context, path string, opts *websocket.DialOpti
354354
if opts.HTTPHeader == nil {
355355
opts.HTTPHeader = http.Header{}
356356
}
357-
if opts.HTTPHeader.Get("tokenHeader") == "" {
357+
if opts.HTTPHeader.Get(tokenHeader) == "" {
358358
opts.HTTPHeader.Set(tokenHeader, c.SessionToken())
359359
}
360360

vpn/client.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ func (*client) NewConn(initCtx context.Context, serverURL *url.URL, token string
9292
sdk.SetSessionToken(token)
9393
sdk.HTTPClient.Transport = &codersdk.HeaderTransport{
9494
Transport: http.DefaultTransport,
95-
Header: headers,
95+
Header: headers.Clone(),
9696
}
9797

9898
// New context, separate from initCtx. We don't want to cancel the
@@ -129,17 +129,18 @@ func (*client) NewConn(initCtx context.Context, serverURL *url.URL, token string
129129
headers.Set(codersdk.SessionTokenHeader, token)
130130
dialer := workspacesdk.NewWebsocketDialer(options.Logger, rpcURL, &websocket.DialOptions{
131131
HTTPClient: sdk.HTTPClient,
132-
HTTPHeader: headers,
132+
HTTPHeader: headers.Clone(),
133133
CompressionMode: websocket.CompressionDisabled,
134134
}, workspacesdk.WithWorkspaceUpdates(&proto.WorkspaceUpdatesRequest{
135135
WorkspaceOwnerId: tailnet.UUIDToByteSlice(me.ID),
136136
}))
137137

138+
clonedHeaders := headers.Clone()
138139
ip := tailnet.CoderServicePrefix.RandomAddr()
139140
conn, err := tailnet.NewConn(&tailnet.Options{
140141
Addresses: []netip.Prefix{netip.PrefixFrom(ip, 128)},
141142
DERPMap: connInfo.DERPMap,
142-
DERPHeader: &headers,
143+
DERPHeader: &clonedHeaders,
143144
DERPForceWebSockets: connInfo.DERPForceWebSockets,
144145
Logger: options.Logger,
145146
BlockEndpoints: connInfo.DisableDirectConnections,

vpn/client_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ func TestClient_WorkspaceUpdates(t *testing.T) {
9090
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
9191
switch r.URL.Path {
9292
case "/api/v2/users/me":
93+
values := r.Header.Values(codersdk.SessionTokenHeader)
94+
assert.Len(t, values, 1, "expected exactly one session token header value")
9395
httpapi.Write(ctx, w, http.StatusOK, codersdk.User{
9496
ReducedUser: codersdk.ReducedUser{
9597
MinimalUser: codersdk.MinimalUser{
@@ -101,6 +103,8 @@ func TestClient_WorkspaceUpdates(t *testing.T) {
101103
user <- struct{}{}
102104

103105
case "/api/v2/workspaceagents/connection":
106+
values := r.Header.Values(codersdk.SessionTokenHeader)
107+
assert.Len(t, values, 1, "expected exactly one session token header value")
104108
httpapi.Write(ctx, w, http.StatusOK, tc.agentConnectionInfo)
105109
connInfo <- struct{}{}
106110

@@ -109,6 +113,9 @@ func TestClient_WorkspaceUpdates(t *testing.T) {
109113
cVer := r.URL.Query().Get("version")
110114
assert.Equal(t, "2.3", cVer)
111115

116+
values := r.Header.Values(codersdk.SessionTokenHeader)
117+
assert.Len(t, values, 1, "expected exactly one session token header value")
118+
112119
sws, err := websocket.Accept(w, r, nil)
113120
if !assert.NoError(t, err) {
114121
return

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy