1
2
3
4 package hcsv2
5
6 import (
7 "context"
8 "fmt"
9 "path/filepath"
10 "strconv"
11 "strings"
12
13 int_oci "github.com/Microsoft/hcsshim/internal/oci"
14 "github.com/opencontainers/runc/libcontainer/devices"
15 "github.com/opencontainers/runc/libcontainer/user"
16 oci "github.com/opencontainers/runtime-spec/specs-go"
17 "github.com/pkg/errors"
18
19 "github.com/Microsoft/hcsshim/internal/log"
20 "github.com/Microsoft/hcsshim/pkg/annotations"
21 )
22
23 const (
24 devShmPath = "/dev/shm"
25 )
26
27
28
29 func getNetworkNamespaceID(spec *oci.Spec) string {
30 if spec.Windows != nil &&
31 spec.Windows.Network != nil {
32 return strings.ToLower(spec.Windows.Network.NetworkNamespace)
33 }
34 return ""
35 }
36
37
38 func isRootReadonly(spec *oci.Spec) bool {
39 if spec.Root != nil {
40 return spec.Root.Readonly
41 }
42 return false
43 }
44
45
46 func removeMount(target string, mounts []oci.Mount) []oci.Mount {
47 var result []oci.Mount
48 for _, m := range mounts {
49 if m.Destination == target {
50 continue
51 }
52 result = append(result, m)
53 }
54 return result
55 }
56
57 func setProcess(spec *oci.Spec) {
58 if spec.Process == nil {
59 spec.Process = &oci.Process{}
60 }
61 }
62
63 func setCoreRLimit(spec *oci.Spec, value string) error {
64 setProcess(spec)
65
66 vals := strings.Split(value, ";")
67 if len(vals) != 2 {
68 return errors.New("wrong number of values supplied for rlimit core")
69 }
70
71 soft, err := strconv.ParseUint(vals[0], 10, 64)
72 if err != nil {
73 return errors.Wrap(err, "failed to parse soft core rlimit")
74 }
75 hard, err := strconv.ParseUint(vals[1], 10, 64)
76 if err != nil {
77 return errors.Wrap(err, "failed to parse hard core rlimit")
78 }
79
80 spec.Process.Rlimits = append(spec.Process.Rlimits, oci.POSIXRlimit{
81 Type: "RLIMIT_CORE",
82 Soft: soft,
83 Hard: hard,
84 })
85
86 return nil
87 }
88
89
90
91
92
93
94
95 func setUserStr(spec *oci.Spec, userstr string) error {
96 setProcess(spec)
97
98 parts := strings.Split(userstr, ":")
99 switch len(parts) {
100 case 1:
101 v, err := strconv.Atoi(parts[0])
102 if err != nil {
103
104 return setUsername(spec, userstr)
105 }
106 return setUserID(spec, int(v))
107 case 2:
108 var (
109 username, groupname string
110 uid, gid int
111 )
112 v, err := strconv.Atoi(parts[0])
113 if err != nil {
114 username = parts[0]
115 } else {
116 uid = int(v)
117 }
118 v, err = strconv.Atoi(parts[1])
119 if err != nil {
120 groupname = parts[1]
121 } else {
122 gid = int(v)
123 }
124 if username != "" {
125 u, err := getUser(spec, func(u user.User) bool {
126 return u.Name == username
127 })
128 if err != nil {
129 return errors.Wrapf(err, "failed to find user by username: %s", username)
130 }
131 uid = u.Uid
132 }
133 if groupname != "" {
134 g, err := getGroup(spec, func(g user.Group) bool {
135 return g.Name == groupname
136 })
137 if err != nil {
138 return errors.Wrapf(err, "failed to find group by groupname: %s", groupname)
139 }
140 gid = g.Gid
141 }
142 spec.Process.User.UID, spec.Process.User.GID = uint32(uid), uint32(gid)
143 return nil
144 default:
145 return fmt.Errorf("invalid userstr: '%s'", userstr)
146 }
147 }
148
149 func setUsername(spec *oci.Spec, username string) error {
150 u, err := getUser(spec, func(u user.User) bool {
151 return u.Name == username
152 })
153 if err != nil {
154 return errors.Wrapf(err, "failed to find user by username: %s", username)
155 }
156 spec.Process.User.UID, spec.Process.User.GID = uint32(u.Uid), uint32(u.Gid)
157 return nil
158 }
159
160 func setUserID(spec *oci.Spec, uid int) error {
161 u, err := getUser(spec, func(u user.User) bool {
162 return u.Uid == uid
163 })
164 if err != nil {
165 spec.Process.User.UID, spec.Process.User.GID = uint32(uid), 0
166 return nil
167 }
168 spec.Process.User.UID, spec.Process.User.GID = uint32(u.Uid), uint32(u.Gid)
169 return nil
170 }
171
172 func getUser(spec *oci.Spec, filter func(user.User) bool) (user.User, error) {
173 users, err := user.ParsePasswdFileFilter(filepath.Join(spec.Root.Path, "/etc/passwd"), filter)
174 if err != nil {
175 return user.User{}, err
176 }
177 if len(users) != 1 {
178 return user.User{}, errors.Errorf("expected exactly 1 user matched '%d'", len(users))
179 }
180 return users[0], nil
181 }
182
183 func getGroup(spec *oci.Spec, filter func(user.Group) bool) (user.Group, error) {
184 groups, err := user.ParseGroupFileFilter(filepath.Join(spec.Root.Path, "/etc/group"), filter)
185 if err != nil {
186 return user.Group{}, err
187 }
188 if len(groups) != 1 {
189 return user.Group{}, errors.Errorf("expected exactly 1 group matched '%d'", len(groups))
190 }
191 return groups[0], nil
192 }
193
194
195 func applyAnnotationsToSpec(ctx context.Context, spec *oci.Spec) error {
196
197 if val, ok := spec.Annotations[annotations.LCOWDevShmSizeInKb]; ok {
198 mt, err := devShmMountWithSize(val)
199 if err != nil {
200 return err
201 }
202 spec.Mounts = removeMount(devShmPath, spec.Mounts)
203 spec.Mounts = append(spec.Mounts, *mt)
204 log.G(ctx).WithField("sizeKB", val).Debug("set custom /dev/shm size")
205 }
206
207
208 if int_oci.ParseAnnotationsBool(ctx, spec.Annotations, annotations.LCOWPrivileged, false) {
209 log.G(ctx).Debugf("'%s' set for privileged container", annotations.LCOWPrivileged)
210
211
212 hostDevices, err := devices.HostDevices()
213 if err != nil {
214 return err
215 }
216 for _, hostDevice := range hostDevices {
217 addLinuxDeviceToSpec(ctx, hostDevice, spec, false)
218 }
219
220
221 spec.Linux.Resources.Devices = []oci.LinuxDeviceCgroup{
222 {
223 Allow: true,
224 Access: "rwm",
225 },
226 }
227 } else {
228 tempLinuxDevices := spec.Linux.Devices
229 spec.Linux.Devices = []oci.LinuxDevice{}
230 for _, ld := range tempLinuxDevices {
231 hostDevice, err := devices.DeviceFromPath(ld.Path, "rwm")
232 if err != nil {
233 return err
234 }
235 addLinuxDeviceToSpec(ctx, hostDevice, spec, true)
236 }
237 }
238
239 return nil
240 }
241
242
243
244 func addDevSev(ctx context.Context, spec *oci.Spec) error {
245
246 devSev, err := devices.DeviceFromPath("/dev/sev", "rwm")
247 if err != nil {
248
249 sevErr := fmt.Errorf("failed to add SEV device to spec: %w", err)
250 var errSevGuest error
251 devSev, errSevGuest = devices.DeviceFromPath("/dev/sev-guest", "rwm")
252 if errSevGuest != nil {
253 return fmt.Errorf("%s: %w", sevErr, errSevGuest)
254 }
255 }
256 addLinuxDeviceToSpec(ctx, devSev, spec, true)
257 return nil
258 }
259
260
261
262 func devShmMountWithSize(sizeString string) (*oci.Mount, error) {
263 size, err := strconv.ParseUint(sizeString, 10, 64)
264 if err != nil {
265 return nil, fmt.Errorf("/dev/shm size must be a valid integer: %w", err)
266 }
267 if size == 0 {
268 return nil, errors.New("/dev/shm size must be non-zero")
269 }
270
271
272 sizeKB := fmt.Sprintf("size=%sk", sizeString)
273 return &oci.Mount{
274 Source: "shm",
275 Destination: devShmPath,
276 Type: "tmpfs",
277 Options: []string{"nosuid", "noexec", "nodev", "mode=1777", sizeKB},
278 }, nil
279 }
280
View as plain text