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

Skip to content

fix: Remove ICEServer proxying from client to server #400

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion agent/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func TestAgent(t *testing.T) {
func setup(t *testing.T) proto.DRPCPeerBrokerClient {
client, server := provisionersdk.TransportPipe()
closer := agent.New(func(ctx context.Context) (*peerbroker.Listener, error) {
return peerbroker.Listen(server, &peer.ConnOptions{
return peerbroker.Listen(server, nil, &peer.ConnOptions{
Logger: slogtest.Make(t, nil),
})
}, &agent.Options{
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ require (
github.com/hashicorp/go-version v1.4.0
github.com/hashicorp/terraform-config-inspect v0.0.0-20211115214459-90acf1ca460f
github.com/hashicorp/terraform-exec v0.15.0
github.com/hashicorp/terraform-json v0.13.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.10.1
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87
github.com/justinas/nosurf v1.1.1
Expand Down Expand Up @@ -109,7 +110,6 @@ require (
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/hcl/v2 v2.11.1 // indirect
github.com/hashicorp/logutils v1.0.0 // indirect
github.com/hashicorp/terraform-json v0.13.0 // indirect
github.com/hashicorp/terraform-plugin-go v0.5.0 // indirect
github.com/hashicorp/terraform-plugin-log v0.2.0 // indirect
github.com/hashicorp/terraform-registry-address v0.0.0-20210412075316-9b2996cce896 // indirect
Expand Down
46 changes: 8 additions & 38 deletions peerbroker/dial.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,6 @@ import (

// Dial consumes the PeerBroker gRPC connection negotiation stream to produce a WebRTC peered connection.
func Dial(stream proto.DRPCPeerBroker_NegotiateConnectionClient, iceServers []webrtc.ICEServer, opts *peer.ConnOptions) (*peer.Conn, error) {
// Convert WebRTC ICE servers to the protobuf type.
protoIceServers := make([]*proto.WebRTCICEServer, 0, len(iceServers))
for _, iceServer := range iceServers {
var credentialString string
if value, ok := iceServer.Credential.(string); ok {
credentialString = value
}
protoIceServers = append(protoIceServers, &proto.WebRTCICEServer{
Urls: iceServer.URLs,
Username: iceServer.Username,
Credential: credentialString,
CredentialType: int32(iceServer.CredentialType),
})
}
if len(protoIceServers) > 0 {
// Send ICE servers to connect with.
// Client sends ICE servers so clients can determine the node
// servers will meet at. eg. us-west1.coder.com could be a TURN server.
err := stream.Send(&proto.NegotiateConnection_ClientToServer{
Message: &proto.NegotiateConnection_ClientToServer_Servers{
Servers: &proto.WebRTCICEServers{
Servers: protoIceServers,
},
},
})
Comment on lines -33 to -39
Copy link
Contributor

@bryphe-coder bryphe-coder Mar 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It wasn't clear to me where the client is sending these up now?

EDIT: I should've read the PR description & title more closely, that explains why - it's insecure

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was curious because it looks like the ice connection is failing in the tests now:

    t.go:56: 2022-03-04 18:18:57.801 [DEBUG]	(server)	<conn.go:243>	sending local candidate	{"candidate": "candidate:3781755249 1 udp 1694498815 23.98.128.67 1986 typ srflx raddr 0.0.0.0 rport 49007"}
    t.go:56: 2022-03-04 18:18:57.809 [DEBUG]	(client)	<conn.go:357>	accepting candidate	{"candidate": "candidate:3781755249 1 udp 1694498815 23.98.128.67 1986 typ srflx raddr 0.0.0.0 rport 49007"}
    t.go:56: 2022-03-04 18:19:27.905 [DEBUG]	(server)	<conn.go:155>	ice connection state updated	{"state": "failed"}

https://github.com/coder/coder/runs/5426432416?check_suite_focus=true#step:7:130

And I suspect it must be related to this change 🤔

if err != nil {
return nil, xerrors.Errorf("write ice servers: %w", err)
}
}

peerConn, err := peer.Client(iceServers, opts)
if err != nil {
return nil, xerrors.Errorf("create peer connection: %w", err)
Expand All @@ -54,9 +24,9 @@ func Dial(stream proto.DRPCPeerBroker_NegotiateConnectionClient, iceServers []we
case <-peerConn.Closed():
return
case sessionDescription := <-peerConn.LocalSessionDescription():
err = stream.Send(&proto.NegotiateConnection_ClientToServer{
Message: &proto.NegotiateConnection_ClientToServer_Offer{
Offer: &proto.WebRTCSessionDescription{
err = stream.Send(&proto.Exchange{
Message: &proto.Exchange_Sdp{
Sdp: &proto.WebRTCSessionDescription{
SdpType: int32(sessionDescription.Type),
Sdp: sessionDescription.SDP,
},
Expand All @@ -67,8 +37,8 @@ func Dial(stream proto.DRPCPeerBroker_NegotiateConnectionClient, iceServers []we
return
}
case iceCandidate := <-peerConn.LocalCandidate():
err = stream.Send(&proto.NegotiateConnection_ClientToServer{
Message: &proto.NegotiateConnection_ClientToServer_IceCandidate{
err = stream.Send(&proto.Exchange{
Message: &proto.Exchange_IceCandidate{
IceCandidate: iceCandidate.Candidate,
},
})
Expand All @@ -89,10 +59,10 @@ func Dial(stream proto.DRPCPeerBroker_NegotiateConnectionClient, iceServers []we
}

switch {
case serverToClientMessage.GetAnswer() != nil:
case serverToClientMessage.GetSdp() != nil:
peerConn.SetRemoteSessionDescription(webrtc.SessionDescription{
Type: webrtc.SDPType(serverToClientMessage.GetAnswer().SdpType),
SDP: serverToClientMessage.GetAnswer().Sdp,
Type: webrtc.SDPType(serverToClientMessage.GetSdp().SdpType),
SDP: serverToClientMessage.GetSdp().Sdp,
})
case serverToClientMessage.GetIceCandidate() != "":
peerConn.AddRemoteCandidate(webrtc.ICECandidateInit{
Expand Down
14 changes: 11 additions & 3 deletions peerbroker/dial_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,26 @@ func TestDial(t *testing.T) {
defer client.Close()
defer server.Close()

listener, err := peerbroker.Listen(server, &peer.ConnOptions{
Logger: slogtest.Make(t, nil).Named("server").Leveled(slog.LevelDebug),
settingEngine := webrtc.SettingEngine{}
listener, err := peerbroker.Listen(server, func(ctx context.Context) ([]webrtc.ICEServer, error) {
return []webrtc.ICEServer{{
URLs: []string{"stun:stun.l.google.com:19302"},
}}, nil
Comment on lines +35 to +38
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I switch the ICEServersFunc discovery function to nil here - the tests seem to pass: https://github.com/coder/coder/runs/5426432416?check_suite_focus=true#step:7:130

I guess it indicates there may be a bug with how we're interacting with a 'real' STUN server?

Copy link
Contributor

@bryphe-coder bryphe-coder Mar 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(or just removing the settingEngine.SetHostAcceptanceMinWait(time.Hour) line seems to make it pass too)

I had trouble finding docs on what the parameter was controlling - I found the code here https://github.com/pion/webrtc/blob/1765e9b913535f5e6aeacf91e7d4b75d1dbcdc9f/settingengine.go#L107 which sets https://github.com/pion/webrtc/blob/157220e800257ee4090f181e7edcca6435adb9f2/icegatherer.go#L102 - but wasn't clear to me how this enforces calling through to the STUN server.

}, &peer.ConnOptions{
Logger: slogtest.Make(t, nil).Named("server").Leveled(slog.LevelDebug),
SettingEngine: settingEngine,
})
require.NoError(t, err)

api := proto.NewDRPCPeerBrokerClient(provisionersdk.Conn(client))
stream, err := api.NegotiateConnection(ctx)
require.NoError(t, err)

clientConn, err := peerbroker.Dial(stream, []webrtc.ICEServer{{
URLs: []string{"stun:stun.l.google.com:19302"},
}}, &peer.ConnOptions{
Logger: slogtest.Make(t, nil).Named("client").Leveled(slog.LevelDebug),
Logger: slogtest.Make(t, nil).Named("client").Leveled(slog.LevelDebug),
SettingEngine: settingEngine,
})
require.NoError(t, err)
defer clientConn.Close()
Expand Down
51 changes: 24 additions & 27 deletions peerbroker/listen.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,21 @@ import (
"github.com/coder/coder/peerbroker/proto"
)

// ICEServersFunc returns ICEServers when a new connection is requested.
type ICEServersFunc func(ctx context.Context) ([]webrtc.ICEServer, error)

// Listen consumes the transport as the server-side of the PeerBroker dRPC service.
// The Accept function must be serviced, or new connections will hang.
func Listen(connListener net.Listener, opts *peer.ConnOptions) (*Listener, error) {
func Listen(connListener net.Listener, iceServersFunc ICEServersFunc, opts *peer.ConnOptions) (*Listener, error) {
if iceServersFunc == nil {
iceServersFunc = func(ctx context.Context) ([]webrtc.ICEServer, error) {
return []webrtc.ICEServer{}, nil
}
}
ctx, cancelFunc := context.WithCancel(context.Background())
listener := &Listener{
connectionChannel: make(chan *peer.Conn),
iceServersFunc: iceServersFunc,

closeFunc: cancelFunc,
closed: make(chan struct{}),
Expand All @@ -48,6 +57,7 @@ func Listen(connListener net.Listener, opts *peer.ConnOptions) (*Listener, error

type Listener struct {
connectionChannel chan *peer.Conn
iceServersFunc ICEServersFunc

closeFunc context.CancelFunc
closed chan struct{}
Expand Down Expand Up @@ -104,8 +114,12 @@ type peerBrokerService struct {

// NegotiateConnection negotiates a WebRTC connection.
func (b *peerBrokerService) NegotiateConnection(stream proto.DRPCPeerBroker_NegotiateConnectionStream) error {
iceServers, err := b.listener.iceServersFunc(stream.Context())
if err != nil {
return xerrors.Errorf("get ice servers: %w", err)
}
// Start with no ICE servers. They can be sent by the client if provided.
Copy link
Contributor

@bryphe-coder bryphe-coder Mar 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like this comment might need to be updated now - if I understand correctly, the client won't send ICE servers anymore.

peerConn, err := peer.Server([]webrtc.ICEServer{}, b.connOptions)
peerConn, err := peer.Server(iceServers, b.connOptions)
if err != nil {
return xerrors.Errorf("create peer connection: %w", err)
}
Expand All @@ -121,9 +135,9 @@ func (b *peerBrokerService) NegotiateConnection(stream proto.DRPCPeerBroker_Nego
case <-peerConn.Closed():
return
case sessionDescription := <-peerConn.LocalSessionDescription():
err = stream.Send(&proto.NegotiateConnection_ServerToClient{
Message: &proto.NegotiateConnection_ServerToClient_Answer{
Answer: &proto.WebRTCSessionDescription{
err = stream.Send(&proto.Exchange{
Message: &proto.Exchange_Sdp{
Sdp: &proto.WebRTCSessionDescription{
SdpType: int32(sessionDescription.Type),
Sdp: sessionDescription.SDP,
},
Expand All @@ -134,8 +148,8 @@ func (b *peerBrokerService) NegotiateConnection(stream proto.DRPCPeerBroker_Nego
return
}
case iceCandidate := <-peerConn.LocalCandidate():
err = stream.Send(&proto.NegotiateConnection_ServerToClient{
Message: &proto.NegotiateConnection_ServerToClient_IceCandidate{
err = stream.Send(&proto.Exchange{
Message: &proto.Exchange_IceCandidate{
IceCandidate: iceCandidate.Candidate,
},
})
Expand All @@ -156,28 +170,11 @@ func (b *peerBrokerService) NegotiateConnection(stream proto.DRPCPeerBroker_Nego
}

switch {
case clientToServerMessage.GetOffer() != nil:
case clientToServerMessage.GetSdp() != nil:
peerConn.SetRemoteSessionDescription(webrtc.SessionDescription{
Type: webrtc.SDPType(clientToServerMessage.GetOffer().SdpType),
SDP: clientToServerMessage.GetOffer().Sdp,
Type: webrtc.SDPType(clientToServerMessage.GetSdp().SdpType),
SDP: clientToServerMessage.GetSdp().Sdp,
})
case clientToServerMessage.GetServers() != nil:
// Convert protobuf ICE servers to the WebRTC type.
iceServers := make([]webrtc.ICEServer, 0, len(clientToServerMessage.GetServers().Servers))
for _, iceServer := range clientToServerMessage.GetServers().Servers {
iceServers = append(iceServers, webrtc.ICEServer{
URLs: iceServer.Urls,
Username: iceServer.Username,
Credential: iceServer.Credential,
CredentialType: webrtc.ICECredentialType(iceServer.CredentialType),
})
}
err = peerConn.SetConfiguration(webrtc.Configuration{
ICEServers: iceServers,
})
if err != nil {
return peerConn.CloseWithError(xerrors.Errorf("set ice configuration: %w", err))
}
case clientToServerMessage.GetIceCandidate() != "":
peerConn.AddRemoteCandidate(webrtc.ICECandidateInit{
Candidate: clientToServerMessage.GetIceCandidate(),
Expand Down
4 changes: 2 additions & 2 deletions peerbroker/listen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func TestListen(t *testing.T) {
defer client.Close()
defer server.Close()

listener, err := peerbroker.Listen(server, nil)
listener, err := peerbroker.Listen(server, nil, nil)
require.NoError(t, err)

api := proto.NewDRPCPeerBrokerClient(provisionersdk.Conn(client))
Expand All @@ -43,7 +43,7 @@ func TestListen(t *testing.T) {
defer client.Close()
defer server.Close()

listener, err := peerbroker.Listen(server, nil)
listener, err := peerbroker.Listen(server, nil, nil)
require.NoError(t, err)
go listener.Close()
_, err = listener.Accept()
Expand Down
Loading