1 package validate
2
3 import (
4 "os"
5 "path/filepath"
6 "testing"
7
8 "github.com/opencontainers/runc/libcontainer/configs"
9 "golang.org/x/sys/unix"
10 )
11
12 func TestValidate(t *testing.T) {
13 config := &configs.Config{
14 Rootfs: "/var",
15 }
16
17 validator := New()
18 err := validator.Validate(config)
19 if err != nil {
20 t.Errorf("Expected error to not occur: %+v", err)
21 }
22 }
23
24 func TestValidateWithInvalidRootfs(t *testing.T) {
25 dir := "rootfs"
26 if err := os.Symlink("/var", dir); err != nil {
27 t.Fatal(err)
28 }
29 defer os.Remove(dir)
30
31 config := &configs.Config{
32 Rootfs: dir,
33 }
34
35 validator := New()
36 err := validator.Validate(config)
37 if err == nil {
38 t.Error("Expected error to occur but it was nil")
39 }
40 }
41
42 func TestValidateNetworkWithoutNETNamespace(t *testing.T) {
43 network := &configs.Network{Type: "loopback"}
44 config := &configs.Config{
45 Rootfs: "/var",
46 Namespaces: []configs.Namespace{},
47 Networks: []*configs.Network{network},
48 }
49
50 validator := New()
51 err := validator.Validate(config)
52 if err == nil {
53 t.Error("Expected error to occur but it was nil")
54 }
55 }
56
57 func TestValidateNetworkRoutesWithoutNETNamespace(t *testing.T) {
58 route := &configs.Route{Gateway: "255.255.255.0"}
59 config := &configs.Config{
60 Rootfs: "/var",
61 Namespaces: []configs.Namespace{},
62 Routes: []*configs.Route{route},
63 }
64
65 validator := New()
66 err := validator.Validate(config)
67 if err == nil {
68 t.Error("Expected error to occur but it was nil")
69 }
70 }
71
72 func TestValidateHostname(t *testing.T) {
73 config := &configs.Config{
74 Rootfs: "/var",
75 Hostname: "runc",
76 Namespaces: configs.Namespaces(
77 []configs.Namespace{
78 {Type: configs.NEWUTS},
79 },
80 ),
81 }
82
83 validator := New()
84 err := validator.Validate(config)
85 if err != nil {
86 t.Errorf("Expected error to not occur: %+v", err)
87 }
88 }
89
90 func TestValidateHostnameWithoutUTSNamespace(t *testing.T) {
91 config := &configs.Config{
92 Rootfs: "/var",
93 Hostname: "runc",
94 }
95
96 validator := New()
97 err := validator.Validate(config)
98 if err == nil {
99 t.Error("Expected error to occur but it was nil")
100 }
101 }
102
103 func TestValidateSecurityWithMaskPaths(t *testing.T) {
104 config := &configs.Config{
105 Rootfs: "/var",
106 MaskPaths: []string{"/proc/kcore"},
107 Namespaces: configs.Namespaces(
108 []configs.Namespace{
109 {Type: configs.NEWNS},
110 },
111 ),
112 }
113
114 validator := New()
115 err := validator.Validate(config)
116 if err != nil {
117 t.Errorf("Expected error to not occur: %+v", err)
118 }
119 }
120
121 func TestValidateSecurityWithROPaths(t *testing.T) {
122 config := &configs.Config{
123 Rootfs: "/var",
124 ReadonlyPaths: []string{"/proc/sys"},
125 Namespaces: configs.Namespaces(
126 []configs.Namespace{
127 {Type: configs.NEWNS},
128 },
129 ),
130 }
131
132 validator := New()
133 err := validator.Validate(config)
134 if err != nil {
135 t.Errorf("Expected error to not occur: %+v", err)
136 }
137 }
138
139 func TestValidateSecurityWithoutNEWNS(t *testing.T) {
140 config := &configs.Config{
141 Rootfs: "/var",
142 MaskPaths: []string{"/proc/kcore"},
143 ReadonlyPaths: []string{"/proc/sys"},
144 }
145
146 validator := New()
147 err := validator.Validate(config)
148 if err == nil {
149 t.Error("Expected error to occur but it was nil")
150 }
151 }
152
153 func TestValidateUserNamespace(t *testing.T) {
154 if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) {
155 t.Skip("Test requires userns.")
156 }
157 config := &configs.Config{
158 Rootfs: "/var",
159 Namespaces: configs.Namespaces(
160 []configs.Namespace{
161 {Type: configs.NEWUSER},
162 },
163 ),
164 UidMappings: []configs.IDMap{{HostID: 0, ContainerID: 123, Size: 100}},
165 GidMappings: []configs.IDMap{{HostID: 0, ContainerID: 123, Size: 100}},
166 }
167
168 validator := New()
169 err := validator.Validate(config)
170 if err != nil {
171 t.Errorf("expected error to not occur %+v", err)
172 }
173 }
174
175 func TestValidateUsernsMappingWithoutNamespace(t *testing.T) {
176 config := &configs.Config{
177 Rootfs: "/var",
178 UidMappings: []configs.IDMap{{HostID: 0, ContainerID: 123, Size: 100}},
179 GidMappings: []configs.IDMap{{HostID: 0, ContainerID: 123, Size: 100}},
180 }
181
182 validator := New()
183 err := validator.Validate(config)
184 if err == nil {
185 t.Error("Expected error to occur but it was nil")
186 }
187 }
188
189
190
191 func TestConvertSysctlVariableToDotsSeparator(t *testing.T) {
192 type testCase struct {
193 in string
194 out string
195 }
196 valid := []testCase{
197 {in: "kernel.shm_rmid_forced", out: "kernel.shm_rmid_forced"},
198 {in: "kernel/shm_rmid_forced", out: "kernel.shm_rmid_forced"},
199 {in: "net.ipv4.conf.eno2/100.rp_filter", out: "net.ipv4.conf.eno2/100.rp_filter"},
200 {in: "net/ipv4/conf/eno2.100/rp_filter", out: "net.ipv4.conf.eno2/100.rp_filter"},
201 {in: "net/ipv4/ip_local_port_range", out: "net.ipv4.ip_local_port_range"},
202 {in: "kernel/msgmax", out: "kernel.msgmax"},
203 {in: "kernel/sem", out: "kernel.sem"},
204 }
205
206 for _, test := range valid {
207 convertSysctlVal := convertSysctlVariableToDotsSeparator(test.in)
208 if convertSysctlVal != test.out {
209 t.Errorf("The sysctl variable was not converted correctly. got: %s, want: %s", convertSysctlVal, test.out)
210 }
211 }
212 }
213
214 func TestValidateSysctl(t *testing.T) {
215 sysctl := map[string]string{
216 "fs.mqueue.ctl": "ctl",
217 "fs/mqueue/ctl": "ctl",
218 "net.ctl": "ctl",
219 "net/ctl": "ctl",
220 "net.ipv4.conf.eno2/100.rp_filter": "ctl",
221 "kernel.ctl": "ctl",
222 "kernel/ctl": "ctl",
223 }
224
225 for k, v := range sysctl {
226 config := &configs.Config{
227 Rootfs: "/var",
228 Sysctl: map[string]string{k: v},
229 }
230
231 validator := New()
232 err := validator.Validate(config)
233 if err == nil {
234 t.Error("Expected error to occur but it was nil")
235 }
236 }
237 }
238
239 func TestValidateValidSysctl(t *testing.T) {
240 sysctl := map[string]string{
241 "fs.mqueue.ctl": "ctl",
242 "fs/mqueue/ctl": "ctl",
243 "net.ctl": "ctl",
244 "net/ctl": "ctl",
245 "net.ipv4.conf.eno2/100.rp_filter": "ctl",
246 "kernel.msgmax": "ctl",
247 "kernel/msgmax": "ctl",
248 }
249
250 for k, v := range sysctl {
251 config := &configs.Config{
252 Rootfs: "/var",
253 Sysctl: map[string]string{k: v},
254 Namespaces: []configs.Namespace{
255 {
256 Type: configs.NEWNET,
257 },
258 {
259 Type: configs.NEWIPC,
260 },
261 },
262 }
263
264 validator := New()
265 err := validator.Validate(config)
266 if err != nil {
267 t.Errorf("Expected error to not occur with {%s=%s} but got: %q", k, v, err)
268 }
269 }
270 }
271
272 func TestValidateSysctlWithSameNs(t *testing.T) {
273 config := &configs.Config{
274 Rootfs: "/var",
275 Sysctl: map[string]string{"net.ctl": "ctl"},
276 Namespaces: configs.Namespaces(
277 []configs.Namespace{
278 {
279 Type: configs.NEWNET,
280 Path: "/proc/self/ns/net",
281 },
282 },
283 ),
284 }
285
286 validator := New()
287 err := validator.Validate(config)
288 if err == nil {
289 t.Error("Expected error to occur but it was nil")
290 }
291 }
292
293 func TestValidateSysctlWithBindHostNetNS(t *testing.T) {
294 if os.Getuid() != 0 {
295 t.Skip("requires root")
296 }
297
298 const selfnet = "/proc/self/ns/net"
299
300 file := filepath.Join(t.TempDir(), "default")
301 fd, err := os.Create(file)
302 if err != nil {
303 t.Fatal(err)
304 }
305 defer os.Remove(file)
306 fd.Close()
307
308 if err := unix.Mount(selfnet, file, "bind", unix.MS_BIND, ""); err != nil {
309 t.Fatalf("can't bind-mount %s to %s: %s", selfnet, file, err)
310 }
311 defer func() {
312 _ = unix.Unmount(file, unix.MNT_DETACH)
313 }()
314
315 config := &configs.Config{
316 Rootfs: "/var",
317 Sysctl: map[string]string{"net.ctl": "ctl", "net.foo": "bar"},
318 Namespaces: configs.Namespaces(
319 []configs.Namespace{
320 {
321 Type: configs.NEWNET,
322 Path: file,
323 },
324 },
325 ),
326 }
327
328 validator := New()
329 if err := validator.Validate(config); err == nil {
330 t.Error("Expected error to occur but it was nil")
331 }
332 }
333
334 func TestValidateSysctlWithoutNETNamespace(t *testing.T) {
335 config := &configs.Config{
336 Rootfs: "/var",
337 Sysctl: map[string]string{"net.ctl": "ctl"},
338 Namespaces: []configs.Namespace{},
339 }
340
341 validator := New()
342 err := validator.Validate(config)
343 if err == nil {
344 t.Error("Expected error to occur but it was nil")
345 }
346 }
347
348 func TestValidateMounts(t *testing.T) {
349 testCases := []struct {
350 isErr bool
351 dest string
352 }{
353
354 {isErr: false, dest: "not/an/abs/path"},
355 {isErr: false, dest: "./rel/path"},
356 {isErr: false, dest: "./rel/path"},
357 {isErr: false, dest: "../../path"},
358
359 {isErr: false, dest: "/abs/path"},
360 {isErr: false, dest: "/abs/but/../unclean"},
361 }
362
363 validator := New()
364
365 for _, tc := range testCases {
366 config := &configs.Config{
367 Rootfs: "/var",
368 Mounts: []*configs.Mount{
369 {Destination: tc.dest},
370 },
371 }
372
373 err := validator.Validate(config)
374 if tc.isErr && err == nil {
375 t.Errorf("mount dest: %s, expected error, got nil", tc.dest)
376 }
377 if !tc.isErr && err != nil {
378 t.Errorf("mount dest: %s, expected nil, got error %v", tc.dest, err)
379 }
380 }
381 }
382
View as plain text