Skip to content
Merged
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
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,20 @@ Use `<INSTANCE>:<FILENAME>` to specify a source or target inside an instance.

`limactl disk list`: list all existing disks

#### `limactl show-ssh`
- `limactl show-ssh --format=cmd <INSTANCE>` (default): Full `ssh` command line
- `limactl show-ssh --format=args <INSTANCE>`: Similar to the `cmd` format but omits `ssh` and the destination address
- `limactl show-ssh --format=options <INSTANCE>`: ssh option key value pairs
- `limactl show-ssh --format=config <INSTANCE>`: `~/.ssh/config` format

The config file is also automatically created inside the instance directory:
```console
$ limactl ls --format='{{.SSHConfigFile}}' default
/Users/example/.lima/default/ssh.config

$ ssh -F /Users/example/.lima/default/ssh.config lima-default
```

#### `limactl completion`
- To enable bash completion, add `source <(limactl completion bash)` to `~/.bash_profile`.

Expand Down
4 changes: 4 additions & 0 deletions cmd/limactl/show_ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ const showSSHExample = `
User example
Hostname 127.0.0.1
Port 60022

To show the config file path:
$ limactl ls --format='{{.SSHConfigFile}}' default
/Users/example/.lima/default/ssh.config
`

func newShowSSHCommand() *cobra.Command {
Expand Down
1 change: 1 addition & 0 deletions docs/internal.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ VZ:

SSH:
- `ssh.sock`: SSH control master socket
- `ssh.config`: SSH config file for `ssh -F`. Not consumed by Lima itself.

Guest agent:
- `ga.sock`: Forwarded to `/run/lima-guestagent.sock` in the guest, via SSH
Expand Down
26 changes: 26 additions & 0 deletions pkg/hostagent/hostagent.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package hostagent

import (
"bytes"
"context"
"encoding/json"
"errors"
Expand Down Expand Up @@ -111,6 +112,9 @@ func New(instName string, stdout io.Writer, sigintCh chan os.Signal, opts ...Opt
if err != nil {
return nil, err
}
if err = writeSSHConfigFile(inst, sshLocalPort, sshOpts); err != nil {
return nil, err
}
sshConfig := &ssh.SSHConfig{
AdditionalArgs: sshutil.SSHArgsFromOpts(sshOpts),
}
Expand Down Expand Up @@ -150,6 +154,28 @@ func New(instName string, stdout io.Writer, sigintCh chan os.Signal, opts ...Opt
return a, nil
}

func writeSSHConfigFile(inst *store.Instance, sshLocalPort int, sshOpts []string) error {
if inst.Dir == "" {
return fmt.Errorf("directory is unknown for the instance %q", inst.Name)
}
var b bytes.Buffer
if _, err := fmt.Fprintf(&b, `# This SSH config file can be passed to 'ssh -F'.
# This file is created by Lima, but not used by Lima itself currently.
# Modifications to this file will be lost on restarting the Lima instance.
`); err != nil {
return err
}
if err := sshutil.Format(&b, inst.Name, sshutil.FormatConfig,
append(sshOpts,
"Hostname=127.0.0.1",
fmt.Sprintf("Port=%d", sshLocalPort),
)); err != nil {
return err
}
fileName := filepath.Join(inst.Dir, filenames.SSHConfig)
return os.WriteFile(fileName, b.Bytes(), 0600)
}

func determineSSHLocalPort(y *limayaml.LimaYAML, instName string) (int, error) {
if *y.SSH.LocalPort > 0 {
return *y.SSH.LocalPort, nil
Expand Down
1 change: 1 addition & 0 deletions pkg/store/filenames/filenames.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const (
SerialLog = "serial.log"
SerialSock = "serial.sock"
SSHSock = "ssh.sock"
SSHConfig = "ssh.config"
GuestAgentSock = "ga.sock"
HostAgentPID = "ha.pid"
HostAgentSock = "ha.sock"
Expand Down
2 changes: 2 additions & 0 deletions pkg/store/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type Instance struct {
AdditionalDisks []limayaml.Disk `json:"additionalDisks,omitempty"`
Networks []limayaml.Network `json:"network,omitempty"`
SSHLocalPort int `json:"sshLocalPort,omitempty"`
SSHConfigFile string `json:"sshConfigFile,omitempty"`
HostAgentPID int `json:"hostAgentPID,omitempty"`
DriverPID int `json:"driverPID,omitempty"`
Errors []error `json:"errors,omitempty"`
Expand Down Expand Up @@ -101,6 +102,7 @@ func Inspect(instName string) (*Instance, error) {
inst.AdditionalDisks = y.AdditionalDisks
inst.Networks = y.Networks
inst.SSHLocalPort = *y.SSH.LocalPort // maybe 0
inst.SSHConfigFile = filepath.Join(instDir, filenames.SSHConfig)

inst.HostAgentPID, err = ReadPIDFile(filepath.Join(instDir, filenames.HostAgentPID))
if err != nil {
Expand Down