github.com/schwarzm/garden-linux@v0.0.0-20150507151835-33bca2147c47/old/integration/wshd/main_test.go (about) 1 // +build linux 2 3 package wshd_test 4 5 import ( 6 "fmt" 7 "io" 8 "io/ioutil" 9 "net" 10 "os" 11 "os/exec" 12 "path" 13 "path/filepath" 14 "syscall" 15 16 . "github.com/onsi/ginkgo" 17 . "github.com/onsi/gomega" 18 . "github.com/onsi/gomega/gbytes" 19 . "github.com/onsi/gomega/gexec" 20 ) 21 22 var _ = Describe("Running wshd", func() { 23 wshd := "../../linux_backend/skeleton/bin/wshd" 24 25 wsh := "../../linux_backend/skeleton/bin/wsh" 26 27 shmTest, err := Build("github.com/cloudfoundry-incubator/garden-linux/old/integration/wshd/shm_test") 28 if err != nil { 29 panic(err) 30 } 31 32 var socketPath string 33 var containerPath string 34 35 var binDir string 36 var libDir string 37 var runDir string 38 var mntDir string 39 40 var userNs string 41 42 var beforeWshd func() 43 44 BeforeEach(func() { 45 var err error 46 47 containerPath, err = ioutil.TempDir(os.TempDir(), "wshd-test-container") 48 Expect(err).ToNot(HaveOccurred()) 49 50 userNs = "disabled" 51 beforeWshd = func() {} 52 53 binDir = path.Join(containerPath, "bin") 54 libDir = path.Join(containerPath, "lib") 55 runDir = path.Join(containerPath, "run") 56 mntDir = path.Join(containerPath, "mnt") 57 58 os.Mkdir(binDir, 0755) 59 os.Mkdir(libDir, 0755) 60 os.Mkdir(runDir, 0755) 61 62 err = copyFile(wshd, path.Join(binDir, "wshd")) 63 Expect(err).ToNot(HaveOccurred()) 64 65 hookPath, err := Build("github.com/cloudfoundry-incubator/garden-linux/old/integration/wshd/fake_hook") 66 Expect(err).ToNot(HaveOccurred()) 67 err = copyFile(hookPath, path.Join(libDir, "hook")) 68 Expect(err).ToNot(HaveOccurred()) 69 70 ioutil.WriteFile(path.Join(libDir, "hook-parent-before-clone.sh"), []byte(`#!/bin/sh 71 72 set -o nounset 73 set -o errexit 74 75 cd $(dirname $0)/../ 76 77 cp bin/wshd mnt/sbin/wshd 78 chmod 700 mnt/sbin/wshd 79 `), 0755) 80 81 ioutil.WriteFile(path.Join(libDir, "hook-parent-after-clone.sh"), []byte(`#!/bin/sh 82 set -o nounset 83 set -o errexit 84 85 cd $(dirname $0)/../ 86 87 cat > /proc/$PID/uid_map 2> /dev/null <<EOF || true 88 0 0 1 89 10000 10000 1 90 EOF 91 92 cat > /proc/$PID/gid_map 2> /dev/null <<EOF || true 93 0 0 1 94 10000 10000 1 95 EOF 96 97 echo $PID > ./run/wshd.pid 98 `), 0755) 99 100 ioutil.WriteFile(path.Join(libDir, "hook-child-after-pivot.sh"), []byte(`#!/bin/sh 101 102 set -o nounset 103 set -o errexit 104 105 cd $(dirname $0)/../ 106 107 mkdir -p /proc 108 mount -t proc none /proc 109 110 adduser -u 10000 -g 10000 -s /bin/sh -D vcap 111 `), 0755) 112 113 ioutil.WriteFile(path.Join(libDir, "set-up-root.sh"), []byte(`#!/bin/bash 114 115 set -o nounset 116 set -o errexit 117 118 rootfs_path=$1 119 120 function overlay_directory_in_rootfs() { 121 # Skip if exists 122 if [ ! -d tmp/rootfs/$1 ] 123 then 124 if [ -d mnt/$1 ] 125 then 126 cp -r mnt/$1 tmp/rootfs/ 127 else 128 mkdir -p tmp/rootfs/$1 129 fi 130 fi 131 132 mount -n --bind tmp/rootfs/$1 mnt/$1 133 mount -n --bind -o remount,$2 tmp/rootfs/$1 mnt/$1 134 } 135 136 function setup_fs() { 137 mkdir -p tmp/rootfs mnt 138 139 mkdir -p $rootfs_path/proc 140 141 mount -n --bind $rootfs_path mnt 142 mount -n --bind -o remount,ro $rootfs_path mnt 143 144 overlay_directory_in_rootfs /dev rw 145 overlay_directory_in_rootfs /etc rw 146 overlay_directory_in_rootfs /home rw 147 overlay_directory_in_rootfs /sbin rw 148 overlay_directory_in_rootfs /var rw 149 150 mkdir -p tmp/rootfs/tmp 151 152 # test asserts that wshd changes this to 0777 153 chmod 755 tmp/rootfs/tmp 154 155 overlay_directory_in_rootfs /tmp rw 156 } 157 158 setup_fs 159 `), 0755) 160 161 setUpRoot := exec.Command(path.Join(libDir, "set-up-root.sh"), os.Getenv("GARDEN_TEST_ROOTFS")) 162 setUpRoot.Dir = containerPath 163 164 setUpRootSession, err := Start(setUpRoot, GinkgoWriter, GinkgoWriter) 165 Expect(err).ToNot(HaveOccurred()) 166 Eventually(setUpRootSession, 5.0).Should(Exit(0)) 167 }) 168 169 JustBeforeEach(func() { 170 171 beforeWshd() 172 173 wshdCommand := exec.Command( 174 wshd, 175 "--run", runDir, 176 "--lib", libDir, 177 "--root", mntDir, 178 "--title", "test wshd", 179 "--userns", userNs, 180 ) 181 182 socketPath = path.Join(runDir, "wshd.sock") 183 184 wshdSession, err := Start(wshdCommand, GinkgoWriter, GinkgoWriter) 185 Expect(err).ToNot(HaveOccurred()) 186 187 Eventually(wshdSession, 30).Should(Exit(0)) 188 189 Eventually(ErrorDialingUnix(socketPath)).ShouldNot(HaveOccurred()) 190 }) 191 192 AfterEach(func() { 193 wshdPidfile, err := os.Open(path.Join(containerPath, "run", "wshd.pid")) 194 Expect(err).ToNot(HaveOccurred()) 195 196 var wshdPid int 197 _, err = fmt.Fscanf(wshdPidfile, "%d", &wshdPid) 198 Expect(err).ToNot(HaveOccurred()) 199 200 proc, err := os.FindProcess(wshdPid) 201 Expect(err).ToNot(HaveOccurred()) 202 203 err = proc.Kill() 204 Expect(err).ToNot(HaveOccurred()) 205 206 for _, submount := range []string{"dev", "etc", "home", "sbin", "var", "tmp"} { 207 mountPoint := path.Join(containerPath, "mnt", submount) 208 209 err := syscall.Unmount(mountPoint, 0) 210 Expect(err).ToNot(HaveOccurred()) 211 } 212 213 err = syscall.Unmount(path.Join(containerPath, "mnt"), 0) 214 Expect(err).ToNot(HaveOccurred()) 215 216 Eventually(func() error { 217 return os.RemoveAll(containerPath) 218 }, 10).ShouldNot(HaveOccurred()) 219 }) 220 221 It("starts the daemon as a session leader with process isolation and the given title", func() { 222 ps := exec.Command(wsh, "--socket", socketPath, "/bin/ps", "-o", "pid,comm") 223 224 psSession, err := Start(ps, GinkgoWriter, GinkgoWriter) 225 Expect(err).ToNot(HaveOccurred()) 226 227 Eventually(psSession).Should(Say(`\s+1\s+wshd`)) 228 Eventually(psSession).Should(Exit(0)) 229 }) 230 231 It("starts the daemon with mount space isolation", func() { 232 mkdir := exec.Command(wsh, "--socket", socketPath, "/bin/mkdir", "/home/vcap/lawn") 233 mkdirSession, err := Start(mkdir, GinkgoWriter, GinkgoWriter) 234 Expect(err).ToNot(HaveOccurred()) 235 Eventually(mkdirSession).Should(Exit(0)) 236 237 mkdir = exec.Command(wsh, "--socket", socketPath, "/bin/mkdir", "/home/vcap/gnome") 238 mkdirSession, err = Start(mkdir, GinkgoWriter, GinkgoWriter) 239 Expect(err).ToNot(HaveOccurred()) 240 Eventually(mkdirSession).Should(Exit(0)) 241 242 mount := exec.Command(wsh, "--socket", socketPath, "/bin/mount", "--bind", "/home/vcap/lawn", "/home/vcap/gnome") 243 mountSession, err := Start(mount, GinkgoWriter, GinkgoWriter) 244 Expect(err).ToNot(HaveOccurred()) 245 Eventually(mountSession).Should(Exit(0)) 246 247 cat := exec.Command("/bin/cat", "/proc/mounts") 248 catSession, err := Start(cat, GinkgoWriter, GinkgoWriter) 249 Expect(err).ToNot(HaveOccurred()) 250 Eventually(catSession).Should(Exit(0)) 251 Expect(catSession).ToNot(Say("gnome")) 252 }) 253 254 It("places the daemon in each cgroup subsystem", func() { 255 cat := exec.Command(wsh, "--socket", socketPath, "sh", "-c", "cat /proc/$$/cgroup") 256 catSession, err := Start(cat, GinkgoWriter, GinkgoWriter) 257 Expect(err).ToNot(HaveOccurred()) 258 Eventually(catSession).Should(Exit(0)) 259 Expect(catSession.Out.Contents()).To(MatchRegexp(`\bcpu\b`)) 260 Expect(catSession.Out.Contents()).To(MatchRegexp(`\bcpuacct\b`)) 261 Expect(catSession.Out.Contents()).To(MatchRegexp(`\bcpuset\b`)) 262 Expect(catSession.Out.Contents()).To(MatchRegexp(`\bdevices\b`)) 263 Expect(catSession.Out.Contents()).To(MatchRegexp(`\bmemory\b`)) 264 }) 265 266 It("starts the daemon with network namespace isolation", func() { 267 ifconfig := exec.Command(wsh, "--socket", socketPath, "/sbin/ifconfig", "lo:0", "1.2.3.4", "up") 268 ifconfigSession, err := Start(ifconfig, GinkgoWriter, GinkgoWriter) 269 Expect(err).ToNot(HaveOccurred()) 270 Eventually(ifconfigSession).Should(Exit(0)) 271 272 localIfconfig := exec.Command("ifconfig") 273 localIfconfigSession, err := Start(localIfconfig, GinkgoWriter, GinkgoWriter) 274 Expect(err).ToNot(HaveOccurred()) 275 Eventually(localIfconfigSession).Should(Exit(0)) 276 Expect(localIfconfigSession).ToNot(Say("lo:0")) 277 }) 278 279 It("starts the daemon with a new IPC namespace", func() { 280 err = copyFile(shmTest, path.Join(mntDir, "sbin", "shmtest")) 281 Expect(err).ToNot(HaveOccurred()) 282 283 localSHM := exec.Command(shmTest) 284 createLocal, err := Start( 285 localSHM, 286 GinkgoWriter, 287 GinkgoWriter, 288 ) 289 Expect(err).ToNot(HaveOccurred()) 290 291 Eventually(createLocal).Should(Say("ok")) 292 293 createRemote, err := Start( 294 exec.Command(wsh, "--socket", socketPath, "/sbin/shmtest", "create"), 295 GinkgoWriter, 296 GinkgoWriter, 297 ) 298 Expect(err).ToNot(HaveOccurred()) 299 Eventually(createRemote).Should(Say("ok")) 300 301 localSHM.Process.Signal(syscall.SIGUSR2) 302 303 Eventually(createLocal).Should(Exit(0)) 304 }) 305 306 It("starts the daemon with a new UTS namespace", func() { 307 hostname := exec.Command(wsh, "--socket", socketPath, "/bin/hostname", "newhostname") 308 hostnameSession, err := Start(hostname, GinkgoWriter, GinkgoWriter) 309 Expect(err).ToNot(HaveOccurred()) 310 Eventually(hostnameSession).Should(Exit(0)) 311 312 localHostname := exec.Command("hostname") 313 localHostnameSession, err := Start(localHostname, GinkgoWriter, GinkgoWriter) 314 Eventually(localHostnameSession).Should(Exit(0)) 315 Expect(localHostnameSession).ToNot(Say("newhostname")) 316 }) 317 318 It("does not leak any shared memory to the child", func() { 319 ipcs, err := Start( 320 exec.Command(wsh, "--socket", socketPath, "ipcs"), 321 GinkgoWriter, 322 GinkgoWriter, 323 ) 324 Expect(err).ToNot(HaveOccurred()) 325 Eventually(ipcs).Should(Exit(0)) 326 Expect(ipcs).ToNot(Say("deadbeef")) 327 }) 328 329 It("ensures /tmp is world-writable", func() { 330 ls, err := Start( 331 exec.Command(wsh, "--socket", socketPath, "ls", "-al", "/tmp"), 332 GinkgoWriter, 333 GinkgoWriter, 334 ) 335 Expect(err).ToNot(HaveOccurred()) 336 Eventually(ls).Should(Exit(0)) 337 338 Expect(ls).To(Say(`drwxrwxrwt`)) 339 }) 340 341 It("unmounts /tmp/garden-host* in the child", func() { 342 cat := exec.Command(wsh, "--socket", socketPath, "/bin/cat", "/proc/mounts") 343 344 catSession, err := Start(cat, GinkgoWriter, GinkgoWriter) 345 Expect(err).ToNot(HaveOccurred()) 346 347 Eventually(catSession).Should(Exit(0)) 348 Expect(catSession).ToNot(Say(" /tmp/garden-host")) 349 }) 350 351 Context("when mount points on the host are deleted", func() { 352 BeforeEach(func() { 353 tmpdir, err := ioutil.TempDir("", "wshd-bogus-mount") 354 Expect(err).ToNot(HaveOccurred()) 355 356 fooDir := filepath.Join(tmpdir, "foo") 357 barDir := filepath.Join(tmpdir, "bar") 358 359 err = os.MkdirAll(fooDir, 0755) 360 Expect(err).ToNot(HaveOccurred()) 361 362 err = os.MkdirAll(barDir, 0755) 363 Expect(err).ToNot(HaveOccurred()) 364 365 mount := exec.Command("mount", "--bind", fooDir, barDir) 366 mountSession, err := Start(mount, GinkgoWriter, GinkgoWriter) 367 Expect(err).ToNot(HaveOccurred()) 368 Eventually(mountSession).Should(Exit(0)) 369 370 err = os.RemoveAll(fooDir) 371 Expect(err).ToNot(HaveOccurred()) 372 373 cat := exec.Command("/bin/cat", "/proc/mounts") 374 catSession, err := Start(cat, GinkgoWriter, GinkgoWriter) 375 Expect(err).ToNot(HaveOccurred()) 376 Eventually(catSession).Should(Say("(deleted)")) 377 Eventually(catSession).Should(Exit(0)) 378 }) 379 380 It("unmounts the un-mangled mount point name", func() { 381 cat := exec.Command(wsh, "--socket", socketPath, "/bin/cat", "/proc/mounts") 382 383 catSession, err := Start(cat, GinkgoWriter, GinkgoWriter) 384 Expect(err).ToNot(HaveOccurred()) 385 386 Eventually(catSession).Should(Exit(0)) 387 Expect(catSession).ToNot(Say("(deleted)")) 388 }) 389 }) 390 391 Context("when running a command in a working dir", func() { 392 It("executes with setuid and setgid", func() { 393 pwd := exec.Command(wsh, "--socket", socketPath, "--dir", "/usr", "pwd") 394 395 pwdSession, err := Start(pwd, GinkgoWriter, GinkgoWriter) 396 Expect(err).ToNot(HaveOccurred()) 397 398 Eventually(pwdSession).Should(Say("^/usr\n")) 399 Eventually(pwdSession).Should(Exit(0)) 400 }) 401 }) 402 403 Context("when running without specifying a --pidfile", func() { 404 It("should exit cleanly with the correct status", func() { 405 pwd := exec.Command(wsh, "--socket", socketPath, "--dir", "/usr", "/bin/sh", "-c", "exit 3") 406 407 stdout := NewBuffer() 408 stderr := NewBuffer() 409 pwdSession, err := Start(pwd, io.MultiWriter(stdout, GinkgoWriter), io.MultiWriter(stderr, GinkgoWriter)) 410 Expect(err).ToNot(HaveOccurred()) 411 412 Eventually(pwdSession).Should(Exit(3)) 413 Expect(string(stderr.Contents())).To(Equal("")) 414 Expect(string(stdout.Contents())).To(Equal("")) 415 }) 416 }) 417 418 It("allows children to receive SIGCHLD when grandchildren die", func() { 419 trap := exec.Command(wsh, "--socket", socketPath, "--dir", "/usr", "/bin/sh", "-c", "trap 'echo caught sigchld' SIGCHLD; $(ls / >/dev/null 2>&1); sleep 5;") 420 421 trapSession, err := Start(trap, GinkgoWriter, GinkgoWriter) 422 Expect(err).ToNot(HaveOccurred()) 423 424 Eventually(trapSession).Should(Say("caught sigchld")) 425 }) 426 427 Context("when running a command as a user", func() { 428 It("executes with setuid and setgid", func() { 429 sh := exec.Command(wsh, "--socket", socketPath, "--user", "vcap", "/bin/sh", "-c", "id -u; id -g") 430 431 shSession, err := Start(sh, GinkgoWriter, GinkgoWriter) 432 Expect(err).ToNot(HaveOccurred()) 433 434 Eventually(shSession).Should(Say("^10000\n")) 435 Eventually(shSession).Should(Say("^10000\n")) 436 Eventually(shSession).Should(Exit(0)) 437 }) 438 439 It("sets $HOME, $USER, and $PATH", func() { 440 sh := exec.Command(wsh, "--socket", socketPath, "--user", "vcap", "/bin/sh", "-c", "env | sort") 441 442 shSession, err := Start(sh, GinkgoWriter, GinkgoWriter) 443 Expect(err).ToNot(HaveOccurred()) 444 445 Eventually(shSession).Should(Say("HOME=/home/vcap\n")) 446 Eventually(shSession).Should(Say("PATH=/usr/local/bin:/usr/bin:/bin\n")) 447 Eventually(shSession).Should(Say("USER=vcap\n")) 448 Eventually(shSession).Should(Exit(0)) 449 }) 450 451 It("executes in their home directory", func() { 452 pwd := exec.Command(wsh, "--socket", socketPath, "--user", "vcap", "/bin/pwd") 453 454 pwdSession, err := Start(pwd, GinkgoWriter, GinkgoWriter) 455 Expect(err).ToNot(HaveOccurred()) 456 457 Eventually(pwdSession).Should(Say("/home/vcap\n")) 458 Eventually(pwdSession).Should(Exit(0)) 459 }) 460 461 It("sets the specified environment variables", func() { 462 pwd := exec.Command(wsh, 463 "--socket", socketPath, 464 "--user", "vcap", 465 "--env", "VAR1=VALUE1", 466 "--env", "VAR2=VALUE2", 467 "sh", "-c", "env | sort", 468 ) 469 470 session, err := Start(pwd, GinkgoWriter, GinkgoWriter) 471 Expect(err).ToNot(HaveOccurred()) 472 473 Eventually(session).Should(Say("VAR1=VALUE1\n")) 474 Eventually(session).Should(Say("VAR2=VALUE2\n")) 475 }) 476 477 It("searches a sanitized path not including sbin for the executable", func() { 478 ls := exec.Command(wsh, 479 "--socket", socketPath, 480 "--user", "vcap", 481 "ls", 482 ) 483 484 session, err := Start(ls, GinkgoWriter, GinkgoWriter) 485 Expect(err).ToNot(HaveOccurred()) 486 487 Eventually(session).Should(Exit(0)) 488 489 onlyInSbin := exec.Command(wsh, 490 "--socket", socketPath, 491 "--user", "vcap", 492 "ifconfig", 493 ) 494 495 session, err = Start(onlyInSbin, GinkgoWriter, GinkgoWriter) 496 Expect(err).ToNot(HaveOccurred()) 497 498 Eventually(session).Should(Exit(255)) 499 }) 500 501 It("saves the child's pid in a pidfile and cleans the pidfile up after the process exits", func() { 502 tmp, err := ioutil.TempDir("", "wshdchildpid") 503 Expect(err).ToNot(HaveOccurred()) 504 505 pwd := exec.Command(wsh, 506 "--socket", socketPath, 507 "--user", "vcap", 508 "--pidfile", filepath.Join(tmp, "foo.pid"), 509 "sh", "-c", "echo $$; read", 510 ) 511 512 in, err := pwd.StdinPipe() 513 Expect(err).ToNot(HaveOccurred()) 514 515 session, err := Start(pwd, GinkgoWriter, GinkgoWriter) 516 Expect(err).ToNot(HaveOccurred()) 517 518 Eventually(func() error { 519 _, err := os.Stat(filepath.Join(tmp, "foo.pid")) 520 return err 521 }).Should(BeNil()) 522 523 read, err := ioutil.ReadFile(filepath.Join(tmp, "foo.pid")) 524 Expect(err).ToNot(HaveOccurred()) 525 526 in.Write([]byte("\n")) 527 528 Eventually(session).Should(Exit()) 529 Expect(string(read)).To(Equal(string(session.Out.Contents()))) 530 531 _, err = os.Stat(filepath.Join(tmp, "foo.pid")) 532 Expect(err).To(HaveOccurred(), "pid file was not cleaned up") 533 }) 534 }) 535 536 Context("when running a command as root", func() { 537 It("executes with setuid and setgid", func() { 538 sh := exec.Command(wsh, "--socket", socketPath, "--user", "root", "/bin/sh", "-c", "id -u; id -g") 539 540 shSession, err := Start(sh, GinkgoWriter, GinkgoWriter) 541 Expect(err).ToNot(HaveOccurred()) 542 543 Eventually(shSession).Should(Say("^0\n")) 544 Eventually(shSession).Should(Say("^0\n")) 545 Eventually(shSession).Should(Exit(0)) 546 }) 547 548 It("sets $HOME, $USER, and a $PATH with sbin dirs", func() { 549 sh := exec.Command(wsh, "--socket", socketPath, "--user", "root", "/bin/sh", "-c", "env | sort") 550 551 shSession, err := Start(sh, GinkgoWriter, GinkgoWriter) 552 Expect(err).ToNot(HaveOccurred()) 553 554 Eventually(shSession).Should(Say("HOME=/root\n")) 555 Eventually(shSession).Should(Say("PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\n")) 556 Eventually(shSession).Should(Say("USER=root\n")) 557 Eventually(shSession).Should(Exit(0)) 558 }) 559 560 It("searches a sanitized path for the executable containing sbin directories", func() { 561 onlyInSbin := exec.Command(wsh, 562 "--socket", socketPath, 563 "--user", "root", 564 "ifconfig", 565 ) 566 567 session, err := Start(onlyInSbin, GinkgoWriter, GinkgoWriter) 568 Expect(err).ToNot(HaveOccurred()) 569 570 Eventually(session).Should(Exit(0)) 571 }) 572 573 It("executes in their home directory", func() { 574 pwd := exec.Command(wsh, "--socket", socketPath, "--user", "root", "/bin/pwd") 575 576 pwdSession, err := Start(pwd, GinkgoWriter, GinkgoWriter) 577 Expect(err).ToNot(HaveOccurred()) 578 579 Eventually(pwdSession).Should(Say("/root\n")) 580 Eventually(pwdSession).Should(Exit(0)) 581 }) 582 }) 583 584 Context("when piping stdin", func() { 585 It("terminates when the input stream terminates", func() { 586 sh := exec.Command(wsh, "--socket", socketPath, "/bin/sh") 587 588 stdin, err := sh.StdinPipe() 589 Expect(err).ToNot(HaveOccurred()) 590 591 shSession, err := Start(sh, GinkgoWriter, GinkgoWriter) 592 Expect(err).ToNot(HaveOccurred()) 593 594 stdin.Write([]byte("echo hello")) 595 stdin.Close() 596 597 Eventually(shSession).Should(Say("hello\n")) 598 Eventually(shSession).Should(Exit(0)) 599 }) 600 }) 601 602 Context("setting rlimits", func() { 603 shouldSetRlimit := func(env []string, limitQueryCmd, expectedValue string) { 604 ulimit := exec.Command(wsh, "--socket", socketPath, "--user", "root", "/bin/sh", "-c", limitQueryCmd) 605 ulimit.Env = env 606 607 ulimitSession, err := Start(ulimit, GinkgoWriter, GinkgoWriter) 608 Expect(err).ToNot(HaveOccurred()) 609 610 Eventually(ulimitSession).Should(Say(expectedValue)) 611 Eventually(ulimitSession).Should(Exit(0)) 612 } 613 614 var ( 615 rlimitResource int // the resource being limited, e.g. RLIMIT_CORE 616 limit string // the string suffix of the resource being limited, e.g. "CORE" 617 limitValue uint64 // a (non-default) limit for the resource, e.g. 4096 618 overrideEnv []string // environment specifying a (non-default) limit to wshd, e.g. ["RLIMIT_CORE=4096"] 619 limitQueryCmd string // a command used to query a limit inside a container, e.g. "ulimit -c" 620 expectedDefaultQueryResponse string // the expected query response for the default limit, e.g. "0" 621 expectedQueryResponse string // the expected query response for the non-default limit, e.g. "8" 622 623 originalRlimit *syscall.Rlimit 624 ) 625 626 JustBeforeEach(func() { 627 overrideEnv = []string{fmt.Sprintf("RLIMIT_%s=%d", limit, limitValue)} 628 }) 629 630 BeforeEach(func() { 631 // Ensure the resource limit being tested is set to a low value. 632 // beforeWshd is called just before wshd is launched. 633 beforeWshd = func() { 634 originalRlimit = getAndReduceRlimit(rlimitResource, limitValue/2) 635 } 636 }) 637 638 AfterEach(func() { 639 Expect(syscall.Setrlimit(rlimitResource, originalRlimit)).To(Succeed()) 640 }) 641 642 Describe("AS", func() { 643 BeforeEach(func() { 644 rlimitResource = RLIMIT_AS 645 limit = "AS" 646 limitValue = 2147483648 * 2 647 648 limitQueryCmd = "ulimit -v" 649 expectedDefaultQueryResponse = "unlimited" 650 expectedQueryResponse = "4194304" 651 }) 652 653 Context("when user namespacing is disabled", func() { 654 BeforeEach(func() { 655 userNs = "disabled" 656 }) 657 It("defaults the rlimit when the environment variable is not set", func() { 658 shouldSetRlimit([]string{}, limitQueryCmd, expectedDefaultQueryResponse) 659 }) 660 It("overrides the rlimit when the environment variable is set", func() { 661 shouldSetRlimit(overrideEnv, limitQueryCmd, expectedQueryResponse) 662 }) 663 }) 664 665 Context("when user namespacing is enabled", func() { 666 BeforeEach(func() { 667 userNs = "enabled" 668 }) 669 It("defaults the rlimit when the environment variable is not set", func() { 670 shouldSetRlimit([]string{}, limitQueryCmd, expectedDefaultQueryResponse) 671 }) 672 It("overrides the rlimit when the environment variable is set", func() { 673 shouldSetRlimit(overrideEnv, limitQueryCmd, expectedQueryResponse) 674 }) 675 }) 676 }) 677 678 Describe("CORE", func() { 679 BeforeEach(func() { 680 rlimitResource = RLIMIT_CORE 681 limit = "CORE" 682 limitValue = 4096 683 684 limitQueryCmd = "ulimit -c" 685 expectedDefaultQueryResponse = "0" 686 expectedQueryResponse = "8" 687 }) 688 689 Context("when user namespacing is disabled", func() { 690 BeforeEach(func() { 691 userNs = "disabled" 692 }) 693 It("defaults the rlimit when the environment variable is not set", func() { 694 shouldSetRlimit([]string{}, limitQueryCmd, expectedDefaultQueryResponse) 695 }) 696 It("overrides the rlimit when the environment variable is set", func() { 697 shouldSetRlimit(overrideEnv, limitQueryCmd, expectedQueryResponse) 698 }) 699 }) 700 701 Context("when user namespacing is enabled", func() { 702 BeforeEach(func() { 703 userNs = "enabled" 704 }) 705 It("defaults the rlimit when the environment variable is not set", func() { 706 shouldSetRlimit([]string{}, limitQueryCmd, expectedDefaultQueryResponse) 707 }) 708 It("overrides the rlimit when the environment variable is set", func() { 709 shouldSetRlimit(overrideEnv, limitQueryCmd, expectedQueryResponse) 710 }) 711 }) 712 }) 713 714 Describe("CPU", func() { 715 BeforeEach(func() { 716 rlimitResource = RLIMIT_CPU 717 limit = "CPU" 718 limitValue = 3600 719 720 limitQueryCmd = "ulimit -t" 721 expectedDefaultQueryResponse = "unlimited" 722 expectedQueryResponse = "3600" 723 }) 724 725 Context("when user namespacing is disabled", func() { 726 BeforeEach(func() { 727 userNs = "disabled" 728 }) 729 It("defaults the rlimit when the environment variable is not set", func() { 730 shouldSetRlimit([]string{}, limitQueryCmd, expectedDefaultQueryResponse) 731 }) 732 It("overrides the rlimit when the environment variable is set", func() { 733 shouldSetRlimit(overrideEnv, limitQueryCmd, expectedQueryResponse) 734 }) 735 }) 736 737 Context("when user namespacing is enabled", func() { 738 BeforeEach(func() { 739 userNs = "enabled" 740 }) 741 It("defaults the rlimit when the environment variable is not set", func() { 742 shouldSetRlimit([]string{}, limitQueryCmd, expectedDefaultQueryResponse) 743 }) 744 It("overrides the rlimit when the environment variable is set", func() { 745 shouldSetRlimit(overrideEnv, limitQueryCmd, expectedQueryResponse) 746 }) 747 }) 748 }) 749 750 Describe("DATA", func() { 751 BeforeEach(func() { 752 rlimitResource = RLIMIT_DATA 753 limit = "DATA" 754 limitValue = 1024 * 1024 755 756 limitQueryCmd = "ulimit -d" 757 expectedDefaultQueryResponse = "unlimited" 758 expectedQueryResponse = "1024" 759 }) 760 761 Context("when user namespacing is disabled", func() { 762 BeforeEach(func() { 763 userNs = "disabled" 764 }) 765 It("defaults the rlimit when the environment variable is not set", func() { 766 shouldSetRlimit([]string{}, limitQueryCmd, expectedDefaultQueryResponse) 767 }) 768 It("overrides the rlimit when the environment variable is set", func() { 769 shouldSetRlimit(overrideEnv, limitQueryCmd, expectedQueryResponse) 770 }) 771 }) 772 773 Context("when user namespacing is enabled", func() { 774 BeforeEach(func() { 775 userNs = "enabled" 776 }) 777 It("defaults the rlimit when the environment variable is not set", func() { 778 shouldSetRlimit([]string{}, limitQueryCmd, expectedDefaultQueryResponse) 779 }) 780 It("overrides the rlimit when the environment variable is set", func() { 781 shouldSetRlimit(overrideEnv, limitQueryCmd, expectedQueryResponse) 782 }) 783 }) 784 }) 785 786 Describe("FSIZE", func() { 787 BeforeEach(func() { 788 rlimitResource = RLIMIT_FSIZE 789 limit = "FSIZE" 790 limitValue = 4096 * 1024 791 792 limitQueryCmd = "ulimit -f" 793 expectedDefaultQueryResponse = "unlimited" 794 expectedQueryResponse = "8192" 795 }) 796 797 Context("when user namespacing is disabled", func() { 798 BeforeEach(func() { 799 userNs = "disabled" 800 }) 801 It("defaults the rlimit when the environment variable is not set", func() { 802 shouldSetRlimit([]string{}, limitQueryCmd, expectedDefaultQueryResponse) 803 }) 804 It("overrides the rlimit when the environment variable is set", func() { 805 shouldSetRlimit(overrideEnv, limitQueryCmd, expectedQueryResponse) 806 }) 807 }) 808 809 Context("when user namespacing is enabled", func() { 810 BeforeEach(func() { 811 userNs = "enabled" 812 }) 813 It("defaults the rlimit when the environment variable is not set", func() { 814 shouldSetRlimit([]string{}, limitQueryCmd, expectedDefaultQueryResponse) 815 }) 816 It("overrides the rlimit when the environment variable is set", func() { 817 shouldSetRlimit(overrideEnv, limitQueryCmd, expectedQueryResponse) 818 }) 819 }) 820 }) 821 822 Describe("LOCKS", func() { 823 BeforeEach(func() { 824 rlimitResource = RLIMIT_LOCKS 825 limit = "LOCKS" 826 limitValue = 1024 827 828 limitQueryCmd = "ulimit -w" 829 expectedDefaultQueryResponse = "unlimited" 830 expectedQueryResponse = "1024" 831 }) 832 833 Context("when user namespacing is disabled", func() { 834 BeforeEach(func() { 835 userNs = "disabled" 836 }) 837 It("defaults the rlimit when the environment variable is not set", func() { 838 shouldSetRlimit([]string{}, limitQueryCmd, expectedDefaultQueryResponse) 839 }) 840 It("overrides the rlimit when the environment variable is set", func() { 841 shouldSetRlimit(overrideEnv, limitQueryCmd, expectedQueryResponse) 842 }) 843 }) 844 845 Context("when user namespacing is enabled", func() { 846 BeforeEach(func() { 847 userNs = "enabled" 848 }) 849 It("defaults the rlimit when the environment variable is not set", func() { 850 shouldSetRlimit([]string{}, limitQueryCmd, expectedDefaultQueryResponse) 851 }) 852 It("overrides the rlimit when the environment variable is set", func() { 853 shouldSetRlimit(overrideEnv, limitQueryCmd, expectedQueryResponse) 854 }) 855 }) 856 }) 857 858 Describe("MEMLOCK", func() { 859 BeforeEach(func() { 860 rlimitResource = RLIMIT_MEMLOCK 861 limit = "MEMLOCK" 862 limitValue = 1024 * 32 863 864 limitQueryCmd = "ulimit -l" 865 expectedDefaultQueryResponse = "64" 866 expectedQueryResponse = "32" 867 }) 868 869 Context("when user namespacing is disabled", func() { 870 BeforeEach(func() { 871 userNs = "disabled" 872 }) 873 It("defaults the rlimit when the environment variable is not set", func() { 874 shouldSetRlimit([]string{}, limitQueryCmd, expectedDefaultQueryResponse) 875 }) 876 It("overrides the rlimit when the environment variable is set", func() { 877 shouldSetRlimit(overrideEnv, limitQueryCmd, expectedQueryResponse) 878 }) 879 }) 880 881 Context("when user namespacing is enabled", func() { 882 BeforeEach(func() { 883 userNs = "enabled" 884 }) 885 It("defaults the rlimit when the environment variable is not set", func() { 886 shouldSetRlimit([]string{}, limitQueryCmd, expectedDefaultQueryResponse) 887 }) 888 It("overrides the rlimit when the environment variable is set", func() { 889 shouldSetRlimit(overrideEnv, limitQueryCmd, expectedQueryResponse) 890 }) 891 }) 892 }) 893 894 Describe("MSGQUEUE", func() { 895 BeforeEach(func() { 896 rlimitResource = RLIMIT_MSGQUEUE 897 limit = "MSGQUEUE" 898 limitValue = 1024 * 100 899 900 limitQueryCmd = "echo RLIMIT_MSGQUEUE not queryable" 901 expectedDefaultQueryResponse = "RLIMIT_MSGQUEUE not queryable" 902 expectedQueryResponse = "RLIMIT_MSGQUEUE not queryable" 903 }) 904 905 Context("when user namespacing is disabled", func() { 906 BeforeEach(func() { 907 userNs = "disabled" 908 }) 909 It("defaults the rlimit when the environment variable is not set", func() { 910 shouldSetRlimit([]string{}, limitQueryCmd, expectedDefaultQueryResponse) 911 }) 912 It("overrides the rlimit when the environment variable is set", func() { 913 shouldSetRlimit(overrideEnv, limitQueryCmd, expectedQueryResponse) 914 }) 915 }) 916 917 Context("when user namespacing is enabled", func() { 918 BeforeEach(func() { 919 userNs = "enabled" 920 }) 921 It("defaults the rlimit when the environment variable is not set", func() { 922 shouldSetRlimit([]string{}, limitQueryCmd, expectedDefaultQueryResponse) 923 }) 924 It("overrides the rlimit when the environment variable is set", func() { 925 shouldSetRlimit(overrideEnv, limitQueryCmd, expectedQueryResponse) 926 }) 927 }) 928 }) 929 930 Describe("NICE", func() { 931 BeforeEach(func() { 932 rlimitResource = RLIMIT_NICE 933 limit = "NICE" 934 limitValue = 100 935 936 limitQueryCmd = "ulimit -e" 937 expectedDefaultQueryResponse = "0" 938 expectedQueryResponse = "100" 939 }) 940 941 Context("when user namespacing is disabled", func() { 942 BeforeEach(func() { 943 userNs = "disabled" 944 }) 945 It("defaults the rlimit when the environment variable is not set", func() { 946 shouldSetRlimit([]string{}, limitQueryCmd, expectedDefaultQueryResponse) 947 }) 948 It("overrides the rlimit when the environment variable is set", func() { 949 shouldSetRlimit(overrideEnv, limitQueryCmd, expectedQueryResponse) 950 }) 951 }) 952 953 Context("when user namespacing is enabled", func() { 954 BeforeEach(func() { 955 userNs = "enabled" 956 }) 957 It("defaults the rlimit when the environment variable is not set", func() { 958 shouldSetRlimit([]string{}, limitQueryCmd, expectedDefaultQueryResponse) 959 }) 960 It("overrides the rlimit when the environment variable is set", func() { 961 shouldSetRlimit(overrideEnv, limitQueryCmd, expectedQueryResponse) 962 }) 963 }) 964 }) 965 966 Describe("NOFILE", func() { 967 BeforeEach(func() { 968 rlimitResource = RLIMIT_NOFILE 969 limit = "NOFILE" 970 limitValue = 4096 971 972 limitQueryCmd = "ulimit -n" 973 expectedDefaultQueryResponse = "1024" 974 expectedQueryResponse = "4096" 975 }) 976 977 Context("when user namespacing is disabled", func() { 978 BeforeEach(func() { 979 userNs = "disabled" 980 }) 981 It("defaults the rlimit when the environment variable is not set", func() { 982 shouldSetRlimit([]string{}, limitQueryCmd, expectedDefaultQueryResponse) 983 }) 984 It("overrides the rlimit when the environment variable is set", func() { 985 shouldSetRlimit(overrideEnv, limitQueryCmd, expectedQueryResponse) 986 }) 987 }) 988 989 Context("when user namespacing is enabled", func() { 990 BeforeEach(func() { 991 userNs = "enabled" 992 }) 993 It("defaults the rlimit when the environment variable is not set", func() { 994 shouldSetRlimit([]string{}, limitQueryCmd, expectedDefaultQueryResponse) 995 }) 996 It("overrides the rlimit when the environment variable is set", func() { 997 shouldSetRlimit(overrideEnv, limitQueryCmd, expectedQueryResponse) 998 }) 999 }) 1000 }) 1001 1002 Describe("NPROC", func() { 1003 BeforeEach(func() { 1004 rlimitResource = RLIMIT_NPROC 1005 limit = "NPROC" 1006 limitValue = 4096 1007 1008 limitQueryCmd = "ulimit -p" 1009 expectedDefaultQueryResponse = "1024" 1010 expectedQueryResponse = "4096" 1011 }) 1012 1013 Context("when user namespacing is disabled", func() { 1014 BeforeEach(func() { 1015 userNs = "disabled" 1016 }) 1017 It("defaults the rlimit when the environment variable is not set", func() { 1018 shouldSetRlimit([]string{}, limitQueryCmd, expectedDefaultQueryResponse) 1019 }) 1020 It("overrides the rlimit when the environment variable is set", func() { 1021 shouldSetRlimit(overrideEnv, limitQueryCmd, expectedQueryResponse) 1022 }) 1023 }) 1024 1025 Context("when user namespacing is enabled", func() { 1026 BeforeEach(func() { 1027 userNs = "enabled" 1028 }) 1029 It("defaults the rlimit when the environment variable is not set", func() { 1030 shouldSetRlimit([]string{}, limitQueryCmd, expectedDefaultQueryResponse) 1031 }) 1032 It("overrides the rlimit when the environment variable is set", func() { 1033 shouldSetRlimit(overrideEnv, limitQueryCmd, expectedQueryResponse) 1034 }) 1035 }) 1036 }) 1037 1038 Describe("RSS", func() { 1039 BeforeEach(func() { 1040 rlimitResource = RLIMIT_RSS 1041 limit = "RSS" 1042 limitValue = 4096 * 1024 1043 1044 limitQueryCmd = "ulimit -m" 1045 expectedDefaultQueryResponse = "unlimited" 1046 expectedQueryResponse = "4096" 1047 }) 1048 1049 Context("when user namespacing is disabled", func() { 1050 BeforeEach(func() { 1051 userNs = "disabled" 1052 }) 1053 It("defaults the rlimit when the environment variable is not set", func() { 1054 shouldSetRlimit([]string{}, limitQueryCmd, expectedDefaultQueryResponse) 1055 }) 1056 It("overrides the rlimit when the environment variable is set", func() { 1057 shouldSetRlimit(overrideEnv, limitQueryCmd, expectedQueryResponse) 1058 }) 1059 }) 1060 1061 Context("when user namespacing is enabled", func() { 1062 BeforeEach(func() { 1063 userNs = "enabled" 1064 }) 1065 It("defaults the rlimit when the environment variable is not set", func() { 1066 shouldSetRlimit([]string{}, limitQueryCmd, expectedDefaultQueryResponse) 1067 }) 1068 It("overrides the rlimit when the environment variable is set", func() { 1069 shouldSetRlimit(overrideEnv, limitQueryCmd, expectedQueryResponse) 1070 }) 1071 }) 1072 }) 1073 1074 Describe("RTPRIO", func() { 1075 BeforeEach(func() { 1076 rlimitResource = RLIMIT_RTPRIO 1077 limit = "RTPRIO" 1078 limitValue = 100 1079 1080 limitQueryCmd = "ulimit -r" 1081 expectedDefaultQueryResponse = "0" 1082 expectedQueryResponse = "100" 1083 }) 1084 1085 Context("when user namespacing is disabled", func() { 1086 BeforeEach(func() { 1087 userNs = "disabled" 1088 }) 1089 It("defaults the rlimit when the environment variable is not set", func() { 1090 shouldSetRlimit([]string{}, limitQueryCmd, expectedDefaultQueryResponse) 1091 }) 1092 It("overrides the rlimit when the environment variable is set", func() { 1093 shouldSetRlimit(overrideEnv, limitQueryCmd, expectedQueryResponse) 1094 }) 1095 }) 1096 1097 Context("when user namespacing is enabled", func() { 1098 BeforeEach(func() { 1099 userNs = "enabled" 1100 }) 1101 It("defaults the rlimit when the environment variable is not set", func() { 1102 shouldSetRlimit([]string{}, limitQueryCmd, expectedDefaultQueryResponse) 1103 }) 1104 It("overrides the rlimit when the environment variable is set", func() { 1105 shouldSetRlimit(overrideEnv, limitQueryCmd, expectedQueryResponse) 1106 }) 1107 }) 1108 }) 1109 1110 Describe("SIGPENDING", func() { 1111 BeforeEach(func() { 1112 rlimitResource = RLIMIT_SIGPENDING 1113 limit = "SIGPENDING" 1114 limitValue = 1024 * 4 1115 1116 limitQueryCmd = "echo RLIMIT_SIGPENDING not queryable" 1117 expectedDefaultQueryResponse = "RLIMIT_SIGPENDING not queryable" 1118 expectedQueryResponse = "RLIMIT_SIGPENDING not queryable" 1119 }) 1120 1121 Context("when user namespacing is disabled", func() { 1122 BeforeEach(func() { 1123 userNs = "disabled" 1124 }) 1125 It("defaults the rlimit when the environment variable is not set", func() { 1126 shouldSetRlimit([]string{}, limitQueryCmd, expectedDefaultQueryResponse) 1127 }) 1128 It("overrides the rlimit when the environment variable is set", func() { 1129 shouldSetRlimit(overrideEnv, limitQueryCmd, expectedQueryResponse) 1130 }) 1131 }) 1132 1133 Context("when user namespacing is enabled", func() { 1134 BeforeEach(func() { 1135 userNs = "enabled" 1136 }) 1137 It("defaults the rlimit when the environment variable is not set", func() { 1138 shouldSetRlimit([]string{}, limitQueryCmd, expectedDefaultQueryResponse) 1139 }) 1140 It("overrides the rlimit when the environment variable is set", func() { 1141 shouldSetRlimit(overrideEnv, limitQueryCmd, expectedQueryResponse) 1142 }) 1143 }) 1144 }) 1145 1146 Describe("STACK", func() { 1147 BeforeEach(func() { 1148 rlimitResource = RLIMIT_STACK 1149 limit = "STACK" 1150 limitValue = 4 * 1024 * 1024 1151 1152 limitQueryCmd = "ulimit -s" 1153 expectedDefaultQueryResponse = "8192" 1154 expectedQueryResponse = "4096" 1155 }) 1156 1157 Context("when user namespacing is disabled", func() { 1158 BeforeEach(func() { 1159 userNs = "disabled" 1160 }) 1161 It("defaults the rlimit when the environment variable is not set", func() { 1162 shouldSetRlimit([]string{}, limitQueryCmd, expectedDefaultQueryResponse) 1163 }) 1164 It("overrides the rlimit when the environment variable is set", func() { 1165 shouldSetRlimit(overrideEnv, limitQueryCmd, expectedQueryResponse) 1166 }) 1167 }) 1168 1169 Context("when user namespacing is enabled", func() { 1170 BeforeEach(func() { 1171 userNs = "enabled" 1172 }) 1173 It("defaults the rlimit when the environment variable is not set", func() { 1174 shouldSetRlimit([]string{}, limitQueryCmd, expectedDefaultQueryResponse) 1175 }) 1176 It("overrides the rlimit when the environment variable is set", func() { 1177 shouldSetRlimit(overrideEnv, limitQueryCmd, expectedQueryResponse) 1178 }) 1179 }) 1180 }) 1181 }) 1182 }) 1183 1184 func copyFile(src, dst string) error { 1185 s, err := os.Open(src) 1186 if err != nil { 1187 return err 1188 } 1189 1190 defer s.Close() 1191 1192 d, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE, 0755) 1193 if err != nil { 1194 return err 1195 } 1196 1197 _, err = io.Copy(d, s) 1198 if err != nil { 1199 d.Close() 1200 return err 1201 } 1202 1203 return d.Close() 1204 } 1205 1206 func ErrorDialingUnix(socketPath string) func() error { 1207 return func() error { 1208 conn, err := net.Dial("unix", socketPath) 1209 if err == nil { 1210 conn.Close() 1211 } 1212 1213 return err 1214 } 1215 } 1216 1217 func getAndReduceRlimit(rlimitResource int, limitVal uint64) *syscall.Rlimit { 1218 var curLimit syscall.Rlimit 1219 Expect(syscall.Getrlimit(rlimitResource, &curLimit)).To(Succeed()) 1220 1221 Expect(syscall.Setrlimit(rlimitResource, &syscall.Rlimit{Cur: limitVal, Max: limitVal})).To(Succeed()) 1222 return &curLimit 1223 }