1// +build linux,cgo,seccomp 2 3package integration 4 5import ( 6 "strings" 7 "syscall" 8 "testing" 9 10 "github.com/opencontainers/runc/libcontainer" 11 "github.com/opencontainers/runc/libcontainer/configs" 12 libseccomp "github.com/seccomp/libseccomp-golang" 13) 14 15func TestSeccompDenyGetcwd(t *testing.T) { 16 if testing.Short() { 17 return 18 } 19 20 rootfs, err := newRootfs() 21 if err != nil { 22 t.Fatal(err) 23 } 24 defer remove(rootfs) 25 26 config := newTemplateConfig(rootfs) 27 config.Seccomp = &configs.Seccomp{ 28 DefaultAction: configs.Allow, 29 Syscalls: []*configs.Syscall{ 30 { 31 Name: "getcwd", 32 Action: configs.Errno, 33 }, 34 }, 35 } 36 37 container, err := newContainer(config) 38 if err != nil { 39 t.Fatal(err) 40 } 41 defer container.Destroy() 42 43 buffers := newStdBuffers() 44 pwd := &libcontainer.Process{ 45 Cwd: "/", 46 Args: []string{"pwd"}, 47 Env: standardEnvironment, 48 Stdin: buffers.Stdin, 49 Stdout: buffers.Stdout, 50 Stderr: buffers.Stderr, 51 } 52 53 err = container.Start(pwd) 54 if err != nil { 55 t.Fatal(err) 56 } 57 ps, err := pwd.Wait() 58 if err == nil { 59 t.Fatal("Expecting error (negative return code); instead exited cleanly!") 60 } 61 62 var exitCode int 63 status := ps.Sys().(syscall.WaitStatus) 64 if status.Exited() { 65 exitCode = status.ExitStatus() 66 } else if status.Signaled() { 67 exitCode = -int(status.Signal()) 68 } else { 69 t.Fatalf("Unrecognized exit reason!") 70 } 71 72 if exitCode == 0 { 73 t.Fatalf("Getcwd should fail with negative exit code, instead got %d!", exitCode) 74 } 75 76 expected := "pwd: getcwd: Operation not permitted" 77 actual := strings.Trim(buffers.Stderr.String(), "\n") 78 if actual != expected { 79 t.Fatalf("Expected output %s but got %s\n", expected, actual) 80 } 81} 82 83func TestSeccompPermitWriteConditional(t *testing.T) { 84 if testing.Short() { 85 return 86 } 87 88 rootfs, err := newRootfs() 89 if err != nil { 90 t.Fatal(err) 91 } 92 defer remove(rootfs) 93 94 config := newTemplateConfig(rootfs) 95 config.Seccomp = &configs.Seccomp{ 96 DefaultAction: configs.Allow, 97 Syscalls: []*configs.Syscall{ 98 { 99 Name: "write", 100 Action: configs.Errno, 101 Args: []*configs.Arg{ 102 { 103 Index: 0, 104 Value: 1, 105 Op: configs.GreaterThan, 106 }, 107 }, 108 }, 109 }, 110 } 111 112 container, err := newContainer(config) 113 if err != nil { 114 t.Fatal(err) 115 } 116 defer container.Destroy() 117 118 buffers := newStdBuffers() 119 dmesg := &libcontainer.Process{ 120 Cwd: "/", 121 Args: []string{"busybox", "ls", "/"}, 122 Env: standardEnvironment, 123 Stdin: buffers.Stdin, 124 Stdout: buffers.Stdout, 125 Stderr: buffers.Stderr, 126 } 127 128 err = container.Start(dmesg) 129 if err != nil { 130 t.Fatal(err) 131 } 132 if _, err := dmesg.Wait(); err != nil { 133 t.Fatalf("%s: %s", err, buffers.Stderr) 134 } 135} 136 137func TestSeccompDenyWriteConditional(t *testing.T) { 138 if testing.Short() { 139 return 140 } 141 142 // Only test if library version is v2.2.1 or higher 143 // Conditional filtering will always error in v2.2.0 and lower 144 major, minor, micro := libseccomp.GetLibraryVersion() 145 if (major == 2 && minor < 2) || (major == 2 && minor == 2 && micro < 1) { 146 return 147 } 148 149 rootfs, err := newRootfs() 150 if err != nil { 151 t.Fatal(err) 152 } 153 defer remove(rootfs) 154 155 config := newTemplateConfig(rootfs) 156 config.Seccomp = &configs.Seccomp{ 157 DefaultAction: configs.Allow, 158 Syscalls: []*configs.Syscall{ 159 { 160 Name: "write", 161 Action: configs.Errno, 162 Args: []*configs.Arg{ 163 { 164 Index: 0, 165 Value: 1, 166 Op: configs.GreaterThan, 167 }, 168 }, 169 }, 170 }, 171 } 172 173 container, err := newContainer(config) 174 if err != nil { 175 t.Fatal(err) 176 } 177 defer container.Destroy() 178 179 buffers := newStdBuffers() 180 dmesg := &libcontainer.Process{ 181 Cwd: "/", 182 Args: []string{"busybox", "ls", "does_not_exist"}, 183 Env: standardEnvironment, 184 Stdin: buffers.Stdin, 185 Stdout: buffers.Stdout, 186 Stderr: buffers.Stderr, 187 } 188 189 err = container.Start(dmesg) 190 if err != nil { 191 t.Fatal(err) 192 } 193 194 ps, err := dmesg.Wait() 195 if err == nil { 196 t.Fatal("Expecting negative return, instead got 0!") 197 } 198 199 var exitCode int 200 status := ps.Sys().(syscall.WaitStatus) 201 if status.Exited() { 202 exitCode = status.ExitStatus() 203 } else if status.Signaled() { 204 exitCode = -int(status.Signal()) 205 } else { 206 t.Fatalf("Unrecognized exit reason!") 207 } 208 209 if exitCode == 0 { 210 t.Fatalf("Busybox should fail with negative exit code, instead got %d!", exitCode) 211 } 212 213 // We're denying write to stderr, so we expect an empty buffer 214 expected := "" 215 actual := strings.Trim(buffers.Stderr.String(), "\n") 216 if actual != expected { 217 t.Fatalf("Expected output %s but got %s\n", expected, actual) 218 } 219} 220