@@ -18,6 +18,7 @@ import (
18
18
19
19
"github.com/stretchr/testify/require"
20
20
"golang.org/x/sys/unix"
21
+ "golang.org/x/xerrors"
21
22
22
23
"github.com/coder/coder/v2/testutil"
23
24
)
@@ -50,6 +51,32 @@ func TestCLI(t *testing.T) {
50
51
requireOOMScore (t , cmd .Process .Pid , expectedOOM )
51
52
requireNiceScore (t , cmd .Process .Pid , expectedNice )
52
53
})
54
+
55
+ t .Run ("Capabilities" , func (t * testing.T ) {
56
+ testdir := filepath .Dir (TestBin )
57
+ capDir := filepath .Join (testdir , "caps" )
58
+ err := os .Mkdir (capDir , 0o755 )
59
+ require .NoError (t , err )
60
+ bin := buildBinary (capDir )
61
+ // Try to set capabilities on the binary. This should work fine in CI but
62
+ // it's possible some developers may be working in an environment where they don't have the necessary permissions.
63
+ err = setCaps (t , bin , "cap_net_admin" )
64
+ if os .Getenv ("CI" ) != "" {
65
+ require .NoError (t , err )
66
+ } else if err != nil {
67
+ t .Skipf ("unable to set capabilities for test: %v" , err )
68
+ }
69
+ ctx := testutil .Context (t , testutil .WaitMedium )
70
+ cmd , path := binCmd (ctx , t , bin , 123 , 12 )
71
+ err = cmd .Start ()
72
+ require .NoError (t , err )
73
+ go cmd .Wait ()
74
+
75
+ waitForSentinel (ctx , t , cmd , path )
76
+ // This is what we're really testing, a binary with added capabilities requires setting dumpable.
77
+ requireOOMScore (t , cmd .Process .Pid , 123 )
78
+ requireNiceScore (t , cmd .Process .Pid , 12 )
79
+ })
53
80
}
54
81
55
82
func requireNiceScore (t * testing.T , pid int , score int ) {
@@ -94,7 +121,7 @@ func waitForSentinel(ctx context.Context, t *testing.T, cmd *exec.Cmd, path stri
94
121
}
95
122
}
96
123
97
- func cmd (ctx context.Context , t * testing.T , oom , nice int ) (* exec.Cmd , string ) {
124
+ func binCmd (ctx context.Context , t * testing.T , bin string , oom , nice int ) (* exec.Cmd , string ) {
98
125
var (
99
126
args = execArgs (oom , nice )
100
127
dir = t .TempDir ()
@@ -103,7 +130,7 @@ func cmd(ctx context.Context, t *testing.T, oom, nice int) (*exec.Cmd, string) {
103
130
104
131
args = append (args , "sh" , "-c" , fmt .Sprintf ("touch %s && sleep 10m" , file ))
105
132
//nolint:gosec
106
- cmd := exec .CommandContext (ctx , TestBin , args ... )
133
+ cmd := exec .CommandContext (ctx , bin , args ... )
107
134
108
135
// We set this so we can also easily kill the sleep process the shell spawns.
109
136
cmd .SysProcAttr = & syscall.SysProcAttr {
@@ -127,6 +154,10 @@ func cmd(ctx context.Context, t *testing.T, oom, nice int) (*exec.Cmd, string) {
127
154
return cmd , file
128
155
}
129
156
157
+ func cmd (ctx context.Context , t * testing.T , oom , nice int ) (* exec.Cmd , string ) {
158
+ return binCmd (ctx , t , TestBin , oom , nice )
159
+ }
160
+
130
161
func expectedOOMScore (t * testing.T ) int {
131
162
t .Helper ()
132
163
@@ -171,3 +202,14 @@ func execArgs(oom int, nice int) []string {
171
202
execArgs = append (execArgs , "--" )
172
203
return execArgs
173
204
}
205
+
206
+ func setCaps (t * testing.T , bin string , caps ... string ) error {
207
+ t .Helper ()
208
+
209
+ setcap := fmt .Sprintf ("sudo -n setcap %s=ep %s" , strings .Join (caps , ", " ), bin )
210
+ out , err := exec .CommandContext (context .Background (), "sh" , "-c" , setcap ).CombinedOutput ()
211
+ if err != nil {
212
+ return xerrors .Errorf ("setcap %q (%s): %w" , setcap , out , err )
213
+ }
214
+ return nil
215
+ }
0 commit comments