diff options
| author | Xe Iaso <me@xeiaso.net> | 2025-04-20 13:28:24 -0400 |
|---|---|---|
| committer | Xe Iaso <me@xeiaso.net> | 2025-04-21 16:17:30 -0400 |
| commit | c56a114a91656d7a8a644f3e606b422d28ab10bb (patch) | |
| tree | 738b8c1eef38dd9460de63c2b21d35d6ffd67853 | |
| parent | 5d25e91ccd642a7522468e5a4b5c434d84a65917 (diff) | |
| download | x-c56a114a91656d7a8a644f3e606b422d28ab10bb.tar.xz x-c56a114a91656d7a8a644f3e606b422d28ab10bb.zip | |
cmd/relayd: start implementing tcp fingerprinting
Signed-off-by: Xe Iaso <me@xeiaso.net>
| -rw-r--r-- | cmd/relayd/fingerprint.go | 42 | ||||
| -rw-r--r-- | cmd/relayd/main.go | 4 | ||||
| -rw-r--r-- | cmd/relayd/tcpfingerprint.go | 72 | ||||
| -rw-r--r-- | cmd/relayd/var/.gitignore | 2 | ||||
| -rw-r--r-- | go.mod | 3 | ||||
| -rw-r--r-- | go.sum | 6 |
6 files changed, 110 insertions, 19 deletions
diff --git a/cmd/relayd/fingerprint.go b/cmd/relayd/fingerprint.go index 32439e0..7ef2d13 100644 --- a/cmd/relayd/fingerprint.go +++ b/cmd/relayd/fingerprint.go @@ -7,6 +7,7 @@ import ( "crypto/tls" "encoding/hex" "fmt" + "log/slog" "net" "net/http" "slices" @@ -16,34 +17,37 @@ import ( ) func applyTLSFingerprinter(server *http.Server) { + if server.TLSConfig == nil { + return + } server.TLSConfig = server.TLSConfig.Clone() - getCertificate := server.TLSConfig.GetCertificate - if getCertificate == nil { - server.TLSConfig.GetCertificate = func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { - ja3n, ja4 := buildTLSFingerprint(clientHello) - ptr := clientHello.Context().Value(tlsFingerprintKey{}) - if fpPtr, ok := ptr.(*TLSFingerprint); ok && ptr != nil && fpPtr != nil { - fpPtr.ja3n.Store(&ja3n) - fpPtr.ja4.Store(&ja4) - } + getConfigForClient := server.TLSConfig.GetConfigForClient + if getConfigForClient == nil { + getConfigForClient = func(info *tls.ClientHelloInfo) (*tls.Config, error) { return nil, nil } - } else { - server.TLSConfig.GetCertificate = func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { - ja3n, ja4 := buildTLSFingerprint(clientHello) - ptr := clientHello.Context().Value(tlsFingerprintKey{}) - if fpPtr, ok := ptr.(*TLSFingerprint); ok && ptr != nil && fpPtr != nil { - fpPtr.ja3n.Store(&ja3n) - fpPtr.ja4.Store(&ja4) - } + } - return getCertificate(clientHello) + server.TLSConfig.GetConfigForClient = func(clientHello *tls.ClientHelloInfo) (*tls.Config, error) { + ja3n, ja4 := buildTLSFingerprint(clientHello) + ptr := clientHello.Context().Value(tlsFingerprintKey{}) + if fpPtr, ok := ptr.(*TLSFingerprint); ok && ptr != nil && fpPtr != nil { + fpPtr.ja3n.Store(&ja3n) + fpPtr.ja4.Store(&ja4) } + return getConfigForClient(clientHello) } server.ConnContext = func(ctx context.Context, c net.Conn) context.Context { - return context.WithValue(ctx, tlsFingerprintKey{}, &TLSFingerprint{}) + ctx = context.WithValue(ctx, tlsFingerprintKey{}, &TLSFingerprint{}) + tcpFP, err := assignTCPFingerprint(c) + if err == nil { + ctx = context.WithValue(ctx, tcpFingerprintKey{}, tcpFP) + } else { + slog.Debug("ja4t error", "err", err) + } + return ctx } } diff --git a/cmd/relayd/main.go b/cmd/relayd/main.go index 8258a28..14f1564 100644 --- a/cmd/relayd/main.go +++ b/cmd/relayd/main.go @@ -99,6 +99,10 @@ func main() { req.Header.Set("X-JA4-Fingerprint", fp.JA4().String()) } } + + if tcpFP := GetTCPFingerprint(req); tcpFP != nil { + req.Header.Set("X-JA4T-Fingerprint", tcpFP.String()) + } } srv := &http.Server{ diff --git a/cmd/relayd/tcpfingerprint.go b/cmd/relayd/tcpfingerprint.go new file mode 100644 index 0000000..a5f611a --- /dev/null +++ b/cmd/relayd/tcpfingerprint.go @@ -0,0 +1,72 @@ +package main + +import ( + "fmt" + "net" + "net/http" + "strings" + + "github.com/mikioh/tcp" + "github.com/mikioh/tcpinfo" +) + +func assignTCPFingerprint(c net.Conn) (*JA4T, error) { + tc, err := tcp.NewConn(c) + if err != nil { + return nil, err + } + + var o tcpinfo.Info + var b [256]byte + i, err := tc.Option(o.Level(), o.Name(), b[:]) + if err != nil { + return nil, err + } + + ci, ok := i.(*tcpinfo.Info) + if !ok { + return nil, fmt.Errorf("can't make %T into *tcpinfo.Info") + } + + result := &JA4T{ + Window: uint32(ci.Sys.SenderWindow), + MSS: uint16(ci.SenderMSS), + WindowScale: 0, + } + fmt.Println(result.String()) + + return result, nil +} + +type tcpFingerprintKey struct{} + +func GetTCPFingerprint(r *http.Request) *JA4T { + ptr := r.Context().Value(tcpFingerprintKey{}) + if fpPtr, ok := ptr.(*JA4T); ok && ptr != nil && fpPtr != nil { + return fpPtr + } + return nil +} + +type JA4T struct { + Window uint32 + Options []uint8 + MSS uint16 + WindowScale uint8 +} + +func (j JA4T) String() string { + var sb strings.Builder + fmt.Fprintf(&sb, "%d_", j.Window) + + for i, opt := range j.Options { + fmt.Fprint(&sb, opt) + if i-1 != len(j.Options) { + fmt.Fprint(&sb, "-") + } + } + fmt.Fprint(&sb, "_") + fmt.Fprintf(&sb, "%d_%d", j.MSS, j.WindowScale) + + return sb.String() +} diff --git a/cmd/relayd/var/.gitignore b/cmd/relayd/var/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/cmd/relayd/var/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore
\ No newline at end of file @@ -166,6 +166,9 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-tty v0.0.3 // indirect + github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c // indirect + github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect + github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect github.com/minio/sha256-simd v1.0.1 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect @@ -637,6 +637,12 @@ github.com/mattn/go-tty v0.0.3 h1:5OfyWorkyO7xP52Mq7tB36ajHDG5OHrmBGIS/DtakQI= github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= +github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms= +github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= +github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKoFL8DUUmalo2yJJUCxbPKtm8OKfqr2/FTNU= +github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdnNMiD9ZejrlswWrCpBEZgWOiTrc= +github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= |
