Skip to content

Commit e1d81d2

Browse files
committed
Only validate networks.yaml when a managed network is used
This allows running lima with the default networks.yaml even when the vde daemons are not installed. Similarly, don't throw an error when the sudoers file does not exist as long as password-less sudo seems to work. Signed-off-by: Jan Dubois <[email protected]>
1 parent 5799a2a commit e1d81d2

File tree

7 files changed

+69
-19
lines changed

7 files changed

+69
-19
lines changed

cmd/limactl/sudoers.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,17 @@ func sudoersAction(cmd *cobra.Command, args []string) error {
4646
default:
4747
return errors.New("can check only a single sudoers file")
4848
}
49-
if err := networks.CheckSudoers(file); err != nil {
49+
config, err := networks.Config()
50+
if err != nil {
5051
return err
5152
}
52-
fmt.Printf("%q is up-to-date\n", file)
53+
if err := config.Validate(); err != nil {
54+
return err
55+
}
56+
if err := config.VerifySudoAccess(file); err != nil {
57+
return err
58+
}
59+
fmt.Printf("%q is up-to-date (or sudo doesn't require a password)\n", file)
5360
return nil
5461
}
5562
sudoers, err := networks.Sudoers()

pkg/limayaml/validate.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package limayaml
22

33
import (
44
"fmt"
5-
"github.com/lima-vm/lima/pkg/networks"
65
"net"
76
"os"
87
"path/filepath"
@@ -13,6 +12,7 @@ import (
1312

1413
"github.com/docker/go-units"
1514
"github.com/lima-vm/lima/pkg/localpathutil"
15+
"github.com/lima-vm/lima/pkg/networks"
1616
"github.com/lima-vm/lima/pkg/osutil"
1717
"github.com/lima-vm/lima/pkg/qemu/const"
1818
"github.com/sirupsen/logrus"

pkg/networks/config.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ import (
44
_ "embed"
55
"errors"
66
"fmt"
7-
"gopkg.in/yaml.v2"
87
"os"
98
"path/filepath"
109
"runtime"
1110
"sync"
1211

1312
"github.com/lima-vm/lima/pkg/store/dirnames"
1413
"github.com/lima-vm/lima/pkg/store/filenames"
14+
"gopkg.in/yaml.v2"
1515
)
1616

1717
//go:embed networks.yaml
@@ -56,7 +56,6 @@ func load() {
5656
if cache.err != nil {
5757
cache.err = fmt.Errorf("cannot parse %q: %w", configFile, cache.err)
5858
}
59-
cache.err = cache.config.validate()
6059
})
6160
}
6261

pkg/networks/networks.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ paths:
99
vdeSwitch: /opt/vde/bin/vde_switch
1010
vdeVMNet: /opt/vde/bin/vde_vmnet
1111
varRun: /private/var/run/lima
12-
sudoers: /etc/sudoers.d/lima
12+
sudoers: /private/etc/sudoers.d/lima
1313

1414
group: staff
1515

pkg/networks/reconcile/reconcile.go

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -122,29 +122,31 @@ func startDaemon(config *networks.NetworksConfig, ctx context.Context, name, dae
122122
return nil
123123
}
124124

125-
var sudoersCheck struct {
125+
var validation struct {
126126
sync.Once
127127
err error
128128
}
129129

130-
func checkSudoers(config *networks.NetworksConfig) error {
131-
sudoersCheck.Do(func() {
132-
if config.Paths.Sudoers != "" {
133-
sudoersCheck.err = networks.CheckSudoers(config.Paths.Sudoers)
130+
func validateConfig(config *networks.NetworksConfig) error {
131+
validation.Do(func() {
132+
// make sure all config.Paths.* are secure
133+
validation.err = config.Validate()
134+
if validation.err == nil {
135+
validation.err = config.VerifySudoAccess(config.Paths.Sudoers)
134136
}
135137
})
136-
return sudoersCheck.err
138+
return validation.err
137139
}
138140

139141
func startNetwork(config *networks.NetworksConfig, ctx context.Context, name string) error {
140142
logrus.Debugf("Make sure %q network is running", name)
143+
if err := validateConfig(config); err != nil {
144+
return err
145+
}
141146
for _, daemon := range []string{networks.Switch, networks.VMNet} {
142147
pid, _ := store.ReadPIDFile(config.PIDFile(name, daemon))
143148
if pid == 0 {
144149
logrus.Infof("Starting %s daemon for %q network", daemon, name)
145-
if err := checkSudoers(config); err != nil {
146-
return err
147-
}
148150
if err := startDaemon(config, ctx, name, daemon); err != nil {
149151
return err
150152
}
@@ -155,11 +157,13 @@ func startNetwork(config *networks.NetworksConfig, ctx context.Context, name str
155157

156158
func stopNetwork(config *networks.NetworksConfig, name string) error {
157159
logrus.Debugf("Make sure %q network is stopped", name)
160+
// Don't call validateConfig() until we actually need to stop a daemon because
161+
// stopNetwork() may be called even when the vde daemons are not installed.
158162
for _, daemon := range []string{networks.VMNet, networks.Switch} {
159163
pid, _ := store.ReadPIDFile(config.PIDFile(name, daemon))
160164
if pid != 0 {
161165
logrus.Infof("Stopping %s daemon for %q network", daemon, name)
162-
if err := checkSudoers(config); err != nil {
166+
if err := validateConfig(config); err != nil {
163167
return err
164168
}
165169
err := sudo(config.DaemonUser(daemon), config.DaemonGroup(daemon), config.StopCmd(name, daemon))

pkg/networks/sudoers.go

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
package networks
22

33
import (
4+
"errors"
45
"fmt"
56
"os"
7+
"os/exec"
68
"sort"
79
"strings"
10+
11+
"github.com/sirupsen/logrus"
812
)
913

1014
func Sudoers() (string, error) {
@@ -38,9 +42,45 @@ func Sudoers() (string, error) {
3842
return sb.String(), nil
3943
}
4044

41-
func CheckSudoers(sudoersFile string) error {
45+
func (config *NetworksConfig) passwordLessSudo() error {
46+
// Flush cached sudo password
47+
cmd := exec.Command("sudo", "-k")
48+
if err := cmd.Run(); err != nil {
49+
return fmt.Errorf("failed to run %v: %w", cmd.Args, err)
50+
}
51+
// Verify that user/groups for both daemons work without a password, e.g.
52+
// %admin ALL = (ALL:ALL) NOPASSWD: ALL
53+
for _, daemon := range []string{Switch, VMNet} {
54+
cmd = exec.Command("sudo", "--user", config.DaemonUser(daemon),
55+
"--group", config.DaemonGroup(daemon), "--non-interactive", "true")
56+
if err := cmd.Run(); err != nil {
57+
return fmt.Errorf("failed to run %v: %w", cmd.Args, err)
58+
}
59+
}
60+
return nil
61+
}
62+
63+
func (config *NetworksConfig) VerifySudoAccess(sudoersFile string) error {
64+
if sudoersFile == "" {
65+
if err := config.passwordLessSudo(); err == nil {
66+
logrus.Debug("sudo doesn't seem to require a password")
67+
return nil
68+
} else {
69+
return fmt.Errorf("passwordLessSudo error: %w", err)
70+
}
71+
}
4272
b, err := os.ReadFile(sudoersFile)
4373
if err != nil {
74+
// Default networks.yaml specifies /etc/sudoers.d/lima file. Don't throw an error when the
75+
// file doesn't exist, as long as password-less sudo still works.
76+
if errors.Is(err, os.ErrNotExist) {
77+
if err := config.passwordLessSudo(); err == nil {
78+
logrus.Debugf("%q does not exist, but sudo doesn't seem to require a password", sudoersFile)
79+
return nil
80+
} else {
81+
logrus.Debugf("%q does not exist; passwordLessSudo error: %s", sudoersFile, err)
82+
}
83+
}
4484
return fmt.Errorf("can't read %q: %s", sudoersFile, err)
4585
}
4686
sudoers, err := Sudoers()

pkg/networks/validate.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
"syscall"
1212
)
1313

14-
func (config *NetworksConfig) validate() error {
14+
func (config *NetworksConfig) Validate() error {
1515
// validate all paths.* values
1616
paths := reflect.ValueOf(&config.Paths).Elem()
1717
for i := 0; i < paths.NumField(); i++ {
@@ -31,7 +31,7 @@ func (config *NetworksConfig) validate() error {
3131
if name == "sudoers" && errors.Is(err, os.ErrNotExist) {
3232
continue
3333
}
34-
return fmt.Errorf("network.yaml field `paths.%s` error: %w", name, err)
34+
return fmt.Errorf("networks.yaml field `paths.%s` error: %w", name, err)
3535
}
3636
}
3737
// TODO: validate network definitions

0 commit comments

Comments
 (0)