diff --git a/CHANGELOG.md b/CHANGELOG.md index a0d8021..2f5f8b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # CHANGELOG +## v1.20.1 (31 January 2026) + +- Fix transparent HTTPS mode showing IP addresses instead of domain names +- Fixed SNI extraction to enable domain-based rule matching in transparent proxy mode +- Fixed SNI parsing bug in TLS Client Hello parser + ## v1.20.0 (31 January 2026) - Fix transparent mode not picking up rules or logging traffic. diff --git a/gatesentryproxy/clienthello/clientHello.go b/gatesentryproxy/clienthello/clientHello.go index 593f2fd..efdacbd 100644 --- a/gatesentryproxy/clienthello/clientHello.go +++ b/gatesentryproxy/clienthello/clientHello.go @@ -206,7 +206,7 @@ func (ch *ClientHello) Unmarshall(payload []byte) error { if len(data) < 2 { return ErrHandshakeExtBadLength } - sniLen := int(data[0])<<8 | int(data[0]) + sniLen := int(data[0])<<8 | int(data[1]) data = data[2:] diff --git a/gatesentryproxy/transparent_listener.go b/gatesentryproxy/transparent_listener.go index 967b02e..252b087 100644 --- a/gatesentryproxy/transparent_listener.go +++ b/gatesentryproxy/transparent_listener.go @@ -13,6 +13,8 @@ import ( "strings" "syscall" "time" + + gsClientHello "bitbucket.org/abdullah_irfan/gatesentryproxy/clienthello" ) type TransparentProxyListener struct { @@ -173,13 +175,46 @@ func (l *TransparentProxyListener) handleTransparentHTTPS(conn net.Conn, origina passthru := NewGSProxyPassthru() - // Check proxy rules before processing - shouldBlock, ruleMatch, ruleShouldMitm := CheckProxyRules(host, user) + // Read the TLS Client Hello to extract SNI (domain name) BEFORE rule matching + clientHello, err := gsClientHello.ReadClientHello(conn) + if err != nil { + if DebugLogging { + log.Printf("[Transparent] Error reading client hello: %v", err) + } + // If we can't read the client hello, fall back to direct connection with IP + LogProxyAction("https://"+serverAddr, user, ProxyActionSSLDirect) + ConnectDirect(conn, serverAddr, clientHello, passthru) + return + } + + // Extract SNI from the client hello + serverName := "" + var hello gsClientHello.ClientHello + if err := hello.Unmarshall(clientHello); err == nil && hello.SNI != "" { + serverName = hello.SNI + if DebugLogging { + log.Printf("[Transparent] Extracted SNI: %s from connection to %s", serverName, serverAddr) + } + } + + // Use SNI for rule matching if available, otherwise fall back to IP + ruleMatchHost := host + if serverName != "" { + ruleMatchHost = serverName + } + + // Check proxy rules using the domain name (SNI) instead of IP + shouldBlock, ruleMatch, ruleShouldMitm := CheckProxyRules(ruleMatchHost, user) if shouldBlock { if DebugLogging { - log.Printf("[Transparent] Blocking HTTPS connection to %s by rule", serverAddr) + log.Printf("[Transparent] Blocking HTTPS connection to %s (SNI: %s) by rule", serverAddr, serverName) + } + // Log with domain name if available + logUrl := "https://" + serverAddr + if serverName != "" { + logUrl = "https://" + serverName } - LogProxyAction("https://"+serverAddr, user, ProxyActionBlockedUrl) + LogProxyAction(logUrl, user, ProxyActionBlockedUrl) conn.Close() return } @@ -191,7 +226,12 @@ func (l *TransparentProxyListener) handleTransparentHTTPS(conn net.Conn, origina shouldMitm := false if IProxy != nil && IProxy.DoMitm != nil { - shouldMitm = IProxy.DoMitm(serverAddr) + // Use domain for MITM decision if available + mitmCheckHost := serverAddr + if serverName != "" { + mitmCheckHost = net.JoinHostPort(serverName, port) + } + shouldMitm = IProxy.DoMitm(mitmCheckHost) } // If rule matched and specified MITM setting, use it @@ -199,17 +239,29 @@ func (l *TransparentProxyListener) handleTransparentHTTPS(conn net.Conn, origina shouldMitm = ruleShouldMitm } + // Use domain name for logging if available + logUrl := "https://" + serverAddr + if serverName != "" { + logUrl = "https://" + serverName + } + if shouldMitm { if DebugLogging { - log.Printf("[Transparent] Performing SSL Bump for %s", serverAddr) + log.Printf("[Transparent] Performing SSL Bump for %s (SNI: %s)", serverAddr, serverName) } - SSLBump(conn, serverAddr, user, "", nil, passthru, l.ProxyHandler.Iproxy) + // Pass the already-read client hello to SSLBump via an insertingConn + wrappedConn := &prependConn{ + Conn: conn, + buf: clientHello, + offset: 0, + } + SSLBump(wrappedConn, serverAddr, user, "", nil, passthru, l.ProxyHandler.Iproxy) } else { if DebugLogging { - log.Printf("[Transparent] Direct tunnel for %s", serverAddr) + log.Printf("[Transparent] Direct tunnel for %s (SNI: %s)", serverAddr, serverName) } - LogProxyAction("https://"+serverAddr, user, ProxyActionSSLDirect) - ConnectDirect(conn, serverAddr, nil, passthru) + LogProxyAction(logUrl, user, ProxyActionSSLDirect) + ConnectDirect(conn, serverAddr, clientHello, passthru) } } diff --git a/main.go b/main.go index 6a27b28..68689a2 100644 --- a/main.go +++ b/main.go @@ -29,7 +29,7 @@ var GSPROXYPORT = "10413" var GSWEBADMINPORT = "10786" var GSBASEDIR = "" var Baseendpointv2 = "https://www.gatesentryfilter.com/api/" -var GATESENTRY_VERSION = "1.20.0" +var GATESENTRY_VERSION = "1.20.1" var GS_BOUND_ADDRESS = ":" var R *application.GSRuntime