-
Notifications
You must be signed in to change notification settings - Fork 7
Conversation
dial.go
Outdated
} | ||
maconn = pconn | ||
} | ||
// TODO: reenable the protector |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Solved by libp2p/go-libp2p-pnet#8.
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 { |
There was a problem hiding this comment.
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:
}
There was a problem hiding this comment.
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" |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
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 |
There was a problem hiding this comment.
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)) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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{}) |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 MultiStreamConn
s, 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).
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). |
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. 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? |
425c27d
to
603b821
Compare
d006af1
to
17df6b5
Compare
49d8cf6
to
dff3ff6
Compare
listen.go
Outdated
defer stream.Close() | ||
} | ||
|
||
if _, _, err := l.mux.Negotiate(stream); err != nil { |
There was a problem hiding this comment.
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
@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. |
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). |
aa5f361
to
463aed2
Compare
There was a problem hiding this 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)) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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, | ||
} |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment to be reverted.
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
Also, I'll go ahead and update the secio dep version. |
ad3a802
to
ee6eb04
Compare
listen.go
Outdated
} | ||
return nil, fmt.Errorf("listener is closed") | ||
|
||
c := <-l.incoming |
There was a problem hiding this comment.
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
...).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right. Fixed.
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). |
ee6eb04
to
1820ac9
Compare
listen.go
Outdated
} | ||
|
||
// If we have a wrapper func, wrap this conn | ||
if l.wrapper != nil { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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(): |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrong error.
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
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.
@Stebalien: Is there anything else I need to fix here? |
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). |
Port of async connection accepting from #9
That is, #18 |
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).
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.