@@ -27,23 +27,27 @@ import (
27
27
"io"
28
28
"log/slog"
29
29
"net"
30
+ "slices"
30
31
"strings"
31
32
"sync"
32
33
"time"
33
34
34
35
"github.com/gravitational/trace"
35
36
"github.com/jonboulle/clockwork"
37
+ "github.com/prometheus/client_golang/prometheus"
36
38
37
39
"github.com/gravitational/teleport"
38
40
"github.com/gravitational/teleport/api/constants"
39
41
"github.com/gravitational/teleport/api/utils/pingconn"
40
42
"github.com/gravitational/teleport/lib/auth/authclient"
41
43
"github.com/gravitational/teleport/lib/authz"
42
44
"github.com/gravitational/teleport/lib/defaults"
45
+ "github.com/gravitational/teleport/lib/observability/metrics"
43
46
"github.com/gravitational/teleport/lib/srv/alpnproxy/common"
44
47
"github.com/gravitational/teleport/lib/srv/db/dbutils"
45
48
"github.com/gravitational/teleport/lib/tlsca"
46
49
"github.com/gravitational/teleport/lib/utils"
50
+ logutils "github.com/gravitational/teleport/lib/utils/log"
47
51
)
48
52
49
53
// ProxyConfig is the configuration for an ALPN proxy server.
@@ -298,6 +302,14 @@ func New(cfg ProxyConfig) (*Proxy, error) {
298
302
return nil , trace .Wrap (err )
299
303
}
300
304
305
+ if err := metrics .RegisterPrometheusCollectors (
306
+ proxyConnectionsTotal ,
307
+ proxyActiveConnections ,
308
+ proxyConnectionErrorsTotal ,
309
+ ); err != nil {
310
+ return nil , trace .Wrap (err )
311
+ }
312
+
301
313
return & Proxy {
302
314
cfg : cfg ,
303
315
log : cfg .Log ,
@@ -332,7 +344,7 @@ func (p *Proxy) Serve(ctx context.Context) error {
332
344
// For example in ReverseTunnel handles connection asynchronously and closing conn after
333
345
// service handler returned will break service logic.
334
346
// https://github.com/gravitational/teleport/blob/master/lib/sshutils/server.go#L397
335
- if err := p .handleConn (ctx , clientConn , nil ); err != nil {
347
+ if err := p .handleConn (ctx , clientConn , nil , common . ConnHandlerSourceListener ); err != nil {
336
348
if cerr := clientConn .Close (); cerr != nil && ! utils .IsOKNetworkError (cerr ) {
337
349
p .log .WarnContext (ctx , "Failed to close client connection" , "error" , cerr )
338
350
}
@@ -372,8 +384,24 @@ type HandlerFuncWithInfo func(ctx context.Context, conn net.Conn, info Connectio
372
384
// 5. For backward compatibility check RouteToDatabase identity field
373
385
// was set if yes forward to the generic TLS DB handler.
374
386
// 6. Forward connection to the handler obtained in step 2.
375
- func (p * Proxy ) handleConn (ctx context.Context , clientConn net.Conn , defaultOverride * tls.Config ) error {
376
- hello , conn , err := p .readHelloMessageWithoutTLSTermination (ctx , clientConn )
387
+ func (p * Proxy ) handleConn (ctx context.Context , clientConn net.Conn , defaultOverride * tls.Config , connSource common.ConnHandlerSource ) (err error ) {
388
+ var hello * tls.ClientHelloInfo
389
+ var conn net.Conn
390
+ defer func () {
391
+ if err != nil {
392
+ proxyConnectionErrorsTotal .WithLabelValues (
393
+ getRequestedALPNFromHello (hello ),
394
+ string (connSource ),
395
+ ).Inc ()
396
+ }
397
+ }()
398
+
399
+ // Attempt to read TLS hello. Always increment total counters even on failures.
400
+ hello , conn , err = p .readHelloMessageWithoutTLSTermination (ctx , clientConn , connSource )
401
+ proxyConnectionsTotal .WithLabelValues (
402
+ getRequestedALPNFromHello (hello ),
403
+ string (connSource ),
404
+ ).Inc ()
377
405
if err != nil {
378
406
return trace .Wrap (err )
379
407
}
@@ -503,7 +531,7 @@ func (p *Proxy) getTLSConfig(desc *HandlerDecs, defaultOverride *tls.Config) *tl
503
531
// readHelloMessageWithoutTLSTermination allows reading a ClientHelloInfo message without termination of
504
532
// incoming TLS connection. After calling readHelloMessageWithoutTLSTermination function a returned
505
533
// net.Conn should be used for further operation.
506
- func (p * Proxy ) readHelloMessageWithoutTLSTermination (ctx context.Context , conn net.Conn ) (* tls.ClientHelloInfo , net.Conn , error ) {
534
+ func (p * Proxy ) readHelloMessageWithoutTLSTermination (ctx context.Context , conn net.Conn , connSource common. ConnHandlerSource ) (* tls.ClientHelloInfo , net.Conn , error ) {
507
535
buff := new (bytes.Buffer )
508
536
var hello * tls.ClientHelloInfo
509
537
tlsConn := tls .Server (readOnlyConn {reader : io .TeeReader (conn , buff )}, & tls.Config {
@@ -526,7 +554,13 @@ func (p *Proxy) readHelloMessageWithoutTLSTermination(ctx context.Context, conn
526
554
if err := conn .SetReadDeadline (time.Time {}); err != nil {
527
555
return nil , nil , trace .Wrap (err )
528
556
}
529
- return hello , newBufferedConn (conn , buff ), nil
557
+ return hello ,
558
+ newReportingConn (
559
+ newBufferedConn (conn , buff ),
560
+ getRequestedALPNFromHello (hello ),
561
+ string (connSource ),
562
+ ),
563
+ nil
530
564
}
531
565
532
566
func (p * Proxy ) handleDatabaseConnection (ctx context.Context , conn net.Conn , connInfo ConnectionInfo ) error {
@@ -630,9 +664,20 @@ func (p *Proxy) Close() error {
630
664
631
665
// MakeConnectionHandler creates a ConnectionHandler which provides a callback
632
666
// to handle incoming connections by this ALPN proxy server.
633
- func (p * Proxy ) MakeConnectionHandler (defaultOverride * tls.Config ) ConnectionHandler {
667
+ //
668
+ // Note that some registered handlers are async. The input client connection
669
+ // will be automatically closed upon handler errors.
670
+ func (p * Proxy ) MakeConnectionHandler (defaultOverride * tls.Config , connSource common.ConnHandlerSource ) ConnectionHandler {
634
671
return func (ctx context.Context , conn net.Conn ) error {
635
- return p .handleConn (ctx , conn , defaultOverride )
672
+ if err := p .handleConn (ctx , conn , defaultOverride , connSource ); err != nil {
673
+ // Make sure we close the connection on error.
674
+ if cerr := conn .Close (); cerr != nil && ! utils .IsOKNetworkError (cerr ) {
675
+ p .log .WarnContext (ctx , "Failed to close client connection" , "error" , cerr , "remote_addr" , logutils .StringerAttr (conn .RemoteAddr ()))
676
+ }
677
+ // Still return the error for caller to report/log.
678
+ return trace .Wrap (err )
679
+ }
680
+ return nil
636
681
}
637
682
}
638
683
@@ -664,3 +709,56 @@ func isConnRemoteError(err error) bool {
664
709
var opErr * net.OpError
665
710
return errors .As (err , & opErr ) && opErr .Op == "remote error"
666
711
}
712
+
713
+ // getRequestedALPNFromHello returns the primary requested ALPN by the client.
714
+ // The server may not always terminate TLS so we won't know the negotiated
715
+ // protocol. Here we just return the first supported ALPN from the client hello
716
+ // and assumes that will likely be the one gets selected. If no supported ALPN
717
+ // found, the function returns "unknown".
718
+ func getRequestedALPNFromHello (hello * tls.ClientHelloInfo ) string {
719
+ if hello == nil {
720
+ return "unknown"
721
+ }
722
+
723
+ for i := range hello .SupportedProtos {
724
+ protocol := common .Protocol (hello .SupportedProtos [i ])
725
+ if strings .HasPrefix (string (protocol ), string (common .ProtocolAuth )) {
726
+ protocol = common .ProtocolAuth
727
+ }
728
+
729
+ if slices .Contains (common .SupportedProtocols , protocol ) {
730
+ return string (protocol )
731
+ }
732
+ }
733
+ return "unknown"
734
+ }
735
+
736
+ var (
737
+ proxyConnectionsTotal = prometheus .NewGaugeVec (
738
+ prometheus.GaugeOpts {
739
+ Namespace : teleport .MetricNamespace ,
740
+ Subsystem : "alpn_proxy" ,
741
+ Name : "connections_total" ,
742
+ Help : "Number of total connections handled by TLS routing proxy server." ,
743
+ },
744
+ []string {"alpn" , "source" },
745
+ )
746
+ proxyActiveConnections = prometheus .NewGaugeVec (
747
+ prometheus.GaugeOpts {
748
+ Namespace : teleport .MetricNamespace ,
749
+ Subsystem : "alpn_proxy" ,
750
+ Name : "active_connections" ,
751
+ Help : "Number of active connections handled by TLS routing proxy server." ,
752
+ },
753
+ []string {"alpn" , "source" },
754
+ )
755
+ proxyConnectionErrorsTotal = prometheus .NewGaugeVec (
756
+ prometheus.GaugeOpts {
757
+ Namespace : teleport .MetricNamespace ,
758
+ Subsystem : "alpn_proxy" ,
759
+ Name : "connection_errors_total" ,
760
+ Help : "Number of connection handler errors encountered by TLS routing proxy server." ,
761
+ },
762
+ []string {"alpn" , "source" },
763
+ )
764
+ )
0 commit comments