Thanks to visit codestin.com
Credit goes to github.com

Skip to content
This repository was archived by the owner on Aug 30, 2024. It is now read-only.

Commit 1294dd9

Browse files
authored
fix: Add TURN URL to handshake message for custom proxying URLs (#389)
* fix: Enable TURN over HTTP with proper callback * Remove TURN proxy dialer * Don't use credential to authenticate
1 parent dd00acd commit 1294dd9

File tree

8 files changed

+70
-48
lines changed

8 files changed

+70
-48
lines changed

internal/cmd/agent.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ coder agent start --coder-url https://my-coder.com --token xxxx-xxxx
7373
}
7474
}
7575

76-
listener, err := wsnet.Listen(context.Background(), wsnet.ListenEndpoint(u, token), wsnet.TURNProxyWebSocket(u, token))
76+
listener, err := wsnet.Listen(context.Background(), wsnet.ListenEndpoint(u, token), token)
7777
if err != nil {
7878
return xerrors.Errorf("listen: %w", err)
7979
}

internal/cmd/tunnel.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
"cdr.dev/slog"
1313
"cdr.dev/slog/sloggers/sloghuman"
14+
"github.com/pion/webrtc/v3"
1415
"github.com/spf13/cobra"
1516
"golang.org/x/xerrors"
1617

@@ -107,7 +108,9 @@ func (c *tunnneler) start(ctx context.Context) error {
107108
ctx,
108109
wsnet.ConnectEndpoint(c.brokerAddr, c.workspaceID, c.token),
109110
&wsnet.DialOptions{
110-
TURNProxy: wsnet.TURNProxyWebSocket(c.brokerAddr, c.token),
111+
TURNProxyAuthToken: c.token,
112+
TURNProxyURL: c.brokerAddr,
113+
ICEServers: []webrtc.ICEServer{wsnet.TURNProxyICECandidate()},
111114
},
112115
)
113116
if err != nil {

wsnet/conn.go

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ import (
1111

1212
"github.com/pion/datachannel"
1313
"github.com/pion/webrtc/v3"
14-
"golang.org/x/net/proxy"
1514
"nhooyr.io/websocket"
1615

1716
"cdr.dev/coder-cli/coder-sdk"
1817
)
1918

2019
const (
21-
httpScheme = "http"
20+
httpScheme = "http"
21+
turnProxyMagicUsername = "~magicalusername~"
2222

2323
bufferedAmountLowThreshold uint64 = 512 * 1024 // 512 KB
2424
maxBufferedAmount uint64 = 1024 * 1024 // 1 MB
@@ -46,25 +46,17 @@ func ConnectEndpoint(baseURL *url.URL, workspace, token string) string {
4646
return fmt.Sprintf("%s://%s%s%s%s%s", wsScheme, baseURL.Host, "/api/private/envagent/", workspace, "/connect?session_token=", token)
4747
}
4848

49-
// TURNWebSocketICECandidate returns a valid relay ICEServer that can be used to
50-
// trigger a TURNWebSocketDialer.
49+
// TURNWebSocketICECandidate returns a fake TCP relay ICEServer.
50+
// It's used to trigger the ICEProxyDialer.
5151
func TURNProxyICECandidate() webrtc.ICEServer {
5252
return webrtc.ICEServer{
5353
URLs: []string{"turn:127.0.0.1:3478?transport=tcp"},
54-
Username: "~magicalusername~",
55-
Credential: "~magicalpassword~",
54+
Username: turnProxyMagicUsername,
55+
Credential: turnProxyMagicUsername,
5656
CredentialType: webrtc.ICECredentialTypePassword,
5757
}
5858
}
5959

60-
// TURNWebSocketDialer proxies all TURN traffic through a WebSocket.
61-
func TURNProxyWebSocket(baseURL *url.URL, token string) proxy.Dialer {
62-
return &turnProxyDialer{
63-
baseURL: baseURL,
64-
token: token,
65-
}
66-
}
67-
6860
// Proxies all TURN ICEServer traffic through this dialer.
6961
// References Coder APIs with a specific token.
7062
type turnProxyDialer struct {

wsnet/dial.go

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88
"io"
99
"net"
10+
"net/url"
1011
"sync"
1112
"time"
1213

@@ -24,10 +25,13 @@ type DialOptions struct {
2425
// See: https://developer.mozilla.org/en-US/docs/Web/API/RTCConfiguration/iceServers
2526
ICEServers []webrtc.ICEServer
2627

27-
// TURNProxy is a function used to proxy all TURN traffic.
28-
// If specified without ICEServers, `TURNProxyICECandidate`
29-
// will be used.
30-
TURNProxy proxy.Dialer
28+
// TURNProxyAuthToken is used to authenticate a TURN proxy request.
29+
TURNProxyAuthToken string
30+
31+
// TURNProxyURL is the URL to proxy all TURN data through.
32+
// This URL is sent to the listener during handshake so both
33+
// ends connect to the same TURN endpoint.
34+
TURNProxyURL *url.URL
3135
}
3236

3337
// DialWebsocket dials the broker with a WebSocket and negotiates a connection.
@@ -59,13 +63,15 @@ func Dial(conn net.Conn, options *DialOptions) (*Dialer, error) {
5963
if options.ICEServers == nil {
6064
options.ICEServers = []webrtc.ICEServer{}
6165
}
62-
// If the TURNProxy is specified and ICEServers aren't,
63-
// it's safe to assume we can inject the default proxy candidate.
64-
if len(options.ICEServers) == 0 && options.TURNProxy != nil {
65-
options.ICEServers = []webrtc.ICEServer{TURNProxyICECandidate()}
66-
}
6766

68-
rtc, err := newPeerConnection(options.ICEServers, options.TURNProxy)
67+
var turnProxy proxy.Dialer
68+
if options.TURNProxyURL != nil {
69+
turnProxy = &turnProxyDialer{
70+
baseURL: options.TURNProxyURL,
71+
token: options.TURNProxyAuthToken,
72+
}
73+
}
74+
rtc, err := newPeerConnection(options.ICEServers, turnProxy)
6975
if err != nil {
7076
return nil, fmt.Errorf("create peer connection: %w", err)
7177
}
@@ -89,9 +95,15 @@ func Dial(conn net.Conn, options *DialOptions) (*Dialer, error) {
8995
return nil, fmt.Errorf("set local offer: %w", err)
9096
}
9197

98+
var turnProxyURL string
99+
if options.TURNProxyURL != nil {
100+
turnProxyURL = options.TURNProxyURL.String()
101+
}
102+
92103
offerMessage, err := json.Marshal(&BrokerMessage{
93-
Offer: &offer,
94-
Servers: options.ICEServers,
104+
Offer: &offer,
105+
Servers: options.ICEServers,
106+
TURNProxyURL: turnProxyURL,
95107
})
96108
if err != nil {
97109
return nil, fmt.Errorf("marshal offer message: %w", err)

wsnet/dial_test.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ func TestDial(t *testing.T) {
5555
t.Parallel()
5656

5757
connectAddr, listenAddr := createDumbBroker(t)
58-
_, err := Listen(context.Background(), listenAddr, nil)
58+
_, err := Listen(context.Background(), listenAddr, "")
5959
if err != nil {
6060
t.Error(err)
6161
return
@@ -75,7 +75,7 @@ func TestDial(t *testing.T) {
7575
t.Parallel()
7676

7777
connectAddr, listenAddr := createDumbBroker(t)
78-
_, err := Listen(context.Background(), listenAddr, nil)
78+
_, err := Listen(context.Background(), listenAddr, "")
7979
if err != nil {
8080
t.Error(err)
8181
return
@@ -106,7 +106,7 @@ func TestDial(t *testing.T) {
106106
t.Parallel()
107107

108108
connectAddr, listenAddr := createDumbBroker(t)
109-
_, err := Listen(context.Background(), listenAddr, nil)
109+
_, err := Listen(context.Background(), listenAddr, "")
110110
if err != nil {
111111
t.Error(err)
112112
return
@@ -145,7 +145,7 @@ func TestDial(t *testing.T) {
145145
}()
146146

147147
connectAddr, listenAddr := createDumbBroker(t)
148-
_, err = Listen(context.Background(), listenAddr, nil)
148+
_, err = Listen(context.Background(), listenAddr, "")
149149
if err != nil {
150150
t.Error(err)
151151
return
@@ -184,7 +184,7 @@ func TestDial(t *testing.T) {
184184
_, _ = listener.Accept()
185185
}()
186186
connectAddr, listenAddr := createDumbBroker(t)
187-
srv, err := Listen(context.Background(), listenAddr, nil)
187+
srv, err := Listen(context.Background(), listenAddr, "")
188188
if err != nil {
189189
t.Error(err)
190190
return
@@ -211,7 +211,7 @@ func TestDial(t *testing.T) {
211211
t.Parallel()
212212

213213
connectAddr, listenAddr := createDumbBroker(t)
214-
_, err := Listen(context.Background(), listenAddr, nil)
214+
_, err := Listen(context.Background(), listenAddr, "")
215215
if err != nil {
216216
t.Error(err)
217217
return
@@ -245,7 +245,7 @@ func TestDial(t *testing.T) {
245245
}()
246246

247247
connectAddr, listenAddr := createDumbBroker(t)
248-
_, err = Listen(context.Background(), listenAddr, nil)
248+
_, err = Listen(context.Background(), listenAddr, "")
249249
if err != nil {
250250
t.Error(err)
251251
return
@@ -282,7 +282,7 @@ func TestDial(t *testing.T) {
282282
t.Parallel()
283283

284284
connectAddr, listenAddr := createDumbBroker(t)
285-
_, err := Listen(context.Background(), listenAddr, nil)
285+
_, err := Listen(context.Background(), listenAddr, "")
286286
if err != nil {
287287
t.Error(err)
288288
return
@@ -333,7 +333,7 @@ func BenchmarkThroughput(b *testing.B) {
333333
}
334334
}()
335335
connectAddr, listenAddr := createDumbBroker(b)
336-
_, err = Listen(context.Background(), listenAddr, nil)
336+
_, err = Listen(context.Background(), listenAddr, "")
337337
if err != nil {
338338
b.Error(err)
339339
return

wsnet/listen.go

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88
"io"
99
"net"
10+
"net/url"
1011
"sync"
1112
"time"
1213

@@ -40,11 +41,11 @@ type DialChannelResponse struct {
4041

4142
// Listen connects to the broker proxies connections to the local net.
4243
// Close will end all RTC connections.
43-
func Listen(ctx context.Context, broker string, tcpProxy proxy.Dialer) (io.Closer, error) {
44+
func Listen(ctx context.Context, broker string, turnProxyAuthToken string) (io.Closer, error) {
4445
l := &listener{
45-
broker: broker,
46-
connClosers: make([]io.Closer, 0),
47-
tcpProxy: tcpProxy,
46+
broker: broker,
47+
connClosers: make([]io.Closer, 0),
48+
turnProxyAuthToken: turnProxyAuthToken,
4849
}
4950
// We do a one-off dial outside of the loop to ensure the initial
5051
// connection is successful. If not, there's likely an error the
@@ -85,8 +86,8 @@ func Listen(ctx context.Context, broker string, tcpProxy proxy.Dialer) (io.Close
8586
}
8687

8788
type listener struct {
88-
broker string
89-
tcpProxy proxy.Dialer
89+
broker string
90+
turnProxyAuthToken string
9091

9192
acceptError error
9293
ws *websocket.Conn
@@ -189,7 +190,7 @@ func (l *listener) negotiate(conn net.Conn) {
189190
return
190191
}
191192
for _, server := range msg.Servers {
192-
if server.Username == TURNProxyICECandidate().Username {
193+
if server.Username == turnProxyMagicUsername {
193194
// This candidate is only used when proxying,
194195
// so it will not validate.
195196
continue
@@ -200,7 +201,19 @@ func (l *listener) negotiate(conn net.Conn) {
200201
return
201202
}
202203
}
203-
rtc, err = newPeerConnection(msg.Servers, l.tcpProxy)
204+
var turnProxy proxy.Dialer
205+
if msg.TURNProxyURL != "" {
206+
u, err := url.Parse(msg.TURNProxyURL)
207+
if err != nil {
208+
closeError(fmt.Errorf("parse turn proxy url: %w", err))
209+
return
210+
}
211+
turnProxy = &turnProxyDialer{
212+
baseURL: u,
213+
token: l.turnProxyAuthToken,
214+
}
215+
}
216+
rtc, err = newPeerConnection(msg.Servers, turnProxy)
204217
if err != nil {
205218
closeError(err)
206219
return

wsnet/listen_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ func TestListen(t *testing.T) {
4545
addr := listener.Addr()
4646
broker := fmt.Sprintf("http://%s/", addr.String())
4747

48-
_, err = Listen(context.Background(), broker, nil)
48+
_, err = Listen(context.Background(), broker, "")
4949
if err != nil {
5050
t.Error(err)
5151
return

wsnet/proto.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,10 @@ func (p DialPolicy) permits(network, host string, port uint16) bool {
4949
// sides can begin exchanging candidates.
5050
type BrokerMessage struct {
5151
// Dialer -> Listener
52-
Offer *webrtc.SessionDescription `json:"offer"`
53-
Servers []webrtc.ICEServer `json:"servers"`
52+
Offer *webrtc.SessionDescription `json:"offer"`
53+
Servers []webrtc.ICEServer `json:"servers"`
54+
TURNProxyURL string `json:"turn_proxy_url"`
55+
5456
// Policies denote which addresses the client can dial. If empty or nil, all
5557
// addresses are permitted.
5658
Policies []DialPolicy `json:"ports"`

0 commit comments

Comments
 (0)