diff --git a/go.mod b/go.mod index 8ffb5436c7..77b31277e1 100644 --- a/go.mod +++ b/go.mod @@ -113,7 +113,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/rancher/dynamiclistener v0.3.3 github.com/rancher/lasso v0.0.0-20210616224652-fc3ebd901c08 - github.com/rancher/remotedialer v0.2.0 + github.com/rancher/remotedialer v0.2.6-0.20220624190122-ea57207bf2b8 github.com/rancher/wharfie v0.5.1 github.com/rancher/wrangler v0.8.10 github.com/robfig/cron/v3 v3.0.1 diff --git a/go.sum b/go.sum index 3370db9597..4760b8c8d6 100644 --- a/go.sum +++ b/go.sum @@ -992,8 +992,8 @@ github.com/rancher/dynamiclistener v0.3.3 h1:pNwVc3vzuEHsbqAh1e76asq4aeDzHFV/5Ha github.com/rancher/dynamiclistener v0.3.3/go.mod h1:QwTpy+drx4gvPMefrrUUKpVaWiy74O7vNvkwBXJ+s3E= github.com/rancher/lasso v0.0.0-20210616224652-fc3ebd901c08 h1:NxR8Fh0eE7/5/5Zvlog9B5NVjWKqBSb1WYMUF7/IE5c= github.com/rancher/lasso v0.0.0-20210616224652-fc3ebd901c08/go.mod h1:9qZd/S8DqWzfKtjKGgSoHqGEByYmUE3qRaBaaAHwfEM= -github.com/rancher/remotedialer v0.2.0 h1:xD7t3K6JYwTdAsxmGtTHQMkEkFgKouQ1foLxVW424Dc= -github.com/rancher/remotedialer v0.2.0/go.mod h1:tkU8ZvrR5lRgaKWaX71nAy6daeqvPFx/lJEnbW7tXSI= +github.com/rancher/remotedialer v0.2.6-0.20220624190122-ea57207bf2b8 h1:leqh0chjBsXhKWebxxFd5QPcoQLu51EpaHo04ce0o+8= +github.com/rancher/remotedialer v0.2.6-0.20220624190122-ea57207bf2b8/go.mod h1:BwwztuvViX2JrLLUwDlsYt5DiyUwHLlzynRwkZLAY0Q= github.com/rancher/wharfie v0.5.1 h1:TUqZyNj6BaGe2+tqhwAGwZouuwx02mvAMMjNuyejc5I= github.com/rancher/wharfie v0.5.1/go.mod h1:5AHZRFBAOWYPDNCwj/y5Dpj+MMwXLoitPwxjYAIbcxQ= github.com/rancher/wrangler v0.8.11-0.20220211163748-d5a8ee98be5f h1:0Z+sioLE7Ai0PLiwG81Lmh2kMFnT78cKUApArXQECzY= diff --git a/pkg/agent/tunnel/tunnel.go b/pkg/agent/tunnel/tunnel.go index a52220bf87..81d7a2b677 100644 --- a/pkg/agent/tunnel/tunnel.go +++ b/pkg/agent/tunnel/tunnel.go @@ -341,7 +341,7 @@ func (a *agentTunnel) connect(rootCtx context.Context, waitGroup *sync.WaitGroup for { remotedialer.ClientConnect(ctx, wsURL, nil, ws, func(proto, address string) bool { return a.authorized(rootCtx, proto, address) - }, func(_ context.Context) error { + }, func(_ context.Context, _ *remotedialer.Session) error { if waitGroup != nil { once.Do(waitGroup.Done) } diff --git a/pkg/daemons/control/tunnel.go b/pkg/daemons/control/tunnel.go index 4fc08d8906..816901c69f 100644 --- a/pkg/daemons/control/tunnel.go +++ b/pkg/daemons/control/tunnel.go @@ -27,6 +27,8 @@ import ( "k8s.io/client-go/kubernetes" ) +var defaultDialer = net.Dialer{} + func loggingErrorWriter(rw http.ResponseWriter, req *http.Request, code int, err error) { logrus.Debugf("Tunnel server error: %d %v", code, err) rw.WriteHeader(code) @@ -167,10 +169,10 @@ func (t *TunnelServer) onChangePod(podName string, pod *v1.Pod) (*v1.Pod, error) // 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.Host) + bconn, err := t.dialBackend(req.Context(), req.Host) if err != nil { responsewriters.ErrorNegotiated( - apierrors.NewInternalError(errors.Wrap(err, "no tunnels available")), + apierrors.NewServiceUnavailable(err.Error()), scheme.Codecs.WithoutConversion(), schema.GroupVersion{}, resp, req, ) return @@ -203,7 +205,7 @@ func (t *TunnelServer) serveConnect(resp http.ResponseWriter, req *http.Request) // 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. -func (t *TunnelServer) dialBackend(addr string) (net.Conn, error) { +func (t *TunnelServer) dialBackend(ctx context.Context, addr string) (net.Conn, error) { host, port, err := net.SplitHostPort(addr) if err != nil { return nil, err @@ -245,14 +247,26 @@ func (t *TunnelServer) dialBackend(addr string) (net.Conn, error) { useTunnel = false } - if useTunnel && t.server.HasSession(nodeName) { - // Have a session and it is safe to use for this destination, do so. - logrus.Debugf("Tunnel server egress proxy dialing %s via session to %s", addr, nodeName) - return t.server.Dial(nodeName, 15*time.Second, "tcp", addr) + if useTunnel { + // Dialer(nodeName) returns a dial function that calls getDialer internally, which does the same locked session search + // as HasSession(nodeName). Rather than checking twice, just attempt the dial and handle the error if no session is found. + dialContext := t.server.Dialer(nodeName) + if conn, err := dialContext(ctx, "tcp", addr); err != nil { + logrus.Debugf("Tunnel server egress proxy dial error: %v", err) + if toKubelet && strings.HasPrefix(err.Error(), "failed to find Session for client") { + // Don't have a session and we're trying to remote dial the kubelet via loopback, reject the connection. + return conn, err + } + // any other error is ignored; fall back to to dialing directly. + } else { + // Have a session and it is safe to use for this destination, do so. + logrus.Debugf("Tunnel server egress proxy dialing %s via Session to %s", addr, nodeName) + return conn, err + } } // Don't have a session, the agent doesn't support tunneling to this destination, or // the destination is local; fall back to direct connection. logrus.Debugf("Tunnel server egress proxy dialing %s directly", addr) - return net.Dial("tcp", addr) + return defaultDialer.DialContext(ctx, "tcp", addr) }