kubescape / regolibrary

The regolibrary package contains the controls Kubescape uses for detecting misconfigurations in Kubernetes manifests.
Apache License 2.0
121 stars 48 forks source link
compliance kubernetes kubescape opa security

Version release-date

GitHub

OpenSSF Scorecard

Kubescape Regolibrary

This repository contains a library of security controls that codify Kubernetes best practices derived from the most prevalent security frameworks in the industry. Kubescape uses these controls to scan again running clusters or manifest files under development. They’re written in Rego, the purpose-built declarative policy language that supports Open Policy Agent (OPA).

Terminology

Contributing

Add a framework

Add frameworkName.json file in the /frameworks directory

Example of a framework:

{
    "name": "DevOpsBest",
    "description": "This framework is recommended for use by devops.",
    "attributes": {
    },
    "scanningScope": {
        "matches": [
            "cluster",
            "file"
        ]
    },
    "controlsNames": [
        "Naked pods",
        "Container runtime socket mounted",
        "Image pull policy on latest tag",
        "Label usage for resources",
        "K8s common labels usage",
        "Pods in default namespace",
        "Container hostPort",
        "Resources CPU limit and request",
        "Resources memory limit and request",
        "Configured liveness probe",
        "Configured readiness probe"
    ]
}

Add a control

Add controlName.json file in the /controls directory.

Example of a control:

{
    "name": "Pods in default namespace",
    "attributes": {
    },
    "description": "It is recommended to avoid running pods in cluster without explicit namespace assignment. This control identifies all the pods running in the default namespace.",
    "remediation": "Create necessary namespaces and move all the pods from default namespace there.",
    "rulesNames": [
        "pods-in-default-namespace"
    ],
    "long_description": "It is recommended to avoid running pods in cluster without explicit namespace assignment. This may lead to wrong capabilities and permissions assignment and potential compromises. This control identifies all the pods running in the default namespace.",
    "test": "Check that there are no pods in the 'default' namespace",
    "id": "C-0061",
    "controlID": "C-0061",
    "baseScore": 3, 
    "scanningScope": {
        "matches": [
            "cluster",
            "file"
        ]
    },
     "category": {
        "name" : "Workload",
        "subCategory": {
            "name": "Resource management"
        }
   }
}

Add a rule:

  1. Add to /rules a new directory with the rule name

  2. Add to the rule directory file - rule.metadata.json:

Example of rule.metadata.json:

{
    "name": "resources-cpu-limit-and-request",
    "attributes": {
    },
    "ruleLanguage": "Rego",
    "match": [
      {
        "apiGroups": [
          ""
        ],
        "apiVersions": [
          "v1"
        ],
        "resources": [
          "Pod"
        ]
      }
    ],
    "ruleDependencies": [
    ],
    "controlConfigInputs": [
      {
        "path": "settings.postureControlInputs.cpu_request_max",
        "name": "cpu_request_max",
        "description": "Ensure a CPU resource request is set and is under this defined maximum value."
      }
    ],
    "description": "CPU limits and requests are not set.",
    "remediation": "Ensure CPU limits and requests are set.",
    "ruleQuery": "armo_builtins"
}
  1. Add to the new rule directory a new file - raw.rego

    This is where the logic of the rule is. Example of raw.rego:

    package armo_builtins
    
    deny[msga] {
    
        pod := input[_]
        pod.kind == "Pod"
        container := pod.spec.containers[i]
        container.securityContext.privileged == true
        path := sprintf("containers[%d].securityContext.privileged", [i])
    
        msga := {
            "alertMessage": sprintf("pod: %v is defined as privileged", [pod.metadata.name]),
            "packagename": "armo_builtins",
            "fixPaths": [],
            "failedPaths": path,
            "alertObject": {
                "k8sApiObjects": [pod]
            }
        }
    }

    Use opa rego reference for help with syntax

    See structure of a rule response

  2. Add a test for the new rule (and run it!). Learn how to add a test here and how to run it here.

  3. Add filter.rego if needed - If it exists, the filter is run by Kubescape to calculate ‘all resources’ = the number of potential resources to fail. It affects the risk score. This is needed in cases where a rule asks for resources that wil not potentially fail. Example: if a rule asks for pods and service accounts to see if they are connected but only fails the pods, we would create a filter rego that returns only pods.

N.B. To speed up the rule creation, we provided the script scripts/init-rule.py. This tool for scaffolding and code generation can be used to bootstrap a new rule fast. Let's see an example. To create a new rule, type the command:

python3 scripts/init-rule.py \
    --name "ensure-something-is-set" \
    --fix-command "chmod 700 /tmp/file" \
    --rule-description "this is an example description" \
    --rule-remediation "this is an example remediation" \
    --alert-message "found something weird" \
    --test-list "success,failed_1,failed_2"

This command will create the following directory structure in the regolibrary repository.

rules/ensure-something-is-set/
├── raw.rego
├── rule.metadata.json
└── test
    ├── failed_1
    │   ├── expected.json
    │   └── input
    ├── failed_2
    │   ├── expected.json
    │   └── input
    └── success
        ├── expected.json
        └── input

To have a complete overview about the script, type this command: python3 scripts/init-rule.py --help.

OPA bundles

The Kubescape regolibrary is available as an OPA bundle, for both targets, WASM and Rego.

Using the bundles

Endpoint names are normalized to be used as a Rego package name. Here are some examples:

host-pid -> host_pid
Host_Ipc -> Host_Ipc
foobar -> foobar

To be sure, you can use the following regex to validate the endpoint name:

import re
def normalize_rule_name(name) -> str:
     return re.sub(r'[^a-zA-Z0-9_]', '_', name)

Rules

Rules endpoints uses the following naming convention:

data.armo_builtins.rules.<rule_name>.raw.deny

If there is a filter rule, it's available at the following endpoint:

data.armo_builtins.rules.<rule_name>.filter.deny

Controls

Controls endpoints uses the following naming convention:

data.armo_builtins.controls.<control_id>.deny

Frameworks

Frameworks endpoints uses the following naming convention:

data.armo_builtins.frameworks.<framework_name>.deny

Settings

When evaluating frameworks or controls, you can control the amount of metadata the results will contain by using the data.settings.

Available settings:

Build

To build the OPA bundles, use the python script /scripts/bundle.py.

For example:

python3 scripts/bundle.py . -o release

Unsupported rules and controls

Some rules and controls are not supported in the OPA bundles, because they require extra customized Rego built-in functions (you can always use Kubescape to evaluate them :wink:).

Rules

The following rules are not supported in the OPA bundles:

Support & Communication

Reach out if you have any questions:

Learn more:

Contributions

Thanks to all our contributors! Check out our CONTRIBUTING file to learn how to join them.