Skip to content

net/http: TLS handshake errors are not (reasonably) accessible for servers #38877

Closed
@joeshaw

Description

@joeshaw

What version of Go are you using (go version)?

$ go version
go version go1.14.2 darwin/amd64

Does this issue reproduce with the latest release?

Yes.

Description of the issue

The current http server implementation makes it very cumbersome to access TLS handshake errors. I would like to be able to see them in order to add metrics and alerting to my service. (A recent misconfiguration caused all connections to fail, and we were unable to alert on these kinds of errors.)

Today, TLS handshake errors are reported by logging to the http.Server ErrorLog logger, if it is set. (Otherwise it goes to the standard logger.)

The line in question:

c.server.logf("http: TLS handshake error from %s: %v", c.rwc.RemoteAddr(), err)

c.server.logf("http: TLS handshake error from %s: %v", c.rwc.RemoteAddr(), err)

And the implementation of logf:

go/src/net/http/server.go

Lines 3062 to 3080 in 9b18968

func (s *Server) logf(format string, args ...interface{}) {
if s.ErrorLog != nil {
s.ErrorLog.Printf(format, args...)
} else {
log.Printf(format, args...)
}
}
// logf prints to the ErrorLog of the *Server associated with request r
// via ServerContextKey. If there's no associated server, or if ErrorLog
// is nil, logging is done via the log package's standard logger.
func logf(r *Request, format string, args ...interface{}) {
s, _ := r.Context().Value(ServerContextKey).(*Server)
if s != nil && s.ErrorLog != nil {
s.ErrorLog.Printf(format, args...)
} else {
log.Printf(format, args...)
}
}

func (s *Server) logf(format string, args ...interface{}) {
	if s.ErrorLog != nil {
		s.ErrorLog.Printf(format, args...)
	} else {
		log.Printf(format, args...)
	}
}

ErrorLog is a *log.Logger, which is a struct and not an interface. It wraps an io.Writer. So in order to capture TLS handshake errors, one must write an io.Writer wrapper that searches for the string TLS handshake error. I think (but am not sure) that error messages are not covered by the Go 1 compatibility guarantee, so this method is both ugly and fragile.

It would be nice if there were another way to get notified of TLS handshake errors. This could be implemented as an optional callback function on the http.Server or tls.Config structs, or as a channel on http.Server. It may make sense to generalize it to more HTTP server errors, but we could start with handshake errors.

Reproducing the issue

An easy way to illustrate this in action is to start up a TLS HTTP server (http.ListenAndServeTLS) and then run nc localhost 1234 </dev/null. On the server you'll get a log line like 2020/05/05 11:41:59 http: TLS handshake error from 127.0.0.1:63162: EOF

Metadata

Metadata

Assignees

No one assigned

    Labels

    FrozenDueToAgeNeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions