diff --git a/internal/cosesign1/infra.rego b/internal/cosesign1/infra.rego index 4c0bc58abd..fbc8073f6f 100644 --- a/internal/cosesign1/infra.rego +++ b/internal/cosesign1/infra.rego @@ -1,6 +1,7 @@ package infra -svn := "1.0.0" +svn := "1" +framework_version := "0.1.0" containers := [ { diff --git a/internal/tools/policyenginesimulator/README.md b/internal/tools/policyenginesimulator/README.md index a6c4f9fbf7..4075495eef 100644 --- a/internal/tools/policyenginesimulator/README.md +++ b/internal/tools/policyenginesimulator/README.md @@ -138,7 +138,7 @@ for more detail. ``` rego package policy -api_svn := "0.7.0" +api_version := "0.7.0" import future.keywords.every import future.keywords.in @@ -187,7 +187,7 @@ reason := {"errors": data.framework.errors} ``` rego package policy -api_svn := "0.7.0" +api_version := "0.7.0" overlays := { "pause": { diff --git a/internal/tools/policyenginesimulator/samples/simple_custom/policy.rego b/internal/tools/policyenginesimulator/samples/simple_custom/policy.rego index ff4857b8be..61aba3e66b 100644 --- a/internal/tools/policyenginesimulator/samples/simple_custom/policy.rego +++ b/internal/tools/policyenginesimulator/samples/simple_custom/policy.rego @@ -1,6 +1,6 @@ package policy -api_svn := "0.7.0" +api_version := "0.7.0" overlays := { "pause": { diff --git a/internal/tools/policyenginesimulator/samples/simple_framework/commands.json b/internal/tools/policyenginesimulator/samples/simple_framework/commands.json index f36fbe58ce..75452a087a 100644 --- a/internal/tools/policyenginesimulator/samples/simple_framework/commands.json +++ b/internal/tools/policyenginesimulator/samples/simple_framework/commands.json @@ -2,223 +2,940 @@ { "name": "load_fragment", "input": { - "issuer": "did:web:contoso.github.io", - "feed": "contoso.azurecr.io/fragment", + "feed": "contoso.azurecr.io/infra", + "issuer": "did:web:contoso.com", "namespace": "fragment", - "local_path": "fragment.rego" + "local_path": "relative/path/to/local/fragment.rego" + } + }, + { + "name": "exec_external", + "input": { + "argList": [ + "bash" + ], + "envList": [ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + ], + "workingDir": "/" + } + }, + { + "name": "mount_device", + "input": { + "deviceHash": "1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766", + "target": "/run/layers/p0-layer0" + } + }, + { + "name": "mount_device", + "input": { + "deviceHash": "e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c", + "target": "/run/layers/p0-layer1" + } + }, + { + "name": "mount_device", + "input": { + "deviceHash": "eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79", + "target": "/run/layers/p0-layer2" + } + }, + { + "name": "mount_device", + "input": { + "deviceHash": "41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156", + "target": "/run/layers/p0-layer3" + } + }, + { + "name": "mount_device", + "input": { + "deviceHash": "4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c", + "target": "/run/layers/p0-layer4" } }, { "name": "mount_device", "input": { - "target": "/mnt/layer0", - "deviceHash": "16b514057a06ad665f92c02863aca074fd5976c755d26bff16365299169e8415" + "deviceHash": "fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a", + "target": "/run/layers/p0-layer5" } }, { "name": "mount_overlay", "input": { - "target": "/mnt/overlay0", "containerID": "container0", "layerPaths": [ - "/mnt/layer0" - ] + "/run/layers/p0-layer0", + "/run/layers/p0-layer1", + "/run/layers/p0-layer2", + "/run/layers/p0-layer3", + "/run/layers/p0-layer4", + "/run/layers/p0-layer5" + ], + "target": "/run/gcs/c/container0/rootfs" + } + }, + { + "name": "scratch_mount", + "input": { + "encrypted": true, + "target": "/mnt/layer6" } }, { "name": "create_container", "input": { + "argList": [ + "rustc", + "--help" + ], + "capabilities": { + "ambient": [ + "CAP_SYS_ADMIN" + ], + "bounding": [ + "CAP_SYS_ADMIN" + ], + "effective": [ + "CAP_SYS_ADMIN" + ], + "inheritable": [ + "CAP_SYS_ADMIN" + ], + "permitted": [ + "CAP_SYS_ADMIN" + ] + }, + "containerID": "container0", + "envList": [ + "CARGO_HOME=/usr/local/cargo", + "RUST_VERSION=1.52.1", + "TERM=xterm", + "PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "RUSTUP_HOME=/usr/local/rustup" + ], + "groups": [ + { + "id": "0", + "name": "root" + } + ], + "hugePagesDir": "/run/gcs/c/sandbox0/hugepages", + "mounts": [ + { + "destination": "/container/path/one", + "options": [ + "rbind", + "rshared", + "rw" + ], + "source": "/run/gcs/c/sandbox0/sandboxMounts/host/path/one", + "type": "bind" + }, + { + "destination": "/container/path/two", + "options": [ + "rbind", + "rshared", + "ro" + ], + "source": "/run/gcs/c/sandbox0/sandboxMounts/host/path/two", + "type": "bind" + } + ], + "noNewPrivileges": true, + "privileged": false, + "sandboxDir": "/run/gcs/c/sandbox0/sandboxMounts", + "seccompProfileSHA256": "", + "umask": "0022", + "user": { + "id": "0", + "name": "root" + }, + "workingDir": "/home/user" + } + }, + { + "name": "exec_in_container", + "input": { + "argList": [ + "top" + ], "containerID": "container0", + "envList": [ + "CARGO_HOME=/usr/local/cargo", + "RUST_VERSION=1.52.1", + "TERM=xterm", + "PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "RUSTUP_HOME=/usr/local/rustup" + ], + "workingDir": "/home/user" + } + }, + { + "name": "shutdown_container", + "input": { + "containerID": "container0" + } + }, + { + "name": "scratch_unmount", + "input": { + "unmountTarget": "/mnt/layer6" + } + }, + { + "name": "unmount_overlay", + "input": { + "unmountTarget": "/run/gcs/c/container0/rootfs" + } + }, + { + "name": "unmount_device", + "input": { + "unmountTarget": "/run/layers/p0-layer0" + } + }, + { + "name": "unmount_device", + "input": { + "unmountTarget": "/run/layers/p0-layer1" + } + }, + { + "name": "unmount_device", + "input": { + "unmountTarget": "/run/layers/p0-layer2" + } + }, + { + "name": "unmount_device", + "input": { + "unmountTarget": "/run/layers/p0-layer3" + } + }, + { + "name": "unmount_device", + "input": { + "unmountTarget": "/run/layers/p0-layer4" + } + }, + { + "name": "unmount_device", + "input": { + "unmountTarget": "/run/layers/p0-layer5" + } + }, + { + "name": "mount_device", + "input": { + "deviceHash": "1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766", + "target": "/run/layers/p0-layer7" + } + }, + { + "name": "mount_device", + "input": { + "deviceHash": "e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c", + "target": "/run/layers/p0-layer8" + } + }, + { + "name": "mount_device", + "input": { + "deviceHash": "eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79", + "target": "/run/layers/p0-layer9" + } + }, + { + "name": "mount_device", + "input": { + "deviceHash": "41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156", + "target": "/run/layers/p0-layer10" + } + }, + { + "name": "mount_device", + "input": { + "deviceHash": "4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c", + "target": "/run/layers/p0-layer11" + } + }, + { + "name": "mount_device", + "input": { + "deviceHash": "fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a", + "target": "/run/layers/p0-layer12" + } + }, + { + "name": "mount_overlay", + "input": { + "containerID": "container1", + "layerPaths": [ + "/run/layers/p0-layer7", + "/run/layers/p0-layer8", + "/run/layers/p0-layer9", + "/run/layers/p0-layer10", + "/run/layers/p0-layer11", + "/run/layers/p0-layer12" + ], + "target": "/run/gcs/c/container1/rootfs" + } + }, + { + "name": "scratch_mount", + "input": { + "encrypted": true, + "target": "/mnt/layer13" + } + }, + { + "name": "create_container", + "input": { + "argList": [ + "rustc", + "--help" + ], + "capabilities": { + "ambient": [ + "CAP_SYS_ADMIN" + ], + "bounding": [ + "CAP_SYS_ADMIN" + ], + "effective": [ + "CAP_SYS_ADMIN" + ], + "inheritable": [ + "CAP_SYS_ADMIN" + ], + "permitted": [ + "CAP_SYS_ADMIN" + ] + }, + "containerID": "container1", + "envList": [ + "PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "RUSTUP_HOME=/usr/local/rustup", + "CARGO_HOME=/usr/local/cargo", + "RUST_VERSION=1.52.1", + "TERM=xterm" + ], + "groups": [ + { + "id": "0", + "name": "root" + } + ], + "hugePagesDir": "/run/gcs/c/sandbox1/hugepages", + "mounts": [ + { + "destination": "/container/path/one", + "options": [ + "rbind", + "rshared", + "rw" + ], + "source": "/run/gcs/c/sandbox1/sandboxMounts/host/path/one", + "type": "bind" + }, + { + "destination": "/container/path/two", + "options": [ + "rbind", + "rshared", + "ro" + ], + "source": "/run/gcs/c/sandbox1/sandboxMounts/host/path/two", + "type": "bind" + } + ], + "noNewPrivileges": true, + "privileged": true, + "sandboxDir": "/run/gcs/c/sandbox1/sandboxMounts", + "seccompProfileSHA256": "", + "umask": "0022", + "user": { + "id": "0", + "name": "root" + }, + "workingDir": "/home/user" + } + }, + { + "name": "exec_in_container", + "input": { + "argList": [ + "top" + ], + "containerID": "container1", + "envList": [ + "PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "RUSTUP_HOME=/usr/local/rustup", + "CARGO_HOME=/usr/local/cargo", + "RUST_VERSION=1.52.1", + "TERM=xterm" + ], + "workingDir": "/home/user" + } + }, + { + "name": "shutdown_container", + "input": { + "containerID": "container1" + } + }, + { + "name": "scratch_unmount", + "input": { + "unmountTarget": "/mnt/layer13" + } + }, + { + "name": "unmount_overlay", + "input": { + "unmountTarget": "/run/gcs/c/container1/rootfs" + } + }, + { + "name": "unmount_device", + "input": { + "unmountTarget": "/run/layers/p0-layer7" + } + }, + { + "name": "unmount_device", + "input": { + "unmountTarget": "/run/layers/p0-layer8" + } + }, + { + "name": "unmount_device", + "input": { + "unmountTarget": "/run/layers/p0-layer9" + } + }, + { + "name": "unmount_device", + "input": { + "unmountTarget": "/run/layers/p0-layer10" + } + }, + { + "name": "unmount_device", + "input": { + "unmountTarget": "/run/layers/p0-layer11" + } + }, + { + "name": "unmount_device", + "input": { + "unmountTarget": "/run/layers/p0-layer12" + } + }, + { + "name": "mount_device", + "input": { + "deviceHash": "16b514057a06ad665f92c02863aca074fd5976c755d26bff16365299169e8415", + "target": "/run/layers/p0-layer14" + } + }, + { + "name": "mount_overlay", + "input": { + "containerID": "container2", + "layerPaths": [ + "/run/layers/p0-layer14" + ], + "target": "/run/gcs/c/container2/rootfs" + } + }, + { + "name": "scratch_mount", + "input": { + "encrypted": true, + "target": "/mnt/layer15" + } + }, + { + "name": "create_container", + "input": { "argList": [ "/pause" ], + "capabilities": null, + "containerID": "container2", "envList": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "TERM=xterm" ], + "groups": [ + { + "id": "0", + "name": "root" + } + ], + "hugePagesDir": "/run/gcs/c/sandbox2/hugepages", "mounts": [], - "workingDir": "/", - "sandboxDir": "/sandbox", - "hugePagesDir": "/hugepages" + "noNewPrivileges": true, + "privileged": false, + "sandboxDir": "/run/gcs/c/sandbox2/sandboxMounts", + "seccompProfileSHA256": "", + "umask": "0022", + "user": { + "id": "0", + "name": "root" + }, + "workingDir": "/" } }, { - "name": "mount_device", + "name": "shutdown_container", "input": { - "target": "/mnt/layer1", - "deviceHash": "998fe7a12356e0de0f2ffb4134615b42c9510e281c0ecfc7628c121442544309" + "containerID": "container2" } }, { - "name": "mount_device", + "name": "scratch_unmount", "input": { - "target": "/mnt/layer2", - "deviceHash": "f65ec804a63b85f507ac11d187434ea135a18cdc16202551d8dff292f942fdf0" + "unmountTarget": "/mnt/layer15" + } + }, + { + "name": "unmount_overlay", + "input": { + "unmountTarget": "/run/gcs/c/container2/rootfs" + } + }, + { + "name": "unmount_device", + "input": { + "unmountTarget": "/run/layers/p0-layer14" } }, { "name": "mount_device", "input": { - "target": "/mnt/layer3", - "deviceHash": "04c110e9406d2b57079f1eac4c9c5247747caa3bcaab6d83651de6e7da97cb40" + "deviceHash": "16b514057a06ad665f92c02863aca074fd5976c755d26bff16365299169e8415", + "target": "/run/layers/p0-layer16" + } + }, + { + "name": "mount_overlay", + "input": { + "containerID": "container3", + "layerPaths": [ + "/run/layers/p0-layer16" + ], + "target": "/run/gcs/c/container3/rootfs" + } + }, + { + "name": "scratch_mount", + "input": { + "encrypted": true, + "target": "/mnt/layer17" + } + }, + { + "name": "create_container", + "input": { + "argList": [ + "/pause" + ], + "capabilities": null, + "containerID": "container3", + "envList": [ + "TERM=xterm", + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + ], + "groups": [ + { + "id": "0", + "name": "root" + } + ], + "hugePagesDir": "/run/gcs/c/sandbox3/hugepages", + "mounts": [], + "noNewPrivileges": true, + "privileged": true, + "sandboxDir": "/run/gcs/c/sandbox3/sandboxMounts", + "seccompProfileSHA256": "", + "umask": "0022", + "user": { + "id": "0", + "name": "root" + }, + "workingDir": "/" + } + }, + { + "name": "shutdown_container", + "input": { + "containerID": "container3" + } + }, + { + "name": "scratch_unmount", + "input": { + "unmountTarget": "/mnt/layer17" + } + }, + { + "name": "unmount_overlay", + "input": { + "unmountTarget": "/run/gcs/c/container3/rootfs" + } + }, + { + "name": "unmount_device", + "input": { + "unmountTarget": "/run/layers/p0-layer16" } }, { "name": "mount_device", "input": { - "target": "/mnt/layer4", - "deviceHash": "e7fbe653352d546497c534c629269c4c04f1997f6892bd66c273f0c9753a4de3" + "deviceHash": "1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766", + "target": "/run/layers/p0-layer18" } }, { "name": "mount_device", "input": { - "target": "/mnt/layer5", - "deviceHash": "b99a9ced77c45fc4dc96bac8ea1e4d9bc1d2a66696cc057d3f3cca79dc999702" + "deviceHash": "e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c", + "target": "/run/layers/p0-layer19" } }, { "name": "mount_device", "input": { - "target": "/mnt/layer6", - "deviceHash": "3413e98a178646d4703ea70b9bff2d4410e606a22062046992cda8c8aedaa387" + "deviceHash": "eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79", + "target": "/run/layers/p0-layer20" } }, { "name": "mount_device", "input": { - "target": "/mnt/layer7", - "deviceHash": "1e66649e162d99c4d675d8d8c3af90ece3799b33d24671bc83fe9ea5143daf2f" + "deviceHash": "41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156", + "target": "/run/layers/p0-layer21" } }, { "name": "mount_device", "input": { - "target": "/mnt/layer8", - "deviceHash": "97112ba1d4a2c86c1c15a3e13f606e8fcc0fb1b49154743cadd1f065c42fee5a" + "deviceHash": "4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c", + "target": "/run/layers/p0-layer22" } }, { "name": "mount_device", "input": { - "target": "/mnt/layer9", - "deviceHash": "37e9dcf799048b7d35ce53584e0984198e1bc3366c3bb5582fd97553d31beb4e" + "deviceHash": "fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a", + "target": "/run/layers/p0-layer23" } }, { "name": "mount_overlay", "input": { - "target": "/mnt/overlay1", - "containerID": "container1", + "containerID": "container4", "layerPaths": [ - "/mnt/layer1", - "/mnt/layer2", - "/mnt/layer3", - "/mnt/layer4", - "/mnt/layer5", - "/mnt/layer6", - "/mnt/layer7", - "/mnt/layer8", - "/mnt/layer9" - ] + "/run/layers/p0-layer18", + "/run/layers/p0-layer19", + "/run/layers/p0-layer20", + "/run/layers/p0-layer21", + "/run/layers/p0-layer22", + "/run/layers/p0-layer23" + ], + "target": "/run/gcs/c/container4/rootfs" + } + }, + { + "name": "scratch_mount", + "input": { + "encrypted": true, + "target": "/mnt/layer24" } }, { "name": "create_container", "input": { - "containerID": "container1", "argList": [ - "python3", - "WebAttestationReport.py" + "rustc", + "--version" ], + "capabilities": null, + "containerID": "container4", "envList": [ - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - "PYTHONUNBUFFERED=1", + "PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "RUSTUP_HOME=/usr/local/rustup", + "CARGO_HOME=/usr/local/cargo", + "RUST_VERSION=1.52.1", "TERM=xterm" ], + "groups": [ + { + "id": "0", + "name": "root" + } + ], + "hugePagesDir": "/run/gcs/c/sandbox4/hugepages", "mounts": [], - "workingDir": "/demo-attestion", - "sandboxDir": "/sandbox", - "hugePagesDir": "/hugepages" + "noNewPrivileges": true, + "privileged": false, + "sandboxDir": "/run/gcs/c/sandbox4/sandboxMounts", + "seccompProfileSHA256": "", + "umask": "0022", + "user": { + "id": "0", + "name": "root" + }, + "workingDir": "/home/fragment" + } + }, + { + "name": "exec_in_container", + "input": { + "argList": [ + "bash" + ], + "containerID": "container4", + "envList": [ + "PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "RUSTUP_HOME=/usr/local/rustup", + "CARGO_HOME=/usr/local/cargo", + "RUST_VERSION=1.52.1", + "TERM=xterm" + ], + "workingDir": "/home/fragment" + } + }, + { + "name": "shutdown_container", + "input": { + "containerID": "container4" + } + }, + { + "name": "scratch_unmount", + "input": { + "unmountTarget": "/mnt/layer24" + } + }, + { + "name": "unmount_overlay", + "input": { + "unmountTarget": "/run/gcs/c/container4/rootfs" + } + }, + { + "name": "unmount_device", + "input": { + "unmountTarget": "/run/layers/p0-layer18" + } + }, + { + "name": "unmount_device", + "input": { + "unmountTarget": "/run/layers/p0-layer19" + } + }, + { + "name": "unmount_device", + "input": { + "unmountTarget": "/run/layers/p0-layer20" + } + }, + { + "name": "unmount_device", + "input": { + "unmountTarget": "/run/layers/p0-layer21" + } + }, + { + "name": "unmount_device", + "input": { + "unmountTarget": "/run/layers/p0-layer22" + } + }, + { + "name": "unmount_device", + "input": { + "unmountTarget": "/run/layers/p0-layer23" } }, { "name": "mount_device", "input": { - "target": "/mnt/layer10", - "deviceHash": "606fd6baf5eb1a71fd286aea29672a06bfe55f0007ded92ee73142a37590ed19" + "deviceHash": "1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766", + "target": "/run/layers/p0-layer25" } }, { "name": "mount_device", "input": { - "target": "/mnt/layer11", - "deviceHash": "97adfda6943f3af972b9bf4fa684f533f10c023d913d195048fef03f9c3c60fd" + "deviceHash": "e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c", + "target": "/run/layers/p0-layer26" } }, { "name": "mount_device", "input": { - "target": "/mnt/layer12", - "deviceHash": "296e5baa5b9ded863ca0170e05cd9ecf4136f86c830a9da906184ab147415c7b" + "deviceHash": "eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79", + "target": "/run/layers/p0-layer27" } }, { "name": "mount_device", "input": { - "target": "/mnt/layer13", - "deviceHash": "a6a6918c07c85e29e48d4a87c1194781251d5185f682c26f20d6ee4e955a239f" + "deviceHash": "41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156", + "target": "/run/layers/p0-layer28" } }, { "name": "mount_device", "input": { - "target": "/mnt/layer14", - "deviceHash": "285cb680a55d09f548d4baa804a663764788619824565685b32b8097cbed3d26" + "deviceHash": "4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c", + "target": "/run/layers/p0-layer29" + } + }, + { + "name": "mount_device", + "input": { + "deviceHash": "fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a", + "target": "/run/layers/p0-layer30" } }, { "name": "mount_overlay", "input": { - "target": "/mnt/overlay2", - "containerID": "container2", + "containerID": "container5", "layerPaths": [ - "/mnt/layer10", - "/mnt/layer11", - "/mnt/layer12", - "/mnt/layer13", - "/mnt/layer14" - ] + "/run/layers/p0-layer25", + "/run/layers/p0-layer26", + "/run/layers/p0-layer27", + "/run/layers/p0-layer28", + "/run/layers/p0-layer29", + "/run/layers/p0-layer30" + ], + "target": "/run/gcs/c/container5/rootfs" + } + }, + { + "name": "scratch_mount", + "input": { + "encrypted": true, + "target": "/mnt/layer31" } }, { "name": "create_container", "input": { - "containerID": "container2", "argList": [ - "bash", - "/copy_resolv_conf.sh" + "rustc", + "--version" ], + "capabilities": null, + "containerID": "container5", "envList": [ - "TERM=xterm", - "HOSTNAME=SandboxHost-637913201120076875", - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + "PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "RUSTUP_HOME=/usr/local/rustup", + "CARGO_HOME=/usr/local/cargo", + "RUST_VERSION=1.52.1", + "TERM=xterm" ], - "mounts": [ + "groups": [ { - "destination": "/mount/resolvconf", - "options": [ - "rbind", - "rshared", - "rw" - ], - "source": "/sandbox/tmp/atlas/resolvconf/mnt/container2", - "type": "bind" + "id": "0", + "name": "root" } ], - "workingDir": "/", - "sandboxDir": "/sandbox", - "hugePagesDir": "/hugepages" + "hugePagesDir": "/run/gcs/c/sandbox5/hugepages", + "mounts": [], + "noNewPrivileges": true, + "privileged": true, + "sandboxDir": "/run/gcs/c/sandbox5/sandboxMounts", + "seccompProfileSHA256": "", + "umask": "0022", + "user": { + "id": "0", + "name": "root" + }, + "workingDir": "/home/fragment" + } + }, + { + "name": "exec_in_container", + "input": { + "argList": [ + "bash" + ], + "containerID": "container5", + "envList": [ + "PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "RUSTUP_HOME=/usr/local/rustup", + "CARGO_HOME=/usr/local/cargo", + "RUST_VERSION=1.52.1", + "TERM=xterm" + ], + "workingDir": "/home/fragment" + } + }, + { + "name": "shutdown_container", + "input": { + "containerID": "container5" + } + }, + { + "name": "scratch_unmount", + "input": { + "unmountTarget": "/mnt/layer31" + } + }, + { + "name": "unmount_overlay", + "input": { + "unmountTarget": "/run/gcs/c/container5/rootfs" + } + }, + { + "name": "unmount_device", + "input": { + "unmountTarget": "/run/layers/p0-layer25" + } + }, + { + "name": "unmount_device", + "input": { + "unmountTarget": "/run/layers/p0-layer26" + } + }, + { + "name": "unmount_device", + "input": { + "unmountTarget": "/run/layers/p0-layer27" + } + }, + { + "name": "unmount_device", + "input": { + "unmountTarget": "/run/layers/p0-layer28" + } + }, + { + "name": "unmount_device", + "input": { + "unmountTarget": "/run/layers/p0-layer29" + } + }, + { + "name": "unmount_device", + "input": { + "unmountTarget": "/run/layers/p0-layer30" } } -] \ No newline at end of file +] diff --git a/internal/tools/policyenginesimulator/samples/simple_framework/fragment.rego b/internal/tools/policyenginesimulator/samples/simple_framework/fragment.rego index ef5aa7874e..bb3377f67e 100644 --- a/internal/tools/policyenginesimulator/samples/simple_framework/fragment.rego +++ b/internal/tools/policyenginesimulator/samples/simple_framework/fragment.rego @@ -1,43 +1,26 @@ package fragment -svn := "1.0.0" -framework_svn := "0.1.0" +svn := "1" +framework_version := "0.3.0" containers := [ { - "command": ["python3","WebAttestationReport.py"], - "env_rules": [ - { - "pattern": "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - "strategy": "string", - "required": false - }, - { - "pattern": "PYTHONUNBUFFERED=1", - "strategy": "string", - "required": false - }, - { - "pattern": "TERM=xterm", - "strategy": "string", - "required": false - } - ], - "layers": [ - "37e9dcf799048b7d35ce53584e0984198e1bc3366c3bb5582fd97553d31beb4e", - "97112ba1d4a2c86c1c15a3e13f606e8fcc0fb1b49154743cadd1f065c42fee5a", - "1e66649e162d99c4d675d8d8c3af90ece3799b33d24671bc83fe9ea5143daf2f", - "3413e98a178646d4703ea70b9bff2d4410e606a22062046992cda8c8aedaa387", - "b99a9ced77c45fc4dc96bac8ea1e4d9bc1d2a66696cc057d3f3cca79dc999702", - "e7fbe653352d546497c534c629269c4c04f1997f6892bd66c273f0c9753a4de3", - "04c110e9406d2b57079f1eac4c9c5247747caa3bcaab6d83651de6e7da97cb40", - "f65ec804a63b85f507ac11d187434ea135a18cdc16202551d8dff292f942fdf0", - "998fe7a12356e0de0f2ffb4134615b42c9510e281c0ecfc7628c121442544309"], + "command": ["rustc","--version"], + "env_rules": [{"pattern": `PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`, "strategy": "string", "required": true},{"pattern": `RUSTUP_HOME=/usr/local/rustup`, "strategy": "string", "required": true},{"pattern": `CARGO_HOME=/usr/local/cargo`, "strategy": "string", "required": true},{"pattern": `RUST_VERSION=1.52.1`, "strategy": "string", "required": true},{"pattern": `TERM=xterm`, "strategy": "string", "required": false}], + "layers": ["fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a","4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c","41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156","eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79","e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c","1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766"], "mounts": [], - "exec_processes": [], + "exec_processes": [{"command": ["bash"], "signals": []}], "signals": [], - "allow_elevated": true, - "working_dir": "/demo-attestion", - "allow_stdio_access": true, - } -] \ No newline at end of file + "user": { + "user_idname": {"pattern": ``, "strategy": "any"}, + "group_idnames": [{"pattern": ``, "strategy": "any"}], + "umask": "0022" + }, + "capabilities": null, + "seccomp_profile_sha256": "", + "allow_elevated": false, + "working_dir": "/home/fragment", + "allow_stdio_access": false, + "no_new_privileges": true, + }, +] diff --git a/internal/tools/policyenginesimulator/samples/simple_framework/policy.rego b/internal/tools/policyenginesimulator/samples/simple_framework/policy.rego index f6c0dd4c2c..e8df7475fc 100644 --- a/internal/tools/policyenginesimulator/samples/simple_framework/policy.rego +++ b/internal/tools/policyenginesimulator/samples/simple_framework/policy.rego @@ -1,96 +1,66 @@ package policy -api_svn := "0.7.0" -framework_svn := "0.1.0" - -import future.keywords.every -import future.keywords.in +api_version := "0.10.0" +framework_version := "0.3.0" fragments := [ - {"issuer": "did:web:contoso.github.io", "feed": "contoso.azurecr.io/fragment", "minimum_svn": "1.0.0", "includes": ["containers"]}, + {"issuer": "did:web:contoso.com", "feed": "contoso.azurecr.io/infra", "minimum_svn": "1", "includes": ["containers"]}, ] containers := [ + { + "command": ["rustc","--help"], + "env_rules": [{"pattern": `PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`, "strategy": "string", "required": true},{"pattern": `RUSTUP_HOME=/usr/local/rustup`, "strategy": "string", "required": true},{"pattern": `CARGO_HOME=/usr/local/cargo`, "strategy": "string", "required": true},{"pattern": `RUST_VERSION=1.52.1`, "strategy": "string", "required": true},{"pattern": `TERM=xterm`, "strategy": "string", "required": false},{"pattern": `PREFIX_.+=.+`, "strategy": "re2", "required": false}], + "layers": ["fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a","4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c","41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156","eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79","e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c","1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766"], + "mounts": [{"destination": "/container/path/one", "options": ["rbind","rshared","rw"], "source": "sandbox:///host/path/one", "type": "bind"},{"destination": "/container/path/two", "options": ["rbind","rshared","ro"], "source": "sandbox:///host/path/two", "type": "bind"}], + "exec_processes": [{"command": ["top"], "signals": []}], + "signals": [], + "user": { + "user_idname": {"pattern": ``, "strategy": "any"}, + "group_idnames": [{"pattern": ``, "strategy": "any"}], + "umask": "0022" + }, + "capabilities": { + "bounding": ["CAP_SYS_ADMIN"], + "effective": ["CAP_SYS_ADMIN"], + "inheritable": ["CAP_SYS_ADMIN"], + "permitted": ["CAP_SYS_ADMIN"], + "ambient": ["CAP_SYS_ADMIN"], + }, + "seccomp_profile_sha256": "", + "allow_elevated": true, + "working_dir": "/home/user", + "allow_stdio_access": false, + "no_new_privileges": true, + }, { "command": ["/pause"], - "env_rules": [ - { - "pattern": "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - "strategy": "string", - "required": false - }, - { - "pattern": "TERM=xterm", - "strategy": "string", - "required": false - } - ], + "env_rules": [{"pattern": `PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`, "strategy": "string", "required": true},{"pattern": `TERM=xterm`, "strategy": "string", "required": false}], "layers": ["16b514057a06ad665f92c02863aca074fd5976c755d26bff16365299169e8415"], "mounts": [], "exec_processes": [], "signals": [], + "user": { + "user_idname": {"pattern": ``, "strategy": "any"}, + "group_idnames": [{"pattern": ``, "strategy": "any"}], + "umask": "0022" + }, + "capabilities": null, + "seccomp_profile_sha256": "", "allow_elevated": false, "working_dir": "/", - "allow_stdio_access": true, - }, - { - "id": "user_0", - "command": ["bash", "/copy_resolv_conf.sh"], - "env_rules": [ - { - "pattern": "IDENTITY_API_VERSION=.+", - "strategy": "re2" - }, - { - "pattern": "IDENTITY_HEADER=.+", - "strategy": "re2" - }, - { - "pattern": "SOURCE_RESOLV_CONF_LOCATION=/etc/resolv.conf", - "strategy": "string" - }, - { - "pattern": "DESTINATION_RESOLV_CONF_LOCATION=/mount/resolvconf/resolv.conf", - "strategy": "string" - }, - { - "pattern": "IDENTITY_SERVER_THUMBPRINT=.+", - "strategy": "re2" - }, - { - "pattern": "HOSTNAME=.+", - "strategy": "re2" - }, - { - "pattern": "TERM=xterm", - "strategy": "string" - }, - { - "pattern": "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - "strategy": "string" - } - ], - "layers": [ - "285cb680a55d09f548d4baa804a663764788619824565685b32b8097cbed3d26", - "a6a6918c07c85e29e48d4a87c1194781251d5185f682c26f20d6ee4e955a239f", - "296e5baa5b9ded863ca0170e05cd9ecf4136f86c830a9da906184ab147415c7b", - "97adfda6943f3af972b9bf4fa684f533f10c023d913d195048fef03f9c3c60fd", - "606fd6baf5eb1a71fd286aea29672a06bfe55f0007ded92ee73142a37590ed19" - ], - - "mounts": [ - { - "destination": "/mount/resolvconf", - "options": ["rbind", "rshared", "rw"], - "source": "sandbox:///tmp/atlas/resolvconf/.+", - "type": "bind" - } - ], - - "allow_elevated": true, - "working_dir": "/", - "allow_stdio_access": true, + "allow_stdio_access": false, + "no_new_privileges": true, }, ] +external_processes := [ + {"command": ["bash"], "env_rules": [{"pattern": `PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`, "strategy": "string", "required": true}], "working_dir": "/", "allow_stdio_access": false}, +] +allow_properties_access := false +allow_dump_stacks := false +allow_runtime_logging := false +allow_environment_variable_dropping := false +allow_unencrypted_scratch := false +allow_capability_dropping := true mount_device := data.framework.mount_device @@ -104,5 +74,13 @@ shutdown_container := data.framework.shutdown_container signal_container_process := data.framework.signal_container_process plan9_mount := data.framework.plan9_mount plan9_unmount := data.framework.plan9_unmount +get_properties := data.framework.get_properties +dump_stacks := data.framework.dump_stacks +runtime_logging := data.framework.runtime_logging load_fragment := data.framework.load_fragment -reason := {"errors": data.framework.errors} \ No newline at end of file +scratch_mount := data.framework.scratch_mount +scratch_unmount := data.framework.scratch_unmount +reason := { + "errors": data.framework.errors, + "error_objects": data.framework.error_objects, +} diff --git a/internal/tools/securitypolicy/README.md b/internal/tools/securitypolicy/README.md index bb98bdbcef..8e63a95556 100644 --- a/internal/tools/securitypolicy/README.md +++ b/internal/tools/securitypolicy/README.md @@ -61,7 +61,7 @@ working_dir = "/" [[fragment]] issuer = "did:web:contoso.com" feed = "contoso.azurecr.io/infra" -minimum_svn = "1.0.1" +minimum_svn = "1" include = ["containers"] ``` @@ -219,58 +219,67 @@ Is the following Rego policy: ``` rego package policy -api_svn := "0.10.0" -framework_svn := "0.2.1" +api_version := "0.10.0" +framework_version := "0.3.0" fragments := [ - {"issuer": "did:web:contoso.com", "feed": "contoso.azurecr.io/infra", "minimum_svn": "1.0.1", "includes": ["containers"]}, + {"issuer": "did:web:contoso.com", "feed": "contoso.azurecr.io/infra", "minimum_svn": 1, "includes": ["containers"]}, ] containers := [ { "command": ["rustc","--help"], - "env_rules": [{"pattern": "PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "strategy": "string", "required": true},{"pattern": "RUSTUP_HOME=/usr/local/rustup", "strategy": "string", "required": true},{"pattern": "CARGO_HOME=/usr/local/cargo", "strategy": "string", "required": true},{"pattern": "RUST_VERSION=1.52.1", "strategy": "string", "required": true},{"pattern": "TERM=xterm", "strategy": "string", "required": false},{"pattern": "PREFIX_.+=.+", "strategy": "re2", "required": false}], + "env_rules": [{"pattern": `PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`, "strategy": "string", "required": true},{"pattern": `RUSTUP_HOME=/usr/local/rustup`, "strategy": "string", "required": true},{"pattern": `CARGO_HOME=/usr/local/cargo`, "strategy": "string", "required": true},{"pattern": `RUST_VERSION=1.52.1`, "strategy": "string", "required": true},{"pattern": `TERM=xterm`, "strategy": "string", "required": false},{"pattern": `PREFIX_.+=.+`, "strategy": "re2", "required": false}], "layers": ["fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a","4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c","41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156","eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79","e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c","1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766"], "mounts": [{"destination": "/container/path/one", "options": ["rbind","rshared","rw"], "source": "sandbox:///host/path/one", "type": "bind"},{"destination": "/container/path/two", "options": ["rbind","rshared","ro"], "source": "sandbox:///host/path/two", "type": "bind"}], "exec_processes": [{"command": ["top"], "signals": []}], "signals": [], - "allow_elevated": true, - "working_dir": "/home/user", - "allow_stdio_access": false, + "user": { + "user_idname": {"pattern": ``, "strategy": "any"}, + "group_idnames": [{"pattern": ``, "strategy": "any"}], + "umask": "0022" + }, "capabilities": { "bounding": ["CAP_SYS_ADMIN"], "effective": ["CAP_SYS_ADMIN"], "inheritable": ["CAP_SYS_ADMIN"], "permitted": ["CAP_SYS_ADMIN"], "ambient": ["CAP_SYS_ADMIN"], - } + }, + "seccomp_profile_sha256": "", + "allow_elevated": true, + "working_dir": "/home/user", + "allow_stdio_access": false, + "no_new_privileges": true, }, { "command": ["/pause"], - "env_rules": [{"pattern": "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "strategy": "string", "required": true},{"pattern": "TERM=xterm", "strategy": "string", "required": false}], + "env_rules": [{"pattern": `PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`, "strategy": "string", "required": true},{"pattern": `TERM=xterm`, "strategy": "string", "required": false}], "layers": ["16b514057a06ad665f92c02863aca074fd5976c755d26bff16365299169e8415"], "mounts": [], "exec_processes": [], "signals": [], + "user": { + "user_idname": {"pattern": ``, "strategy": "any"}, + "group_idnames": [{"pattern": ``, "strategy": "any"}], + "umask": "0022" + }, + "capabilities": null, + "seccomp_profile_sha256": "", "allow_elevated": false, "working_dir": "/", "allow_stdio_access": false, - "capabilities": { - "bounding": ["CAP_CHOWN","CAP_DAC_OVERRIDE","CAP_FSETID","CAP_FOWNER","CAP_MKNOD","CAP_NET_RAW","CAP_SETGID","CAP_SETUID","CAP_SETFCAP","CAP_SETPCAP","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_KILL","CAP_AUDIT_WRITE"], - "effective": ["CAP_CHOWN","CAP_DAC_OVERRIDE","CAP_FSETID","CAP_FOWNER","CAP_MKNOD","CAP_NET_RAW","CAP_SETGID","CAP_SETUID","CAP_SETFCAP","CAP_SETPCAP","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_KILL","CAP_AUDIT_WRITE"], - "inheritable": [], - "permitted": ["CAP_CHOWN","CAP_DAC_OVERRIDE","CAP_FSETID","CAP_FOWNER","CAP_MKNOD","CAP_NET_RAW","CAP_SETGID","CAP_SETUID","CAP_SETFCAP","CAP_SETPCAP","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_KILL","CAP_AUDIT_WRITE"], - "ambient": [], - } + "no_new_privileges": true, }, ] external_processes := [ - {"command": ["bash"], "env_rules": [{"pattern": "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "strategy": "string", "required": true}], "working_dir": "/", "allow_stdio_access": false}, + {"command": ["bash"], "env_rules": [{"pattern": `PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`, "strategy": "string", "required": true}], "working_dir": "/", "allow_stdio_access": false}, ] allow_properties_access := false allow_dump_stacks := false allow_runtime_logging := false allow_environment_variable_dropping := false allow_unencrypted_scratch := false +allow_capability_dropping := true mount_device := data.framework.mount_device @@ -290,49 +299,77 @@ runtime_logging := data.framework.runtime_logging load_fragment := data.framework.load_fragment scratch_mount := data.framework.scratch_mount scratch_unmount := data.framework.scratch_unmount -reason := {"errors": data.framework.errors} +reason := { + "errors": data.framework.errors, + "error_objects": data.framework.error_objects, +} ``` ## Converted to Rego Fragment The result of the command - securitypolicytool -c sample.toml -t fragment -n sample -v 1.0.0 -r + securitypolicytool -c sample.toml -t fragment -n sample -v 1 -r is the following Rego fragment: ``` rego package sample -svn := "1.0.0" +svn := 1 +framework_version := "0.3.0" fragments := [ - {"issuer": "did:web:contoso.com", "feed": "contoso.azurecr.io/infra", "minimum_svn": "1.0.1", "includes": ["containers"]}, + {"issuer": "did:web:contoso.com", "feed": "contoso.azurecr.io/infra", "minimum_svn": 1, "includes": ["containers"]}, ] containers := [ { "command": ["rustc","--help"], - "env_rules": [{"pattern": "PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "strategy": "string", "required": false},{"pattern": "RUSTUP_HOME=/usr/local/rustup", "strategy": "string", "required": false},{"pattern": "CARGO_HOME=/usr/local/cargo", "strategy": "string", "required": false},{"pattern": "RUST_VERSION=1.52.1", "strategy": "string", "required": false},{"pattern": "TERM=xterm", "strategy": "string", "required": false},{"pattern": "PREFIX_.+=.+", "strategy": "re2", "required": false}], + "env_rules": [{"pattern": `PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`, "strategy": "string", "required": true},{"pattern": `RUSTUP_HOME=/usr/local/rustup`, "strategy": "string", "required": true},{"pattern": `CARGO_HOME=/usr/local/cargo`, "strategy": "string", "required": true},{"pattern": `RUST_VERSION=1.52.1`, "strategy": "string", "required": true},{"pattern": `TERM=xterm`, "strategy": "string", "required": false},{"pattern": `PREFIX_.+=.+`, "strategy": "re2", "required": false}], "layers": ["fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a","4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c","41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156","eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79","e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c","1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766"], - "mounts": [{"destination": "/container/path/one", "options": ["rbind","rshared","rw"], "source": "sandbox:///host/path/one", "type": "bind"},{"destination": "/container/path/two", "options": ["rbind","rshared","ro"], "source": "sandbox://host/path/two", "type": "bind"}], + "mounts": [{"destination": "/container/path/one", "options": ["rbind","rshared","rw"], "source": "sandbox:///host/path/one", "type": "bind"},{"destination": "/container/path/two", "options": ["rbind","rshared","ro"], "source": "sandbox:///host/path/two", "type": "bind"}], "exec_processes": [{"command": ["top"], "signals": []}], "signals": [], + "user": { + "user_idname": {"pattern": ``, "strategy": "any"}, + "group_idnames": [{"pattern": ``, "strategy": "any"}], + "umask": "0022" + }, + "capabilities": { + "bounding": ["CAP_SYS_ADMIN"], + "effective": ["CAP_SYS_ADMIN"], + "inheritable": ["CAP_SYS_ADMIN"], + "permitted": ["CAP_SYS_ADMIN"], + "ambient": ["CAP_SYS_ADMIN"], + }, + "seccomp_profile_sha256": "", "allow_elevated": true, - "working_dir": "/home/user" + "working_dir": "/home/user", + "allow_stdio_access": false, + "no_new_privileges": true, }, { "command": ["/pause"], - "env_rules": [{"pattern": "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "strategy": "string", "required": false},{"pattern": "TERM=xterm", "strategy": "string", "required": false}], + "env_rules": [{"pattern": `PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`, "strategy": "string", "required": true},{"pattern": `TERM=xterm`, "strategy": "string", "required": false}], "layers": ["16b514057a06ad665f92c02863aca074fd5976c755d26bff16365299169e8415"], "mounts": [], "exec_processes": [], "signals": [], + "user": { + "user_idname": {"pattern": ``, "strategy": "any"}, + "group_idnames": [{"pattern": ``, "strategy": "any"}], + "umask": "0022" + }, + "capabilities": null, + "seccomp_profile_sha256": "", "allow_elevated": false, - "working_dir": "/" + "working_dir": "/", + "allow_stdio_access": false, + "no_new_privileges": true, }, ] external_processes := [ - {"command": ["bash"], "env_rules": [{"pattern": "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "strategy": "string", "required": true}], "working_dir": "/"}, + {"command": ["bash"], "env_rules": [{"pattern": `PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`, "strategy": "string", "required": true}], "working_dir": "/", "allow_stdio_access": false}, ] ``` diff --git a/pkg/securitypolicy/README.md b/pkg/securitypolicy/README.md index aea3adf095..77670bdd19 100644 --- a/pkg/securitypolicy/README.md +++ b/pkg/securitypolicy/README.md @@ -30,8 +30,7 @@ Here is a helpful checklist: 4. Add the enforcement point to [`api.rego`](./api.rego) and bump one minor version. 5. Add the enforcement point rule to [`policy.rego`](./policy.rego) and - [`open_door.rego`](./open_door.rego). Update their `api_svn` values to - the latest API version. + [`open_door.rego`](./open_door.rego). 6. Add the enforcement point rule logic to [`framework.rego`](./framework.rego) 7. Add useful error messages to [`framework.rego`](./framework.rego). Be sure to gate them with the rule name. diff --git a/pkg/securitypolicy/api.rego b/pkg/securitypolicy/api.rego index 62bab2e741..82e79c9040 100644 --- a/pkg/securitypolicy/api.rego +++ b/pkg/securitypolicy/api.rego @@ -1,6 +1,6 @@ package api -svn := "@@API_SVN@@" +version := "@@API_VERSION@@" enforcement_points := { "mount_device": {"introducedVersion": "0.1.0", "default_results": {"allowed": false}}, diff --git a/pkg/securitypolicy/api_test.rego b/pkg/securitypolicy/api_test.rego index 7124fb56a5..2d2de733c6 100644 --- a/pkg/securitypolicy/api_test.rego +++ b/pkg/securitypolicy/api_test.rego @@ -1,6 +1,6 @@ package api -svn := "0.0.2" +version := "0.0.2" enforcement_points := { "__fixture_for_future_test__": {"introducedVersion": "100.0.0", "default_results": {"allowed": true}}, diff --git a/pkg/securitypolicy/framework.rego b/pkg/securitypolicy/framework.rego index fb94dbb0d3..4335031a24 100644 --- a/pkg/securitypolicy/framework.rego +++ b/pkg/securitypolicy/framework.rego @@ -3,7 +3,7 @@ package framework import future.keywords.every import future.keywords.in -svn := "@@FRAMEWORK_SVN@@" +version := "@@FRAMEWORK_VERSION@@" device_mounted(target) { data.metadata.devices[target] @@ -70,7 +70,7 @@ overlay_mounted(target) { default candidate_containers := [] candidate_containers := containers { - semver.compare(data.policy.framework_svn, svn) == 0 + semver.compare(policy_framework_version, version) == 0 policy_containers := [c | c := data.policy.containers[_]] fragment_containers := [c | @@ -83,9 +83,9 @@ candidate_containers := containers { } candidate_containers := containers { - semver.compare(data.policy.framework_svn, svn) < 0 + semver.compare(policy_framework_version, version) < 0 - policy_containers := apply_defaults("container", data.policy.containers, data.policy.framework_svn) + policy_containers := apply_defaults("container", data.policy.containers, policy_framework_version) fragment_containers := [c | feed := data.metadata.issuers[_].feeds[_] fragment := feed[_] @@ -733,20 +733,20 @@ default enforcement_point_info := {"available": false, "default_results": {"allo enforcement_point_info := {"available": available, "default_results": default_results, "unknown": false, "invalid": false} { enforcement_point := data.api.enforcement_points[input.name] - semver.compare(data.api.svn, enforcement_point.introducedVersion) >= 0 - available := semver.compare(data.policy.api_svn, enforcement_point.introducedVersion) >= 0 + semver.compare(data.api.version, enforcement_point.introducedVersion) >= 0 + available := semver.compare(policy_api_version, enforcement_point.introducedVersion) >= 0 default_results := enforcement_point.default_results } enforcement_point_info := {"available": false, "default_results": {"allow": false}, "unknown": false, "invalid": true} { enforcement_point := data.api.enforcement_points[input.name] - semver.compare(data.api.svn, enforcement_point.introducedVersion) < 0 + semver.compare(data.api.version, enforcement_point.introducedVersion) < 0 } default candidate_external_processes := [] candidate_external_processes := external_processes { - semver.compare(data.policy.framework_svn, svn) == 0 + semver.compare(policy_framework_version, version) == 0 policy_external_processes := [e | e := data.policy.external_processes[_]] fragment_external_processes := [e | @@ -759,9 +759,9 @@ candidate_external_processes := external_processes { } candidate_external_processes := external_processes { - semver.compare(data.policy.framework_svn, svn) < 0 + semver.compare(policy_framework_version, version) < 0 - policy_external_processes := apply_defaults("external_process", data.policy.external_processes, data.policy.framework_svn) + policy_external_processes := apply_defaults("external_process", data.policy.external_processes, policy_framework_version) fragment_external_processes := [e | feed := data.metadata.issuers[_].feeds[_] fragment := feed[_] @@ -839,41 +839,44 @@ default fragment_external_processes := [] fragment_external_processes := data[input.namespace].external_processes -apply_defaults(name, raw_values, framework_svn) := values { - semver.compare(framework_svn, svn) == 0 +apply_defaults(name, raw_values, framework_version) := values { + semver.compare(framework_version, version) == 0 values := raw_values } -apply_defaults("container", raw_values, framework_svn) := values { - semver.compare(framework_svn, svn) < 0 +apply_defaults("container", raw_values, framework_version) := values { + semver.compare(framework_version, version) < 0 values := [checked | raw := raw_values[_] - checked := check_container(raw, framework_svn) + checked := check_container(raw, framework_version) ] } -apply_defaults("external_process", raw_values, framework_svn) := values { - semver.compare(framework_svn, svn) < 0 +apply_defaults("external_process", raw_values, framework_version) := values { + semver.compare(framework_version, version) < 0 values := [checked | raw := raw_values[_] - checked := check_external_process(raw, framework_svn) + checked := check_external_process(raw, framework_version) ] } -apply_defaults("fragment", raw_values, framework_svn) := values { - semver.compare(framework_svn, svn) < 0 +apply_defaults("fragment", raw_values, framework_version) := values { + semver.compare(framework_version, version) < 0 values := [checked | raw := raw_values[_] - checked := check_fragment(raw, framework_svn) + checked := check_fragment(raw, framework_version) ] } +default fragment_framework_version := null +fragment_framework_version := data[input.namespace].framework_version + extract_fragment_includes(includes) := fragment { - framework_svn := data[input.namespace].framework_svn + framework_version := fragment_framework_version objects := { - "containers": apply_defaults("container", fragment_containers, framework_svn), - "fragments": apply_defaults("fragment", fragment_fragments, framework_svn), - "external_processes": apply_defaults("external_process", fragment_external_processes, framework_svn) + "containers": apply_defaults("container", fragment_containers, framework_version), + "fragments": apply_defaults("fragment", fragment_fragments, framework_version), + "external_processes": apply_defaults("external_process", fragment_external_processes, framework_version) } fragment := { @@ -914,22 +917,22 @@ update_issuer(includes) := issuer { default candidate_fragments := [] candidate_fragments := fragments { - semver.compare(data.policy.framework_svn, svn) == 0 + semver.compare(policy_framework_version, version) == 0 - policy_fragmemnts := [f | f := data.policy.fragments[_]] + policy_fragments := [f | f := data.policy.fragments[_]] fragment_fragments := [f | feed := data.metadata.issuers[_].feeds[_] fragment := feed[_] f := fragment.fragments[_] ] - fragments := array.concat(policy_fragmemnts, fragment_fragments) + fragments := array.concat(policy_fragments, fragment_fragments) } candidate_fragments := fragments { - semver.compare(data.policy.framework_svn, svn) < 0 + semver.compare(policy_framework_version, version) < 0 - policy_fragments := apply_defaults("fragment", data.policy.fragments, data.policy.framework_svn) + policy_fragments := apply_defaults("fragment", data.policy.fragments, policy_framework_version) fragment_fragments := [f | feed := data.metadata.issuers[_].feeds[_] fragment := feed[_] @@ -941,10 +944,21 @@ candidate_fragments := fragments { default load_fragment := {"allowed": false} +svn_ok(svn, minimum_svn) { + # deprecated + semver.is_valid(svn) + semver.is_valid(minimum_svn) + semver.compare(svn, minimum_svn) >= 0 +} + +svn_ok(svn, minimum_svn) { + to_number(svn) >= to_number(minimum_svn) +} + fragment_ok(fragment) { input.issuer == fragment.issuer input.feed == fragment.feed - semver.compare(data[input.namespace].svn, fragment.minimum_svn) >= 0 + svn_ok(data[input.namespace].svn, fragment.minimum_svn) } load_fragment := {"metadata": [updateIssuer], "add_module": add_module, "allowed": true} { @@ -1039,14 +1053,7 @@ errors["overlay has already been mounted"] { default overlay_matches := false overlay_matches { - some container in data.policy.containers - layerPaths_ok(container.layers) -} - -overlay_matches { - feed := data.metadata.issuers[_].feeds[_] - some fragment in feed - some container in fragment.containers + some container in candidate_containers layerPaths_ok(container.layers) } @@ -1090,7 +1097,7 @@ command_matches { command_matches { input.rule == "exec_external" - some process in data.policy.external_processes + some process in candidate_external_processes command_ok(process.command) } @@ -1108,7 +1115,7 @@ env_matches(env) { env_matches(env) { input.rule in ["exec_external"] - some process in data.policy.external_processes + some process in candidate_external_processes some rule in process.env_rules env_ok(rule.pattern, rule.strategy, env) } @@ -1231,7 +1238,7 @@ workingDirectory_matches { workingDirectory_matches { input.rule == "exec_external" - some process in data.policy.external_processes + some process in candidate_external_processes workingDirectory_ok(process.working_dir) } @@ -1288,14 +1295,10 @@ errors["no device at path to unmount"] { default fragment_issuer_matches := false fragment_issuer_matches { - some fragment in data.policy.fragments + some fragment in candidate_fragments fragment.issuer == input.issuer } -fragment_issuer_matches { - input.issuer in data.metadata.issuers -} - errors["invalid fragment issuer"] { input.rule == "load_fragment" not fragment_issuer_matches @@ -1304,7 +1307,7 @@ errors["invalid fragment issuer"] { default fragment_feed_matches := false fragment_feed_matches { - some fragment in data.policy.fragments + some fragment in candidate_fragments fragment.issuer == input.issuer fragment.feed == input.feed } @@ -1322,23 +1325,43 @@ errors["invalid fragment feed"] { default fragment_version_is_valid := false fragment_version_is_valid { - some fragment in data.policy.fragments + some fragment in candidate_fragments fragment.issuer == input.issuer fragment.feed == input.feed - semver.compare(data[input.namespace].svn, fragment.minimum_svn) >= 0 + svn_ok(data[input.namespace].svn, fragment.minimum_svn) } -fragment_version_is_valid { - some fragment in data.metadata.issuers[input.issuer][input.feed] - semver.compare(data[input.namespace].svn, fragment.minimum_svn) >= 0 +default svn_mismatch := false + +svn_mismatch { + some fragment in candidate_fragments + fragment.issuer == input.issuer + fragment.feed == input.feed + to_number(data[input.namespace].svn) + semver.is_valid(fragment.minimum_svn) } -errors["fragment version is below the specified minimum"] { +svn_mismatch { + some fragment in candidate_fragments + fragment.issuer == input.issuer + fragment.feed == input.feed + semver.is_valid(data[input.namespace].svn) + to_number(fragment.minimum_svn) +} + +errors["fragment svn is below the specified minimum"] { input.rule == "load_fragment" fragment_feed_matches + not svn_mismatch not fragment_version_is_valid } +errors["fragment svn and the specified minimum are different types"] { + input.rule == "load_fragment" + fragment_feed_matches + svn_mismatch +} + errors["scratch already mounted at path"] { input.rule == "scratch_mount" scratch_mounted(input.target) @@ -1355,24 +1378,24 @@ errors["no scratch at path to unmount"] { not scratch_mounted(input.unmountTarget) } -errors[framework_svn_error] { - not data.policy.framework_svn - framework_svn_error := concat(" ", ["framework_svn is missing. Current svn:", svn]) +errors[framework_version_error] { + policy_framework_version == null + framework_version_error := concat(" ", ["framework_version is missing. Current version:", version]) } -errors[framework_svn_error] { - semver.compare(data.policy.framework_svn, svn) > 0 - framework_svn_error := concat(" ", ["framework_svn is ahead of the current svn:", data.policy.framework_svn, ">", svn]) +errors[framework_version_error] { + semver.compare(policy_framework_version, version) > 0 + framework_version_error := concat(" ", ["framework_version is ahead of the current version:", policy_framework_version, ">", version]) } -errors[fragment_framework_svn_error] { - not data[input.namespace].framework_svn - fragment_framework_svn_error := concat(" ", ["fragment framework_svn is missing. Current svn:", svn]) +errors[fragment_framework_version_error] { + fragment_framework_version == null + fragment_framework_version_error := concat(" ", ["fragment framework_version is missing. Current version:", version]) } -errors[fragment_framework_svn_error] { - semver.compare(data[input.namespace].framework_svn, svn) > 0 - fragment_framework_svn_error := concat(" ", ["fragment framework_svn is ahead of the current svn:", data[input.namespace].framework_svn, ">", svn]) +errors[fragment_framework_version_error] { + semver.compare(fragment_framework_version, version) > 0 + fragment_framework_version_error := concat(" ", ["fragment framework_version is ahead of the current version:", fragment_framework_version, ">", version]) } errors["containers only distinguishable by allow_stdio_access"] { @@ -1650,13 +1673,13 @@ error_objects := fragments { ################################################################################ -check_container(raw_container, framework_svn) := container { - semver.compare(framework_svn, svn) == 0 +check_container(raw_container, framework_version) := container { + semver.compare(framework_version, version) == 0 container := raw_container } -check_container(raw_container, framework_svn) := container { - semver.compare(framework_svn, svn) < 0 +check_container(raw_container, framework_version) := container { + semver.compare(framework_version, version) < 0 container := { # Base fields "command": raw_container.command, @@ -1669,30 +1692,30 @@ check_container(raw_container, framework_svn) := container { "signals": raw_container.signals, "allow_stdio_access": raw_container.allow_stdio_access, # Additional fields need to have default logic applied - "no_new_privileges": check_no_new_privileges(raw_container, framework_svn), - "user": check_user(raw_container, framework_svn), - "capabilities": check_capabilities(raw_container, framework_svn), - "seccomp_profile_sha256": check_seccomp_profile_sha256(raw_container, framework_svn), + "no_new_privileges": check_no_new_privileges(raw_container, framework_version), + "user": check_user(raw_container, framework_version), + "capabilities": check_capabilities(raw_container, framework_version), + "seccomp_profile_sha256": check_seccomp_profile_sha256(raw_container, framework_version), } } -check_no_new_privileges(raw_container, framework_svn) := no_new_privileges { - semver.compare(framework_svn, "0.2.0") >= 0 +check_no_new_privileges(raw_container, framework_version) := no_new_privileges { + semver.compare(framework_version, "0.2.0") >= 0 no_new_privileges := raw_container.no_new_privileges } -check_no_new_privileges(raw_container, framework_svn) := no_new_privileges { - semver.compare(framework_svn, "0.2.0") < 0 +check_no_new_privileges(raw_container, framework_version) := no_new_privileges { + semver.compare(framework_version, "0.2.0") < 0 no_new_privileges := false } -check_user(raw_container, framework_svn) := user { - semver.compare(framework_svn, "0.2.1") >= 0 +check_user(raw_container, framework_version) := user { + semver.compare(framework_version, "0.2.1") >= 0 user := raw_container.user } -check_user(raw_container, framework_svn) := user { - semver.compare(framework_svn, "0.2.1") < 0 +check_user(raw_container, framework_version) := user { + semver.compare(framework_version, "0.2.1") < 0 user := { "umask": "0022", "user_idname": { @@ -1708,13 +1731,13 @@ check_user(raw_container, framework_svn) := user { } } -check_capabilities(raw_container, framework_svn) := capabilities { - semver.compare(framework_svn, "0.2.2") >= 0 +check_capabilities(raw_container, framework_version) := capabilities { + semver.compare(framework_version, "0.2.2") >= 0 capabilities := raw_container.capabilities } -check_capabilities(raw_container, framework_svn) := capabilities { - semver.compare(framework_svn, "0.2.2") < 0 +check_capabilities(raw_container, framework_version) := capabilities { + semver.compare(framework_version, "0.2.2") < 0 # we cannot determine a reasonable default at the time this is called, # which is either during `mount_overlay` or `load_fragment`, and so # we set it to `null`, which indicates that the capabilities should @@ -1722,23 +1745,23 @@ check_capabilities(raw_container, framework_svn) := capabilities { capabilities := null } -check_seccomp_profile_sha256(raw_container, framework_svn) := seccomp_profile_sha256 { - semver.compare(framework_svn, "0.2.3") >= 0 +check_seccomp_profile_sha256(raw_container, framework_version) := seccomp_profile_sha256 { + semver.compare(framework_version, "0.2.3") >= 0 seccomp_profile_sha256 := raw_container.seccomp_profile_sha256 } -check_seccomp_profile_sha256(raw_container, framework_svn) := seccomp_profile_sha256 { - semver.compare(framework_svn, "0.2.3") < 0 +check_seccomp_profile_sha256(raw_container, framework_version) := seccomp_profile_sha256 { + semver.compare(framework_version, "0.2.3") < 0 seccomp_profile_sha256 := "" } -check_external_process(raw_process, framework_svn) := process { - semver.compare(framework_svn, svn) == 0 +check_external_process(raw_process, framework_version) := process { + semver.compare(framework_version, version) == 0 process := raw_process } -check_external_process(raw_process, framework_svn) := process { - semver.compare(framework_svn, svn) < 0 +check_external_process(raw_process, framework_version) := process { + semver.compare(framework_version, version) < 0 process := { # Base fields "command": raw_process.command, @@ -1749,13 +1772,13 @@ check_external_process(raw_process, framework_svn) := process { } } -check_fragment(raw_fragment, framework_svn) := fragment { - semver.compare(framework_svn, svn) == 0 +check_fragment(raw_fragment, framework_version) := fragment { + semver.compare(framework_version, version) == 0 fragment := raw_fragment } -check_fragment(raw_fragment, framework_svn) := fragment { - semver.compare(framework_svn, svn) < 0 +check_fragment(raw_fragment, framework_version) := fragment { + semver.compare(framework_version, version) < 0 fragment := { # Base fields "issuer": raw_fragment.issuer, @@ -1778,6 +1801,17 @@ allow_unencrypted_scratch := data.policy.allow_unencrypted_scratch default allow_capability_dropping := false allow_capability_dropping := flag { - semver.compare(data.policy.framework_svn, "0.2.2") >= 0 + semver.compare(policy_framework_version, "0.2.2") >= 0 flag := data.policy.allow_capability_dropping } + +default policy_framework_version := null +default policy_api_version := null + +policy_framework_version := data.policy.framework_version +policy_api_version := data.policy.api_version + +# deprecated +policy_framework_version := data.policy.framework_svn +policy_api_version := data.policy.api_svn +fragment_framework_version := data[input.namespace].framework_svn diff --git a/pkg/securitypolicy/open_door.rego b/pkg/securitypolicy/open_door.rego index 328ce42772..2bc36123d8 100644 --- a/pkg/securitypolicy/open_door.rego +++ b/pkg/securitypolicy/open_door.rego @@ -1,11 +1,11 @@ package policy -api_svn := "@@API_SVN@@" +api_version := "@@API_VERSION@@" mount_device := {"allowed": true} mount_overlay := {"allowed": true} create_container := {"allowed": true, "env_list": null, "allow_stdio_access": true} -unmount_device := {"allowed": true} +unmount_device := {"allowed": true} unmount_overlay := {"allowed": true} exec_in_container := {"allowed": true, "env_list": null} exec_external := {"allowed": true, "env_list": null, "allow_stdio_access": true} diff --git a/pkg/securitypolicy/policy.rego b/pkg/securitypolicy/policy.rego index f7854483fd..163319c33f 100644 --- a/pkg/securitypolicy/policy.rego +++ b/pkg/securitypolicy/policy.rego @@ -1,7 +1,7 @@ package policy -api_svn := "@@API_SVN@@" -framework_svn := "@@FRAMEWORK_SVN@@" +api_version := "@@API_VERSION@@" +framework_version := "@@FRAMEWORK_VERSION@@" @@OBJECTS@@ diff --git a/pkg/securitypolicy/regopolicy_test.go b/pkg/securitypolicy/regopolicy_test.go index 7e59906e96..c13b3bc825 100644 --- a/pkg/securitypolicy/regopolicy_test.go +++ b/pkg/securitypolicy/regopolicy_test.go @@ -51,19 +51,18 @@ func Test_RegoTemplates(t *testing.T) { } apiRules := resultSet[0].Expressions[0].Value.(map[string]interface{}) - apiSVN := apiRules["svn"].(string) enforcementPoints := apiRules["enforcement_points"].(map[string]interface{}) policyCode := strings.Replace(policyRegoTemplate, "@@OBJECTS@@", "", 1) - policyCode = strings.Replace(policyCode, "@@API_SVN@@", apiSVN, 1) - policyCode = strings.Replace(policyCode, "@@FRAMEWORK_SVN@@", frameworkSVN, 1) + policyCode = strings.Replace(policyCode, "@@API_VERSION@@", apiVersion, 1) + policyCode = strings.Replace(policyCode, "@@FRAMEWORK_VERSION@@", frameworkVersion, 1) - err = verifyPolicyRules(apiSVN, enforcementPoints, policyCode) + err = verifyPolicyRules(apiVersion, enforcementPoints, policyCode) if err != nil { t.Errorf("Policy Rego Template is invalid: %s", err) } - err = verifyPolicyRules(apiSVN, enforcementPoints, openDoorRego) + err = verifyPolicyRules(apiVersion, enforcementPoints, openDoorRego) if err != nil { t.Errorf("Open Door Rego Template is invalid: %s", err) } @@ -1829,7 +1828,7 @@ func Test_Rego_Version_Future_Enforcement_Point(t *testing.T) { // framework that was released after the policy was authored as indicated // by their respective version information. func Test_Rego_Version_Unavailable_Enforcement_Point(t *testing.T) { - code := "package policy\n\napi_svn := \"0.0.1\"" + code := "package policy\n\napi_version := \"0.0.1\"" policy, err := newRegoPolicy(code, []oci.Mount{}, []oci.Mount{}) if err != nil { t.Fatalf("unable to create a new Rego policy: %v", err) @@ -1862,7 +1861,7 @@ func Test_Rego_Version_Unavailable_Enforcement_Point(t *testing.T) { } func Test_Rego_Enforcement_Point_Allowed(t *testing.T) { - code := "package policy\n\napi_svn := \"0.0.1\"" + code := "package policy\n\napi_version := \"0.0.1\"" policy, err := newRegoPolicy(code, []oci.Mount{}, []oci.Mount{}) if err != nil { t.Fatalf("unable to create a new Rego policy: %v", err) @@ -1909,7 +1908,7 @@ func Test_Rego_Enforcement_Point_Allowed(t *testing.T) { func Test_Rego_Enforcement_Point_Extra(t *testing.T) { code := `package policy -api_svn := "0.0.1" +api_version := "0.0.1" __fixture_for_allowed_extra__ := {"allowed": true} ` @@ -2451,8 +2450,8 @@ exec_external := { func Test_Rego_InvalidEnvList(t *testing.T) { rego := fmt.Sprintf(`package policy - api_svn := "%s" - framework_svn := "%s" + api_version := "%s" + framework_version := "%s" create_container := { "allowed": true, @@ -2465,7 +2464,7 @@ func Test_Rego_InvalidEnvList(t *testing.T) { exec_external := { "allowed": true, "env_list": true - }`, apiSVN, frameworkSVN) + }`, apiVersion, frameworkVersion) policy, err := newRegoPolicy(rego, []oci.Mount{}, []oci.Mount{}) if err != nil { @@ -2499,8 +2498,8 @@ func Test_Rego_InvalidEnvList(t *testing.T) { func Test_Rego_InvalidEnvList_Member(t *testing.T) { rego := fmt.Sprintf(`package policy - api_svn := "%s" - framework_svn := "%s" + api_version := "%s" + framework_version := "%s" create_container := { "allowed": true, @@ -2513,7 +2512,7 @@ func Test_Rego_InvalidEnvList_Member(t *testing.T) { exec_external := { "allowed": true, "env_list": ["one", ["two"], "three"] - }`, apiSVN, frameworkSVN) + }`, apiVersion, frameworkVersion) policy, err := newRegoPolicy(rego, []oci.Mount{}, []oci.Mount{}) if err != nil { @@ -3687,9 +3686,122 @@ func Test_Rego_LoadFragment_BadFeed(t *testing.T) { } } -func Test_Rego_LoadFragment_InvalidVersion(t *testing.T) { +func Test_Rego_LoadFragment_InvalidSVN(t *testing.T) { f := func(p *generatedConstraints) bool { - tc, err := setupRegoFragmentVersionErrorTestConfig(p) + tc, err := setupRegoFragmentSVNErrorTestConfig(p) + if err != nil { + t.Error(err) + return false + } + + fragment := tc.fragments[0] + err = tc.policy.LoadFragment(fragment.info.issuer, fragment.info.feed, fragment.code) + if err == nil { + t.Error("expected to be unable to load fragment due to invalid svn") + return false + } + + if !strings.Contains(err.Error(), "fragment svn is below the specified minimum") { + t.Error("expected error string to contain 'fragment svn is below the specified minimum'") + return false + } + + if tc.policy.rego.IsModuleActive(rpi.ModuleID(fragment.info.issuer, fragment.info.feed)) { + t.Error("module not removed upon failure") + return false + } + + return true + } + + if err := quick.Check(f, &quick.Config{MaxCount: 25, Rand: testRand}); err != nil { + t.Errorf("Test_Rego_LoadFragment_InvalidSVN: %v", err) + } +} + +func Test_Rego_LoadFragment_Fragment_InvalidSVN(t *testing.T) { + f := func(p *generatedConstraints) bool { + tc, err := setupRegoSubfragmentSVNErrorTestConfig(p) + if err != nil { + t.Error(err) + return false + } + + fragment := tc.fragments[0] + err = tc.policy.LoadFragment(fragment.info.issuer, fragment.info.feed, fragment.code) + if err != nil { + t.Error("unable to load fragment: %w", err) + return false + } + + subFragment := tc.subFragments[0] + err = tc.policy.LoadFragment(subFragment.info.issuer, subFragment.info.feed, subFragment.code) + if err == nil { + t.Error("expected to be unable to load subfragment due to invalid svn") + return false + } + + if !strings.Contains(err.Error(), "fragment svn is below the specified minimum") { + t.Error("expected error string to contain 'fragment svn is below the specified minimum'") + return false + } + + if tc.policy.rego.IsModuleActive(rpi.ModuleID(subFragment.info.issuer, fragment.info.feed)) { + t.Error("module not removed upon failure") + return false + } + + return true + } + + if err := quick.Check(f, &quick.Config{MaxCount: 25, Rand: testRand}); err != nil { + t.Errorf("Test_Rego_LoadFragment_Fragment_InvalidSVN: %v", err) + } +} + +func Test_Rego_LoadFragment_SemverVersion(t *testing.T) { + f := func(p *generatedConstraints) bool { + p.fragments = generateFragments(testRand, 1) + p.fragments[0].minimumSVN = generate_semver(testRand) + securityPolicy := p.toPolicy() + + defaultMounts := toOCIMounts(generateMounts(testRand)) + privilegedMounts := toOCIMounts(generateMounts(testRand)) + policy, err := newRegoPolicy(securityPolicy.marshalRego(), defaultMounts, privilegedMounts) + + if err != nil { + t.Fatalf("error compiling policy: %v", err) + } + + issuer := p.fragments[0].issuer + feed := p.fragments[0].feed + + fragmentConstraints := generateConstraints(testRand, 1) + fragmentConstraints.svn = mustIncrementSVN(p.fragments[0].minimumSVN) + code := fragmentConstraints.toFragment().marshalRego() + + err = policy.LoadFragment(issuer, feed, code) + if err != nil { + t.Error("unable to load fragment: %w", err) + return false + } + + if policy.rego.IsModuleActive(rpi.ModuleID(issuer, feed)) { + t.Error("module not removed after load") + return false + } + + return true + } + + if err := quick.Check(f, &quick.Config{MaxCount: 25, Rand: testRand}); err != nil { + t.Errorf("Test_Rego_LoadFragment_SemverVersion: %v", err) + } +} + +func Test_Rego_LoadFragment_SVNMismatch(t *testing.T) { + f := func(p *generatedConstraints) bool { + tc, err := setupRegoFragmentSVNMismatchTestConfig(p) if err != nil { t.Error(err) return false @@ -3702,8 +3814,8 @@ func Test_Rego_LoadFragment_InvalidVersion(t *testing.T) { return false } - if !strings.Contains(err.Error(), "fragment version is below the specified minimum") { - t.Error("expected error string to contain 'fragment version is below the specified minimum'") + if !strings.Contains(err.Error(), "fragment svn and the specified minimum are different types") { + t.Error("expected error string to contain 'fragment svn and the specified minimum are different types'") return false } @@ -3716,7 +3828,7 @@ func Test_Rego_LoadFragment_InvalidVersion(t *testing.T) { } if err := quick.Check(f, &quick.Config{MaxCount: 25, Rand: testRand}); err != nil { - t.Errorf("Test_Rego_LoadFragment_InvalidVersion: %v", err) + t.Errorf("Test_Rego_LoadFragment_SVNMismatch: %v", err) } } @@ -3984,8 +4096,8 @@ func Test_Rego_LoadFragment_FragmentNamespace(t *testing.T) { value := randVariableString(testRand, 32) fragmentCode := fmt.Sprintf(`package fragment -svn := "1.0.0" -framework_svn := "%s" +svn := 1 +framework_version := "%s" layer := "%s" @@ -3997,25 +4109,25 @@ mount_device := {"allowed": allowed, "metadata": [addCustom]} { "key": "%s", "value": "%s" } -}`, frameworkSVN, deviceHash, key, value) +}`, frameworkVersion, deviceHash, key, value) issuer := testDataGenerator.uniqueFragmentIssuer() feed := testDataGenerator.uniqueFragmentFeed() policyCode := fmt.Sprintf(`package policy -api_svn := "%s" -framework_svn := "%s" +api_version := "%s" +framework_version := "%s" default load_fragment := {"allowed": false} load_fragment := {"allowed": true, "add_module": true} { input.issuer == "%s" input.feed == "%s" - semver.compare(data[input.namespace].svn, "1.0.0") >= 0 + data[input.namespace].svn >= 1 } mount_device := data.fragment.mount_device - `, apiSVN, frameworkSVN, issuer, feed) + `, apiVersion, frameworkVersion, issuer, feed) policy, err := newRegoPolicy(policyCode, []oci.Mount{}, []oci.Mount{}) if err != nil { @@ -4433,7 +4545,7 @@ func Test_Rego_EnforceCreateContainerPolicy_NoAllowElevatedAllowsUnprivilegedCon func Test_Rego_CreateContainer_NoNewPrivileges_Default(t *testing.T) { gc := generateConstraints(testRand, maxContainersInGeneratedConstraints) - tc, err := setupFrameworkSVNSimpleTest(gc, "0.1.0", frameworkSVN) + tc, err := setupFrameworkVersionSimpleTest(gc, "0.1.0", frameworkVersion) if err != nil { t.Fatalf("error setting up test: %v", err) } @@ -4465,7 +4577,7 @@ func Test_Rego_CreateContainer_NoNewPrivileges_Default(t *testing.T) { func Test_Rego_CreateContainer_User_Default(t *testing.T) { gc := generateConstraints(testRand, maxContainersInGeneratedConstraints) - tc, err := setupFrameworkSVNSimpleTest(gc, "0.1.0", frameworkSVN) + tc, err := setupFrameworkVersionSimpleTest(gc, "0.1.0", frameworkVersion) if err != nil { t.Fatalf("error setting up test: %v", err) } @@ -4524,7 +4636,7 @@ func Test_Rego_CreateContainer_User_Default(t *testing.T) { func Test_Rego_CreateContainer_Capabilities_Default(t *testing.T) { gc := generateConstraints(testRand, maxContainersInGeneratedConstraints) - tc, err := setupFrameworkSVNSimpleTest(gc, "0.1.0", frameworkSVN) + tc, err := setupFrameworkVersionSimpleTest(gc, "0.1.0", frameworkVersion) if err != nil { t.Fatalf("error setting up test: %v", err) } @@ -4563,7 +4675,7 @@ func Test_Rego_CreateContainer_Capabilities_Default(t *testing.T) { func Test_Rego_CreateContainer_AllowCapabilityDropping_Default(t *testing.T) { gc := generateConstraints(testRand, 1) - tc, err := setupFrameworkSVNSimpleTest(gc, "0.1.0", frameworkSVN) + tc, err := setupFrameworkVersionSimpleTest(gc, "0.1.0", frameworkVersion) if err != nil { t.Fatalf("error setting up test: %v", err) } @@ -4587,7 +4699,7 @@ func Test_Rego_CreateContainer_AllowCapabilityDropping_Default(t *testing.T) { func Test_Rego_CreateContainer_Seccomp_Default(t *testing.T) { gc := generateConstraints(testRand, maxContainersInGeneratedConstraints) - tc, err := setupFrameworkSVNSimpleTest(gc, "0.1.0", frameworkSVN) + tc, err := setupFrameworkVersionSimpleTest(gc, "0.1.0", frameworkVersion) if err != nil { t.Fatalf("error setting up test: %v", err) } @@ -4617,9 +4729,9 @@ func Test_Rego_CreateContainer_Seccomp_Default(t *testing.T) { } } -func Test_FrameworkSVN_Missing(t *testing.T) { +func Test_FrameworkVersion_Missing(t *testing.T) { gc := generateConstraints(testRand, 1) - tc, err := setupFrameworkSVNSimpleTest(gc, "", frameworkSVN) + tc, err := setupFrameworkVersionSimpleTest(gc, "", frameworkVersion) if err != nil { t.Fatalf("unable to setup test: %v", err) } @@ -4631,19 +4743,19 @@ func Test_FrameworkSVN_Missing(t *testing.T) { err = tc.policy.EnforceOverlayMountPolicy(containerID, layerPaths, testDataGenerator.uniqueMountTarget()) if err == nil { - t.Error("unexpected success. Missing framework_svn should trigger an error.") + t.Error("unexpected success. Missing framework_version should trigger an error.") } actual := err.Error() - expected := fmt.Sprintf("framework_svn is missing. Current svn: %s", frameworkSVN) + expected := fmt.Sprintf("framework_version is missing. Current version: %s", frameworkVersion) if !strings.Contains(actual, expected) { t.Errorf("missing expected error message: %s", expected) } } -func Test_Fragment_FrameworkSVN_Missing(t *testing.T) { +func Test_Fragment_FrameworkVersion_Missing(t *testing.T) { gc := generateConstraints(testRand, 1) - tc, err := setupFrameworkSVNTest(gc, frameworkSVN, frameworkSVN, 1, "", []string{}) + tc, err := setupFrameworkVersionTest(gc, frameworkVersion, frameworkVersion, 1, "", []string{}) if err != nil { t.Fatalf("unable to setup test: %v", err) } @@ -4651,19 +4763,19 @@ func Test_Fragment_FrameworkSVN_Missing(t *testing.T) { fragment := tc.fragments[0] err = tc.policy.LoadFragment(fragment.info.issuer, fragment.info.feed, fragment.code) if err == nil { - t.Error("unexpected success. Missing framework_svn should trigger an error.") + t.Error("unexpected success. Missing framework_version should trigger an error.") } actual := err.Error() - expected := fmt.Sprintf("fragment framework_svn is missing. Current svn: %s", frameworkSVN) + expected := fmt.Sprintf("fragment framework_version is missing. Current version: %s", frameworkVersion) if !strings.Contains(actual, expected) { t.Errorf("missing expected error message: %s", expected) } } -func Test_FrameworkSVN_In_Future(t *testing.T) { +func Test_FrameworkVersion_In_Future(t *testing.T) { gc := generateConstraints(testRand, 1) - tc, err := setupFrameworkSVNSimpleTest(gc, "100.0.0", frameworkSVN) + tc, err := setupFrameworkVersionSimpleTest(gc, "100.0.0", frameworkVersion) if err != nil { t.Fatalf("unable to setup test: %v", err) } @@ -4675,19 +4787,19 @@ func Test_FrameworkSVN_In_Future(t *testing.T) { err = tc.policy.EnforceOverlayMountPolicy(containerID, layerPaths, testDataGenerator.uniqueMountTarget()) if err == nil { - t.Error("unexpected success. Future framework_svn should trigger an error.") + t.Error("unexpected success. Future framework_version should trigger an error.") } actual := err.Error() - expected := fmt.Sprintf("framework_svn is ahead of the current svn: 100.0.0 > %s", frameworkSVN) + expected := fmt.Sprintf("framework_version is ahead of the current version: 100.0.0 > %s", frameworkVersion) if !strings.Contains(actual, expected) { t.Errorf("missing expected error message: %s", expected) } } -func Test_Fragment_FrameworkSVN_In_Future(t *testing.T) { +func Test_Fragment_FrameworkVersion_In_Future(t *testing.T) { gc := generateConstraints(testRand, 1) - tc, err := setupFrameworkSVNTest(gc, frameworkSVN, frameworkSVN, 1, "100.0.0", []string{}) + tc, err := setupFrameworkVersionTest(gc, frameworkVersion, frameworkVersion, 1, "100.0.0", []string{}) if err != nil { t.Fatalf("unable to setup test: %v", err) } @@ -4695,11 +4807,11 @@ func Test_Fragment_FrameworkSVN_In_Future(t *testing.T) { fragment := tc.fragments[0] err = tc.policy.LoadFragment(fragment.info.issuer, fragment.info.feed, fragment.code) if err == nil { - t.Error("unexpected success. Future framework_svn should trigger an error.") + t.Error("unexpected success. Future framework_version should trigger an error.") } actual := err.Error() - expected := fmt.Sprintf("fragment framework_svn is ahead of the current svn: 100.0.0 > %s", frameworkSVN) + expected := fmt.Sprintf("fragment framework_version is ahead of the current version: 100.0.0 > %s", frameworkVersion) if !strings.Contains(actual, expected) { t.Errorf("missing expected error message: %s", expected) } @@ -4708,12 +4820,12 @@ func Test_Fragment_FrameworkSVN_In_Future(t *testing.T) { func Test_Rego_MissingEnvList(t *testing.T) { code := fmt.Sprintf(`package policy - api_svn := "%s" + api_version := "%s" create_container := {"allowed": true} exec_in_container := {"allowed": true} exec_external := {"allowed": true} - `, apiSVN) + `, apiVersion) policy, err := newRegoPolicy(code, []oci.Mount{}, []oci.Mount{}) if err != nil { @@ -5448,6 +5560,115 @@ func Test_Rego_EnforceCreateContainerSeccompPolicy_NoMatch(t *testing.T) { } } +func Test_Rego_FrameworkSVN(t *testing.T) { + gc := generateConstraints(testRand, 1) + securityPolicy := gc.toPolicy() + defaultMounts := generateMounts(testRand) + privilegedMounts := generateMounts(testRand) + + code := securityPolicy.marshalRego() + code = strings.Replace(code, "framework_version", "framework_svn", 1) + + policy, err := newRegoPolicy(code, + toOCIMounts(defaultMounts), + toOCIMounts(privilegedMounts)) + if err != nil { + t.Fatalf("unable to create policy: %v", err) + } + + value, err := policy.rego.RawQuery("data.framework.policy_framework_version", map[string]interface{}{}) + if err != nil { + t.Fatalf("unable to query policy: %v", err) + } + + policyFrameworkVersion, ok := value[0].Expressions[0].Value.(string) + if ok { + if policyFrameworkVersion != frameworkVersion { + t.Error("policy_framework_version is not set correctly from framework_svn") + } + } else { + t.Error("no result set from querying data.framework.policy_framework_version") + } +} + +func Test_Rego_Fragment_FrameworkSVN(t *testing.T) { + gc := generateConstraints(testRand, 1) + gc.fragments = generateFragments(testRand, 1) + + gc.fragments = generateFragments(testRand, 1) + gc.fragments[0].minimumSVN = generate_semver(testRand) + securityPolicy := gc.toPolicy() + + defaultMounts := toOCIMounts(generateMounts(testRand)) + privilegedMounts := toOCIMounts(generateMounts(testRand)) + policy, err := newRegoPolicy(securityPolicy.marshalRego(), defaultMounts, privilegedMounts) + + if err != nil { + t.Fatalf("error compiling policy: %v", err) + } + + fragmentConstraints := generateConstraints(testRand, 1) + fragmentConstraints.svn = mustIncrementSVN(gc.fragments[0].minimumSVN) + code := fragmentConstraints.toFragment().marshalRego() + + policy.rego.AddModule(fragmentConstraints.namespace, &rpi.RegoModule{ + Namespace: fragmentConstraints.namespace, + Feed: gc.fragments[0].feed, + Issuer: gc.fragments[0].issuer, + Code: code, + }) + + input := map[string]interface{}{ + "namespace": fragmentConstraints.namespace, + } + result, err := policy.rego.RawQuery("data.framework.fragment_framework_version", input) + + if err != nil { + t.Fatalf("error querying policy: %v", err) + } + + fragmentFrameworkVersion, ok := result[0].Expressions[0].Value.(string) + + if ok { + if fragmentFrameworkVersion != frameworkVersion { + t.Error("fragment_framework_version is not set correctly from framework_svn") + } + } else { + t.Error("no result set from querying data.framework.fragment_framework_version") + } +} + +func Test_Rego_APISVN(t *testing.T) { + gc := generateConstraints(testRand, 1) + securityPolicy := gc.toPolicy() + defaultMounts := generateMounts(testRand) + privilegedMounts := generateMounts(testRand) + + code := securityPolicy.marshalRego() + code = strings.Replace(code, "api_version", "api_svn", 1) + + policy, err := newRegoPolicy(code, + toOCIMounts(defaultMounts), + toOCIMounts(privilegedMounts)) + if err != nil { + t.Fatalf("unable to create policy: %v", err) + } + + value, err := policy.rego.RawQuery("data.framework.policy_api_version", map[string]interface{}{}) + if err != nil { + t.Fatalf("unable to query policy: %v", err) + } + + policyAPIVersion, ok := value[0].Expressions[0].Value.(string) + if ok { + if policyAPIVersion != apiVersion { + t.Error("policy_api_version is not set correctly from api_svn") + } + } else { + t.Error("no result set from querying data.framework.policy_api_version") + } +} + // // Setup and "fixtures" follow... // @@ -6063,26 +6284,46 @@ type regoFragmentContainer struct { } func setupSimpleRegoFragmentTestConfig(gc *generatedConstraints) (*regoFragmentTestConfig, error) { - return setupRegoFragmentTestConfig(gc, 1, []string{"containers"}, []string{}, false, false, false) + return setupRegoFragmentTestConfig(gc, 1, []string{"containers"}, []string{}, false, false, false, false) } func setupRegoFragmentTestConfigWithIncludes(gc *generatedConstraints, includes []string) (*regoFragmentTestConfig, error) { - return setupRegoFragmentTestConfig(gc, 1, includes, []string{}, false, false, false) + return setupRegoFragmentTestConfig(gc, 1, includes, []string{}, false, false, false, false) } func setupRegoFragmentTestConfigWithExcludes(gc *generatedConstraints, excludes []string) (*regoFragmentTestConfig, error) { - return setupRegoFragmentTestConfig(gc, 1, []string{}, excludes, false, false, false) + return setupRegoFragmentTestConfig(gc, 1, []string{}, excludes, false, false, false, false) +} + +func setupRegoFragmentSVNErrorTestConfig(gc *generatedConstraints) (*regoFragmentTestConfig, error) { + return setupRegoFragmentTestConfig(gc, 1, []string{"containers"}, []string{}, true, false, false, false) } -func setupRegoFragmentVersionErrorTestConfig(gc *generatedConstraints) (*regoFragmentTestConfig, error) { - return setupRegoFragmentTestConfig(gc, 1, []string{"containers"}, []string{}, true, false, false) +func setupRegoSubfragmentSVNErrorTestConfig(gc *generatedConstraints) (*regoFragmentTestConfig, error) { + return setupRegoFragmentTestConfig(gc, 1, []string{"fragments"}, []string{}, true, false, false, false) } func setupRegoFragmentTwoFeedTestConfig(gc *generatedConstraints, sameIssuer bool, sameFeed bool) (*regoFragmentTestConfig, error) { - return setupRegoFragmentTestConfig(gc, 2, []string{"containers"}, []string{}, false, sameIssuer, sameFeed) + return setupRegoFragmentTestConfig(gc, 2, []string{"containers"}, []string{}, false, sameIssuer, sameFeed, false) } -func setupRegoFragmentTestConfig(gc *generatedConstraints, numFragments int, includes []string, excludes []string, versionError bool, sameIssuer bool, sameFeed bool) (tc *regoFragmentTestConfig, err error) { +func setupRegoFragmentSVNMismatchTestConfig(gc *generatedConstraints) (*regoFragmentTestConfig, error) { + return setupRegoFragmentTestConfig(gc, 2, []string{"containers"}, []string{}, false, false, false, true) +} + +func compareSVNs(lhs string, rhs string) int { + lhs_int, err := strconv.Atoi(lhs) + if err == nil { + rhs_int, err := strconv.Atoi(rhs) + if err == nil { + return lhs_int - rhs_int + } + } + + panic("unable to compare SVNs") +} + +func setupRegoFragmentTestConfig(gc *generatedConstraints, numFragments int, includes []string, excludes []string, svnError bool, sameIssuer bool, sameFeed bool, svnMismatch bool) (tc *regoFragmentTestConfig, err error) { gc.fragments = generateFragments(testRand, int32(numFragments)) if sameIssuer { @@ -6094,7 +6335,11 @@ func setupRegoFragmentTestConfig(gc *generatedConstraints, numFragments int, inc } } - fragments := selectFragmentsFromConstraints(gc, numFragments, includes, excludes, versionError, frameworkSVN) + subSVNError := svnError + if len(includes) > 0 && includes[0] == "fragments" { + svnError = false + } + fragments := selectFragmentsFromConstraints(gc, numFragments, includes, excludes, svnError, frameworkVersion, svnMismatch) containers := make([]*regoFragmentContainer, numFragments) subFragments := make([]*regoFragment, numFragments) @@ -6126,7 +6371,7 @@ func setupRegoFragmentTestConfig(gc *generatedConstraints, numFragments int, inc for _, include := range fragment.info.includes { switch include { case "fragments": - subFragments[i] = selectFragmentsFromConstraints(fragment.constraints, 1, []string{"containers"}, []string{}, false, frameworkSVN)[0] + subFragments[i] = selectFragmentsFromConstraints(fragment.constraints, 1, []string{"containers"}, []string{}, subSVNError, frameworkVersion, false)[0] break case "external_processes": @@ -6139,14 +6384,17 @@ func setupRegoFragmentTestConfig(gc *generatedConstraints, numFragments int, inc // we remove the include string so that the generated policy // does not include them. fragment.info.includes = removeStringsFromArray(fragment.info.includes, excludes) + + code := fragment.constraints.toFragment().marshalRego() + fragment.code = setFrameworkVersion(code, frameworkVersion) } if sameFeed { includeSet := make(map[string]bool) - minSVN := semver.MustParse("9.9.9") + minSVN := strconv.Itoa(maxGeneratedVersion) for _, fragment := range gc.fragments { - svn := semver.MustParse(fragment.minimumSVN) - if svn.LT(minSVN) { + svn := fragment.minimumSVN + if compareSVNs(svn, minSVN) < 0 { minSVN = svn } for _, include := range fragment.includes { @@ -6154,7 +6402,7 @@ func setupRegoFragmentTestConfig(gc *generatedConstraints, numFragments int, inc } } frag := gc.fragments[0] - frag.minimumSVN = minSVN.String() + frag.minimumSVN = minSVN frag.includes = make([]string, 0, len(includeSet)) for include := range includeSet { frag.includes = append(frag.includes, include) @@ -6316,40 +6564,40 @@ func setupRegoDropEnvsTest(disjoint bool) (*regoContainerTestConfig, error) { }, nil } -type regoFrameworkSVNTestConfig struct { +type regoFrameworkVersionTestConfig struct { policy *regoEnforcer fragments []*regoFragment } -func setFrameworkSVN(code string, svn string) string { - template := `framework_svn := "%s"` - old := fmt.Sprintf(template, frameworkSVN) - if svn == "" { +func setFrameworkVersion(code string, version string) string { + template := `framework_version := "%s"` + old := fmt.Sprintf(template, frameworkVersion) + if version == "" { return strings.Replace(code, old, "", 1) } - new := fmt.Sprintf(template, svn) + new := fmt.Sprintf(template, version) return strings.Replace(code, old, new, 1) } -func setupFrameworkSVNSimpleTest(gc *generatedConstraints, policy_svn string, svn string) (*regoFrameworkSVNTestConfig, error) { - return setupFrameworkSVNTest(gc, policy_svn, svn, 0, "", []string{}) +func setupFrameworkVersionSimpleTest(gc *generatedConstraints, policyVersion string, version string) (*regoFrameworkVersionTestConfig, error) { + return setupFrameworkVersionTest(gc, policyVersion, version, 0, "", []string{}) } -func setupFrameworkSVNTest(gc *generatedConstraints, policy_svn string, svn string, numFragments int, fragment_svn string, includes []string) (*regoFrameworkSVNTestConfig, error) { +func setupFrameworkVersionTest(gc *generatedConstraints, policyVersion string, version string, numFragments int, fragmentVersion string, includes []string) (*regoFrameworkVersionTestConfig, error) { fragments := make([]*regoFragment, 0, numFragments) if numFragments > 0 { gc.fragments = generateFragments(testRand, int32(numFragments)) - fragments = selectFragmentsFromConstraints(gc, numFragments, includes, []string{}, false, fragment_svn) + fragments = selectFragmentsFromConstraints(gc, numFragments, includes, []string{}, false, fragmentVersion, false) } securityPolicy := gc.toPolicy() - policy, err := newRegoPolicy(setFrameworkSVN(securityPolicy.marshalRego(), policy_svn), []oci.Mount{}, []oci.Mount{}) + policy, err := newRegoPolicy(setFrameworkVersion(securityPolicy.marshalRego(), policyVersion), []oci.Mount{}, []oci.Mount{}) if err != nil { return nil, err } - code := strings.Replace(frameworkCodeTemplate, "@@FRAMEWORK_SVN@@", svn, 1) + code := strings.Replace(frameworkCodeTemplate, "@@FRAMEWORK_VERSION@@", version, 1) policy.rego.RemoveModule("framework.rego") policy.rego.AddModule("framework.rego", &rpi.RegoModule{Namespace: "framework", Code: code}) err = policy.rego.Compile() @@ -6357,7 +6605,7 @@ func setupFrameworkSVNTest(gc *generatedConstraints, policy_svn string, svn stri return nil, err } - return ®oFrameworkSVNTestConfig{policy: policy, fragments: fragments}, nil + return ®oFrameworkVersionTestConfig{policy: policy, fragments: fragments}, nil } type regoFragment struct { @@ -6370,7 +6618,24 @@ func (f *regoFragment) selectContainer() *securityPolicyContainer { return selectContainerFromContainerList(f.constraints.containers, testRand) } -func selectFragmentsFromConstraints(gc *generatedConstraints, numFragments int, includes []string, excludes []string, versionError bool, fragmentFrameworkSVN string) []*regoFragment { +func mustIncrementSVN(svn string) string { + svn_semver, err := semver.Parse(svn) + + if err == nil { + svn_semver.IncrementMajor() + return svn_semver.String() + } + + svn_int, err := strconv.Atoi(svn) + + if err == nil { + return strconv.Itoa(svn_int + 1) + } + + panic("Could not increment SVN") +} + +func selectFragmentsFromConstraints(gc *generatedConstraints, numFragments int, includes []string, excludes []string, svnError bool, frameworkVersion string, svnMismatch bool) []*regoFragment { choices := randChoices(testRand, numFragments, len(gc.fragments)) fragments := make([]*regoFragment, numFragments) for i, choice := range choices { @@ -6395,19 +6660,23 @@ func selectFragmentsFromConstraints(gc *generatedConstraints, numFragments int, break } } - code := constraints.toPolicy().marshalRego() - version := config.minimumSVN - if versionError { - sv := semver.MustParse(version) - sv.IncrementMajor() - config.minimumSVN = sv.String() + svn := config.minimumSVN + if svnMismatch { + if randBool(testRand) { + svn = generate_semver(testRand) + } else { + config.minimumSVN = generate_semver(testRand) + } + } + + constraints.svn = svn + if svnError { + config.minimumSVN = mustIncrementSVN(config.minimumSVN) } - namespace := testDataGenerator.uniqueFragmentNamespace() - fragmentHeader := fmt.Sprintf("package %s\n\nsvn := \"%s\"\n", namespace, version) - code = strings.Replace(code, "package policy", fragmentHeader, 1) - code = setFrameworkSVN(code, fragmentFrameworkSVN) + code := constraints.toFragment().marshalRego() + code = setFrameworkVersion(code, frameworkVersion) fragments[i] = ®oFragment{ info: config, @@ -6776,7 +7045,7 @@ func setupRegoScratchMountTest( }, nil } -func verifyPolicyRules(apiSVN string, enforcementPoints map[string]interface{}, policyCode string) error { +func verifyPolicyRules(apiVersion string, enforcementPoints map[string]interface{}, policyCode string) error { query := rego.New( rego.Query("data.policy"), rego.Module("policy.rego", policyCode), @@ -6790,10 +7059,10 @@ func verifyPolicyRules(apiSVN string, enforcementPoints map[string]interface{}, } policyTemplateRules := resultSet[0].Expressions[0].Value.(map[string]interface{}) - policyTemplateAPISVN := policyTemplateRules["api_svn"].(string) + policyTemplateAPIVersion := policyTemplateRules["api_version"].(string) - if policyTemplateAPISVN != apiSVN { - return fmt.Errorf("Policy template SVN != api SVN: %s != %s", apiSVN, policyTemplateAPISVN) + if policyTemplateAPIVersion != apiVersion { + return fmt.Errorf("Policy template version != api version: %s != %s", apiVersion, policyTemplateAPIVersion) } for rule := range enforcementPoints { @@ -6803,7 +7072,7 @@ func verifyPolicyRules(apiSVN string, enforcementPoints map[string]interface{}, } for rule := range policyTemplateRules { - if rule == "api_svn" || rule == "framework_svn" || rule == "reason" { + if rule == "api_version" || rule == "framework_version" || rule == "reason" { continue } @@ -6907,6 +7176,13 @@ func generateCapabilities(r *rand.Rand) *oci.LinuxCapabilities { } } +func generate_semver(r *rand.Rand) string { + major := randMinMax(r, 0, maxGeneratedVersion) + minor := randMinMax(r, 0, maxGeneratedVersion) + patch := randMinMax(r, 0, maxGeneratedVersion) + return fmt.Sprintf("%d.%d.%d", major, minor, patch) +} + func alterCapabilitySet(r *rand.Rand, set []string) []string { newSet := copyStrings(set) diff --git a/pkg/securitypolicy/securitypolicy.go b/pkg/securitypolicy/securitypolicy.go index 8c8fb1e3dc..3b7ceb1132 100644 --- a/pkg/securitypolicy/securitypolicy.go +++ b/pkg/securitypolicy/securitypolicy.go @@ -22,8 +22,8 @@ var frameworkCodeTemplate string //go:embed api.rego var apiCodeTemplate string -var APICode = strings.Replace(apiCodeTemplate, "@@API_SVN@@", apiSVN, 1) -var FrameworkCode = strings.Replace(frameworkCodeTemplate, "@@FRAMEWORK_SVN@@", frameworkSVN, 1) +var APICode = strings.Replace(apiCodeTemplate, "@@API_VERSION@@", apiVersion, 1) +var FrameworkCode = strings.Replace(frameworkCodeTemplate, "@@FRAMEWORK_VERSION@@", frameworkVersion, 1) var ErrInvalidOpenDoorPolicy = errors.New("allow_all cannot be set to 'true' when Containers are non-empty") @@ -174,11 +174,11 @@ type CapabilitiesConfig struct { Ambient []string `json:"ambient" toml:"ambient"` } -//go:embed svn_api -var apiSVN string +//go:embed version_api +var apiVersion string -//go:embed svn_framework -var frameworkSVN string +//go:embed version_framework +var frameworkVersion string // NewEnvVarRules creates slice of EnvRuleConfig's from environment variables // strings slice. diff --git a/pkg/securitypolicy/securitypolicy_marshal.go b/pkg/securitypolicy/securitypolicy_marshal.go index 04ba0e6cf6..f9ca3a3013 100644 --- a/pkg/securitypolicy/securitypolicy_marshal.go +++ b/pkg/securitypolicy/securitypolicy_marshal.go @@ -47,7 +47,7 @@ var policyRegoTemplate string //go:embed open_door.rego var openDoorRegoTemplate string -var openDoorRego = strings.Replace(openDoorRegoTemplate, "@@API_SVN@@", apiSVN, 1) +var openDoorRego = strings.Replace(openDoorRegoTemplate, "@@API_VERSION@@", apiVersion, 1) func marshalJSON( allowAll bool, @@ -437,8 +437,8 @@ func (p securityPolicyInternal) marshalRego() string { writeLine(builder, "allow_unencrypted_scratch := %t", p.AllowUnencryptedScratch) writeLine(builder, "allow_capability_dropping := %t", p.AllowCapabilityDropping) result := strings.Replace(policyRegoTemplate, "@@OBJECTS@@", builder.String(), 1) - result = strings.Replace(result, "@@API_SVN@@", apiSVN, 1) - result = strings.Replace(result, "@@FRAMEWORK_SVN@@", frameworkSVN, 1) + result = strings.Replace(result, "@@API_VERSION@@", apiVersion, 1) + result = strings.Replace(result, "@@FRAMEWORK_VERSION@@", frameworkVersion, 1) return result } @@ -447,5 +447,5 @@ func (p securityPolicyFragment) marshalRego() string { addFragments(builder, p.Fragments) addContainers(builder, p.Containers) addExternalProcesses(builder, p.ExternalProcesses) - return fmt.Sprintf("package %s\n\nsvn := \"%s\"\nframework_svn := \"%s\"\n\n%s", p.Namespace, p.SVN, frameworkSVN, builder.String()) + return fmt.Sprintf("package %s\n\nsvn := \"%s\"\nframework_version := \"%s\"\n\n%s", p.Namespace, p.SVN, frameworkVersion, builder.String()) } diff --git a/pkg/securitypolicy/securitypolicy_test.go b/pkg/securitypolicy/securitypolicy_test.go index c3e16fc63f..a3520253f8 100644 --- a/pkg/securitypolicy/securitypolicy_test.go +++ b/pkg/securitypolicy/securitypolicy_test.go @@ -1234,10 +1234,7 @@ func generateFragmentNamespace(r *rand.Rand) string { } func generateSVN(r *rand.Rand) string { - major := randMinMax(r, 0, maxGeneratedVersion) - minor := randMinMax(r, 0, maxGeneratedVersion) - patch := randMinMax(r, 0, maxGeneratedVersion) - return fmt.Sprintf("%d.%d.%d", major, minor, patch) + return strconv.FormatInt(int64(randMinMax(r, 0, maxGeneratedVersion)), 10) } func selectRootHashFromConstraints(constraints *generatedConstraints, r *rand.Rand) string { diff --git a/pkg/securitypolicy/svn_framework b/pkg/securitypolicy/svn_framework deleted file mode 100644 index 373f8c6f07..0000000000 --- a/pkg/securitypolicy/svn_framework +++ /dev/null @@ -1 +0,0 @@ -0.2.3 \ No newline at end of file diff --git a/pkg/securitypolicy/svn_api b/pkg/securitypolicy/version_api similarity index 100% rename from pkg/securitypolicy/svn_api rename to pkg/securitypolicy/version_api diff --git a/pkg/securitypolicy/version_framework b/pkg/securitypolicy/version_framework new file mode 100644 index 0000000000..9325c3ccda --- /dev/null +++ b/pkg/securitypolicy/version_framework @@ -0,0 +1 @@ +0.3.0 \ No newline at end of file