forked from cloudfoundry-attic/garden-linux
-
Notifications
You must be signed in to change notification settings - Fork 0
/
prepare.go
131 lines (108 loc) · 3.22 KB
/
prepare.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package container_daemon
import (
"fmt"
"os/exec"
osuser "os/user"
"code.cloudfoundry.org/garden"
"code.cloudfoundry.org/garden-linux/process"
)
//go:generate counterfeiter -o fake_rlimits_env_encoder/fake_rlimits_env_encoder.go . RlimitsEnvEncoder
type RlimitsEnvEncoder interface {
EncodeLimits(garden.ResourceLimits) string
}
//go:generate counterfeiter -o fake_user/fake_user.go . User
type User interface {
Lookup(name string) (*osuser.User, error)
}
//go:generate counterfeiter -o fake_commander/fake_commander.go . Commander
type Commander interface {
Command(args ...string) *exec.Cmd
}
type CommandFunc func(args ...string) *exec.Cmd
func (fn CommandFunc) Command(args ...string) *exec.Cmd {
return fn(args...)
}
type ProcessSpecPreparer struct {
Users User
Rlimits RlimitsEnvEncoder
Reexec Commander
AlwaysDropCapabilities bool
}
func (p *ProcessSpecPreparer) PrepareCmd(spec garden.ProcessSpec) (*exec.Cmd, error) {
rlimitsEnv := p.Rlimits.EncodeLimits(spec.Limits)
dropCapsArg := fmt.Sprintf("-dropCapabilities=%t", p.AlwaysDropCapabilities || spec.User != "root")
extendedWhitelistArg := fmt.Sprintf("-extendedWhitelist=%t", !p.AlwaysDropCapabilities)
rlimitArg := fmt.Sprintf("-rlimits=%s", rlimitsEnv)
usr, err := p.parseUser(spec.User)
if err != nil {
return nil, fmt.Errorf("container_daemon: %s", err)
}
env, err := createEnvironment(spec.Env, usr)
if err != nil {
return nil, fmt.Errorf("container_daemon: %s", err)
}
dir := spec.Dir
if spec.Dir == "" {
dir = usr.homeDir
}
args := append([]string{
dropCapsArg,
extendedWhitelistArg,
rlimitArg,
fmt.Sprintf("-uid=%d", usr.uid),
fmt.Sprintf("-gid=%d", usr.gid),
fmt.Sprintf("-workDir=%s", dir),
}, "--", spec.Path)
args = append(args, spec.Args...)
cmd := p.Reexec.Command(append([]string{"proc_starter"}, args...)...)
cmd.Env = env.Array()
return cmd, nil
}
type parsedUser struct {
username string
uid uint32
gid uint32
homeDir string
}
func (p *ProcessSpecPreparer) parseUser(username string) (parsedUser, error) {
errs := func(err error) (parsedUser, error) {
return parsedUser{}, err
}
ret := parsedUser{
username: username,
}
if osUser, err := p.Users.Lookup(username); err == nil && osUser != nil {
if _, err := fmt.Sscanf(osUser.Uid, "%d", &(ret.uid)); err != nil {
return errs(fmt.Errorf("failed to parse uid %q", osUser.Uid))
}
if _, err := fmt.Sscanf(osUser.Gid, "%d", &(ret.gid)); err != nil {
return errs(fmt.Errorf("failed to parse gid %q", osUser.Gid))
}
ret.homeDir = osUser.HomeDir
return ret, nil
} else if err == nil {
return errs(fmt.Errorf("failed to lookup user %s", username))
} else {
return errs(fmt.Errorf("lookup user %s: %s", username, err))
}
}
func createEnvironment(specEnv []string, usr parsedUser) (process.Env, error) {
env, err := process.NewEnv(specEnv)
if err != nil {
return process.Env{}, fmt.Errorf("invalid environment %v: %s", specEnv, err)
}
env["USER"] = usr.username
_, hasHome := env["HOME"]
if !hasHome {
env["HOME"] = usr.homeDir
}
_, hasPath := env["PATH"]
if !hasPath {
if usr.uid == 0 {
env["PATH"] = DefaultRootPATH
} else {
env["PATH"] = DefaultUserPath
}
}
return env, nil
}