Skip to content

Commit 4c315d7

Browse files
committed
C-WCOW: Add SecurityPolicy tests
Signed-off-by: Mahati Chamarthy <mchamarthy@microsoft.com>
1 parent 0f887a7 commit 4c315d7

File tree

12 files changed

+4896
-2585
lines changed

12 files changed

+4896
-2585
lines changed

internal/gcs-sidecar/handlers.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -322,11 +322,11 @@ func (b *Bridge) executeProcess(req *request) (err error) {
322322
// during container creation, hence skip it here
323323
containerCommandLine := escapeArgs(c.spec.Process.Args)
324324
if processParams.CommandLine != containerCommandLine {
325-
opts := &securitypolicy.ExecOptions{
326-
User: &securitypolicy.IDName{
327-
Name: processParams.User,
328-
},
325+
326+
user := securitypolicy.IDName{
327+
Name: processParams.User,
329328
}
329+
330330
log.G(req.ctx).Tracef("Enforcing policy on exec in container")
331331
_, _, _, err = b.hostState.securityPolicyEnforcer.
332332
EnforceExecInContainerPolicyV2(
@@ -335,7 +335,8 @@ func (b *Bridge) executeProcess(req *request) (err error) {
335335
commandLine,
336336
processParamEnvToOCIEnv(processParams.Environment),
337337
processParams.WorkingDirectory,
338-
opts,
338+
user,
339+
nil,
339340
)
340341
if err != nil {
341342
return errors.Wrapf(err, "exec in container denied due to policy")
@@ -434,10 +435,11 @@ func (b *Bridge) signalProcess(req *request) (err error) {
434435
return err
435436
}
436437
cmdLine := p.processspec.CommandLine
438+
commandLine := []string{cmdLine}
437439
opts := &securitypolicy.SignalContainerOptions{
438440
IsInitProcess: false,
439441
WindowsSignal: wcowOptions.Signal,
440-
WindowsCommand: cmdLine,
442+
WindowsCommand: commandLine,
441443
}
442444
err = b.hostState.securityPolicyEnforcer.EnforceSignalContainerProcessPolicyV2(req.ctx, containerID, opts)
443445
if err != nil {

internal/tools/securitypolicy/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ var (
1818
fragmentNamespace = flag.String("n", "", "fragment namespace")
1919
fragmentSVN = flag.String("v", "", "fragment svn")
2020
outputRaw = flag.Bool("r", false, "whether to print the raw output")
21+
osType = flag.String("os", "windows", "OS type for the policy (windows|linux)")
2122
)
2223

2324
func main() {

pkg/securitypolicy/framework.rego

Lines changed: 90 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -289,18 +289,20 @@ privileged_ok(no_new_privileges) {
289289
}
290290

291291
noNewPrivileges_ok(no_new_privileges) {
292-
is_linux
293292
no_new_privileges
294293
input.noNewPrivileges
295294
}
296295

297296
noNewPrivileges_ok(no_new_privileges) {
297+
not no_new_privileges
298+
}
299+
300+
noNewPrivileges_ok_check(obj) {
298301
is_linux
299-
no_new_privileges == false
302+
noNewPrivileges_ok(obj.no_new_privileges)
300303
}
301304

302-
noNewPrivileges_ok(no_new_privileges) {
303-
# no-op for windows
305+
noNewPrivileges_ok_check(obj) {
304306
is_windows
305307
}
306308

@@ -353,6 +355,7 @@ container_started {
353355
default container_privileged := false
354356

355357
container_privileged {
358+
is_linux
356359
data.metadata.started[input.containerID].privileged
357360
}
358361

@@ -472,12 +475,6 @@ valid_caps_for_all(containers, privileged) := caps {
472475
caps := input.capabilities
473476
}
474477

475-
valid_caps_for_all(containers, privileged) := caps {
476-
# no-op for windows
477-
is_windows
478-
caps := input.capabilities
479-
}
480-
481478
caps_ok(allowed_caps, requested_caps) {
482479
is_linux
483480
capsList_ok(allowed_caps.bounding, requested_caps.bounding)
@@ -552,13 +549,10 @@ create_container := {"metadata": [updateMatches, addStarted],
552549
# NB any change to these narrowing conditions should be reflected in
553550
# the error handling, such that error messaging correctly reflects
554551
# the narrowing process.
555-
noNewPrivileges_ok(container.no_new_privileges)
552+
security_ok(container)
556553
user_ok(container.user)
557-
privileged_ok(container.allow_elevated)
558554
workingDirectory_ok(container.working_dir)
559555
command_ok(container.command)
560-
mountList_ok(container.mounts, container.allow_elevated)
561-
seccomp_ok(container.seccomp_profile_sha256)
562556
]
563557

564558
count(possible_after_initial_containers) > 0
@@ -575,7 +569,8 @@ create_container := {"metadata": [updateMatches, addStarted],
575569

576570
# check to see if the capabilities variables match, dropping
577571
# them if allowed (and necessary)
578-
caps_result := possible_container_after_caps(possible_after_env_containers, input.privileged)
572+
is_exec_eval := false
573+
caps_result := possible_container_after_caps(possible_after_env_containers, is_exec_eval)
579574

580575
possible_after_caps_containers := caps_result.containers
581576
caps_list := caps_result.caps_list
@@ -601,34 +596,72 @@ create_container := {"metadata": [updateMatches, addStarted],
601596
"value": containers,
602597
}
603598

604-
addStarted := {
605-
"name": "started",
606-
"action": "add",
607-
"key": input.containerID,
608-
"value": {
609-
"privileged": input.privileged,
610-
},
611-
}
599+
addStarted := addStarted_filter
612600
}
613-
possible_container_after_caps(env_containers, privileged) := {
601+
602+
addStarted_filter := {
603+
"name": "started",
604+
"action": "add",
605+
"key": input.containerID,
606+
"value": {
607+
"privileged": false,
608+
},
609+
} {
610+
is_windows
611+
}
612+
613+
addStarted_filter := {
614+
"name": "started",
615+
"action": "add",
616+
"key": input.containerID,
617+
"value": {
618+
"privileged": input.privileged,
619+
},
620+
} {
621+
is_linux
622+
}
623+
624+
security_ok(current_container) {
625+
is_linux
626+
noNewPrivileges_ok(current_container.no_new_privileges)
627+
privileged_ok(current_container.allow_elevated)
628+
seccomp_ok(current_container.seccomp_profile_sha256)
629+
mountList_ok(current_container.mounts, current_container.allow_elevated)
630+
}
631+
632+
security_ok(current_container) {
633+
is_windows
634+
}
635+
636+
possible_container_after_caps(env_containers, is_exec) := {
614637
"containers": env_containers,
615638
"caps_list": []
616639
} {
617640
is_windows
618641
}
619642

620-
possible_container_after_caps(env_containers, privileged) := {
643+
possible_container_after_caps(env_containers, is_exec) := {
621644
"containers": filtered,
622645
"caps_list": caps_list
623646
} {
624647
is_linux
625-
caps_list := valid_caps_for_all(env_containers, privileged)
648+
is_privileged := get_privileged_value(is_exec)
649+
650+
caps_list := valid_caps_for_all(env_containers, is_privileged)
626651
filtered := [container |
627652
container := env_containers[_]
628-
caps_ok(get_capabilities(container, privileged), caps_list)
653+
caps_ok(get_capabilities(container, input.privileged), caps_list)
629654
]
630655
}
631656

657+
get_privileged_value(is_exec) := container_privileged {
658+
is_exec
659+
}
660+
661+
get_privileged_value(is_exec) := input.privileged {
662+
not is_exec
663+
}
664+
632665
mountSource_ok(constraint, source) {
633666
startswith(constraint, data.sandboxPrefix)
634667
newConstraint := replace(constraint, data.sandboxPrefix, input.sandboxDir)
@@ -723,7 +756,7 @@ exec_in_container := {"metadata": [updateMatches],
723756
# the error handling, such that error messaging correctly reflects
724757
# the narrowing process.
725758
workingDirectory_ok(container.working_dir)
726-
noNewPrivileges_ok(container.no_new_privileges)
759+
#noNewPrivileges_ok(container.no_new_privileges)
727760
user_ok(container.user)
728761
some process in container.exec_processes
729762
command_ok(process.command)
@@ -743,7 +776,8 @@ exec_in_container := {"metadata": [updateMatches],
743776

744777
# check to see if the capabilities variables match, dropping
745778
# them if allowed (and necessary)
746-
caps_result := possible_container_after_caps(possible_after_env_containers, container_privileged)
779+
is_exec_eval := true
780+
caps_result := possible_container_after_caps(possible_after_env_containers, is_exec_eval)
747781

748782
possible_after_caps_containers := caps_result.containers
749783
caps_list := caps_result.caps_list
@@ -761,6 +795,16 @@ exec_in_container := {"metadata": [updateMatches],
761795
}
762796
}
763797

798+
noNewPrivileges(current_container) {
799+
is_linux
800+
current_container.no_new_privileges
801+
input.noNewPrivileges
802+
}
803+
804+
noNewPrivileges(current_container) {
805+
is_windows
806+
}
807+
764808
default shutdown_container := {"allowed": false}
765809

766810
shutdown_container := {"started": remove, "metadata": [remove], "allowed": true} {
@@ -1271,7 +1315,7 @@ errors["missing required environment variable"] {
12711315
not container_started
12721316
possible_containers := [container |
12731317
container := data.metadata.matches[input.containerID][_]
1274-
noNewPrivileges_ok(container.no_new_privileges)
1318+
noNewPrivileges_ok_check(container)
12751319
user_ok(container.user)
12761320
privileged_ok(container.allow_elevated)
12771321
workingDirectory_ok(container.working_dir)
@@ -1303,7 +1347,7 @@ errors["missing required environment variable"] {
13031347
container_started
13041348
possible_containers := [container |
13051349
container := data.metadata.matches[input.containerID][_]
1306-
noNewPrivileges_ok(container.no_new_privileges)
1350+
noNewPrivileges_ok_check(container)
13071351
user_ok(container.user)
13081352
workingDirectory_ok(container.working_dir)
13091353
some process in container.exec_processes
@@ -1529,19 +1573,21 @@ errors[fragment_framework_version_error] {
15291573
}
15301574

15311575
errors["containers only distinguishable by allow_stdio_access"] {
1532-
is_linux
15331576
input.rule == "create_container"
15341577

1535-
not container_started
1578+
not container_started
1579+
1580+
# narrow the matches based upon command, working directory, and
1581+
# mount list
15361582
possible_after_initial_containers := [container |
15371583
container := data.metadata.matches[input.containerID][_]
1538-
noNewPrivileges_ok(container.no_new_privileges)
1584+
# NB any change to these narrowing conditions should be reflected in
1585+
# the error handling, such that error messaging correctly reflects
1586+
# the narrowing process.
1587+
security_ok(container)
15391588
user_ok(container.user)
1540-
privileged_ok(container.allow_elevated)
15411589
workingDirectory_ok(container.working_dir)
15421590
command_ok(container.command)
1543-
mountList_ok(container.mounts, container.allow_elevated)
1544-
seccomp_ok(container.seccomp_profile_sha256)
15451591
]
15461592

15471593
count(possible_after_initial_containers) > 0
@@ -1558,11 +1604,11 @@ errors["containers only distinguishable by allow_stdio_access"] {
15581604

15591605
# check to see if the capabilities variables match, dropping
15601606
# them if allowed (and necessary)
1561-
caps_list := valid_caps_for_all(possible_after_env_containers, input.privileged)
1562-
possible_after_caps_containers := [container |
1563-
container := possible_after_env_containers[_]
1564-
caps_ok(get_capabilities(container, input.privileged), caps_list)
1565-
]
1607+
is_exec_eval := false
1608+
caps_result := possible_container_after_caps(possible_after_env_containers, is_exec_eval)
1609+
1610+
possible_after_caps_containers := caps_result.containers
1611+
caps_list := caps_result.caps_list
15661612

15671613
count(possible_after_caps_containers) > 0
15681614

@@ -1606,7 +1652,7 @@ default noNewPrivileges_matches := false
16061652
noNewPrivileges_matches {
16071653
input.rule == "create_container"
16081654
some container in data.metadata.matches[input.containerID]
1609-
noNewPrivileges_ok(container.no_new_privileges)
1655+
noNewPrivileges_ok_check(container)
16101656
}
16111657

16121658
noNewPrivileges_matches {
@@ -1615,7 +1661,7 @@ noNewPrivileges_matches {
16151661
some process in container.exec_processes
16161662
command_ok(process.command)
16171663
workingDirectory_ok(process.working_dir)
1618-
noNewPrivileges_ok(process.no_new_privileges)
1664+
noNewPrivileges_ok_check(process)
16191665
}
16201666

16211667
errors["invalid noNewPrivileges"] {

0 commit comments

Comments
 (0)