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

Commit 43edc2f

Browse files
authored
feat: Use internal TURN server for proxying (#322)
* feat: Use internal TURN server for proxying * Use TURNS by default * Adjust TURN scheme when brokerAddr is HTTP or not
1 parent 7adf4a2 commit 43edc2f

File tree

4 files changed

+38
-18
lines changed

4 files changed

+38
-18
lines changed

agent/stream.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,11 @@ func (s *stream) processMessage(msg proto.Message) {
8383
}
8484

8585
if msg.Offer != nil {
86-
rtc, err := xwebrtc.NewPeerConnection()
86+
if msg.Servers == nil {
87+
s.fatal(fmt.Errorf("servers must be sent with offer"))
88+
return
89+
}
90+
rtc, err := xwebrtc.NewPeerConnection(msg.Servers)
8791
if err != nil {
8892
s.fatal(fmt.Errorf("create connection: %w", err))
8993
return

internal/cmd/tunnel.go

+24-10
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"io"
88
"net"
9+
"net/url"
910
"os"
1011
"strconv"
1112
"time"
@@ -57,7 +58,7 @@ coder tunnel my-dev 3000 3000
5758
}
5859
baseURL := sdk.BaseURL()
5960

60-
envs, err := sdk.Environments(ctx)
61+
envs, err := getEnvs(ctx, sdk, coder.Me)
6162
if err != nil {
6263
return err
6364
}
@@ -79,8 +80,8 @@ coder tunnel my-dev 3000 3000
7980
localPort: uint16(localPort),
8081
remotePort: uint16(remotePort),
8182
ctx: context.Background(),
82-
logger: log,
83-
brokerAddr: baseURL.String(),
83+
logger: log.Leveled(slog.LevelDebug),
84+
brokerAddr: baseURL,
8485
token: sdk.Token(),
8586
}
8687

@@ -98,7 +99,7 @@ coder tunnel my-dev 3000 3000
9899

99100
type client struct {
100101
ctx context.Context
101-
brokerAddr string
102+
brokerAddr url.URL
102103
token string
103104
logger slog.Logger
104105
id string
@@ -108,9 +109,13 @@ type client struct {
108109
}
109110

110111
func (c *client) start() error {
111-
url := fmt.Sprintf("%s%s%s%s%s", c.brokerAddr, "/api/private/envagent/", c.id, "/connect?session_token=", c.token)
112-
c.logger.Info(c.ctx, "connecting to broker", slog.F("url", url))
113-
112+
url := fmt.Sprintf("%s%s%s%s%s", c.brokerAddr.String(), "/api/private/envagent/", c.id, "/connect?session_token=", c.token)
113+
turnScheme := "turns"
114+
if c.brokerAddr.Scheme == "http" {
115+
turnScheme = "turn"
116+
}
117+
tcpProxy := fmt.Sprintf("%s:%s:5349?transport=tcp", turnScheme, c.brokerAddr.Host)
118+
c.logger.Info(c.ctx, "connecting to broker", slog.F("url", url), slog.F("tcp-proxy", tcpProxy))
114119
conn, resp, err := websocket.Dial(c.ctx, url, nil)
115120
if err != nil && resp == nil {
116121
return fmt.Errorf("dial: %w", err)
@@ -122,7 +127,15 @@ func (c *client) start() error {
122127
}
123128
nconn := websocket.NetConn(context.Background(), conn, websocket.MessageBinary)
124129

125-
rtc, err := xwebrtc.NewPeerConnection()
130+
// Only enabled under a private feature flag for now,
131+
// so insecure connections are entirely fine to allow.
132+
servers := []webrtc.ICEServer{{
133+
URLs: []string{tcpProxy},
134+
Username: "insecure",
135+
Credential: "pass",
136+
CredentialType: webrtc.ICECredentialTypePassword,
137+
}}
138+
rtc, err := xwebrtc.NewPeerConnection(servers)
126139
if err != nil {
127140
return fmt.Errorf("create connection: %w", err)
128141
}
@@ -150,16 +163,17 @@ func (c *client) start() error {
150163
if err != nil {
151164
return fmt.Errorf("set local desc: %w", err)
152165
}
153-
flushCandidates()
154166

155167
c.logger.Debug(context.Background(), "writing offer")
156168
b, _ := json.Marshal(&proto.Message{
157-
Offer: &localDesc,
169+
Offer: &localDesc,
170+
Servers: servers,
158171
})
159172
_, err = nconn.Write(b)
160173
if err != nil {
161174
return fmt.Errorf("write offer: %w", err)
162175
}
176+
flushCandidates()
163177

164178
go func() {
165179
err = xwebrtc.WaitForDataChannelOpen(context.Background(), channel)

internal/x/xwebrtc/conn.go

+8-7
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
package xwebrtc
22

3-
import "github.com/pion/webrtc/v3"
3+
import (
4+
"time"
5+
6+
"github.com/pion/webrtc/v3"
7+
)
48

59
// NewPeerConnection creates a new peer connection.
610
// It uses the Google stun server by default.
7-
func NewPeerConnection() (*webrtc.PeerConnection, error) {
11+
func NewPeerConnection(servers []webrtc.ICEServer) (*webrtc.PeerConnection, error) {
812
se := webrtc.SettingEngine{}
913
se.DetachDataChannels()
14+
se.SetICETimeouts(time.Second*5, time.Second*5, time.Second*2)
1015
api := webrtc.NewAPI(webrtc.WithSettingEngine(se))
1116

1217
return api.NewPeerConnection(webrtc.Configuration{
13-
ICEServers: []webrtc.ICEServer{
14-
{
15-
URLs: []string{"stun:stun.l.google.com:19302"},
16-
},
17-
},
18+
ICEServers: servers,
1819
})
1920
}

pkg/proto/message.go

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ type Message struct {
1313
Error string `json:"error"`
1414
Candidate string `json:"candidate"`
1515
Offer *webrtc.SessionDescription `json:"offer"`
16+
Servers []webrtc.ICEServer `json:"servers"`
1617
Answer *webrtc.SessionDescription `json:"answer"`
1718
}
1819

0 commit comments

Comments
 (0)