Skip to content

Commit 3ab90da

Browse files
committed
Enable reverse forwarding of portForwards
By default, the portForwards will be forwarded from local (host) to remote (guest). Flag: -L With reverse, the portForwards will be forwarded from remote (guest) to local (host). Flag: -R Only implemented for unix sockets at the moment. Signed-off-by: Anders F Björklund <[email protected]>
1 parent e0c7733 commit 3ab90da

File tree

7 files changed

+42
-14
lines changed

7 files changed

+42
-14
lines changed

examples/default.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,10 @@ networks:
266266
#
267267
# - guestSocket: "/run/user/{{.UID}}/my.sock"
268268
# hostSocket: mysocket
269+
# # default: reverse: false
269270
# # "guestSocket" can include these template variables: {{.Home}}, {{.UID}}, and {{.User}}.
270271
# # "hostSocket" can include {{.Home}}, {{.Dir}}, {{.Name}}, {{.UID}}, and {{.User}}.
272+
# # "reverse" can only be used for unix sockets right now, not for tcp sockets.
271273
# # Put sockets into "{{.Dir}}/sock" to avoid collision with Lima internal sockets!
272274
# # Sockets can also be forwarded to ports and vice versa, but not to/from a range of ports.
273275
# # Forwarding requires the lima user to have rw access to the "guestsocket",

pkg/hostagent/hostagent.go

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,7 @@ func (a *HostAgent) watchGuestAgentEvents(ctx context.Context) {
423423
for _, rule := range a.y.PortForwards {
424424
if rule.GuestSocket != "" {
425425
local := hostAddress(rule, guestagentapi.IPPort{})
426-
_ = forwardSSH(ctx, a.sshConfig, a.sshLocalPort, local, rule.GuestSocket, verbForward)
426+
_ = forwardSSH(ctx, a.sshConfig, a.sshLocalPort, local, rule.GuestSocket, verbForward, rule.Reverse)
427427
}
428428
}
429429

@@ -437,20 +437,20 @@ func (a *HostAgent) watchGuestAgentEvents(ctx context.Context) {
437437
if rule.GuestSocket != "" {
438438
local := hostAddress(rule, guestagentapi.IPPort{})
439439
// using ctx.Background() because ctx has already been cancelled
440-
if err := forwardSSH(context.Background(), a.sshConfig, a.sshLocalPort, local, rule.GuestSocket, verbCancel); err != nil {
440+
if err := forwardSSH(context.Background(), a.sshConfig, a.sshLocalPort, local, rule.GuestSocket, verbCancel, rule.Reverse); err != nil {
441441
mErr = multierror.Append(mErr, err)
442442
}
443443
}
444444
}
445-
if err := forwardSSH(context.Background(), a.sshConfig, a.sshLocalPort, localUnix, remoteUnix, verbCancel); err != nil {
445+
if err := forwardSSH(context.Background(), a.sshConfig, a.sshLocalPort, localUnix, remoteUnix, verbCancel, false); err != nil {
446446
mErr = multierror.Append(mErr, err)
447447
}
448448
return mErr
449449
})
450450

451451
for {
452452
if !isGuestAgentSocketAccessible(ctx, localUnix) {
453-
_ = forwardSSH(ctx, a.sshConfig, a.sshLocalPort, localUnix, remoteUnix, verbForward)
453+
_ = forwardSSH(ctx, a.sshConfig, a.sshLocalPort, localUnix, remoteUnix, verbForward, false)
454454
}
455455
if err := a.processGuestAgentEvents(ctx, localUnix); err != nil {
456456
if !errors.Is(err, context.Canceled) {
@@ -506,12 +506,22 @@ const (
506506
verbCancel = "cancel"
507507
)
508508

509-
func forwardSSH(ctx context.Context, sshConfig *ssh.SSHConfig, port int, local, remote string, verb string) error {
509+
func forwardSSH(ctx context.Context, sshConfig *ssh.SSHConfig, port int, local, remote string, verb string, reverse bool) error {
510510
args := sshConfig.Args()
511511
args = append(args,
512512
"-T",
513513
"-O", verb,
514-
"-L", local+":"+remote,
514+
)
515+
if reverse {
516+
args = append(args,
517+
"-R", remote+":"+local,
518+
)
519+
} else {
520+
args = append(args,
521+
"-L", local+":"+remote,
522+
)
523+
}
524+
args = append(args,
515525
"-N",
516526
"-f",
517527
"-p", strconv.Itoa(port),
@@ -521,15 +531,23 @@ func forwardSSH(ctx context.Context, sshConfig *ssh.SSHConfig, port int, local,
521531
if strings.HasPrefix(local, "/") {
522532
switch verb {
523533
case verbForward:
524-
logrus.Infof("Forwarding %q (guest) to %q (host)", remote, local)
534+
if reverse {
535+
logrus.Infof("Forwarding %q (host) to %q (guest)", local, remote)
536+
} else {
537+
logrus.Infof("Forwarding %q (guest) to %q (host)", remote, local)
538+
}
525539
if err := os.RemoveAll(local); err != nil {
526540
logrus.WithError(err).Warnf("Failed to clean up %q (host) before setting up forwarding", local)
527541
}
528542
if err := os.MkdirAll(filepath.Dir(local), 0750); err != nil {
529543
return fmt.Errorf("can't create directory for local socket %q: %w", local, err)
530544
}
531545
case verbCancel:
532-
logrus.Infof("Stopping forwarding %q (guest) to %q (host)", remote, local)
546+
if reverse {
547+
logrus.Infof("Stopping forwarding %q (host) to %q (guest)", local, remote)
548+
} else {
549+
logrus.Infof("Stopping forwarding %q (guest) to %q (host)", remote, local)
550+
}
533551
defer func() {
534552
if err := os.RemoveAll(local); err != nil {
535553
logrus.WithError(err).Warnf("Failed to clean up %q (host) after stopping forwarding", local)

pkg/hostagent/port_darwin.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import (
1818
// forwardTCP is not thread-safe
1919
func forwardTCP(ctx context.Context, sshConfig *ssh.SSHConfig, port int, local, remote string, verb string) error {
2020
if strings.HasPrefix(local, "/") {
21-
return forwardSSH(ctx, sshConfig, port, local, remote, verb)
21+
return forwardSSH(ctx, sshConfig, port, local, remote, verb, false)
2222
}
2323
localIPStr, localPortStr, err := net.SplitHostPort(local)
2424
if err != nil {
@@ -31,7 +31,7 @@ func forwardTCP(ctx context.Context, sshConfig *ssh.SSHConfig, port int, local,
3131
}
3232

3333
if !localIP.Equal(api.IPv4loopback1) || localPort >= 1024 {
34-
return forwardSSH(ctx, sshConfig, port, local, remote, verb)
34+
return forwardSSH(ctx, sshConfig, port, local, remote, verb, false)
3535
}
3636

3737
// 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,
4646
localUnix := plf.unixAddr.Name
4747
_ = plf.Close()
4848
delete(pseudoLoopbackForwarders, local)
49-
if err := forwardSSH(ctx, sshConfig, port, localUnix, remote, verb); err != nil {
49+
if err := forwardSSH(ctx, sshConfig, port, localUnix, remote, verb, false); err != nil {
5050
return err
5151
}
5252
} else {
@@ -61,12 +61,12 @@ func forwardTCP(ctx context.Context, sshConfig *ssh.SSHConfig, port int, local,
6161
}
6262
localUnix := filepath.Join(localUnixDir, "sock")
6363
logrus.Debugf("forwarding %q to %q", localUnix, remote)
64-
if err := forwardSSH(ctx, sshConfig, port, localUnix, remote, verb); err != nil {
64+
if err := forwardSSH(ctx, sshConfig, port, localUnix, remote, verb, false); err != nil {
6565
return err
6666
}
6767
plf, err := newPseudoLoopbackForwarder(localPort, localUnix)
6868
if err != nil {
69-
if cancelErr := forwardSSH(ctx, sshConfig, port, localUnix, remote, verbCancel); cancelErr != nil {
69+
if cancelErr := forwardSSH(ctx, sshConfig, port, localUnix, remote, verbCancel, false); cancelErr != nil {
7070
logrus.WithError(cancelErr).Warnf("failed to cancel forwarding %q to %q", localUnix, remote)
7171
}
7272
return err

pkg/hostagent/port_others.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ import (
1010
)
1111

1212
func forwardTCP(ctx context.Context, sshConfig *ssh.SSHConfig, port int, local, remote string, verb string) error {
13-
return forwardSSH(ctx, sshConfig, port, local, remote, verb)
13+
return forwardSSH(ctx, sshConfig, port, local, remote, verb, false)
1414
}

pkg/limayaml/defaults_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ func TestFillDefault(t *testing.T) {
8888
HostIP: api.IPv4loopback1,
8989
HostPortRange: [2]int{1, 65535},
9090
Proto: TCP,
91+
Reverse: false,
9192
}
9293

9394
// ------------------------------------------------------------------------------------

pkg/limayaml/limayaml.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ type PortForward struct {
144144
HostPortRange [2]int `yaml:"hostPortRange,omitempty" json:"hostPortRange,omitempty"`
145145
HostSocket string `yaml:"hostSocket,omitempty" json:"hostSocket,omitempty"`
146146
Proto Proto `yaml:"proto,omitempty" json:"proto,omitempty"`
147+
Reverse bool `yaml:"reverse,omitempty" json:"reverse,omitempty"`
147148
Ignore bool `yaml:"ignore,omitempty" json:"ignore,omitempty"`
148149
}
149150

pkg/limayaml/validate.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,12 @@ func Validate(y LimaYAML, warn bool) error {
212212
if rule.Proto != TCP {
213213
return fmt.Errorf("field `%s.proto` must be %q", field, TCP)
214214
}
215+
if rule.Reverse && rule.GuestSocket == "" {
216+
return fmt.Errorf("field `%s.reverse` must be %t", field, false)
217+
}
218+
if rule.Reverse && rule.HostSocket == "" {
219+
return fmt.Errorf("field `%s.reverse` must be %t", field, false)
220+
}
215221
// Not validating that the various GuestPortRanges and HostPortRanges are not overlapping. Rules will be
216222
// processed sequentially and the first matching rule for a guest port determines forwarding behavior.
217223
}

0 commit comments

Comments
 (0)