From 5644f7561085e7dbccdf06b9653107103f525cf3 Mon Sep 17 00:00:00 2001 From: Garrett Delfosse Date: Tue, 16 Sep 2025 12:33:42 -0400 Subject: [PATCH 1/7] fix: always pipe command to stdout --- cli/cli.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cli/cli.go b/cli/cli.go index 720d3b8..3d5cbd3 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -180,6 +180,12 @@ func Run(ctx context.Context, config Config, args []string) error { // Execute command in boundary go func() { defer cancel() + cmd := boundaryInstance.Command(args) + cmd.Stderr = os.Stderr + cmd.Stdout = os.Stdout + cmd.Stdin = os.Stdin + + logger.Debug("Executing command in boundary", "command", strings.Join(args, " ")) err := boundaryInstance.Command(args).Run() if err != nil { logger.Error("Command execution failed", "error", err) @@ -292,4 +298,4 @@ func createJailer(config jail.Config, unprivileged bool) (jail.Jailer, error) { // Use the DefaultOS function for platform-specific jail creation return jail.DefaultOS(config) -} \ No newline at end of file +} From 46e6fa2fe9dc9ab199dcd0a85b97a0256feded36 Mon Sep 17 00:00:00 2001 From: Garrett Delfosse Date: Tue, 16 Sep 2025 14:16:30 -0400 Subject: [PATCH 2/7] basic fixes --- cli/cli.go | 2 +- jail/unprivileged.go | 11 +++++++---- proxy/proxy.go | 26 +++++++++++++++++++------- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/cli/cli.go b/cli/cli.go index 3d5cbd3..9e8b993 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -186,7 +186,7 @@ func Run(ctx context.Context, config Config, args []string) error { cmd.Stdin = os.Stdin logger.Debug("Executing command in boundary", "command", strings.Join(args, " ")) - err := boundaryInstance.Command(args).Run() + err := cmd.Run() if err != nil { logger.Error("Command execution failed", "error", err) } diff --git a/jail/unprivileged.go b/jail/unprivileged.go index d64b5a9..cf55536 100644 --- a/jail/unprivileged.go +++ b/jail/unprivileged.go @@ -1,6 +1,7 @@ package jail import ( + "fmt" "log/slog" "os/exec" ) @@ -34,9 +35,11 @@ func (u *Unprivileged) Start() error { u.logger.Debug("Starting in unprivileged mode") e := getEnvs(u.configDir, u.caCertPath) u.commandEnv = mergeEnvs(e, map[string]string{ - "HOME": u.homeDir, - "USER": u.username, - "LOGNAME": u.username, + "HOME": u.homeDir, + "USER": u.username, + "LOGNAME": u.username, + "HTTP_PROXY": fmt.Sprintf("http://localhost:%d", u.httpProxyPort), + "HTTPS_PROXY": fmt.Sprintf("https://localhost:%d", u.httpProxyPort), }) return nil } @@ -53,4 +56,4 @@ func (u *Unprivileged) Command(command []string) *exec.Cmd { func (u *Unprivileged) Close() error { u.logger.Debug("Closing unprivileged jail") return nil -} \ No newline at end of file +} diff --git a/proxy/proxy.go b/proxy/proxy.go index 39344c4..2a39c96 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -133,16 +133,20 @@ func (p *Server) handleHTTP(w http.ResponseWriter, r *http.Request) { } // Forward regular HTTP request - p.forwardHTTPRequest(w, r) + p.forwardRequest(w, r, false) } -// forwardHTTPRequest forwards a regular HTTP request -func (p *Server) forwardHTTPRequest(w http.ResponseWriter, r *http.Request) { +// forwardRequest forwards a regular HTTP request +func (p *Server) forwardRequest(w http.ResponseWriter, r *http.Request, https bool) { p.logger.Debug("forwardHTTPRequest called", "method", r.Method, "url", r.URL.String(), "host", r.Host) + s := "http" + if https { + s = "https" + } // Create a new request to the target server targetURL := &url.URL{ - Scheme: "http", + Scheme: s, Host: r.Host, Path: r.URL.Path, RawQuery: r.URL.RawQuery, @@ -357,13 +361,21 @@ func (p *Server) handleTLSConnection(tlsConn *tls.Conn, hostname string) { // handleDecryptedHTTPS handles decrypted HTTPS requests and applies rules func (p *Server) handleDecryptedHTTPS(w http.ResponseWriter, r *http.Request) { + fullURL := r.URL.String() + if r.URL.Host == "" { + // Fallback: construct URL from Host header + fullURL = fmt.Sprintf("https://%s%s", r.Host, r.URL.Path) + if r.URL.RawQuery != "" { + fullURL += "?" + r.URL.RawQuery + } + } // Check if request should be allowed - result := p.ruleEngine.Evaluate(r.Method, r.URL.String()) + result := p.ruleEngine.Evaluate(r.Method, fullURL) // Audit the request p.auditor.AuditRequest(audit.Request{ Method: r.Method, - URL: r.URL.String(), + URL: fullURL, Allowed: result.Allowed, Rule: result.Rule, }) @@ -374,7 +386,7 @@ func (p *Server) handleDecryptedHTTPS(w http.ResponseWriter, r *http.Request) { } // Forward the HTTPS request (now handled same as HTTP after TLS termination) - p.forwardHTTPRequest(w, r) + p.forwardRequest(w, r, true) } // handleConnectionWithTLSDetection detects TLS vs HTTP and handles appropriately From ae7f20220d42d0f48900c1c79352652e02990044 Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Tue, 16 Sep 2025 18:28:45 +0000 Subject: [PATCH 3/7] Fix CONNECT handling in TLS termination path CONNECT requests coming through TLS termination were incorrectly forwarded as regular HTTP requests instead of being handled by handleConnect. This caused 400 errors when clients used CONNECT. The fix adds proper CONNECT detection in handleDecryptedHTTPS, matching the behavior in handleHTTP. --- proxy/proxy.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/proxy/proxy.go b/proxy/proxy.go index 2a39c96..9348e8b 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -361,6 +361,12 @@ func (p *Server) handleTLSConnection(tlsConn *tls.Conn, hostname string) { // handleDecryptedHTTPS handles decrypted HTTPS requests and applies rules func (p *Server) handleDecryptedHTTPS(w http.ResponseWriter, r *http.Request) { + // Handle CONNECT method for HTTPS tunneling + if r.Method == "CONNECT" { + p.handleConnect(w, r) + return + } + fullURL := r.URL.String() if r.URL.Host == "" { // Fallback: construct URL from Host header From 33295803b4870085720a6b5641abb5a5d071df40 Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Tue, 16 Sep 2025 18:31:17 +0000 Subject: [PATCH 4/7] Use http:// for HTTPS_PROXY in unprivileged mode HTTPS_PROXY should use http:// to establish CONNECT tunnels to the proxy, not https://. This is the standard approach for HTTP proxies handling HTTPS traffic via CONNECT. The proxy will still perform TLS termination on the tunneled connections for full request visibility. --- jail/unprivileged.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jail/unprivileged.go b/jail/unprivileged.go index cf55536..0c79f37 100644 --- a/jail/unprivileged.go +++ b/jail/unprivileged.go @@ -39,7 +39,7 @@ func (u *Unprivileged) Start() error { "USER": u.username, "LOGNAME": u.username, "HTTP_PROXY": fmt.Sprintf("http://localhost:%d", u.httpProxyPort), - "HTTPS_PROXY": fmt.Sprintf("https://localhost:%d", u.httpProxyPort), + "HTTPS_PROXY": fmt.Sprintf("http://localhost:%d", u.httpProxyPort), }) return nil } From c6ef8d59fd11d711cc75b76e9a835b2b0787a493 Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Tue, 16 Sep 2025 18:36:50 +0000 Subject: [PATCH 5/7] Add debugging for TLS connection hangs in CONNECT handling - Add read timeout to detect clients that don't send HTTP requests - Add detailed TLS connection state logging - Better error handling for timeout scenarios This should help diagnose why CONNECT tunnels hang after TLS handshake. --- proxy/proxy.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/proxy/proxy.go b/proxy/proxy.go index 9348e8b..699a2c1 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -307,6 +307,10 @@ func (p *Server) handleConnect(w http.ResponseWriter, r *http.Request) { } p.logger.Debug("TLS handshake successful", "hostname", hostname) + // Log connection state after handshake + state := tlsConn.ConnectionState() + p.logger.Debug("TLS connection established", "hostname", hostname, "version", state.Version, "cipher_suite", state.CipherSuite, "negotiated_protocol", state.NegotiatedProtocol) + // Now we have a TLS connection - handle HTTPS requests p.logger.Debug("Starting HTTPS request handling", "hostname", hostname) p.handleTLSConnection(tlsConn, hostname) @@ -317,6 +321,9 @@ func (p *Server) handleConnect(w http.ResponseWriter, r *http.Request) { func (p *Server) handleTLSConnection(tlsConn *tls.Conn, hostname string) { p.logger.Debug("Creating HTTP server for TLS connection", "hostname", hostname) + // Set read timeout to detect hanging connections + tlsConn.SetReadTimeout(5 * time.Second) + // Use ReadRequest to manually read HTTP requests from the TLS connection bufReader := bufio.NewReader(tlsConn) for { @@ -325,6 +332,8 @@ func (p *Server) handleTLSConnection(tlsConn *tls.Conn, hostname string) { if err != nil { if err == io.EOF { p.logger.Debug("TLS connection closed by client", "hostname", hostname) + } else if netErr, ok := err.(net.Error); ok && netErr.Timeout() { + p.logger.Debug("TLS connection read timeout - client not sending HTTP requests", "hostname", hostname) } else { p.logger.Debug("Failed to read HTTP request", "hostname", hostname, "error", err) } @@ -511,4 +520,4 @@ func (sl *singleConnectionListener) Addr() net.Addr { return nil } return sl.conn.LocalAddr() -} +} \ No newline at end of file From d9efd45a444e3467d9939aac8bf393b447cc5d21 Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Tue, 16 Sep 2025 18:37:40 +0000 Subject: [PATCH 6/7] Fix SetReadTimeout compilation error Replace SetReadTimeout (which doesn't exist) with SetReadDeadline. Also reset the deadline after each successful request to allow multiple requests on the same TLS connection. --- proxy/proxy.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/proxy/proxy.go b/proxy/proxy.go index 699a2c1..ef6fdb2 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -322,7 +322,7 @@ func (p *Server) handleTLSConnection(tlsConn *tls.Conn, hostname string) { p.logger.Debug("Creating HTTP server for TLS connection", "hostname", hostname) // Set read timeout to detect hanging connections - tlsConn.SetReadTimeout(5 * time.Second) + tlsConn.SetReadDeadline(time.Now().Add(5 * time.Second)) // Use ReadRequest to manually read HTTP requests from the TLS connection bufReader := bufio.NewReader(tlsConn) @@ -363,6 +363,9 @@ func (p *Server) handleTLSConnection(tlsConn *tls.Conn, hostname string) { p.logger.Debug("Failed to write response", "hostname", hostname, "error", err) break } + + // Reset read deadline for next request + tlsConn.SetReadDeadline(time.Now().Add(5 * time.Second)) } p.logger.Debug("TLS connection handling completed", "hostname", hostname) From 68c5876300ff1f3ae7c531af5d0a137777d49321 Mon Sep 17 00:00:00 2001 From: Garrett Delfosse Date: Tue, 16 Sep 2025 14:41:10 -0400 Subject: [PATCH 7/7] fix --- jail/unprivileged.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/jail/unprivileged.go b/jail/unprivileged.go index 0c79f37..516ac9c 100644 --- a/jail/unprivileged.go +++ b/jail/unprivileged.go @@ -34,12 +34,15 @@ func NewUnprivileged(config Config) (*Unprivileged, error) { func (u *Unprivileged) Start() error { u.logger.Debug("Starting in unprivileged mode") e := getEnvs(u.configDir, u.caCertPath) + p := fmt.Sprintf("http://localhost:%d", u.httpProxyPort) u.commandEnv = mergeEnvs(e, map[string]string{ "HOME": u.homeDir, "USER": u.username, "LOGNAME": u.username, - "HTTP_PROXY": fmt.Sprintf("http://localhost:%d", u.httpProxyPort), - "HTTPS_PROXY": fmt.Sprintf("http://localhost:%d", u.httpProxyPort), + "HTTP_PROXY": p, + "HTTPS_PROXY": p, + "http_proxy": p, + "https_proxy": p, }) return nil }