Skip to content

Commit c96f16b

Browse files
committed
Don't use hard-coded user/group names and ids
Signed-off-by: Jan Dubois <[email protected]>
1 parent e3dd4e2 commit c96f16b

File tree

5 files changed

+119
-37
lines changed

5 files changed

+119
-37
lines changed

pkg/networks/commands.go

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package networks
22

33
import (
44
"fmt"
5+
"github.com/lima-vm/lima/pkg/osutil"
56
"github.com/lima-vm/lima/pkg/store/dirnames"
67
)
78

@@ -33,24 +34,21 @@ func (config *NetworksConfig) LogFile(name, daemon, stream string) string {
3334
return fmt.Sprintf("%s/%s_%s.%s.log", networksDir, name, daemon, stream)
3435
}
3536

36-
func (config *NetworksConfig) DaemonUser(daemon string) string {
37+
func (config *NetworksConfig) User(daemon string) (osutil.User, error) {
3738
switch daemon {
3839
case Switch:
39-
return "daemon"
40-
case VMNet:
41-
return "root"
42-
}
43-
panic("daemonuser")
44-
}
45-
46-
func (config *NetworksConfig) DaemonGroup(daemon string) string {
47-
switch daemon {
48-
case Switch:
49-
return config.Group
40+
user, err := osutil.LookupUser("daemon")
41+
if err != nil {
42+
return user, err
43+
}
44+
group, err := osutil.LookupGroup(config.Group)
45+
user.Group = group.Name
46+
user.Gid = group.Gid
47+
return user, err
5048
case VMNet:
51-
return "wheel"
49+
return osutil.LookupUser("root")
5250
}
53-
panic("daemongroup")
51+
return osutil.User{}, fmt.Errorf("daemon %q not defined", daemon)
5452
}
5553

5654
func (config *NetworksConfig) MkdirCmd() string {

pkg/networks/reconcile/reconcile.go

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"time"
1414

1515
"github.com/lima-vm/lima/pkg/networks"
16+
"github.com/lima-vm/lima/pkg/osutil"
1617
"github.com/lima-vm/lima/pkg/store"
1718
"github.com/lima-vm/lima/pkg/store/dirnames"
1819
"github.com/sirupsen/logrus"
@@ -98,9 +99,13 @@ func makeVarRun(config *networks.NetworksConfig) error {
9899
// should never happen
99100
return fmt.Errorf("could not retrieve stat buffer for %q", config.Paths.VarRun)
100101
}
101-
if fi.Mode()&020 == 0 || stat.Gid != networks.DaemonGID {
102+
daemon, err := osutil.LookupUser("daemon")
103+
if err != nil {
104+
return err
105+
}
106+
if fi.Mode()&020 == 0 || stat.Gid != daemon.Gid {
102107
return fmt.Errorf("%q doesn't seem to be writable by the daemon (gid:%d) group",
103-
config.Paths.VarRun, networks.DaemonGID)
108+
config.Paths.VarRun, daemon.Gid)
104109
}
105110
return nil
106111
}
@@ -116,8 +121,12 @@ func startDaemon(config *networks.NetworksConfig, ctx context.Context, name, dae
116121
if err := os.MkdirAll(networksDir, 0755); err != nil {
117122
return err
118123
}
124+
user, err := config.User(daemon)
125+
if err != nil {
126+
return err
127+
}
119128

120-
args := []string{"--user", config.DaemonUser(daemon), "--group", config.DaemonGroup(daemon), "--non-interactive"}
129+
args := []string{"--user", user.User, "--group", user.Group, "--non-interactive"}
121130
args = append(args, strings.Split(config.StartCmd(name, daemon), " ")...)
122131
cmd := exec.CommandContext(ctx, "sudo", args...)
123132
// set directory to a path the daemon user has read access to because vde_switch calls getcwd() which
@@ -193,7 +202,11 @@ func stopNetwork(config *networks.NetworksConfig, name string) error {
193202
if err := validateConfig(config); err != nil {
194203
return err
195204
}
196-
err := sudo(config.DaemonUser(daemon), config.DaemonGroup(daemon), config.StopCmd(name, daemon))
205+
user, err := config.User(daemon)
206+
if err != nil {
207+
return err
208+
}
209+
err = sudo(user.User, user.Group, config.StopCmd(name, daemon))
197210
if err != nil {
198211
return err
199212
}

pkg/networks/sudoers.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,13 @@ func Sudoers() (string, error) {
3131
sb.WriteRune('\n')
3232
sb.WriteString(fmt.Sprintf("# Manage %q network daemons\n", name))
3333
for _, daemon := range []string{Switch, VMNet} {
34+
user, err := config.User(daemon)
35+
if err != nil {
36+
return "", err
37+
}
3438
sb.WriteRune('\n')
3539
sb.WriteString(fmt.Sprintf("%%%s ALL=(%s:%s) NOPASSWD:NOSETENV: \\\n",
36-
config.Group, config.DaemonUser(daemon), config.DaemonGroup(daemon)))
40+
config.Group, user.User, user.Group))
3741
sb.WriteString(fmt.Sprintf(" %s, \\\n", config.StartCmd(name, daemon)))
3842
sb.WriteString(fmt.Sprintf(" %s\n", config.StopCmd(name, daemon)))
3943
}
@@ -50,8 +54,11 @@ func (config *NetworksConfig) passwordLessSudo() error {
5054
// Verify that user/groups for both daemons work without a password, e.g.
5155
// %admin ALL = (ALL:ALL) NOPASSWD: ALL
5256
for _, daemon := range []string{Switch, VMNet} {
53-
cmd = exec.Command("sudo", "--user", config.DaemonUser(daemon),
54-
"--group", config.DaemonGroup(daemon), "--non-interactive", "true")
57+
user, err := config.User(daemon)
58+
if err != nil {
59+
return err
60+
}
61+
cmd = exec.Command("sudo", "--user", user.User, "--group", user.Group, "--non-interactive", "true")
5562
if err := cmd.Run(); err != nil {
5663
return fmt.Errorf("failed to run %v: %w", cmd.Args, err)
5764
}

pkg/networks/validate.go

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import (
99
"reflect"
1010
"strings"
1111
"syscall"
12+
13+
"github.com/lima-vm/lima/pkg/osutil"
1214
)
1315

1416
func (config *NetworksConfig) Validate() error {
@@ -48,12 +50,6 @@ func findBaseDirectory(path string) string {
4850
return path
4951
}
5052

51-
const (
52-
RootUID = 0
53-
WheelGID = 0
54-
DaemonGID = 1
55-
)
56-
5753
func validatePath(path string, allowDaemonGroupWritable bool) error {
5854
if path == "" {
5955
return nil
@@ -82,20 +78,28 @@ func validatePath(path string, allowDaemonGroupWritable bool) error {
8278
// should never happen
8379
return fmt.Errorf("could not retrieve stat buffer for %q", path)
8480
}
85-
if stat.Uid != RootUID {
86-
return fmt.Errorf(`%s %q is not owned by root (uid: %d), but by uid %d`, file, path, RootUID, stat.Uid)
81+
root, err := osutil.LookupUser("root")
82+
if err != nil {
83+
return err
84+
}
85+
if stat.Uid != root.Uid {
86+
return fmt.Errorf(`%s %q is not owned by %q (uid: %d), but by uid %d`, file, path, root.User, root.Uid, stat.Uid)
8787
}
8888
if allowDaemonGroupWritable {
89-
if fi.Mode()&020 != 0 && stat.Gid != WheelGID && stat.Gid != DaemonGID {
90-
return fmt.Errorf(`%s %q is group-writable and group is neither "wheel" (gid: %d) nor "daemon" (guid: %d), but is gid: %d`,
91-
file, path, WheelGID, DaemonGID, stat.Gid)
89+
daemon, err := osutil.LookupUser("daemon")
90+
if err != nil {
91+
return err
92+
}
93+
if fi.Mode()&020 != 0 && stat.Gid != root.Gid && stat.Gid != daemon.Gid {
94+
return fmt.Errorf(`%s %q is group-writable and group is neither %q (gid: %d) nor %q (gid: %d), but is gid: %d`,
95+
file, path, root.User, root.Gid, daemon.User, daemon.Gid, stat.Gid)
9296
}
93-
if fi.Mode().IsDir() && fi.Mode()&1 == 0 && (fi.Mode()&0010 == 0 || stat.Gid != DaemonGID) {
94-
return fmt.Errorf(`%s %q is not executable by the "daemon" (gid: %d)" group`, file, path, DaemonGID)
97+
if fi.Mode().IsDir() && fi.Mode()&1 == 0 && (fi.Mode()&0010 == 0 || stat.Gid != daemon.Gid) {
98+
return fmt.Errorf(`%s %q is not executable by the %q (gid: %d)" group`, file, path, daemon.User, daemon.Gid)
9599
}
96-
} else if fi.Mode()&020 != 0 && stat.Gid != WheelGID {
97-
return fmt.Errorf(`%s %q is group-writable and group is not "wheel" (gid: %d), but is gid: %d`,
98-
file, path, WheelGID, stat.Gid)
100+
} else if fi.Mode()&020 != 0 && stat.Gid != root.Gid {
101+
return fmt.Errorf(`%s %q is group-writable and group is not %q (gid: %d), but is gid: %d`,
102+
file, path, root.User, root.Gid, stat.Gid)
99103
}
100104
if fi.Mode()&002 != 0 {
101105
return fmt.Errorf("%s %q is world-writable", file, path)

pkg/osutil/user.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,69 @@ import (
55
"github.com/sirupsen/logrus"
66
"os/user"
77
"regexp"
8+
"strconv"
89
"sync"
910
)
1011

12+
type User struct {
13+
User string
14+
Uid uint32
15+
Group string
16+
Gid uint32
17+
}
18+
19+
type Group struct {
20+
Name string
21+
Gid uint32
22+
}
23+
24+
var users map[string]User
25+
var groups map[string]Group
26+
27+
func LookupUser(name string) (User, error) {
28+
if users == nil {
29+
users = make(map[string]User)
30+
}
31+
if _, ok := users[name]; !ok {
32+
u, err := user.Lookup(name)
33+
if err != nil {
34+
return User{}, err
35+
}
36+
g, err := user.LookupGroupId(u.Gid)
37+
if err != nil {
38+
return User{}, err
39+
}
40+
uid, err := strconv.ParseUint(u.Uid, 10, 32)
41+
if err != nil {
42+
return User{}, err
43+
}
44+
gid, err := strconv.ParseUint(u.Gid, 10, 32)
45+
if err != nil {
46+
return User{}, err
47+
}
48+
users[name] = User{User: u.Username, Uid: uint32(uid), Group: g.Name, Gid: uint32(gid)}
49+
}
50+
return users[name], nil
51+
}
52+
53+
func LookupGroup(name string) (Group, error) {
54+
if groups == nil {
55+
groups = make(map[string]Group)
56+
}
57+
if _, ok := groups[name]; !ok {
58+
g, err := user.LookupGroup(name)
59+
if err != nil {
60+
return Group{}, err
61+
}
62+
gid, err := strconv.ParseUint(g.Gid, 10, 32)
63+
if err != nil {
64+
return Group{}, err
65+
}
66+
groups[name] = Group{Name: g.Name, Gid: uint32(gid)}
67+
}
68+
return groups[name], nil
69+
}
70+
1171
const (
1272
fallbackUser = "lima"
1373
)

0 commit comments

Comments
 (0)