diff --git a/examples/default.yaml b/examples/default.yaml index 8e760fee3c9a..465bc08194d7 100644 --- a/examples/default.yaml +++ b/examples/default.yaml @@ -266,6 +266,7 @@ networks: # # - guestSocket: "/run/user/{{.UID}}/my.sock" # hostSocket: mysocket +# # default: reverse: false # # "guestSocket" can include these template variables: {{.Home}}, {{.UID}}, and {{.User}}. # # "hostSocket" can include {{.Home}}, {{.Dir}}, {{.Name}}, {{.UID}}, and {{.User}}. # # Put sockets into "{{.Dir}}/sock" to avoid collision with Lima internal sockets! diff --git a/pkg/hostagent/hostagent.go b/pkg/hostagent/hostagent.go index bbf2e7940057..3755edc8486c 100644 --- a/pkg/hostagent/hostagent.go +++ b/pkg/hostagent/hostagent.go @@ -423,7 +423,7 @@ func (a *HostAgent) watchGuestAgentEvents(ctx context.Context) { for _, rule := range a.y.PortForwards { if rule.GuestSocket != "" { local := hostAddress(rule, guestagentapi.IPPort{}) - _ = forwardSSH(ctx, a.sshConfig, a.sshLocalPort, local, rule.GuestSocket, verbForward) + _ = forwardSSH(ctx, a.sshConfig, a.sshLocalPort, local, rule.GuestSocket, verbForward, rule.Reverse) } } @@ -437,12 +437,12 @@ func (a *HostAgent) watchGuestAgentEvents(ctx context.Context) { if rule.GuestSocket != "" { local := hostAddress(rule, guestagentapi.IPPort{}) // using ctx.Background() because ctx has already been cancelled - if err := forwardSSH(context.Background(), a.sshConfig, a.sshLocalPort, local, rule.GuestSocket, verbCancel); err != nil { + if err := forwardSSH(context.Background(), a.sshConfig, a.sshLocalPort, local, rule.GuestSocket, verbCancel, rule.Reverse); err != nil { mErr = multierror.Append(mErr, err) } } } - if err := forwardSSH(context.Background(), a.sshConfig, a.sshLocalPort, localUnix, remoteUnix, verbCancel); err != nil { + if err := forwardSSH(context.Background(), a.sshConfig, a.sshLocalPort, localUnix, remoteUnix, verbCancel, false); err != nil { mErr = multierror.Append(mErr, err) } return mErr @@ -450,7 +450,7 @@ func (a *HostAgent) watchGuestAgentEvents(ctx context.Context) { for { if !isGuestAgentSocketAccessible(ctx, localUnix) { - _ = forwardSSH(ctx, a.sshConfig, a.sshLocalPort, localUnix, remoteUnix, verbForward) + _ = forwardSSH(ctx, a.sshConfig, a.sshLocalPort, localUnix, remoteUnix, verbForward, false) } if err := a.processGuestAgentEvents(ctx, localUnix); err != nil { if !errors.Is(err, context.Canceled) { @@ -506,12 +506,22 @@ const ( verbCancel = "cancel" ) -func forwardSSH(ctx context.Context, sshConfig *ssh.SSHConfig, port int, local, remote string, verb string) error { +func forwardSSH(ctx context.Context, sshConfig *ssh.SSHConfig, port int, local, remote string, verb string, reverse bool) error { args := sshConfig.Args() args = append(args, "-T", "-O", verb, - "-L", local+":"+remote, + ) + if reverse { + args = append(args, + "-R", remote+":"+local, + ) + } else { + args = append(args, + "-L", local+":"+remote, + ) + } + args = append(args, "-N", "-f", "-p", strconv.Itoa(port), @@ -521,7 +531,11 @@ func forwardSSH(ctx context.Context, sshConfig *ssh.SSHConfig, port int, local, if strings.HasPrefix(local, "/") { switch verb { case verbForward: - logrus.Infof("Forwarding %q (guest) to %q (host)", remote, local) + if reverse { + logrus.Infof("Forwarding %q (host) to %q (guest)", local, remote) + } else { + logrus.Infof("Forwarding %q (guest) to %q (host)", remote, local) + } if err := os.RemoveAll(local); err != nil { logrus.WithError(err).Warnf("Failed to clean up %q (host) before setting up forwarding", local) } @@ -529,7 +543,11 @@ func forwardSSH(ctx context.Context, sshConfig *ssh.SSHConfig, port int, local, return fmt.Errorf("can't create directory for local socket %q: %w", local, err) } case verbCancel: - logrus.Infof("Stopping forwarding %q (guest) to %q (host)", remote, local) + if reverse { + logrus.Infof("Stopping forwarding %q (host) to %q (guest)", local, remote) + } else { + logrus.Infof("Stopping forwarding %q (guest) to %q (host)", remote, local) + } defer func() { if err := os.RemoveAll(local); err != nil { logrus.WithError(err).Warnf("Failed to clean up %q (host) after stopping forwarding", local) diff --git a/pkg/hostagent/port_darwin.go b/pkg/hostagent/port_darwin.go index 86617b25d5ac..fead0401b449 100644 --- a/pkg/hostagent/port_darwin.go +++ b/pkg/hostagent/port_darwin.go @@ -18,7 +18,7 @@ import ( // forwardTCP is not thread-safe func forwardTCP(ctx context.Context, sshConfig *ssh.SSHConfig, port int, local, remote string, verb string) error { if strings.HasPrefix(local, "/") { - return forwardSSH(ctx, sshConfig, port, local, remote, verb) + return forwardSSH(ctx, sshConfig, port, local, remote, verb, false) } localIPStr, localPortStr, err := net.SplitHostPort(local) if err != nil { @@ -31,7 +31,7 @@ func forwardTCP(ctx context.Context, sshConfig *ssh.SSHConfig, port int, local, } if !localIP.Equal(api.IPv4loopback1) || localPort >= 1024 { - return forwardSSH(ctx, sshConfig, port, local, remote, verb) + return forwardSSH(ctx, sshConfig, port, local, remote, verb, false) } // on macOS, listening on 127.0.0.1:80 requires root while 0.0.0.0:80 does not require root. @@ -46,7 +46,7 @@ func forwardTCP(ctx context.Context, sshConfig *ssh.SSHConfig, port int, local, localUnix := plf.unixAddr.Name _ = plf.Close() delete(pseudoLoopbackForwarders, local) - if err := forwardSSH(ctx, sshConfig, port, localUnix, remote, verb); err != nil { + if err := forwardSSH(ctx, sshConfig, port, localUnix, remote, verb, false); err != nil { return err } } else { @@ -61,12 +61,12 @@ func forwardTCP(ctx context.Context, sshConfig *ssh.SSHConfig, port int, local, } localUnix := filepath.Join(localUnixDir, "sock") logrus.Debugf("forwarding %q to %q", localUnix, remote) - if err := forwardSSH(ctx, sshConfig, port, localUnix, remote, verb); err != nil { + if err := forwardSSH(ctx, sshConfig, port, localUnix, remote, verb, false); err != nil { return err } plf, err := newPseudoLoopbackForwarder(localPort, localUnix) if err != nil { - if cancelErr := forwardSSH(ctx, sshConfig, port, localUnix, remote, verbCancel); cancelErr != nil { + if cancelErr := forwardSSH(ctx, sshConfig, port, localUnix, remote, verbCancel, false); cancelErr != nil { logrus.WithError(cancelErr).Warnf("failed to cancel forwarding %q to %q", localUnix, remote) } return err diff --git a/pkg/hostagent/port_others.go b/pkg/hostagent/port_others.go index 6854c43f7a94..35d276d6f316 100644 --- a/pkg/hostagent/port_others.go +++ b/pkg/hostagent/port_others.go @@ -10,5 +10,5 @@ import ( ) func forwardTCP(ctx context.Context, sshConfig *ssh.SSHConfig, port int, local, remote string, verb string) error { - return forwardSSH(ctx, sshConfig, port, local, remote, verb) + return forwardSSH(ctx, sshConfig, port, local, remote, verb, false) } diff --git a/pkg/limayaml/defaults_test.go b/pkg/limayaml/defaults_test.go index a7327a4cecb3..72ca04b3750c 100644 --- a/pkg/limayaml/defaults_test.go +++ b/pkg/limayaml/defaults_test.go @@ -88,6 +88,7 @@ func TestFillDefault(t *testing.T) { HostIP: api.IPv4loopback1, HostPortRange: [2]int{1, 65535}, Proto: TCP, + Reverse: false, } // ------------------------------------------------------------------------------------ diff --git a/pkg/limayaml/limayaml.go b/pkg/limayaml/limayaml.go index c7ba62f0f95d..e165241a9bb6 100644 --- a/pkg/limayaml/limayaml.go +++ b/pkg/limayaml/limayaml.go @@ -144,6 +144,7 @@ type PortForward struct { HostPortRange [2]int `yaml:"hostPortRange,omitempty" json:"hostPortRange,omitempty"` HostSocket string `yaml:"hostSocket,omitempty" json:"hostSocket,omitempty"` Proto Proto `yaml:"proto,omitempty" json:"proto,omitempty"` + Reverse bool `yaml:"reverse,omitempty" json:"reverse,omitempty"` Ignore bool `yaml:"ignore,omitempty" json:"ignore,omitempty"` }