mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
feat(proxy): accept transparent redirected connections
The proxy only accepted explicit clients, which announce their destination with a CONNECT request. A client whose connection is redirected at the kernel level speaks TLS immediately instead, so http.Server tries to parse a TLS record as an HTTP request line and drops the connection. Demultiplex on the first byte of an accepted connection. A TLS handshake record (0x16) cannot begin an HTTP method, so it separates the two cleanly. Redirected connections have their destination recovered from the ClientHello SNI and are served by synthesising the CONNECT the client never sent, which keeps the MITM decision, cert generation and interceptor chain on the existing code path. Redirected connections deliberately bypass http.Server. It issues a background read while a handler runs, which consumes the first byte of the replayed ClientHello and corrupts the handshake. The CONNECT response is also suppressed, since a client mid handshake expects a ServerHello and would read those bytes as a malformed TLS record. Off by default. Enabled with `pmg proxy start --transparent` or proxy.server.transparent, and only useful alongside a redirect mechanism such as an eBPF connect rewrite.
This commit is contained in:
@@ -72,6 +72,14 @@ type ProxyConfig struct {
|
||||
RequestTimeout time.Duration
|
||||
ConnectTimeout time.Duration
|
||||
|
||||
// EnableTransparent accepts redirected connections on the same listener as
|
||||
// explicit proxy clients. A redirected client (e.g. via an eBPF connect
|
||||
// rewrite) believes it reached the real registry, so it speaks TLS
|
||||
// immediately instead of sending CONNECT. Its destination is recovered from
|
||||
// the ClientHello's SNI. Connections without SNI cannot be routed and are
|
||||
// dropped.
|
||||
EnableTransparent bool
|
||||
|
||||
// ServerReadWriteTimeout is the timeout applied to the http.Server's
|
||||
// ReadTimeout and WriteTimeout. These deadlines are set on the raw TCP
|
||||
// connection and persist after Hijack(), which means they become the
|
||||
@@ -266,6 +274,10 @@ func (ps *proxyServer) Start() error {
|
||||
return fmt.Errorf("failed to start listener: %w", err)
|
||||
}
|
||||
|
||||
if ps.config.EnableTransparent {
|
||||
listener = newTransparentListener(listener, ps.proxy)
|
||||
}
|
||||
|
||||
ps.listener = listener
|
||||
|
||||
serverTimeout := ps.config.ServerReadWriteTimeout
|
||||
|
||||
Reference in New Issue
Block a user