{"record":{"id":"620899219d0500cf","repo":"k3s-io/k3s","slug":"hijacking-not-supported","errorCode":null,"errorMessage":"hijacking not supported","messagePattern":"hijacking not supported","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"pkg/daemons/control/tunnel.go","lineNumber":187,"sourceCode":"\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn pod, nil\n}\n\n// serveConnect attempts to handle the HTTP CONNECT request by dialing\n// a connection, either locally or via the remotedialer tunnel.\nfunc (t *TunnelServer) serveConnect(resp http.ResponseWriter, req *http.Request) {\n\tbconn, err := t.dialBackend(req.Context(), req.Host)\n\tif err != nil {\n\t\tutil.SendError(err, resp, req, http.StatusBadGateway)\n\t\treturn\n\t}\n\n\thijacker, ok := resp.(http.Hijacker)\n\tif !ok {\n\t\tutil.SendError(errors.New(\"hijacking not supported\"), resp, req, http.StatusInternalServerError)\n\t\treturn\n\t}\n\tresp.WriteHeader(http.StatusOK)\n\n\trconn, bufrw, err := hijacker.Hijack()\n\tif err != nil {\n\t\tutil.SendError(err, resp, req, http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tproxy.Proxy(newConnReadWriteCloser(rconn, bufrw), bconn)\n}\n\n// dialBackend determines where to route the connection request to, and returns\n// a dialed connection if possible. Note that in the case of a remotedialer\n// tunnel connection, the agent may return an error if the agent's authorizer\n// denies the connection, or if there is some other error in actually dialing\n// the requested endpoint.","sourceCodeStart":169,"sourceCodeEnd":205,"githubUrl":"https://github.com/k3s-io/k3s/blob/6ba341e396edc16b8dcae978a7c5e3ac7ee5606e/pkg/daemons/control/tunnel.go#L169-L205","documentation":"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.","triggerScenarios":"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.","commonSituations":"Fronting k3s supervisor with an h2-capable proxy that forwards CONNECT; contributed middlewares in forks; clients attempting CONNECT over gRPC-style h2c connections.","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."],"exampleFix":"// before: wrapper hides Hijacker\ntype wrapRW struct{ http.ResponseWriter }\n// after: forward the Hijacker interface\ntype wrapRW struct {\n\thttp.ResponseWriter\n}\nfunc (w *wrapRW) Hijack() (net.Conn, *bufio.ReadWriter, error) {\n\th, ok := w.ResponseWriter.(http.Hijacker)\n\tif !ok {\n\t\treturn nil, nil, errors.New(\"response writer does not support hijacking\")\n\t}\n\treturn h.Hijack()\n}","handlingStrategy":"type-guard","validationCode":"// Force HTTP/1.1 for CONNECT-bearing clients:\ntransport := &http.Transport{ForceAttemptHTTP2: false}\nclient := &http.Client{Transport: transport}","typeGuard":"// Narrow the ResponseWriter before serving CONNECT-style handlers:\nfunc canHijack(rw http.ResponseWriter) bool {\n\t_, ok := rw.(http.Hijacker)\n\treturn ok\n}\n// middleware wrapper that preserves the capability:\ntype hijackRW struct{ http.ResponseWriter }\nfunc (w hijackRW) Hijack() (net.Conn, *bufio.ReadWriter, error) {\n\th := w.ResponseWriter.(http.Hijacker)\n\treturn h.Hijack()\n}","tryCatchPattern":"hijacker, ok := resp.(http.Hijacker)\nif !ok {\n\t// degrade gracefully: 502 with hint to use HTTP/1.1 instead of 500\n\thttp.Error(resp, \"connection proxy requires HTTP/1.1 (hijackable connection)\", http.StatusBadGateway)\n\treturn\n}","preventionTips":["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."],"tags":["k3s","http","hijack","proxy","tunnel","http2"],"backgroundTag":null,"analyzedSha":"6ba341e396edc16b8dcae978a7c5e3ac7ee5606e","analyzedAt":"2026-08-15T16:27:54.286Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}