Skip to content

Commit 732eb4c

Browse files
committed
vmnet: support detecting Homebrew's socket_vmnet path
On Homebrew environments, socket_vmnet is installed on: - /usr/local/opt/socket_vmnet/Cellar/<VERSION>/bin/socket_vmnet (Intel) - /opt/homebrew/opt/socket_vmnet/Cellar/<VERSION>/bin/socket_vmnet (ARM) The binary is usually owned by a non-root admin user. (i.e., the homebrew user) The group is typically set to "admin". Homebrew/homebrew-core@636ac07 Signed-off-by: Akihiro Suda <[email protected]>
1 parent fc3f2a9 commit 732eb4c

File tree

6 files changed

+84
-24
lines changed

6 files changed

+84
-24
lines changed

docs/network.md

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,23 @@ The configuration steps are different across QEMU and VZ:
5353
### QEMU
5454
#### Managed (192.168.105.0/24)
5555

56-
Either [`socket_vmnet`](https://github.com/lima-vm/socket_vmnet) (since Lima v0.12) or [`vde_vmnet`](https://github.com/lima-vm/vde_vmnet) (Deprecated)
57-
is required for adding another guest IP that is accessible from the host and other guests.
56+
[`socket_vmnet`](https://github.com/lima-vm/socket_vmnet) is required for adding another guest IP that is accessible from the host and other guests.
5857

59-
Starting with version v0.7.0 lima can manage the networking daemons automatically. Networks are defined in
60-
`$LIMA_HOME/_config/networks.yaml`. If this file doesn't already exist, it will be created with these default
58+
```bash
59+
# Install socket_vmnet
60+
brew install socket_vmnet
61+
62+
# Set up the sudoers file for launching socket_vmnet from Lima
63+
limactl sudoers >etc_sudoers.d_lima
64+
sudo mv etc_sudoers.d_lima /etc/sudoers.d/lima
65+
```
66+
67+
> **Note**
68+
>
69+
> Lima before v0.12 used `vde_vmnet` for managing the networks.
70+
> `vde_vmnet` is still supported but it is deprecated and no longer documented here.
71+
72+
The networks are defined in `$LIMA_HOME/_config/networks.yaml`. If this file doesn't already exist, it will be created with these default
6173
settings:
6274

6375
<details>
@@ -114,9 +126,8 @@ Instances can then reference these networks from their `lima.yaml` file:
114126

115127
```yaml
116128
networks:
117-
# Lima can manage daemons for networks defined in $LIMA_HOME/_config/networks.yaml
118-
# automatically. The socket_vmnet must be installed into
119-
# secure locations only alterable by the "root" user.
129+
# Lima can manage the socket_vmnet daemon for networks defined in $LIMA_HOME/_config/networks.yaml automatically.
130+
# The socket_vmnet binary must be installed into a secure location only alterable by the admin.
120131
# The same applies to vde_switch and vde_vmnet for the deprecated VDE mode.
121132
# - lima: shared
122133
# # MAC address of the instance; lima will pick one based on the instance name,
@@ -126,18 +137,10 @@ networks:
126137
# interface: ""
127138
```
128139

129-
The network daemons are started automatically when the first instance referencing them is started,
140+
The network daemon is started automatically when the first instance referencing them is started,
130141
and will stop automatically once the last instance has stopped. Daemon logs will be stored in the
131142
`$LIMA_HOME/_networks` directory.
132143

133-
Since the commands to start and stop the `socket_vmnet` daemon (or the `vde_vmnet` daemon) requires root, the user either must
134-
have password-less `sudo` enabled, or add the required commands to a `sudoers` file. This can
135-
be done via:
136-
137-
```shell
138-
limactl sudoers | sudo tee /etc/sudoers.d/lima
139-
```
140-
141144
#### Unmanaged
142145
For Lima >= 0.12:
143146
```yaml

examples/vmnet.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
# Example to enable vmnet.framework for QEMU.
22
# VZ users should refer to experimental/vz.yaml
33

4+
# Usage:
5+
# brew install socket_vmnet
6+
# limactl sudoers >etc_sudoers.d_lima
7+
# sudo mv etc_sudoers.d_lima /etc/sudoers.d/lima
8+
# limactl start template://vmnet
9+
410
# This example requires Lima v0.7.0 or later.
511
# Older versions of Lima were using a different syntax for supporting vmnet.framework.
612
images:

pkg/networks/config.go

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,62 @@ import (
55
"errors"
66
"fmt"
77
"os"
8+
"os/exec"
89
"path/filepath"
910
"runtime"
1011
"sync"
1112

1213
"github.com/goccy/go-yaml"
1314
"github.com/lima-vm/lima/pkg/store/dirnames"
1415
"github.com/lima-vm/lima/pkg/store/filenames"
16+
"github.com/lima-vm/lima/pkg/textutil"
17+
"github.com/sirupsen/logrus"
1518
)
1619

17-
//go:embed networks.yaml
18-
var defaultConfig []byte
20+
//go:embed networks.TEMPLATE.yaml
21+
var defaultConfigTemplate string
22+
23+
type defaultConfigTemplateArgs struct {
24+
SocketVMNet string // "/opt/socket_vmnet/bin/socket_vmnet"
25+
}
26+
27+
func defaultConfigBytes() ([]byte, error) {
28+
args := defaultConfigTemplateArgs{
29+
SocketVMNet: "/opt/socket_vmnet/bin/socket_vmnet", // the hard-code path before v0.14
30+
}
31+
candidates := []string{
32+
"socket_vmnet",
33+
"/usr/local/opt/socket_vmnet/bin/socket_vmnet", // Homebrew (Intel)
34+
"/opt/homebrew/opt/socket_vmnet/bin/socket_vmnet", // Homebrew (Intel)
35+
}
36+
for _, candidate := range candidates {
37+
if p, err := exec.LookPath(candidate); err == nil {
38+
realP, evalErr := filepath.EvalSymlinks(p)
39+
if evalErr != nil {
40+
return nil, evalErr
41+
}
42+
args.SocketVMNet = realP
43+
break
44+
} else if errors.Is(err, os.ErrNotExist) {
45+
logrus.WithError(err).Debugf("Failed to look up socket_vmnet path %q", candidate)
46+
} else {
47+
logrus.WithError(err).Warnf("Failed to look up socket_vmnet path %q", candidate)
48+
}
49+
}
50+
return textutil.ExecuteTemplate(defaultConfigTemplate, args)
51+
}
1952

2053
func DefaultConfig() (YAML, error) {
2154
var config YAML
22-
err := yaml.UnmarshalWithOptions(defaultConfig, &config, yaml.Strict())
23-
return config, err
55+
defaultConfig, err := defaultConfigBytes()
56+
if err != nil {
57+
return config, err
58+
}
59+
err = yaml.UnmarshalWithOptions(defaultConfig, &config, yaml.Strict())
60+
if err != nil {
61+
return config, err
62+
}
63+
return config, nil
2464
}
2565

2666
var cache struct {
@@ -56,6 +96,11 @@ func loadCache() {
5696
cache.err = fmt.Errorf("could not create %q directory: %w", configDir, cache.err)
5797
return
5898
}
99+
var defaultConfig []byte
100+
defaultConfig, cache.err = defaultConfigBytes()
101+
if cache.err != nil {
102+
return
103+
}
59104
cache.err = os.WriteFile(configFile, defaultConfig, 0644)
60105
if cache.err != nil {
61106
return

pkg/networks/networks.yaml renamed to pkg/networks/networks.TEMPLATE.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
paths:
1313
# socketVMNet requires Lima >= 0.12 .
1414
# socketVMNet has precedence over vdeVMNet.
15-
socketVMNet: /opt/socket_vmnet/bin/socket_vmnet
15+
socketVMNet: "{{.SocketVMNet}}"
1616
# vdeSwitch and vdeVMNet are DEPRECATED.
1717
vdeSwitch: /opt/vde/bin/vde_switch
1818
vdeVMNet: /opt/vde/bin/vde_vmnet

pkg/networks/sudoers.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,9 @@ func (config *YAML) VerifySudoAccess(sudoersFile string) error {
104104
return err
105105
}
106106
if string(b) != sudoers {
107-
return fmt.Errorf("sudoers file %q is out of sync and must be regenerated", sudoersFile)
107+
// Happens on upgrading socket_vmnet with Homebrew
108+
return fmt.Errorf("sudoers file %q is out of sync and must be regenerated (Hint: run `%s sudoers >etc_sudoers.d_lima && sudo mv etc_sudoers.d_lima %q`)",
109+
sudoersFile, os.Args[0], sudoersFile)
108110
}
109111
return nil
110112
}

pkg/networks/validate.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"strings"
1111

1212
"github.com/lima-vm/lima/pkg/osutil"
13+
"github.com/sirupsen/logrus"
1314
)
1415

1516
func (config *YAML) Validate() error {
@@ -103,7 +104,9 @@ func validatePath(path string, allowDaemonGroupWritable bool) error {
103104
return err
104105
}
105106
if stat.Uid != root.Uid {
106-
return fmt.Errorf(`%s %q is not owned by %q (uid: %d), but by uid %d`, file, path, root.User, root.Uid, stat.Uid)
107+
// Not ideal, but very typical on Homebrew set up
108+
logrus.Warnf(`%s %q is not owned by %q (uid: %d), but by uid %d (Hint: No action is necessary if uid %d is an admin, but beware that uid %d can take the root)`,
109+
file, path, root.User, root.Uid, stat.Uid, stat.Uid, stat.Uid)
107110
}
108111
if allowDaemonGroupWritable {
109112
daemon, err := osutil.LookupUser("daemon")
@@ -118,7 +121,8 @@ func validatePath(path string, allowDaemonGroupWritable bool) error {
118121
return fmt.Errorf(`%s %q is not executable by the %q (gid: %d)" group`, file, path, daemon.User, daemon.Gid)
119122
}
120123
} else if fi.Mode()&020 != 0 && stat.Gid != root.Gid {
121-
return fmt.Errorf(`%s %q is group-writable and group is not %q (gid: %d), but is gid: %d`,
124+
// Not ideal, but very typical on Homebrew set up
125+
logrus.Warnf(`%s %q is group-writable and group is not %q (gid: %d), but is gid: %d`,
122126
file, path, root.User, root.Gid, stat.Gid)
123127
}
124128
if fi.Mode()&002 != 0 {

0 commit comments

Comments
 (0)