-
Notifications
You must be signed in to change notification settings - Fork 14
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
Add armed Listener with TLS handshake timeout #637
Conversation
http_proxy.go
Outdated
@@ -615,7 +618,9 @@ func (hp *HTTPProxy) listen() (net.Listener, error) { | |||
case HTTPScheme: | |||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about we passed TLSServerConfig to NewTLSListener?
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 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
@@ -78,3 +81,85 @@ func Listen(network, address string) (net.Listener, error) { | |||
// I asked about it here: https://groups.google.com/g/golang-nuts/c/Q1I7Viz9AJc | |||
return defaultListenConfig().Listen(context.Background(), network, address) | |||
} | |||
|
|||
type Listener struct { |
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An interface with the callback would be better if we are planning to have more of them.
http_proxy.go
Outdated
@@ -251,6 +251,7 @@ func (hp *HTTPProxy) configureProxy() error { | |||
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 |
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.
Move it to mitm setup
internal/martian/proxy.go
Outdated
@@ -155,6 +155,10 @@ type Proxy struct { | |||
// A zero or negative value means there will be no timeout. | |||
WriteTimeout time.Duration | |||
|
|||
// 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use Martian/std naming tconn
net.go
Outdated
Address string | ||
Log log.Logger | ||
TLSConfig *tls.Config | ||
HandshakeTimeout time.Duration |
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.
TLSHandshakeTimeout
net_test.go
Outdated
t.Fatal(err) | ||
} | ||
|
||
donec := make(chan struct{}) |
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 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we have var error - less ugly code.
internal/martian/proxy.go
Outdated
@@ -121,6 +121,9 @@ type Proxy struct { | |||
// 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
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.
LGTM
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.