Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
2 changes: 1 addition & 1 deletion gatesentryproxy/clienthello/clientHello.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:]

Expand Down
72 changes: 62 additions & 10 deletions gatesentryproxy/transparent_listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
"strings"
"syscall"
"time"

gsClientHello "bitbucket.org/abdullah_irfan/gatesentryproxy/clienthello"
)

type TransparentProxyListener struct {
Expand Down Expand Up @@ -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
}
Expand All @@ -191,25 +226,42 @@ 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
if ruleMatch != nil {
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)
}
}

Expand Down
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Loading