operator scan/remediate hangs indefinitely if the port-forward to the Operator pod never becomes ready
Problem Description
When running kubescape operator scan or kubescape operator remediate, Kubescape connects to the in-cluster Kubescape Operator by establishing a port-forward to its pod via CreatePortForwarder and StartPortForwarder.
In core/cautils/portforwarder.go, waitForPortForwardReadiness() waits for readiness using an unbounded select:
select {
case <-p.readyChan:
return nil
case err := <-p.errChan:
if err == nil {
err = fmt.Errorf("port-forward exited before becoming ready: %s", strings.TrimSpace(p.errOut.String()))
}
return err
}If the dial stalls (e.g. network partition, dropped packets, or the API server accepts the TCP connection but never completes the SPDY upgrade handshake), neither readyChan nor errChan is ever closed or sent on. Furthermore, http.Client inside CreatePortForwarder has no timeout configured, leaving the connection attempt completely unbounded.
Trigger Sequence & Impact
- User invokes
kubescape operator scanorkubescape operator remediate. - Kubescape initiates a port-forward connection to the operator pod.
- If the remote endpoint hangs during connection setup or the SPDY upgrade handshake stalls,
waitForPortForwardReadinessblocks indefinitely. - The CLI hangs indefinitely with no timeout, no diagnostic error, and no recourse other than sending SIGKILL or terminating the process manually.
Context & Prior Art
This coordination logic has seen prior merged fixes for closely related hang/blocking edge cases:
- #2381 — avoid blocking on repeated stop signals
- #3272 / #3273 — safely stop PortForwarder without dropping stop signals
- Earlier fix adding
ForwardPortserror surfacing towaitForPortForwardReadiness
Adding an explicit bounded timeout to waitForPortForwardReadiness and the underlying dialer will close this remaining gap and provide a clear, actionable diagnostic error instead of an indefinite hang.
Source: kubescape/kubescape