Add armed Listener with TLS handshake timeout#637
Conversation
http_proxy.go
Outdated
| return listener, nil | ||
| case HTTPSScheme, HTTP2Scheme: | ||
| return tls.NewListener(listener, hp.tlsConfig), nil | ||
| l := NewTLSListener(listener, hp.tlsConfig) |
There was a problem hiding this comment.
How about we passed TLSServerConfig to NewTLSListener?
There was a problem hiding this comment.
I decided to leave it as it is, to have logging in the same place where we load certs.
func (hp *HTTPProxy) configureHTTPS() error {
if hp.config.CertFile == "" && hp.config.KeyFile == "" {
hp.log.Infof("no TLS certificate provided, using self-signed certificate")
} else {
hp.log.Debugf("loading TLS certificate from %s and %s", hp.config.CertFile, hp.config.KeyFile)
}
hp.tlsConfig = httpsTLSConfigTemplate()
return hp.config.ConfigureTLSConfig(hp.tlsConfig)
}There was a problem hiding this comment.
Eventually, I changed it.
|
It feels like tlshandshake could be inlined given that we have the TLSListener |
|
Some more context it the commit messages would be helpful |
Listener is one thing. The other is handling handshake in mitm or SC. |
8123ab5 to
add8ce9
Compare
|
Now we can inline handshake in the listener. |
|
I've came to a conclusion adding handshake in the listener might not be the best option. The logic in the martian Proxy.Serve would cause Proxy to exit on a failed handshake. conn, err := l.Accept()
nosigpipe.IgnoreSIGPIPE(conn)
if err != nil {
var nerr net.Error
if ok := errors.As(err, &nerr); ok && nerr.Temporary() {
if delay == 0 {
delay = 5 * time.Millisecond
} else {
delay *= 2
}
if max := time.Second; delay > max {
delay = max
}
log.Debugf(context.TODO(), "temporary error on accept: %v", err)
time.Sleep(delay)
continue
}
if errors.Is(err, net.ErrClosed) {
log.Debugf(context.TODO(), "listener closed, returning")
return err
}
log.Errorf(context.TODO(), "failed to accept: %v", err)
return err
} |
add8ce9 to
5388a6d
Compare
net.go
Outdated
| return defaultListenConfig().Listen(context.Background(), network, address) | ||
| } | ||
|
|
||
| type Listener struct { |
There was a problem hiding this comment.
Let's use exported variables to configure the listener like we do with http servers for instance.
net.go
Outdated
| handshakeTimeout time.Duration | ||
|
|
||
| // OnAccept is called when a new connection is successfully accepted. | ||
| OnAccept func(net.Conn) |
There was a problem hiding this comment.
An interface with the callback would be better if we are planning to have more of them.
http_proxy.go
Outdated
| hp.proxy.ReadTimeout = hp.config.ReadTimeout | ||
| hp.proxy.ReadHeaderTimeout = hp.config.ReadHeaderTimeout | ||
| hp.proxy.WriteTimeout = hp.config.WriteTimeout | ||
| hp.proxy.MITMTLSHandshakeTimeout = hp.config.TLSServerConfig.HandshakeTimeout |
internal/martian/proxy.go
Outdated
|
|
||
| // MITMTLSHandshakeTimeout specifies the maximum amount of time to wait for a TLS handshake for a MITMed connection. | ||
| // Zero means no timeout. | ||
| MITMTLSHandshakeTimeout time.Duration |
There was a problem hiding this comment.
Move it adjanct to MITMFilter
| res, conn, err = d.DialContextR(req.Context(), "tcp", req.URL.Host) | ||
|
|
||
| ctx, cancel := context.WithTimeout(req.Context(), 60*time.Second) | ||
| res, conn, err = d.DialContextR(ctx, "tcp", req.URL.Host) |
There was a problem hiding this comment.
Let's have ConnectTimeout field.
Let's open new issue to add context to socks.
28cd997 to
62e72b4
Compare
|
@mmatczuk I've addressed your comments |
62e72b4 to
b956763
Compare
net.go
Outdated
| } | ||
|
|
||
| func (l *Listener) withTLS(c net.Conn) (net.Conn, error) { | ||
| tc := tls.Server(c, l.TLSConfig) |
There was a problem hiding this comment.
use Martian/std naming tconn
net.go
Outdated
| Address string | ||
| Log log.Logger | ||
| TLSConfig *tls.Config | ||
| HandshakeTimeout time.Duration |
net_test.go
Outdated
| t.Fatal(err) | ||
| } | ||
|
|
||
| donec := make(chan struct{}) |
There was a problem hiding this comment.
use done as it's more popular variant.
net.go
Outdated
| } | ||
| } | ||
|
|
||
| func (l *Listener) withTLS(c net.Conn) (net.Conn, error) { |
There was a problem hiding this comment.
rename c to conn and return tls.Conn ptr
net_test.go
Outdated
| donec := make(chan struct{}) | ||
|
|
||
| l := Listener{ | ||
| Address: "[::]:0", |
There was a problem hiding this comment.
Why bind to IPv6?
Tests should run on localhost as much as possible.
This way you can avoid the macOS banner to allow listening from the test process.
Please one an issue to fix that across the board i.e. in Martian tests.
There was a problem hiding this comment.
Why bind to IPv6?
I guess, I took it from martian tests.
internal/martian/proxy.go
Outdated
| var hctx context.Context | ||
| if p.MITMTLSHandshakeTimeout > 0 { | ||
| var hcancel context.CancelFunc | ||
| hctx, hcancel = context.WithTimeout(req.Context(), p.MITMTLSHandshakeTimeout) | ||
| defer hcancel() | ||
| } else { | ||
| hctx = req.Context() | ||
| } | ||
| if err := tlsconn.HandshakeContext(hctx); err != nil { |
There was a problem hiding this comment.
Can we have var error - less ugly code.
internal/martian/proxy.go
Outdated
| // Implementations can return ErrConnectFallback to indicate that the CONNECT request should be handled by martian. | ||
| ConnectFunc ConnectFunc | ||
|
|
||
| // ConnectTimeout specifies the maximum amount of time to wait for a dial to complete. |
There was a problem hiding this comment.
It's not dial it's connect with upstream.
b956763 to
c81ba63
Compare
|
@mmatczuk I've addressed your comments and opened two issues. |
The goal of this patch is to move listening related tasks to custom Listener type. The Listener supports: - tls connections with handshake timeout - per connection read and write limits - callbacks to easily attach metrics gatherer
The timeout is 60s by default.
c81ba63 to
f9efa12
Compare
This patch adds server side TLS handshake timeout. It will protect forwarders listener and additionally a handshake in mitm.
Due to the lack of a convenient solution upstream connect is given a constant 1m timeout to complete.