Count ALPN connections separately in metrics

Also this required a new attribute on the IMAP and SMTP connection structs, and made it easier to avoid using the reflection tools.
This commit is contained in:
s0ph0s 2024-12-13 23:37:00 -05:00
parent f02e04df72
commit f7976539ea
2 changed files with 19 additions and 14 deletions

View file

@ -52,7 +52,6 @@ import (
"path"
"path/filepath"
"regexp"
"reflect"
"runtime/debug"
"slices"
"sort"
@ -171,6 +170,7 @@ type conn struct {
state state
conn net.Conn
tls bool // Whether TLS has been initialized.
viaHTTPS bool // Whether this connection came in via HTTPS (using TLS ALPN).
br *bufio.Reader // From remote, with TLS unwrapped in case of TLS.
line chan lineErr // If set, instead of reading from br, a line is read from this channel. For reading a line in IDLE while also waiting for mailbox/account updates.
lastLine string // For detecting if syntax error is fatal, i.e. if this ends with a literal. Without crlf.
@ -347,8 +347,9 @@ func Listen() http.FnALPNHelper {
}
if listener.IMAPS.EnableOnHTTPS && alpnHelper == nil {
alpnHelper = func(tc *tls.Config, conn net.Conn) {
protocol = protocol + "https"
metricIMAPConnection.WithLabelValues(protocol).Inc()
serve(name, mox.Cid(), tc, conn, true, false)
serve(name, mox.Cid(), tc, conn, true, false, true)
}
}
}
@ -391,7 +392,7 @@ func listen1(protocol, listenerName, ip string, port int, tlsConfig *tls.Config,
}
metricIMAPConnection.WithLabelValues(protocol).Inc()
go serve(listenerName, mox.Cid(), tlsConfig, conn, xtls, noRequireSTARTTLS)
go serve(listenerName, mox.Cid(), tlsConfig, conn, xtls, noRequireSTARTTLS, false)
}
}
@ -646,7 +647,7 @@ func (c *conn) xhighestModSeq(tx *bstore.Tx, mailboxID int64) store.ModSeq {
var cleanClose struct{} // Sentinel value for panic/recover indicating clean close of connection.
func serve(listenerName string, cid int64, tlsConfig *tls.Config, nc net.Conn, xtls, noRequireSTARTTLS bool) {
func serve(listenerName string, cid int64, tlsConfig *tls.Config, nc net.Conn, xtls, noRequireSTARTTLS, viaHTTPS bool) {
var remoteIP net.IP
if a, ok := nc.RemoteAddr().(*net.TCPAddr); ok {
remoteIP = a.IP
@ -659,6 +660,7 @@ func serve(listenerName string, cid int64, tlsConfig *tls.Config, nc net.Conn, x
cid: cid,
conn: nc,
tls: xtls,
viaHTTPS: viaHTTPS,
lastlog: time.Now(),
baseTLSConfig: tlsConfig,
remoteIP: remoteIP,
@ -728,8 +730,7 @@ func serve(listenerName string, cid int64, tlsConfig *tls.Config, nc net.Conn, x
}
}()
isAlreadyTLS := reflect.TypeOf(nc) == reflect.TypeFor[*tls.Conn]()
if xtls && !isAlreadyTLS {
if xtls && !viaHTTPS {
// Start TLS on connection. We perform the handshake explicitly, so we can set a
// timeout, do client certificate authentication, log TLS details afterwards.
c.xtlsHandshakeAndAuthenticate(c.conn)

View file

@ -23,7 +23,6 @@ import (
"net"
"net/textproto"
"os"
"reflect"
"runtime/debug"
"slices"
"sort"
@ -266,7 +265,7 @@ func Listen() http.FnALPNHelper {
alpnHelper = func(tc *tls.Config, conn net.Conn) {
log := mlog.New("smtpserver", nil)
resolver := dns.StrictResolver{Log: log.Logger}
serve(name, mox.Cid(), hostname, tc, conn, resolver, true, true, maxMsgSize, true, true, true, nil, 0)
serve(name, mox.Cid(), hostname, tc, conn, resolver, true, true, true, maxMsgSize, true, true, true, nil, 0)
}
}
}
@ -310,7 +309,7 @@ func listen1(protocol, name, ip string, port int, hostname dns.Domain, tlsConfig
// Package is set on the resolver by the dkim/spf/dmarc/etc packages.
resolver := dns.StrictResolver{Log: log.Logger}
go serve(name, mox.Cid(), hostname, tlsConfig, conn, resolver, submission, xtls, maxMessageSize, requireTLSForAuth, requireTLSForDelivery, requireTLS, dnsBLs, firstTimeSenderDelay)
go serve(name, mox.Cid(), hostname, tlsConfig, conn, resolver, submission, xtls, false, maxMessageSize, requireTLSForAuth, requireTLSForDelivery, requireTLS, dnsBLs, firstTimeSenderDelay)
}
}
@ -336,6 +335,7 @@ type conn struct {
tls bool
extRequireTLS bool // Whether to announce and allow the REQUIRETLS extension.
viaHTTPS bool // Whether the connection came in via the HTTPS port (using TLS ALPN).
resolver dns.Resolver
r *bufio.Reader
w *bufio.Writer
@ -810,7 +810,7 @@ func (c *conn) writelinef(format string, args ...any) {
var cleanClose struct{} // Sentinel value for panic/recover indicating clean close of connection.
func serve(listenerName string, cid int64, hostname dns.Domain, tlsConfig *tls.Config, nc net.Conn, resolver dns.Resolver, submission, xtls bool, maxMessageSize int64, requireTLSForAuth, requireTLSForDelivery, requireTLS bool, dnsBLs []dns.Domain, firstTimeSenderDelay time.Duration) {
func serve(listenerName string, cid int64, hostname dns.Domain, tlsConfig *tls.Config, nc net.Conn, resolver dns.Resolver, submission, xtls, viaHTTPS bool, maxMessageSize int64, requireTLSForAuth, requireTLSForDelivery, requireTLS bool, dnsBLs []dns.Domain, firstTimeSenderDelay time.Duration) {
var localIP, remoteIP net.IP
if a, ok := nc.LocalAddr().(*net.TCPAddr); ok {
localIP = a.IP
@ -831,6 +831,7 @@ func serve(listenerName string, cid int64, hostname dns.Domain, tlsConfig *tls.C
conn: nc,
submission: submission,
tls: xtls,
viaHTTPS: viaHTTPS,
extRequireTLS: requireTLS,
resolver: resolver,
lastlog: time.Now(),
@ -894,8 +895,7 @@ func serve(listenerName string, cid int64, hostname dns.Domain, tlsConfig *tls.C
}
}()
isAlreadyTLS := reflect.TypeOf(nc) == reflect.TypeFor[*tls.Conn]()
if xtls && !isAlreadyTLS {
if xtls && !viaHTTPS {
// Start TLS on connection. We perform the handshake explicitly, so we can set a
// timeout, do client certificate authentication, log TLS details afterwards.
c.xtlsHandshakeAndAuthenticate(c.conn)
@ -1046,10 +1046,14 @@ func command(c *conn) {
// For use in metric labels.
func (c *conn) kind() string {
k := "smtp"
if c.submission {
return "submission"
k = "submission"
}
return "smtp"
if c.viaHTTPS {
k = k + "https"
}
return k
}
func (c *conn) xneedHello() {