Skip to content

Commit 4554a35

Browse files
authored
Merge pull request #239 from rancher-sandbox/networks
Implement vde_vmnet management
2 parents 8e6bbee + 50fa836 commit 4554a35

File tree

31 files changed

+1312
-192
lines changed

31 files changed

+1312
-192
lines changed

.github/workflows/test.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,9 @@ jobs:
146146
make PREFIX=/opt/vde
147147
sudo make PREFIX=/opt/vde install
148148
)
149+
(
150+
limactl sudoers | sudo tee /etc/sudoers.d/lima
151+
)
149152
- name: Prepare ssh
150153
run: |
151154
if [ -e ~/.ssh/id_rsa ]; then

cmd/limactl/delete.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"os"
77

8+
"github.com/lima-vm/lima/pkg/networks/reconcile"
89
"github.com/lima-vm/lima/pkg/store"
910
"github.com/sirupsen/logrus"
1011
"github.com/spf13/cobra"
@@ -42,7 +43,7 @@ func deleteAction(cmd *cobra.Command, args []string) error {
4243
}
4344
logrus.Infof("Deleted %q (%q)", instName, inst.Dir)
4445
}
45-
return nil
46+
return networks.Reconcile(cmd.Context(), "")
4647
}
4748

4849
func deleteInstance(inst *store.Instance, force bool) error {

cmd/limactl/main.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"os"
66
"strings"
77

8+
"github.com/lima-vm/lima/pkg/store/dirnames"
89
"github.com/lima-vm/lima/pkg/version"
910
"github.com/sirupsen/logrus"
1011
"github.com/spf13/cobra"
@@ -38,6 +39,11 @@ func newApp() *cobra.Command {
3839
if os.Geteuid() == 0 {
3940
return errors.New("must not run as the root")
4041
}
42+
// Make sure either $HOME or $LIMA_HOME is defined, so we don't need
43+
// to check for errors later
44+
if _, err := dirnames.LimaDir(); err != nil {
45+
return err
46+
}
4147
return nil
4248
}
4349
rootCmd.AddCommand(
@@ -48,6 +54,7 @@ func newApp() *cobra.Command {
4854
newListCommand(),
4955
newDeleteCommand(),
5056
newValidateCommand(),
57+
newSudoersCommand(),
5158
newPruneCommand(),
5259
newHostagentCommand(),
5360
)

cmd/limactl/start.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/AlecAivazis/survey/v2"
1313
"github.com/containerd/containerd/identifiers"
1414
"github.com/lima-vm/lima/pkg/limayaml"
15+
"github.com/lima-vm/lima/pkg/networks/reconcile"
1516
"github.com/lima-vm/lima/pkg/osutil"
1617
"github.com/lima-vm/lima/pkg/start"
1718
"github.com/lima-vm/lima/pkg/store"
@@ -119,7 +120,7 @@ func loadOrCreateInstance(cmd *cobra.Command, args []string) (*store.Instance, e
119120
if err != nil {
120121
return nil, err
121122
}
122-
if err := limayaml.Validate(*y); err != nil {
123+
if err := limayaml.Validate(*y, true); err != nil {
123124
if !tty {
124125
return nil, err
125126
}
@@ -216,6 +217,9 @@ func startAction(cmd *cobra.Command, args []string) error {
216217
if err != nil {
217218
return err
218219
}
220+
if len(inst.Errors) > 0 {
221+
return fmt.Errorf("errors inspecting instance: %+v", inst.Errors)
222+
}
219223
switch inst.Status {
220224
case store.StatusRunning:
221225
logrus.Infof("The instance %q is already running. Run `%s` to open the shell.",
@@ -228,6 +232,10 @@ func startAction(cmd *cobra.Command, args []string) error {
228232
logrus.Warnf("expected status %q, got %q", store.StatusStopped, inst.Status)
229233
}
230234
ctx := cmd.Context()
235+
err = networks.Reconcile(ctx, inst.Name)
236+
if err != nil {
237+
return err
238+
}
231239
return start.Start(ctx, inst)
232240
}
233241

cmd/limactl/stop.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"time"
1212

1313
hostagentapi "github.com/lima-vm/lima/pkg/hostagent/api"
14+
"github.com/lima-vm/lima/pkg/networks/reconcile"
1415
"github.com/lima-vm/lima/pkg/store"
1516
"github.com/lima-vm/lima/pkg/store/filenames"
1617
"github.com/sirupsen/logrus"
@@ -47,10 +48,14 @@ func stopAction(cmd *cobra.Command, args []string) error {
4748
}
4849
if force {
4950
stopInstanceForcibly(inst)
50-
return nil
51+
} else {
52+
err = stopInstanceGracefully(inst)
5153
}
52-
53-
return stopInstanceGracefully(inst)
54+
// TODO: should we also reconcile networks if graceful stop returned an error?
55+
if err == nil {
56+
err = networks.Reconcile(cmd.Context(), "")
57+
}
58+
return err
5459
}
5560

5661
func stopInstanceGracefully(inst *store.Instance) error {

cmd/limactl/sudoers.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package main
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"runtime"
7+
8+
"github.com/lima-vm/lima/pkg/networks"
9+
"github.com/spf13/cobra"
10+
)
11+
12+
func newSudoersCommand() *cobra.Command {
13+
sudoersCommand := &cobra.Command{
14+
Use: "sudoers [SUDOERSFILE]",
15+
Short: "Generate /etc/sudoers.d/lima file.",
16+
Args: cobra.MaximumNArgs(1),
17+
RunE: sudoersAction,
18+
}
19+
configFile, _ := networks.ConfigFile()
20+
sudoersCommand.Flags().Bool("check", false,
21+
fmt.Sprintf("check that the sudoers file is up-to-date with %q", configFile))
22+
return sudoersCommand
23+
}
24+
25+
func sudoersAction(cmd *cobra.Command, args []string) error {
26+
if runtime.GOOS != "darwin" {
27+
return errors.New("sudoers command is only supported on macOS right now")
28+
}
29+
check, err := cmd.Flags().GetBool("check")
30+
if err != nil {
31+
return err
32+
}
33+
if check {
34+
return verifySudoAccess(args)
35+
}
36+
sudoers, err := networks.Sudoers()
37+
if err != nil {
38+
return err
39+
}
40+
fmt.Print(sudoers)
41+
return nil
42+
}
43+
44+
func verifySudoAccess(args []string) error {
45+
config, err := networks.Config()
46+
if err != nil {
47+
return err
48+
}
49+
if err := config.Validate(); err != nil {
50+
return err
51+
}
52+
var file string
53+
switch len(args) {
54+
case 0:
55+
file = config.Paths.Sudoers
56+
if file == "" {
57+
configFile, _ := networks.ConfigFile()
58+
return fmt.Errorf("no sudoers file defined in %q", configFile)
59+
}
60+
case 1:
61+
file = args[0]
62+
default:
63+
return errors.New("can check only a single sudoers file")
64+
}
65+
if err := config.VerifySudoAccess(file); err != nil {
66+
return err
67+
}
68+
fmt.Printf("%q is up-to-date (or sudo doesn't require a password)\n", file)
69+
return nil
70+
}

docs/network.md

Lines changed: 82 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,89 @@ the host and other guests.
2828
To enable `vde_vmnet` (in addition the user-mode network), add the following lines to the YAML after installing `vde_vmnet`.
2929

3030
```yaml
31-
network:
32-
# The instance can get routable IP addresses from the vmnet framework using
33-
# https://github.com/lima-vm/vde_vmnet. Both vde_switch and vde_vmnet
34-
# daemons must be running before the instance is started. The interface type
35-
# (host, shared, or bridged) is configured in vde_vmnet and not lima.
36-
vde:
37-
# vnl (virtual network locator) points to the vde_switch socket directory,
38-
# optionally with vde:// prefix
39-
- vnl: "vde:///var/run/vde.ctl"
40-
# MAC address of the instance; lima will pick one based on the instance name,
41-
# so DHCP assigned ip addresses should remain constant over instance restarts.
42-
macAddress: ""
43-
# Interface name, defaults to "vde0", "vde1", etc.
44-
name: ""
31+
networks:
32+
# vnl (virtual network locator) points to the vde_switch socket directory,
33+
# optionally with vde:// prefix
34+
# - vnl: "vde:///var/run/vde.ctl"
35+
# # VDE Switch port number (not TCP/UDP port number). Set to 65535 for PTP mode.
36+
# # Default: 0
37+
# switchPort: 0
38+
# # MAC address of the instance; lima will pick one based on the instance name,
39+
# # so DHCP assigned ip addresses should remain constant over instance restarts.
40+
# macAddress: ""
41+
# # Interface name, defaults to "lima0", "lima1", etc.
42+
# interface: ""
4543
```
4644

4745
The IP address range is typically `192.168.105.0/24`, but depends on the configuration of `vde_vmnet`.
4846
See [the documentation of `vde_vmnet`](https://github.com/lima-vm/vde_vmnet) for further information.
47+
48+
## Managed VMNet networks (via vde_vmnet)
49+
50+
Starting with version v0.7.0 lima can manage the networking daemons automatically. Networks are defined in
51+
`$LIMA_HOME/_config/networks.yaml`. If this file doesn't already exist, it will be created with these default
52+
settings:
53+
54+
```yaml
55+
# Paths to vde executables. Because vde_vmnet is invoked via sudo it should be
56+
# installed where only root can modify/replace it. This means also none of the
57+
# parent directories should be writable by the user.
58+
#
59+
# The varRun directory also must not be writable by the user because it will
60+
# include the vde_vmnet pid files. Those will be terminated via sudo, so replacing
61+
# the pid files would allow killing of arbitrary privileged processes. varRun
62+
# however MUST be writable by the daemon user.
63+
#
64+
# None of the paths segments may be symlinks, why it has to be /private/var
65+
# instead of /var etc.
66+
paths:
67+
vdeSwitch: /opt/vde/bin/vde_switch
68+
vdeVMNet: /opt/vde/bin/vde_vmnet
69+
varRun: /private/var/run/lima
70+
sudoers: /private/etc/sudoers.d/lima
71+
72+
group: staff
73+
74+
networks:
75+
shared:
76+
mode: shared
77+
gateway: 192.168.105.1
78+
dhcpEnd: 192.168.105.254
79+
netmask: 255.255.255.0
80+
bridged:
81+
mode: bridged
82+
interface: en0
83+
# bridged mode doesn't have a gateway; dhcp is managed by outside network
84+
host:
85+
mode: host
86+
gateway: 192.168.106.1
87+
dhcpEnd: 192.168.106.254
88+
netmask: 255.255.255.0
89+
```
90+
91+
Instances can then reference these networks from their `lima.yaml` file:
92+
93+
```yaml
94+
networks:
95+
# Lima can manage daemons for networks defined in $LIMA_HOME/_config/networks.yaml
96+
# automatically. Both vde_switch and vde_vmnet binaries must be installed into
97+
# secure locations only alterable by the "root" user.
98+
# - lima: shared
99+
# # MAC address of the instance; lima will pick one based on the instance name,
100+
# # so DHCP assigned ip addresses should remain constant over instance restarts.
101+
# macAddress: ""
102+
# # Interface name, defaults to "lima0", "lima1", etc.
103+
# interface: ""
104+
```
105+
106+
The network daemons are started automatically when the first instance referencing them is started,
107+
and will stop automatically once the last instance has stopped. Daemon logs will be stored in the
108+
`$LIMA_HOME/_networks` directory.
109+
110+
Since the commands to start and stop the `vde_vmnet` daemon requires root, the user either must
111+
have password-less `sudo` enabled, or add the required commands to a `sudoers` file. This can
112+
be done via:
113+
114+
```shell
115+
limactl sudoers | sudo tee /etc/sudoers.d/lima
116+
```

examples/vmnet.yaml

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,9 @@ ssh:
1414
# (TODO: assign localPort automatically)
1515
localPort: 60105
1616

17-
network:
17+
networks:
1818
# The instance can get routable IP addresses from the vmnet framework using
19-
# https://github.com/lima-vm/vde_vmnet. Both vde_switch and vde_vmnet
20-
# daemons must be running before the instance is started. The interface type
21-
# (host, shared, or bridged) is configured in vde_vmnet and not lima.
22-
vde:
23-
- vnl: "vde:///var/run/vde.ctl"
19+
# https://github.com/lima-vm/vde_vmnet. Available networks are defined in
20+
# $LIMA_HOME/_config/networks.yaml. Supported network types are "host",
21+
# "shared", or "bridged".
22+
- lima: shared

pkg/cidata/cidata.TEMPLATE.d/network-config

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
version: 2
22
ethernets:
33
{{- range $nw := .Networks}}
4-
{{$nw.Name}}:
4+
{{$nw.Interface}}:
55
match:
66
macaddress: '{{$nw.MACAddress}}'
77
dhcp4: true
8-
set-name: {{$nw.Name}}
9-
{{- if and (eq $nw.Name $.SlirpNICName) (gt (len $.DNSAddresses) 0) }}
8+
set-name: {{$nw.Interface}}
9+
{{- if and (eq $nw.Interface $.SlirpNICName) (gt (len $.DNSAddresses) 0) }}
1010
nameservers:
1111
addresses:
1212
{{- range $ns := $.DNSAddresses }}

pkg/cidata/cidata.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
"github.com/lima-vm/lima/pkg/limayaml"
1717
"github.com/lima-vm/lima/pkg/localpathutil"
1818
"github.com/lima-vm/lima/pkg/osutil"
19-
"github.com/lima-vm/lima/pkg/qemu/qemuconst"
19+
"github.com/lima-vm/lima/pkg/qemu/const"
2020
"github.com/lima-vm/lima/pkg/sshutil"
2121
"github.com/lima-vm/lima/pkg/store/filenames"
2222
"github.com/opencontainers/go-digest"
@@ -35,7 +35,7 @@ var (
3535
)
3636

3737
func GenerateISO9660(instDir, name string, y *limayaml.LimaYAML) error {
38-
if err := limayaml.Validate(*y); err != nil {
38+
if err := limayaml.Validate(*y, false); err != nil {
3939
return err
4040
}
4141
u, err := osutil.LimaUser(true)
@@ -51,9 +51,9 @@ func GenerateISO9660(instDir, name string, y *limayaml.LimaYAML) error {
5151
User: u.Username,
5252
UID: uid,
5353
Containerd: Containerd{System: *y.Containerd.System, User: *y.Containerd.User},
54-
SlirpNICName: qemuconst.SlirpNICName,
55-
SlirpGateway: qemuconst.SlirpGateway,
56-
SlirpDNS: qemuconst.SlirpDNS,
54+
SlirpNICName: qemu.SlirpNICName,
55+
SlirpGateway: qemu.SlirpGateway,
56+
SlirpDNS: qemu.SlirpDNS,
5757
Env: y.Env,
5858
}
5959

@@ -77,9 +77,9 @@ func GenerateISO9660(instDir, name string, y *limayaml.LimaYAML) error {
7777
}
7878

7979
slirpMACAddress := limayaml.MACAddress(instDir)
80-
args.Networks = append(args.Networks, Network{MACAddress: slirpMACAddress, Name: qemuconst.SlirpNICName})
81-
for _, vde := range y.Network.VDE {
82-
args.Networks = append(args.Networks, Network{MACAddress: vde.MACAddress, Name: vde.Name})
80+
args.Networks = append(args.Networks, Network{MACAddress: slirpMACAddress, Interface: qemu.SlirpNICName})
81+
for _, nw := range y.Networks {
82+
args.Networks = append(args.Networks, Network{MACAddress: nw.MACAddress, Interface: nw.Interface})
8383
}
8484

8585
if len(y.DNS) > 0 {

0 commit comments

Comments
 (0)