Skip to content
This repository has been archived by the owner on Dec 7, 2019. It is now read-only.

do connection setup here #9

Closed
wants to merge 10 commits into from

Conversation

marten-seemann
Copy link

This package now sets up new connections. A go-libp2p-conn.Conn is an encrypted, stream-multiplexed connection. If the underlying tpt.Conn is a single-stream connection, it is first encrypted (using secio) and then a stream multiplexer is used to provide multistream support. If the tpt.Conn supports transport-level stream multiplexing, the secio handshake is performed on a new stream.

This PR depends on libp2p/go-libp2p-transport#20.
There are still a couple of TODOs here, especially regarding the timeouts for dialing connections as well as the connection protector.

dial.go Outdated
}
maconn = pconn
}
// TODO: reenable the protector
Copy link
Contributor

Choose a reason for hiding this comment

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

@Kubuxu how would the protector work with something like QUIC?

Copy link
Author

Choose a reason for hiding this comment

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

dial.go Outdated
}

cryptoProtoChoice := SecioTag
if !iconn.EncryptConnections || d.PrivateKey == nil {
cryptoProtoChoice = NoEncryptionTag
}

maconn.SetReadDeadline(time.Now().Add(NegotiateReadTimeout))
var stream smux.Stream
if ssc, ok := tptConn.(tpt.SingleStreamConn); ok {
Copy link
Contributor

Choose a reason for hiding this comment

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

Use a type switch here:

switch con := tptConn.(type) {
case tpt.SingleStreamConn:
case tpt.MultiStreamConn:
default:
}

Copy link
Author

Choose a reason for hiding this comment

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

Done.

dial.go Outdated
"fmt"
"math/rand"
"strings"
"time"

smux "github.com/jbenet/go-stream-muxer"
Copy link
Member

Choose a reason for hiding this comment

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

s/jbente/libp2p/

conn.go Outdated
}
defer stream.Close()

secSession, err = setupSecureSession(ctx, local, privKey, stream)
Copy link
Contributor

Choose a reason for hiding this comment

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

Hrm... i'm not sure this is the right way to do this. This is encrypting on top of of the stream muxer, instead of underneath it.

I think that instead of doing it this way, we can just not support private networks and QUIC at the same time. Just error out instead i'm thinking.

cc @Kubuxu @lgierth

Copy link
Contributor

Choose a reason for hiding this comment

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

oh wait, this isnt the private networks protector layer. i was mistaken.

Copy link
Author

Choose a reason for hiding this comment

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

The problem here was that I wasn't using the ReadWriter provided by the secio package. I fixed that.

conn.go Outdated

var streamConn smux.Conn
var secSession secio.Session
if ssc, ok := tptConn.(tpt.SingleStreamConn); ok {
Copy link
Contributor

Choose a reason for hiding this comment

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

you can do a type switch here:

switch sc := tptConn.(type) {
case tpt.SingleStreamConn:
  // sc is a SingleStreamConn in this case block
case tpt.MultiStreamConn:
  // sc is a multistreamconn in this case block
default:
  return errWrongType
}

Copy link
Author

Choose a reason for hiding this comment

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

Done.

@Kubuxu
Copy link
Member

Kubuxu commented Jun 26, 2017

The mixing of the secio with the multiplexed connections looks good, what we also have to do is confirm that person on the other side of the secio is using the exactly the same QUIC connection.

This is to prevent someone proxying the secio over different QUIC. This is a problem as confirming identity over secio over QUIC doesn't prove the identity of the the other side of QUIC.

conn.go Outdated
return nil, err
}
case tpt.MultiStreamConn:
// 1. secure the connection on the first best stream
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we want to add a big TODO and WARNING here, we're only doing this this way for now, for quic, until we have the ability to simply rely on QUICs crypto to validate the peers. Its also insecure (can be man in the middled) so we should note that QUIC is experimental for now.

dial.go Outdated

err = msmux.SelectProtoOrFail(cryptoProtoChoice, maconn)
stream.SetReadDeadline(time.Now().Add(NegotiateReadTimeout))
Copy link
Contributor

Choose a reason for hiding this comment

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

should check the error here

Copy link
Contributor

Choose a reason for hiding this comment

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

(oh, i guess this wasnt checked before, ignore this comment)

dial.go Outdated
if err != nil {
errOut = err
return
}

maconn.SetReadDeadline(time.Time{})
// clear deadline
stream.SetReadDeadline(time.Time{})
Copy link
Contributor

Choose a reason for hiding this comment

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

error check again

dial.go Outdated
if err != nil {
errOut = err
return
}

if d.Protector != nil {
pconn, err := d.Protector.Protect(maconn)
var pconn tpt.Conn
pconn, err = d.Protector.Protect(tptConn)
Copy link
Contributor

Choose a reason for hiding this comment

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

So in this case, if using a 'protector', we end up just stream multiplexing over a single QUIC conn, am i reading that correctly?

If that is the case, then we might just want to (for now) disable the use of QUIC and private networks at the same time.

Copy link
Author

Choose a reason for hiding this comment

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

The protector should work fine.
tptConn is the general tpt.Conn. pnet.Protect does another type assertion in https://github.com/marten-seemann/go-libp2p-pnet/blob/11a8850893ddc8d571abe57b5082d0110b77d412/protector.go#L33-L42. For MultiStreamConns, it wraps OpenStream and AcceptStream and uses the PSK with a separate nonce for every stream (see https://github.com/marten-seemann/go-libp2p-pnet/blob/11a8850893ddc8d571abe57b5082d0110b77d412/psk_multistreamconn.go#L22-L36).

@whyrusleeping
Copy link
Contributor

So to move forward here, I think we want to disable private networks and QUIC being used together, as well as adding a few more comments about the security implications of the secio peerID verification we're doing.

Also, to make future integration easier, I think we should use the servers peer private key as the QUIC cert, even though this doesnt buy us any security right now, it will help make the interfaces clearer for when we are able to rely on tls to do the client<->server verification (aka, when we can trust quics crypto to do the peer handshake for us).

@marten-seemann
Copy link
Author

marten-seemann commented Jul 19, 2017

I think there is an easy (although not very aesthetic) solution for the MITM vulnerability for QUIC connections (I justed pushed that one). The QUIC vulnerability arises because secio only secures the stream the handshake is performed on. All other streams are unencrypted. A MITM would just forward the secio stream and then intercept the data streams.
What we can do here is use the Protector to encrypt the data streams. The PSK needed to do that could is generated by server (it doesn't matter who does that) and sent as the first thing on the secio-secured stream.

I'm really looking forward to get rid of all those hacks. I recently started with the TLS integration in quic-go, but it will probably take me some time to get that production-ready.

@whyrusleeping: I don't understand your command about the certs. I guess you mean something that we establish a CA to sign the server's private key and use that cert as the cert for the QUIC connection?

listen.go Outdated
defer stream.Close()
}

if _, _, err := l.mux.Negotiate(stream); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

Definitely unrelated to this PR, but could we check that the protocol chosen here is secio, and error out otherwise? It would be helpful if nodes on older code don't behave weirdly when we start supporting tls1.3

@whyrusleeping
Copy link
Contributor

@marten-seemann i'm fine moving forward with parts of this PR, if we can do the MultiplexConn stuff in a followup PR, i think we can get the rest in much more quickly.

@marten-seemann
Copy link
Author

I removed the MultiplexConn type assertions (and everything related with that), so we could get the rest merged (this will also require merging another change to go-libp2p-transport).

Copy link
Member

@Stebalien Stebalien left a comment

Choose a reason for hiding this comment

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

Nice. I have a few comments that I can fix myself depending on your timing but this is mostly good to go.

}

cryptoProtoChoice := SecioTag
if !iconn.EncryptConnections || d.PrivateKey == nil {
cryptoProtoChoice = NoEncryptionTag
}

maconn.SetReadDeadline(time.Now().Add(NegotiateReadTimeout))
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't we still be setting this (or, more generally, a timeout on the entire connection)? Otherwise, we could wait here forever. Yes, we'll move on elsewhere but we'll never free these resources.

Maybe change this to the dial timeout (and set both the read and write deadlines)/raw

Copy link
Author

Choose a reason for hiding this comment

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

This should be taken care of by the context. When the context times out, the rawConn is closed, causing the Read to return immediately.

Copy link
Member

Choose a reason for hiding this comment

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

Ah, good point.

c = &secureConn{
insecure: tptConn,
secure: secSession,
}
Copy link
Member

Choose a reason for hiding this comment

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

If remote is not "", we should check if remote == secSession.RemoteID() here. That way, we do this before doing anything else. Currently, we check in swarm but, IMO, that's too late. Note: this is an improvement, not a bug.

Copy link
Author

Choose a reason for hiding this comment

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

Can we do that in a separate PR?

Copy link
Member

Choose a reason for hiding this comment

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

Fair enough.

listen.go Outdated
if l.privk == nil || !iconn.EncryptConnections {
log.Warning("listener %s listening INSECURELY!", l)
return c, nil
for c := range l.incoming {
Copy link
Member

Choose a reason for hiding this comment

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

Don't need a loop. We had one for the IsTemporary error check but that now happens lower down.

This can be simplified to:

c := <- l.incoming
return c.conn, c.err

Copy link
Author

Choose a reason for hiding this comment

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

Fixed.

secure_conn.go Outdated
secio "github.com/libp2p/go-libp2p-secio"
tpt "github.com/libp2p/go-libp2p-transport"
ma "github.com/multiformats/go-multiaddr"
)

// secureConn wraps another Conn object with an encrypted channel.
Copy link
Member

Choose a reason for hiding this comment

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

Comment to be reverted.

Copy link
Author

Choose a reason for hiding this comment

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

Done.

listen.go Outdated
if l.protec != nil {
pc, err := l.protec.Protect(maconn)

ctx, cancel := context.WithTimeout(l.ctx, ConnAcceptTimeout)
Copy link
Member

Choose a reason for hiding this comment

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

We should consider setting a ConnAcceptTimeout deadline on the connection itself to avoid leaking go routines.

Copy link
Member

Choose a reason for hiding this comment

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

As with dialing, the connection will be closed when this expires so ignore this.

@Stebalien
Copy link
Member

Also, I'll go ahead and update the secio dep version.

@ghost ghost assigned Stebalien Oct 26, 2017
listen.go Outdated
}
return nil, fmt.Errorf("listener is closed")

c := <-l.incoming
Copy link
Member

Choose a reason for hiding this comment

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

Actually, I was wrong. We'll also need to check if the channel is closed (c, ok := <-l.incoming...).

Copy link
Author

Choose a reason for hiding this comment

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

Right. Fixed.

@Stebalien
Copy link
Member

So, apparently this still does multiplexing setup and needs libp2p/go-libp2p-interface-conn#1 (which we can't merge as it depends on libp2p/go-libp2p-transport#20 which we reverted).

listen.go Outdated
}

// If we have a wrapper func, wrap this conn
if l.wrapper != nil {
Copy link
Member

Choose a reason for hiding this comment

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

This swaps the order of wrapping and protecting. Was this a bug?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, I believe it was. We wrap/protect in this order when dialing.

listen.go Outdated
}()

select {
case <-ctx.Done():
Copy link
Member

Choose a reason for hiding this comment

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

There's a race here. We can return the connection and then close it.

Copy link
Author

Choose a reason for hiding this comment

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

I pushed a fix for that.

listen.go Outdated
}

if _, _, err := l.mux.Negotiate(conn); err != nil {
log.Warning("incoming conn: negotiation of crypto protocol failed: ", err)
Copy link
Member

Choose a reason for hiding this comment

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

Wrong error.

Copy link
Member

Choose a reason for hiding this comment

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

Actually, what exactly is this doing? You didn't change this...

Copy link
Member

Choose a reason for hiding this comment

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

Ah. This is negotiating security (we overload mux way too much). Nevermind.

@marten-seemann
Copy link
Author

@Stebalien: Is there anything else I need to fix here?

@Stebalien
Copy link
Member

Stebalien commented Nov 6, 2017

The only thing blocking this is #9 (comment). I've been working on a new PR with the secio/async stuff so we can move that forward (I can hand it off to you if you want, I'm hitting some odd test failures around timeouts that you've probably already figured out). I'm also working on the transport interfaces problems but it doesn't look like it's going to be quite as simple as I thought... (which is why I'd like to just get the async accept stuff merged first).

Stebalien added a commit that referenced this pull request Nov 7, 2017
Port of async connection accepting from #9
@Stebalien
Copy link
Member

That is, #18

Stebalien added a commit that referenced this pull request Nov 9, 2017
Port of async connection accepting from #9

We need this to:

1. Perform the secio handshake immediately
2. Use a context as the dial timeout (to do this, we need to do the secio
handshake immediately).
@Stebalien Stebalien closed this Jun 8, 2018
@ghost ghost removed the in progress label Jun 8, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants