diff --git a/netstack2/handlers.go b/netstack2/handlers.go index dabfee9..a63178c 100644 --- a/netstack2/handlers.go +++ b/netstack2/handlers.go @@ -152,10 +152,18 @@ func (h *TCPHandler) handleTCPConn(netstackConn *gonet.TCPConn, id stack.Transpo srcAddr, _ := netip.ParseAddr(srcIP) dstAddr, _ := netip.ParseAddr(dstIP) rule := h.proxyHandler.subnetLookup.Match(srcAddr, dstAddr, dstPort, tcp.ProtocolNumber) - if rule != nil && rule.Protocol != "" { - logger.Info("TCP Forwarder: Routing %s:%d -> %s:%d to HTTP handler (%s)", - srcIP, srcPort, dstIP, dstPort, rule.Protocol) - h.proxyHandler.httpHandler.HandleConn(netstackConn, rule) + if rule != nil { + if rule.Protocol != "" { + logger.Info("TCP Forwarder: Routing %s:%d -> %s:%d to HTTP handler (%s)", + srcIP, srcPort, dstIP, dstPort, rule.Protocol) + h.proxyHandler.httpHandler.HandleConn(netstackConn, rule) + } else { + // A matching HTTP rule exists but has no protocol configured — + // do not fall through to the raw TCP handler; drop the connection. + logger.Info("TCP Forwarder: Dropping %s:%d -> %s:%d (HTTP rule matched but no protocol set)", + srcIP, srcPort, dstIP, dstPort) + netstackConn.Close() + } return } } diff --git a/netstack2/http_handler.go b/netstack2/http_handler.go index 4ff7ed9..df4e686 100644 --- a/netstack2/http_handler.go +++ b/netstack2/http_handler.go @@ -276,10 +276,10 @@ func (h *HTTPHandler) getProxy(target HTTPTarget) *httputil.ReverseProxy { Scheme: scheme, Host: fmt.Sprintf("%s:%d", target.DestAddr, target.DestPort), } - insecureTransport := (*http.Transport)(nil) + var transport http.RoundTripper = http.DefaultTransport if target.Scheme == "https" { // Allow self-signed certificates on downstream HTTPS targets. - insecureTransport = &http.Transport{ + transport = &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, //nolint:gosec // downstream self-signed certs are a supported configuration }, @@ -296,7 +296,7 @@ func (h *HTTPHandler) getProxy(target HTTPTarget) *httputil.ReverseProxy { // X-Forwarded-For entry, so the header is set exactly once. pr.SetXForwarded() }, - Transport: insecureTransport, + Transport: transport, } proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) { @@ -336,6 +336,19 @@ func (h *HTTPHandler) handleRequest(w http.ResponseWriter, r *http.Request) { return } + // If the rule is plain HTTP but has a TLS certificate configured, redirect + // the client to the HTTPS equivalent of the requested URL. + if rule.Protocol == "http" && rule.TLSCert != "" && rule.TLSKey != "" { + host := r.Host + if host == "" { + host = r.URL.Host + } + httpsURL := "https://" + host + r.RequestURI + logger.Info("HTTP handler: redirecting %s %s -> %s (TLS cert present)", r.Method, r.URL.RequestURI(), httpsURL) + http.Redirect(w, r, httpsURL, http.StatusMovedPermanently) + return + } + target := rule.HTTPTargets[0] scheme := target.Scheme logger.Info("HTTP handler: %s %s -> %s://%s:%d", diff --git a/network/interface.go b/network/interface.go index 70556be..089badd 100644 --- a/network/interface.go +++ b/network/interface.go @@ -120,7 +120,7 @@ func configureDarwin(interfaceName string, ip net.IP, ipNet *net.IPNet) error { prefix, _ := ipNet.Mask.Size() ipStr := fmt.Sprintf("%s/%d", ip.String(), prefix) - cmd := exec.Command("ifconfig", interfaceName, "inet", ipStr, ip.String(), "alias") + cmd := exec.Command("/sbin/ifconfig", interfaceName, "inet", ipStr, ip.String(), "alias") logger.Info("Running command: %v", cmd) out, err := cmd.CombinedOutput() @@ -129,7 +129,7 @@ func configureDarwin(interfaceName string, ip net.IP, ipNet *net.IPNet) error { } // Bring up the interface - cmd = exec.Command("ifconfig", interfaceName, "up") + cmd = exec.Command("/sbin/ifconfig", interfaceName, "up") logger.Info("Running command: %v", cmd) out, err = cmd.CombinedOutput()