emersion / go-smtp

📤 An SMTP client & server library written in Go
MIT License
1.72k stars 216 forks source link

Handle zero-byte TCP healthcheck connections #236

Open nimjor opened 1 year ago

nimjor commented 1 year ago

In an environment like kubernetes hosted on AWS, the server needs to handle health checks from load balancers. A TCP health check only checks that a connection can be established, and then closes without sending any data or waiting for a greeting. go-smtp's Server then logs error messages like

handler error: read tcp 127.0.0.1:9025->127.0.0.1:55372: read: connection reset by peer

which in the case of most health check intervals (< 5 min) means noisy logs with lots of pointless error messages (the noise is the problem, not any functionality of the Server itself). Instead, Server.handleConn() should check whether any bytes were sent and only log an error if the connection was non-empty.

emersion commented 1 year ago

A connection close should result in EOF I think, not ECONNRESET…

nimjor commented 1 year ago

This is reproducible locally by simply starting the SMTP server listening on localhost:9025, then in a terminal issuing nc -vz localhost 9025:

❯ nc -vz localhost 9025
Connection to localhost port 9025 [tcp/swa-3] succeeded!

and in the process running the server we see:

handler error: read tcp 127.0.0.1:9025->127.0.0.1:62501: read: connection reset by peer
emersion commented 9 months ago

I can't reproduce this.

foxcpp commented 8 months ago

Similar issue reported for maddy: https://github.com/foxcpp/maddy/discussions/646

2023-11-09T15:17:40.094Z smtp: handler error: EOF   
2023-11-09T15:17:52.242Z smtp: handler error: EOF   
2023-11-09T15:17:59.750Z smtp: 220 [MADDY-NAME] ESMTP Service Ready
2023-11-09T15:18:02.027Z smtp: 220 [MADDY-NAME] ESMTP Service Ready
2023-11-09T15:18:02.028Z smtp: handler error: read tcp [MADDY-IP]:1025->[OTHER-IP]:54266: read: connection reset by peer
2023-11-09T15:18:10.097Z smtp: handler error: EOF   
2023-11-09T15:18:22.238Z smtp: handler error: EOF   

I think at least EOF should be filtered out.

sapmli commented 2 weeks ago

We experience the same within kubernetes. As a workaround, we implemented a log filter like this.

smtpServer.ErrorLog = NewSMTPErrorHandler([]string{"read: connection reset by peer", "proxyproto: proxy protocol signature not present"})

func (h *SMTPErrorHandler) Printf(format string, v ...interface{}) {
    if len(h.filter) > 0 && strings.HasPrefix(format, "error handling") {
        ...