Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experiment with managing external machines with lima #2000

Draft
wants to merge 15 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions cmd/limactl/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ func copyAction(cmd *cobra.Command, args []string) error {
scpFlags = append(scpFlags, "-r")
}
legacySSH := sshutil.DetectOpenSSHVersion().LessThan(*semver.New("8.0.0"))
localhostOnly := true
for _, arg := range args {
path := strings.Split(arg, ":")
switch len(path) {
Expand All @@ -80,9 +81,12 @@ func copyAction(cmd *cobra.Command, args []string) error {
}
if legacySSH {
scpFlags = append(scpFlags, "-P", fmt.Sprintf("%d", inst.SSHLocalPort))
scpArgs = append(scpArgs, fmt.Sprintf("%s@127.0.0.1:%s", *inst.Config.User.Name, path[1]))
scpArgs = append(scpArgs, fmt.Sprintf("%s@%s:%s", *inst.Config.User.Name, inst.SSHAddress, path[1]))
} else {
scpArgs = append(scpArgs, fmt.Sprintf("scp://%[email protected]:%d/%s", *inst.Config.User.Name, inst.SSHLocalPort, path[1]))
scpArgs = append(scpArgs, fmt.Sprintf("scp://%s@%s:%d/%s", *inst.Config.User.Name, inst.SSHAddress, inst.SSHLocalPort, path[1]))
}
if !sshutil.IsLocalhost(inst.SSHAddress) {
localhostOnly = false
}
instances[instName] = inst
default:
Expand All @@ -101,14 +105,14 @@ func copyAction(cmd *cobra.Command, args []string) error {
// arguments such as ControlPath. This is preferred as we can multiplex
// sessions without re-authenticating (MaxSessions permitting).
for _, inst := range instances {
sshOpts, err = sshutil.SSHOpts(inst.Dir, *inst.Config.User.Name, false, false, false, false)
sshOpts, err = sshutil.SSHOpts(inst.Dir, *inst.Config.User.Name, false, inst.SSHAddress, false, false, false)
if err != nil {
return err
}
}
} else {
// Copying among multiple hosts; we can't pass in host-specific options.
sshOpts, err = sshutil.CommonOpts(false)
sshOpts, err = sshutil.CommonOpts(false, localhostOnly)
if err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions cmd/limactl/shell.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ func shellAction(cmd *cobra.Command, args []string) error {
inst.Dir,
*inst.Config.User.Name,
*inst.Config.SSH.LoadDotSSHPubKeys,
*inst.Config.SSH.Address,
*inst.Config.SSH.ForwardAgent,
*inst.Config.SSH.ForwardX11,
*inst.Config.SSH.ForwardX11Trusted)
Expand Down
3 changes: 2 additions & 1 deletion cmd/limactl/show-ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,14 @@ func showSSHAction(cmd *cobra.Command, args []string) error {
inst.Dir,
*inst.Config.User.Name,
*inst.Config.SSH.LoadDotSSHPubKeys,
*inst.Config.SSH.Address,
*inst.Config.SSH.ForwardAgent,
*inst.Config.SSH.ForwardX11,
*inst.Config.SSH.ForwardX11Trusted)
if err != nil {
return err
}
opts = append(opts, "Hostname=127.0.0.1")
opts = append(opts, fmt.Sprintf("Hostname=%s", inst.SSHAddress))
opts = append(opts, fmt.Sprintf("Port=%d", inst.SSHLocalPort))
return sshutil.Format(w, instName, format, opts)
}
Expand Down
1 change: 1 addition & 0 deletions cmd/limactl/tunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ func tunnelAction(cmd *cobra.Command, args []string) error {
inst.Dir,
*inst.Config.User.Name,
*inst.Config.SSH.LoadDotSSHPubKeys,
*inst.Config.SSH.Address,
*inst.Config.SSH.ForwardAgent,
*inst.Config.SSH.ForwardX11,
*inst.Config.SSH.ForwardX11Trusted)
Expand Down
14 changes: 14 additions & 0 deletions examples/experimental/ext.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
vmType: ext

arch: "aarch64"
cpus: 4
memory: 512MiB
disk: 32GiB

mounts:
- location: "~"
- location: "/tmp/lima"
writable: true

ssh:
address: raspberrypi.local
4 changes: 4 additions & 0 deletions pkg/driverutil/driverutil.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package driverutil

import (
"github.com/lima-vm/lima/pkg/ext"
"github.com/lima-vm/lima/pkg/limayaml"
"github.com/lima-vm/lima/pkg/vz"
"github.com/lima-vm/lima/pkg/wsl2"
Expand All @@ -15,5 +16,8 @@ func Drivers() []string {
if wsl2.Enabled {
drivers = append(drivers, limayaml.WSL2)
}
if ext.Enabled {
drivers = append(drivers, limayaml.EXT)
}
return drivers
}
4 changes: 4 additions & 0 deletions pkg/driverutil/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package driverutil

import (
"github.com/lima-vm/lima/pkg/driver"
"github.com/lima-vm/lima/pkg/ext"
"github.com/lima-vm/lima/pkg/limayaml"
"github.com/lima-vm/lima/pkg/qemu"
"github.com/lima-vm/lima/pkg/vz"
Expand All @@ -16,5 +17,8 @@ func CreateTargetDriverInstance(base *driver.BaseDriver) driver.Driver {
if *limaDriver == limayaml.WSL2 {
return wsl2.New(base)
}
if *limaDriver == limayaml.EXT {
return ext.New(base)
}
return qemu.New(base)
}
17 changes: 17 additions & 0 deletions pkg/ext/ext_driver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package ext

import (
"github.com/lima-vm/lima/pkg/driver"
)

const Enabled = true

type LimaExtDriver struct {
*driver.BaseDriver
}

func New(driver *driver.BaseDriver) *LimaExtDriver {
return &LimaExtDriver{
BaseDriver: driver,
}
}
3 changes: 2 additions & 1 deletion pkg/hostagent/events/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ type Status struct {

Errors []string `json:"errors,omitempty"`

SSHLocalPort int `json:"sshLocalPort,omitempty"`
SSHIPAddress string `json:"sshIPAddress,omitempty"`
SSHLocalPort int `json:"sshLocalPort,omitempty"`
}

type Event struct {
Expand Down
58 changes: 41 additions & 17 deletions pkg/hostagent/hostagent.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func New(instName string, stdout io.Writer, signalCh chan os.Signal, opts ...Opt
}

// inst.Config is loaded with FillDefault() already, so no need to care about nil pointers.
sshLocalPort, err := determineSSHLocalPort(*inst.Config.SSH.LocalPort, instName)
sshLocalPort, err := determineSSHLocalPort(*inst.Config.SSH.Address, *inst.Config.SSH.LocalPort, instName)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -138,14 +138,17 @@ func New(instName string, stdout io.Writer, signalCh chan os.Signal, opts ...Opt
if err := cidata.GenerateCloudConfig(inst.Dir, instName, inst.Config); err != nil {
return nil, err
}
if err := cidata.GenerateISO9660(inst.Dir, instName, inst.Config, udpDNSLocalPort, tcpDNSLocalPort, o.nerdctlArchive, vSockPort, virtioPort); err != nil {
return nil, err
if *inst.Config.VMType != limayaml.EXT {
if err := cidata.GenerateISO9660(inst.Dir, instName, inst.Config, udpDNSLocalPort, tcpDNSLocalPort, o.nerdctlArchive, vSockPort, virtioPort); err != nil {
return nil, err
}
}

sshOpts, err := sshutil.SSHOpts(
inst.Dir,
*inst.Config.User.Name,
*inst.Config.SSH.LoadDotSSHPubKeys,
*inst.Config.SSH.Address,
*inst.Config.SSH.ForwardAgent,
*inst.Config.SSH.ForwardX11,
*inst.Config.SSH.ForwardX11Trusted)
Expand Down Expand Up @@ -208,7 +211,7 @@ func New(instName string, stdout io.Writer, signalCh chan os.Signal, opts ...Opt
instName: instName,
instSSHAddress: inst.SSHAddress,
sshConfig: sshConfig,
portForwarder: newPortForwarder(sshConfig, sshLocalPort, rules, ignoreTCP, inst.VMType),
portForwarder: newPortForwarder(sshConfig, inst.SSHAddress, sshLocalPort, rules, ignoreTCP, inst.VMType),
grpcPortForwarder: portfwd.NewPortForwarder(rules, ignoreTCP, ignoreUDP),
driver: limaDriver,
signalCh: signalCh,
Expand Down Expand Up @@ -242,13 +245,16 @@ func writeSSHConfigFile(instName, instDir, instSSHAddress string, sshLocalPort i
return os.WriteFile(fileName, b.Bytes(), 0o600)
}

func determineSSHLocalPort(confLocalPort int, instName string) (int, error) {
func determineSSHLocalPort(confSSHAddress string, confLocalPort int, instName string) (int, error) {
if confLocalPort > 0 {
return confLocalPort, nil
}
if confLocalPort < 0 {
return 0, fmt.Errorf("invalid ssh local port %d", confLocalPort)
}
if confLocalPort == 0 && confSSHAddress != "127.0.0.1" {
return 22, nil
}
if instName == "default" {
// use hard-coded value for "default" instance, for backward compatibility
return 60022, nil
Expand Down Expand Up @@ -378,8 +384,21 @@ func (a *HostAgent) Run(ctx context.Context) error {
return a.startRoutinesAndWait(ctx, errCh)
}

func getIP(address string) string {
ip := net.ParseIP(address)
if ip != nil {
return address
}
ips, err := net.LookupIP(address)
if err == nil && len(ips) > 0 {
return ips[0].String()
}
return address
}

func (a *HostAgent) startRoutinesAndWait(ctx context.Context, errCh <-chan error) error {
stBase := events.Status{
SSHIPAddress: getIP(a.instSSHAddress),
SSHLocalPort: a.sshLocalPort,
}
stBooting := stBase
Expand Down Expand Up @@ -483,6 +502,11 @@ sudo chown -R "${USER}" /run/host-services`
return errors.Join(unlockErrs...)
})
}
if *a.instConfig.VMType == limayaml.EXT {
if err := a.runProvisionScripts(); err != nil {
return err
}
}
if !*a.instConfig.Plain {
go a.watchGuestAgentEvents(ctx)
}
Expand Down Expand Up @@ -543,7 +567,7 @@ func (a *HostAgent) watchGuestAgentEvents(ctx context.Context) {
for _, rule := range a.instConfig.PortForwards {
if rule.GuestSocket != "" {
local := hostAddress(rule, &guestagentapi.IPPort{})
_ = forwardSSH(ctx, a.sshConfig, a.sshLocalPort, local, rule.GuestSocket, verbForward, rule.Reverse)
_ = forwardSSH(ctx, a.sshConfig, a.instSSHAddress, a.sshLocalPort, local, rule.GuestSocket, verbForward, rule.Reverse)
}
}
}
Expand All @@ -558,13 +582,13 @@ 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, rule.Reverse); err != nil {
if err := forwardSSH(context.Background(), a.sshConfig, a.instSSHAddress, a.sshLocalPort, local, rule.GuestSocket, verbCancel, rule.Reverse); err != nil {
errs = append(errs, err)
}
}
}
if a.driver.ForwardGuestAgent() {
if err := forwardSSH(context.Background(), a.sshConfig, a.sshLocalPort, localUnix, remoteUnix, verbCancel, false); err != nil {
if err := forwardSSH(context.Background(), a.sshConfig, a.instSSHAddress, a.sshLocalPort, localUnix, remoteUnix, verbCancel, false); err != nil {
errs = append(errs, err)
}
}
Expand All @@ -575,7 +599,7 @@ func (a *HostAgent) watchGuestAgentEvents(ctx context.Context) {
if a.instConfig.MountInotify != nil && *a.instConfig.MountInotify {
if a.client == nil || !isGuestAgentSocketAccessible(ctx, a.client) {
if a.driver.ForwardGuestAgent() {
_ = forwardSSH(ctx, a.sshConfig, a.sshLocalPort, localUnix, remoteUnix, verbForward, false)
_ = forwardSSH(ctx, a.sshConfig, a.instSSHAddress, a.sshLocalPort, localUnix, remoteUnix, verbForward, false)
}
}
err := a.startInotify(ctx)
Expand All @@ -588,7 +612,7 @@ func (a *HostAgent) watchGuestAgentEvents(ctx context.Context) {
for {
if a.client == nil || !isGuestAgentSocketAccessible(ctx, a.client) {
if a.driver.ForwardGuestAgent() {
_ = forwardSSH(ctx, a.sshConfig, a.sshLocalPort, localUnix, remoteUnix, verbForward, false)
_ = forwardSSH(ctx, a.sshConfig, a.instSSHAddress, a.sshLocalPort, localUnix, remoteUnix, verbForward, false)
}
}
client, err := a.getOrCreateClient(ctx)
Expand Down Expand Up @@ -676,11 +700,11 @@ const (
verbCancel = "cancel"
)

func executeSSH(ctx context.Context, sshConfig *ssh.SSHConfig, port int, command ...string) error {
func executeSSH(ctx context.Context, sshConfig *ssh.SSHConfig, addr string, port int, command ...string) error {
args := sshConfig.Args()
args = append(args,
"-p", strconv.Itoa(port),
"127.0.0.1",
addr,
"--",
)
args = append(args, command...)
Expand All @@ -691,7 +715,7 @@ func executeSSH(ctx context.Context, sshConfig *ssh.SSHConfig, port int, command
return nil
}

func forwardSSH(ctx context.Context, sshConfig *ssh.SSHConfig, port int, local, remote, verb string, reverse bool) error {
func forwardSSH(ctx context.Context, sshConfig *ssh.SSHConfig, addr string, port int, local, remote, verb string, reverse bool) error {
args := sshConfig.Args()
args = append(args,
"-T",
Expand All @@ -710,15 +734,15 @@ func forwardSSH(ctx context.Context, sshConfig *ssh.SSHConfig, port int, local,
"-N",
"-f",
"-p", strconv.Itoa(port),
"127.0.0.1",
addr,
"--",
)
if strings.HasPrefix(local, "/") {
switch verb {
case verbForward:
if reverse {
logrus.Infof("Forwarding %q (host) to %q (guest)", local, remote)
if err := executeSSH(ctx, sshConfig, port, "rm", "-f", remote); err != nil {
if err := executeSSH(ctx, sshConfig, addr, port, "rm", "-f", remote); err != nil {
logrus.WithError(err).Warnf("Failed to clean up %q (guest) before setting up forwarding", remote)
}
} else {
Expand All @@ -733,7 +757,7 @@ func forwardSSH(ctx context.Context, sshConfig *ssh.SSHConfig, port int, local,
case verbCancel:
if reverse {
logrus.Infof("Stopping forwarding %q (host) to %q (guest)", local, remote)
if err := executeSSH(ctx, sshConfig, port, "rm", "-f", remote); err != nil {
if err := executeSSH(ctx, sshConfig, addr, port, "rm", "-f", remote); err != nil {
logrus.WithError(err).Warnf("Failed to clean up %q (guest) after stopping forwarding", remote)
}
} else {
Expand All @@ -753,7 +777,7 @@ func forwardSSH(ctx context.Context, sshConfig *ssh.SSHConfig, port int, local,
if verb == verbForward && strings.HasPrefix(local, "/") {
if reverse {
logrus.WithError(err).Warnf("Failed to set up forward from %q (host) to %q (guest)", local, remote)
if err := executeSSH(ctx, sshConfig, port, "rm", "-f", remote); err != nil {
if err := executeSSH(ctx, sshConfig, addr, port, "rm", "-f", remote); err != nil {
logrus.WithError(err).Warnf("Failed to clean up %q (guest) after forwarding failed", remote)
}
} else {
Expand Down
2 changes: 1 addition & 1 deletion pkg/hostagent/mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func (a *HostAgent) setupMount(m limayaml.Mount) (*mount, error) {
Driver: *m.SSHFS.SFTPDriver,
SSHConfig: a.sshConfig,
LocalPath: location,
Host: "127.0.0.1",
Host: a.instSSHAddress,
Port: a.sshLocalPort,
RemotePath: mountPoint,
Readonly: !(*m.Writable),
Expand Down
8 changes: 5 additions & 3 deletions pkg/hostagent/port.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

type portForwarder struct {
sshConfig *ssh.SSHConfig
sshHostAddr string
sshHostPort int
rules []limayaml.PortForward
ignore bool
Expand All @@ -22,9 +23,10 @@ const sshGuestPort = 22

var IPv4loopback1 = limayaml.IPv4loopback1

func newPortForwarder(sshConfig *ssh.SSHConfig, sshHostPort int, rules []limayaml.PortForward, ignore bool, vmType limayaml.VMType) *portForwarder {
func newPortForwarder(sshConfig *ssh.SSHConfig, sshHostAddr string, sshHostPort int, rules []limayaml.PortForward, ignore bool, vmType limayaml.VMType) *portForwarder {
return &portForwarder{
sshConfig: sshConfig,
sshHostAddr: sshHostAddr,
sshHostPort: sshHostPort,
rules: rules,
ignore: ignore,
Expand Down Expand Up @@ -91,7 +93,7 @@ func (pf *portForwarder) OnEvent(ctx context.Context, ev *api.Event) {
continue
}
logrus.Infof("Stopping forwarding TCP from %s to %s", remote, local)
if err := forwardTCP(ctx, pf.sshConfig, pf.sshHostPort, local, remote, verbCancel); err != nil {
if err := forwardTCP(ctx, pf.sshConfig, pf.sshHostAddr, pf.sshHostPort, local, remote, verbCancel); err != nil {
logrus.WithError(err).Warnf("failed to stop forwarding tcp port %d", f.Port)
}
}
Expand All @@ -107,7 +109,7 @@ func (pf *portForwarder) OnEvent(ctx context.Context, ev *api.Event) {
continue
}
logrus.Infof("Forwarding TCP from %s to %s", remote, local)
if err := forwardTCP(ctx, pf.sshConfig, pf.sshHostPort, local, remote, verbForward); err != nil {
if err := forwardTCP(ctx, pf.sshConfig, pf.sshHostAddr, pf.sshHostPort, local, remote, verbForward); err != nil {
logrus.WithError(err).Warnf("failed to set up forwarding tcp port %d (negligible if already forwarded)", f.Port)
}
}
Expand Down
Loading
Loading