k3s-io/k3s · error
hijacking not supported
Error message
hijacking not supported
What it means
serveConnect handles HTTP CONNECT for the remotedialer tunnel by raw TCP splicing: it needs http.Hijacker to take over the connection from the server. If the ResponseWriter does not implement Hijacker (HTTP/2 connections, or middleware that wraps ResponseWriter without forwarding the interface), hijack is impossible and the proxy request fails.
Source
Thrown at pkg/daemons/control/tunnel.go:187
}
}
}
}
return pod, nil
}
// serveConnect attempts to handle the HTTP CONNECT request by dialing
// a connection, either locally or via the remotedialer tunnel.
func (t *TunnelServer) serveConnect(resp http.ResponseWriter, req *http.Request) {
bconn, err := t.dialBackend(req.Context(), req.Host)
if err != nil {
util.SendError(err, resp, req, http.StatusBadGateway)
return
}
hijacker, ok := resp.(http.Hijacker)
if !ok {
util.SendError(errors.New("hijacking not supported"), resp, req, http.StatusInternalServerError)
return
}
resp.WriteHeader(http.StatusOK)
rconn, bufrw, err := hijacker.Hijack()
if err != nil {
util.SendError(err, resp, req, http.StatusInternalServerError)
return
}
proxy.Proxy(newConnReadWriteCloser(rconn, bufrw), bconn)
}
// dialBackend determines where to route the connection request to, and returns
// a dialed connection if possible. Note that in the case of a remotedialer
// tunnel connection, the agent may return an error if the agent's authorizer
// denies the connection, or if there is some other error in actually dialing
// the requested endpoint.View on GitHub (pinned to 6ba341e396)
Solutions
- Ensure CONNECT traffic reaches the tunnel server over HTTP/1.1: disable HTTP/2 on the fronting proxy for the tunnel routes or connect directly.
- Fix any custom wrapping middleware to implement http.Hijacker by delegating to the underlying writer (embed http.ResponseWriter and add Hijack()).
- Verify with curl --http1.1 -X CONNECT against the endpoint to isolate the transport.
Example fix
// before: wrapper hides Hijacker
type wrapRW struct{ http.ResponseWriter }
// after: forward the Hijacker interface
type wrapRW struct {
http.ResponseWriter
}
func (w *wrapRW) Hijack() (net.Conn, *bufio.ReadWriter, error) {
h, ok := w.ResponseWriter.(http.Hijacker)
if !ok {
return nil, nil, errors.New("response writer does not support hijacking")
}
return h.Hijack()
} Defensive patterns
Strategy: type-guard
Validate before calling
// Force HTTP/1.1 for CONNECT-bearing clients:
transport := &http.Transport{ForceAttemptHTTP2: false}
client := &http.Client{Transport: transport} Type guard
// Narrow the ResponseWriter before serving CONNECT-style handlers:
func canHijack(rw http.ResponseWriter) bool {
_, ok := rw.(http.Hijacker)
return ok
}
// middleware wrapper that preserves the capability:
type hijackRW struct{ http.ResponseWriter }
func (w hijackRW) Hijack() (net.Conn, *bufio.ReadWriter, error) {
h := w.ResponseWriter.(http.Hijacker)
return h.Hijack()
} Try / catch
hijacker, ok := resp.(http.Hijacker)
if !ok {
// degrade gracefully: 502 with hint to use HTTP/1.1 instead of 500
http.Error(resp, "connection proxy requires HTTP/1.1 (hijackable connection)", http.StatusBadGateway)
return
} Prevention
- Never front tunnel/CONNECT endpoints with HTTP/2 or h2c upgrades.
- Any middleware wrapping ResponseWriter must re-expose Hijacker/Flusher/CloseNotifier by delegation.
- Add a canHijack assertion in middleware tests using httptest to catch wrappers early.
When it happens
Trigger: A CONNECT request to the tunnel server arriving over HTTP/2 (h2 prior-knowledge or upgraded via TLS ALPN); custom middleware (logging, compression, metrics) wrapping the ResponseWriter in a struct that lacks Hijack(); tests using httptest.ResponseRecorder.
Common situations: Fronting k3s supervisor with an h2-capable proxy that forwards CONNECT; contributed middlewares in forks; clients attempting CONNECT over gRPC-style h2c connections.
Related errors
AI-assisted analysis of k3s-io/k3s@6ba341e396 (2026-08-15).
Data as JSON: /api/errors/620899219d0500cf.
Report an issue: GitHub.