Skip to content

Commit e0a8b17

Browse files
committed
Log TURN remote address
1 parent 859ec70 commit e0a8b17

File tree

6 files changed

+85
-49
lines changed

6 files changed

+85
-49
lines changed

.github/workflows/coder.yaml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,6 @@ jobs:
163163
terraform_version: 1.1.2
164164
terraform_wrapper: false
165165

166-
- name: Install socat
167-
if: runner.os == 'Linux'
168-
run: sudo apt-get install -y socat
169-
170166
- name: Test with Mock Database
171167
shell: bash
172168
env:

coderd/turnconn/turnconn.go

Lines changed: 68 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package turnconn
22

33
import (
4+
"fmt"
45
"io"
56
"net"
67
"sync"
@@ -13,13 +14,20 @@ import (
1314
)
1415

1516
var (
16-
reservedHost = "coder"
17-
credential = "coder"
17+
// reservedAddress is a magic address that's used exclusively
18+
// for proxying via Coder. We don't proxy all TURN connections,
19+
// because that'd exclude the possibility of a customer using
20+
// their own TURN server.
21+
reservedAddress = "127.0.0.1:12345"
22+
credential = "coder"
23+
localhost = &net.TCPAddr{
24+
IP: net.IPv4(127, 0, 0, 1),
25+
}
1826

1927
// Proxy is a an ICE Server that uses a special hostname
2028
// to indicate traffic should be proxied.
2129
Proxy = webrtc.ICEServer{
22-
URLs: []string{"turns:" + reservedHost},
30+
URLs: []string{"turns:" + reservedAddress},
2331
Username: "coder",
2432
Credential: credential,
2533
}
@@ -29,14 +37,37 @@ var (
2937
// The relay address is used to broadcast the location of an accepted connection.
3038
func New(relayAddress *turn.RelayAddressGeneratorStatic) (*Server, error) {
3139
if relayAddress == nil {
32-
// Default to localhost.
40+
ip := ""
41+
addrs, err := net.InterfaceAddrs()
42+
if err != nil {
43+
return nil, xerrors.Errorf("find interface addrs: %w", err)
44+
}
45+
// Try to find the localhost IP address.
46+
// It will generally be 127.0.0.1, but
47+
// search to be sure!
48+
for _, address := range addrs {
49+
ipNet, ok := address.(*net.IPNet)
50+
if !ok {
51+
continue
52+
}
53+
if !ipNet.IP.IsLoopback() {
54+
continue
55+
}
56+
ip = ipNet.IP.String()
57+
break
58+
}
59+
fmt.Printf("WE HAVE IP %q\n", ip)
60+
// if ip == "" {
61+
ip = "127.0.0.1"
62+
// }
63+
3364
relayAddress = &turn.RelayAddressGeneratorStatic{
34-
RelayAddress: net.IP{127, 0, 0, 1},
35-
Address: "127.0.0.1",
65+
RelayAddress: net.ParseIP(ip),
66+
Address: ip,
3667
}
3768
}
3869
logger := logging.NewDefaultLoggerFactory()
39-
logger.DefaultLogLevel = logging.LogLevelDisabled
70+
logger.DefaultLogLevel = logging.LogLevelDebug
4071
server := &Server{
4172
conns: make(chan net.Conn, 1),
4273
closed: make(chan struct{}),
@@ -47,6 +78,8 @@ func New(relayAddress *turn.RelayAddressGeneratorStatic) (*Server, error) {
4778
var err error
4879
server.turn, err = turn.NewServer(turn.ServerConfig{
4980
AuthHandler: func(username, realm string, srcAddr net.Addr) (key []byte, ok bool) {
81+
// TURN connections require credentials. It's not important
82+
// for our use-case, because our listener is entirely in-memory.
5083
return turn.GenerateAuthKey(Proxy.Username, "", credential), true
5184
},
5285
ListenerConfigs: []turn.ListenerConfig{{
@@ -62,33 +95,6 @@ func New(relayAddress *turn.RelayAddressGeneratorStatic) (*Server, error) {
6295
return server, nil
6396
}
6497

65-
// ProxyDialer accepts a proxy function that's called when the connection
66-
// address matches the reserved host in the "Proxy" ICE server.
67-
//
68-
// This should be passed to WebRTC connections as an ICE dialer.
69-
func ProxyDialer(proxyFunc func() (c net.Conn, err error)) proxy.Dialer {
70-
return dialer(func(network, addr string) (net.Conn, error) {
71-
host, _, err := net.SplitHostPort(addr)
72-
if err != nil {
73-
return nil, err
74-
}
75-
if host != reservedHost {
76-
return proxy.Direct.Dial(network, addr)
77-
}
78-
netConn, err := proxyFunc()
79-
if err != nil {
80-
return nil, err
81-
}
82-
return &Conn{
83-
localAddress: &net.TCPAddr{
84-
IP: net.IPv4(127, 0, 0, 1),
85-
},
86-
closed: make(chan struct{}),
87-
Conn: netConn,
88-
}, nil
89-
})
90-
}
91-
9298
// Server accepts and connects TURN allocations.
9399
//
94100
// This is a thin wrapper around pion/turn that pipes
@@ -105,10 +111,14 @@ type Server struct {
105111
// Accept consumes a new connection into the TURN server.
106112
// A unique remote address must exist per-connection.
107113
// pion/turn indexes allocations based on the address.
108-
func (s *Server) Accept(nc net.Conn, remoteAddress *net.TCPAddr) *Conn {
114+
func (s *Server) Accept(nc net.Conn, remoteAddress, localAddress *net.TCPAddr) *Conn {
115+
if localAddress == nil {
116+
localAddress = localhost
117+
}
109118
conn := &Conn{
110119
Conn: nc,
111120
remoteAddress: remoteAddress,
121+
localAddress: localAddress,
112122
closed: make(chan struct{}),
113123
}
114124
s.conns <- conn
@@ -181,16 +191,38 @@ func (c *Conn) Closed() <-chan struct{} {
181191
}
182192

183193
func (c *Conn) Close() error {
194+
err := c.Conn.Close()
184195
select {
185196
case <-c.closed:
186197
default:
187198
close(c.closed)
188199
}
189-
return c.Conn.Close()
200+
return err
190201
}
191202

192203
type dialer func(network, addr string) (c net.Conn, err error)
193204

194205
func (d dialer) Dial(network, addr string) (c net.Conn, err error) {
195206
return d(network, addr)
196207
}
208+
209+
// ProxyDialer accepts a proxy function that's called when the connection
210+
// address matches the reserved host in the "Proxy" ICE server.
211+
//
212+
// This should be passed to WebRTC connections as an ICE dialer.
213+
func ProxyDialer(proxyFunc func() (c net.Conn, err error)) proxy.Dialer {
214+
return dialer(func(network, addr string) (net.Conn, error) {
215+
if addr != reservedAddress {
216+
return proxy.Direct.Dial(network, addr)
217+
}
218+
netConn, err := proxyFunc()
219+
if err != nil {
220+
return nil, err
221+
}
222+
return &Conn{
223+
localAddress: localhost,
224+
closed: make(chan struct{}),
225+
Conn: netConn,
226+
}, nil
227+
})
228+
}

coderd/turnconn/turnconn_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ func TestTURNConn(t *testing.T) {
3030
clientDialer, clientTURN := net.Pipe()
3131
turnServer.Accept(clientTURN, &net.TCPAddr{
3232
IP: net.IPv4(127, 0, 0, 1),
33-
Port: 1,
34-
})
33+
Port: 16000,
34+
}, nil)
3535
require.NoError(t, err)
3636
clientSettings := webrtc.SettingEngine{}
3737
clientSettings.SetNetworkTypes([]webrtc.NetworkType{webrtc.NetworkTypeTCP4, webrtc.NetworkTypeTCP6})
@@ -48,8 +48,8 @@ func TestTURNConn(t *testing.T) {
4848
serverDialer, serverTURN := net.Pipe()
4949
turnServer.Accept(serverTURN, &net.TCPAddr{
5050
IP: net.IPv4(127, 0, 0, 1),
51-
Port: 2,
52-
})
51+
Port: 16001,
52+
}, nil)
5353
require.NoError(t, err)
5454
serverSettings := webrtc.SettingEngine{}
5555
serverSettings.SetNetworkTypes([]webrtc.NetworkType{webrtc.NetworkTypeTCP4, webrtc.NetworkTypeTCP6})

coderd/workspaceagents.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ func (api *api) workspaceAgentTurn(rw http.ResponseWriter, r *http.Request) {
232232
api.websocketWaitMutex.Unlock()
233233
defer api.websocketWaitGroup.Done()
234234

235+
localAddress, _ := r.Context().Value(http.LocalAddrContextKey).(*net.TCPAddr)
235236
remoteAddress := &net.TCPAddr{
236237
IP: net.ParseIP(r.RemoteAddr),
237238
}
@@ -261,11 +262,16 @@ func (api *api) workspaceAgentTurn(rw http.ResponseWriter, r *http.Request) {
261262
})
262263
return
263264
}
265+
defer func() {
266+
_ = wsConn.Close(websocket.StatusNormalClosure, "")
267+
}()
264268
netConn := websocket.NetConn(r.Context(), wsConn, websocket.MessageBinary)
269+
api.Logger.Debug(r.Context(), "accepting turn connection", slog.F("remote-address", r.RemoteAddr), slog.F("local-address", localAddress))
265270
select {
266-
case <-api.TURNServer.Accept(netConn, remoteAddress).Closed():
271+
case <-api.TURNServer.Accept(netConn, remoteAddress, localAddress).Closed():
267272
case <-r.Context().Done():
268273
}
274+
api.Logger.Debug(r.Context(), "completed turn connection", slog.F("remote-address", r.RemoteAddr), slog.F("local-address", localAddress))
269275
}
270276

271277
func convertWorkspaceAgent(dbAgent database.WorkspaceAgent, agentUpdateFrequency time.Duration) (codersdk.WorkspaceAgent, error) {

coderd/workspaceagents_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,9 @@ func TestWorkspaceAgentTURN(t *testing.T) {
143143
_ = agentCloser.Close()
144144
})
145145
resources := coderdtest.AwaitWorkspaceAgents(t, client, workspace.LatestBuild.ID)
146-
opts := &peer.ConnOptions{}
146+
opts := &peer.ConnOptions{
147+
Logger: slogtest.Make(t, nil).Named("client"),
148+
}
147149
// Force a TURN connection!
148150
opts.SettingEngine.SetNetworkTypes([]webrtc.NetworkType{webrtc.NetworkTypeTCP4})
149151
conn, err := client.DialWorkspaceAgent(context.Background(), resources[0].Agents[0].ID, opts)

peer/conn.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ func newWithClientOrServer(servers []webrtc.ICEServer, client bool, opts *ConnOp
5050
}
5151

5252
opts.SettingEngine.DetachDataChannels()
53-
factory := logging.NewDefaultLoggerFactory()
54-
factory.DefaultLogLevel = logging.LogLevelDisabled
55-
opts.SettingEngine.LoggerFactory = factory
53+
logger := logging.NewDefaultLoggerFactory()
54+
logger.DefaultLogLevel = logging.LogLevelDisabled
55+
opts.SettingEngine.LoggerFactory = logger
5656
api := webrtc.NewAPI(webrtc.WithSettingEngine(opts.SettingEngine))
5757
rtc, err := api.NewPeerConnection(webrtc.Configuration{
5858
ICEServers: servers,

0 commit comments

Comments
 (0)