Skip to content

Commit

Permalink
Report probing errors to teleport
Browse files Browse the repository at this point in the history
  • Loading branch information
oxtoacart committed Oct 17, 2023
1 parent 150fc63 commit c4c3ccb
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 30 deletions.
52 changes: 28 additions & 24 deletions instrument/instrument.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ type Instrument interface {
XBQHeaderSent(ctx context.Context)
SuspectedProbing(ctx context.Context, fromIP net.IP, reason string)
VersionCheck(ctx context.Context, redirect bool, method, reason string)
ProxiedBytes(ctx context.Context, sent, recv int, platform, version, app, locale, dataCapCohort string, clientIP net.IP, deviceID, originHost string)
ProxiedBytes(ctx context.Context, sent, recv int, platform, version, app, locale, dataCapCohort, probingError string, clientIP net.IP, deviceID, originHost string)
ReportToOTELPeriodically(interval time.Duration, tp *sdktrace.TracerProvider, includeDeviceID bool)
ReportToOTEL(tp *sdktrace.TracerProvider, includeDeviceID bool)
quicSentPacket(ctx context.Context)
Expand Down Expand Up @@ -69,7 +69,7 @@ func (i NoInstrument) Throttle(ctx context.Context, m bool, reason string) {}
func (i NoInstrument) XBQHeaderSent(ctx context.Context) {}
func (i NoInstrument) SuspectedProbing(ctx context.Context, fromIP net.IP, reason string) {}
func (i NoInstrument) VersionCheck(ctx context.Context, redirect bool, method, reason string) {}
func (i NoInstrument) ProxiedBytes(ctx context.Context, sent, recv int, platform, version, app, locale, dataCapCohort string, clientIP net.IP, deviceID, originHost string) {
func (i NoInstrument) ProxiedBytes(ctx context.Context, sent, recv int, platform, version, app, locale, dataCapCohort, probingError string, clientIP net.IP, deviceID, originHost string) {
}
func (i NoInstrument) ReportToOTELPeriodically(interval time.Duration, tp *sdktrace.TracerProvider, includeDeviceID bool) {
}
Expand Down Expand Up @@ -235,7 +235,7 @@ func (ins *defaultInstrument) VersionCheck(ctx context.Context, redirect bool, m

// ProxiedBytes records the volume of application data clients sent and
// received via the proxy.
func (ins *defaultInstrument) ProxiedBytes(ctx context.Context, sent, recv int, platform, version, app, locale, dataCapCohort string, clientIP net.IP, deviceID, originHost string) {
func (ins *defaultInstrument) ProxiedBytes(ctx context.Context, sent, recv int, platform, version, app, locale, dataCapCohort, probingError string, clientIP net.IP, deviceID, originHost string) {
// Track the cardinality of clients.
otelinstrument.DistinctClients1m.Add(deviceID)
otelinstrument.DistinctClients10m.Add(deviceID)
Expand Down Expand Up @@ -271,21 +271,23 @@ func (ins *defaultInstrument) ProxiedBytes(ctx context.Context, sent, recv int,
)

clientKey := clientDetails{
platform: platform,
version: version,
locale: locale,
country: country,
isp: isp,
asn: asn,
platform: platform,
version: version,
locale: locale,
country: country,
isp: isp,
asn: asn,
probingError: probingError,
}
clientKeyWithDeviceID := clientDetails{
deviceID: deviceID,
platform: platform,
version: version,
locale: locale,
country: country,
isp: isp,
asn: asn,
deviceID: deviceID,
platform: platform,
version: version,
locale: locale,
country: country,
isp: isp,
asn: asn,
probingError: probingError,
}

var originKey originDetails
Expand Down Expand Up @@ -362,13 +364,14 @@ func (ins *defaultInstrument) MultipathStats(protocols []string) (trackers []mul
}

type clientDetails struct {
deviceID string
platform string
version string
locale string
country string
isp string
asn string
deviceID string
platform string
version string
locale string
country string
isp string
asn string
probingError string
}

type originDetails struct {
Expand Down Expand Up @@ -433,7 +436,8 @@ func (ins *defaultInstrument) ReportToOTEL(tp *sdktrace.TracerProvider, includeD
attribute.String("client_locale", key.locale),
attribute.String("client_country", key.country),
attribute.String("client_isp", key.isp),
attribute.String("client_asn", key.asn)))
attribute.String("client_asn", key.asn),
attribute.String("probing_error", key.probingError)))
span.End()
}
if !includeDeviceID {
Expand Down
11 changes: 11 additions & 0 deletions opsfilter/opsfilter.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import (
"strings"

"github.com/getlantern/golog"
"github.com/getlantern/netx"
"github.com/getlantern/proxy/v2/filters"

"github.com/getlantern/http-proxy-lantern/v2/common"
"github.com/getlantern/http-proxy-lantern/v2/tlslistener"
"github.com/getlantern/http-proxy/listeners"
)

Expand Down Expand Up @@ -66,6 +68,15 @@ func (f *opsfilter) Apply(cs *filters.ConnectionState, req *http.Request, next f
addMeasuredHeader("supported_data_caps", req.Header[common.SupportedDataCaps])
addMeasuredHeader("time_zone", req.Header.Get(common.TimeZoneHeader))

netx.WalkWrapped(cs.Downstream(), func(conn net.Conn) bool {
pdc, ok := conn.(tlslistener.ProbingDetectingConn)
if ok {
addMeasuredHeader("probing_error", pdc.ProbingError())
return false
}
return true
})

clientIP, _, err := net.SplitHostPort(req.RemoteAddr)
if err != nil {
clientIP = req.RemoteAddr
Expand Down
7 changes: 6 additions & 1 deletion reporting.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,12 @@ func newReportingConfig(countryLookup geo.CountryLookup, rc *rclient.Client, ins
if _originHost != nil {
originHost = _originHost.(string)
}
instrument.ProxiedBytes(context.Background(), deltaStats.SentTotal, deltaStats.RecvTotal, platform, version, app, locale, dataCapCohort, client_ip, deviceID, originHost)
_probingError := ctx["probing_error"]
probingError := ""
if _probingError != nil {
probingError = _probingError.(string)
}
instrument.ProxiedBytes(context.Background(), deltaStats.SentTotal, deltaStats.RecvTotal, platform, version, app, locale, dataCapCohort, probingError, client_ip, deviceID, originHost)
}

var reporter listeners.MeasuredReportFN
Expand Down
5 changes: 3 additions & 2 deletions tlslistener/clienthelloconn.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ var bytePool = sync.Pool{
return make([]byte, reflectBufferSize)
}}

func newClientHelloRecordingConn(rawConn net.Conn, cfg *tls.Config, utlsCfg *utls.Config, ticketKeys utls.TicketKeys, missingTicketReaction HandshakeReaction, instrument instrument.Instrument) (net.Conn, *tls.Config) {
func newClientHelloRecordingConn(rawConn net.Conn, cfg *tls.Config, utlsCfg *utls.Config, ticketKeys utls.TicketKeys, missingTicketReaction HandshakeReaction, instrument instrument.Instrument) (*clientHelloRecordingConn, *tls.Config) {
buf := bufferPool.Get().(*bytes.Buffer)
cfgClone := cfg.Clone()
rrc := &clientHelloRecordingConn{
Expand Down Expand Up @@ -166,6 +166,7 @@ type clientHelloRecordingConn struct {
ticketKeys utls.TicketKeys
missingTicketReaction HandshakeReaction
instrument instrument.Instrument
probingError string
}

func (rrc *clientHelloRecordingConn) Read(b []byte) (int, error) {
Expand Down Expand Up @@ -221,7 +222,7 @@ func (rrc *clientHelloRecordingConn) processHello(info *tls.ClientHelloInfo) (*t

func (rrc *clientHelloRecordingConn) helloError(errStr string, continueOnError bool) (*tls.Config, error) {
sourceIP := rrc.RemoteAddr().(*net.TCPAddr).IP
log.Debugf("Responding with hello error '%v' to %v", errStr, sourceIP)
rrc.probingError = errStr
rrc.instrument.SuspectedProbing(context.Background(), sourceIP, errStr)
// For now, we just record that there was a problem with the client hello, but we actually
// proceed as if everything is fine.
Expand Down
23 changes: 20 additions & 3 deletions tlslistener/tlslistener.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,11 @@ func (l *tlslistener) Accept() (net.Conn, error) {
return nil, err
}
if !l.expectTickets || !l.requireTickets {
return &tlsconn{tls.Server(conn, l.cfg), conn}, nil
return &tlsconn{Conn: tls.Server(conn, l.cfg), wrapped: conn}, nil
}

helloConn, cfg := newClientHelloRecordingConn(conn, l.cfg, l.utlsCfg, l.getTicketKeys(), l.missingTicketReaction, l.instrument)
return &tlsconn{tls.Server(helloConn, cfg), conn}, nil
return &tlsconn{Conn: tls.Server(helloConn, cfg), wrapped: conn, helloConn: helloConn}, nil
}

func (l *tlslistener) getTicketKeys() utls.TicketKeys {
Expand All @@ -122,11 +122,28 @@ func (l *tlslistener) Close() error {
return l.wrapped.Close()
}

type ProbingDetectingConn interface {
// If there was suspected probing on this connection, return the error message for that suspected probing
// (e.g. "ClientHello does not support session tickets"), else return empty string.
ProbingError() string
}

type tlsconn struct {
net.Conn
wrapped net.Conn
wrapped net.Conn
helloConn *clientHelloRecordingConn
}

func (conn *tlsconn) Wrapped() net.Conn {
return conn.wrapped
}

func (conn *tlsconn) ProbingError() string {
if conn.helloConn == nil {
return ""
}
conn.helloConn.helloMutex.Lock()
err := conn.helloConn.probingError
conn.helloConn.helloMutex.Unlock()
return err
}

0 comments on commit c4c3ccb

Please sign in to comment.