ComplianceAsCode / content

Security automation content in SCAP, Bash, Ansible, and other formats
https://complianceascode.readthedocs.io/en/latest/
Other
2.23k stars 698 forks source link

implement section 5 rules of CIS Benchmark for OpenShift with "count" function #6529

Open tmishina opened 3 years ago

tmishina commented 3 years ago

Which products and profiles does the rule apply to?

Describe the configuration setting enforced by this rule.

Rules in section 5 of CIS Benchmark for Kubernetes (OpenShift) (Control 5.1.2-5.1.4, 5.2.1-5.2.9) which require counting the number of elements in YAML files. Count the number of elements, and compare them with predefined constants.

Why is the configuration security relevant?

The numbers of the configurations should be minimized to reduce security risks; for example, the grater number of wildcard role (section 5.1.3) indicates higher security risk.

How to check the configuration?

Get resources of kubernetes, count the number of specific elements in the resources and check whether the number is grater than a predefined constant value.

Is it order dependent? (does it need to be at certain place in the file?)

No.

What is correct and incorrect syntax?

(N/A)

How to remediate

(TBD; Automated remediation is difficult)

Does any command need to be run?

api-resource-collector of Compliance Operator will be used to fetch kubernetes resources.

Are there going to be other rules like this one in the future? Is it worth creating template? (similar configuration format, similar remediation process...)

Yes; a number of rules may use this type of rule.

Are there any caveats to be considered when testing?

(TBD)

Is the configuration loaded directly by the or is it stored in some intermediate database (similar to dconf)?

api-resource-collector of Compliance Operator can load the configurations.

Is it possible to check / remediate this configuration in offline mode? (scanning containers or offline systems)

No

Please provide security policy references if possible e.g. STIG

CIS Benchmark for Kubernetes (OpenShift)

tmishina commented 3 years ago

I think count function of OVAL dictionary can be used, but current yamlfile content test does not support including additional elements like count.

@evgenyz Could you help us embed count into template or raw OVAL rule definition? ind:yamlfilecontent_object (in the small set example below) is the point?

<?xml version="1.0"?>
<ds:data-stream-collection xmlns:cat="urn:oasis:names:tc:entity:xmlns:xml:catalog" xmlns:cpe-dict="http://cpe.mitre.org/dictionary/2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:ds="http://scap.nist.gov/schema/scap/source/1.2" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:ind="http://oval.mitre.org/XMLSchema/oval-definitions-5#independent" xmlns:linux="http://oval.mitre.org/XMLSchema/oval-definitions-5#linux" xmlns:ocil="http://scap.nist.gov/schema/ocil/2.0" xmlns:oval="http://oval.mitre.org/XMLSchema/oval-common-5" xmlns:oval-def="http://oval.mitre.org/XMLSchema/oval-definitions-5" xmlns:unix="http://oval.mitre.org/XMLSchema/oval-definitions-5#unix" xmlns:xccdf-1.2="http://checklists.nist.gov/xccdf/1.2" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="scap_org.open-scap_collection_from_xccdf_ssg-ocp4-xccdf-1.2.xml" schematron-version="1.3">
  <ds:component id="scap_org.open-scap_comp_ssg-ocp4-oval.xml" timestamp="2021-01-13T03:46:40">
    <oval-def:oval_definitions xsi:schemaLocation="http://oval.mitre.org/XMLSchema/oval-common-5 oval-common-schema.xsd         http://oval.mitre.org/XMLSchema/oval-definitions-5 oval-definitions-schema.xsd         http://oval.mitre.org/XMLSchema/oval-definitions-5#independent independent-definitions-schema.xsd         http://oval.mitre.org/XMLSchema/oval-definitions-5#unix unix-definitions-schema.xsd         http://oval.mitre.org/XMLSchema/oval-definitions-5#linux linux-definitions-schema.xsd">
      <oval-def:definitions>
        <oval-def:definition class="compliance" id="oval:ssg-kubelet_configure_tls_cipher_suites_ingresscontroller:def:1" version="1">
          <oval-def:metadata>
            <oval-def:title>Ensure that the Kubelet only makes use of Strong Cryptographic Ciphers</oval-def:title>
            <oval-def:affected family="unix">
              <oval-def:platform>Red Hat OpenShift Container Platform 4</oval-def:platform>
            </oval-def:affected>
            <oval-def:description>In the YAML/JSON file '/apis/operator.openshift.io/v1/namespaces/openshift-ingress-operator/ingresscontrollers/default' at path '.status.tlsProfile.ciphers[:]' all: value equals '^(ECDHE-ECDSA-AES128-GCM-SHA256|ECDHE-RSA-AES128-GCM-SHA256|ECDHE-ECDSA-CHACHA20-POLY1305|ECDHE-RSA-AES256-GCM-SHA384|ECDHE-RSA-CHACHA20-POLY1305|ECDHE-ECDSA-AES256-GCM-SHA384|AES256-GCM-SHA384|AES128-GCM-SHA256)$'</oval-def:description>
            <oval-def:reference ref_id="kubelet_configure_tls_cipher_suites_ingresscontroller" source="ssg"/>
          </oval-def:metadata>
          <oval-def:criteria>
            <oval-def:criterion comment="In the YAML/JSON file '/apis/operator.openshift.io/v1/namespaces/openshift-ingress-operator/ingresscontrollers/default' at path '.status.tlsProfile.ciphers[:]' all" test_ref="oval:ssg-test_kubelet_configure_tls_cipher_suites_ingresscontroller:tst:1"/>
            <oval-def:criterion comment="Make sure that the file '/apis/operator.openshift.io/v1/namespaces/openshift-ingress-operator/ingresscontrollers/default' exists." test_ref="oval:ssg-test_file_for_kubelet_configure_tls_cipher_suites_ingresscontroller:tst:1"/>
          </oval-def:criteria>
        </oval-def:definition>
      </oval-def:definitions>
      <oval-def:tests>
        <ind:yamlfilecontent_test id="oval:ssg-test_kubelet_configure_tls_cipher_suites_ingresscontroller:tst:1" check="all" check_existence="only_one_exists" comment="In the file '/apis/operator.openshift.io/v1/namespaces/openshift-ingress-operator/ingresscontrollers/default' find only one object at path '.status.tlsProfile.ciphers[:]'." version="1">
          <ind:object object_ref="oval:ssg-object_kubelet_configure_tls_cipher_suites_ingresscontroller:obj:1"/>
          <ind:state state_ref="oval:ssg-state_kubelet_configure_tls_cipher_suites_ingresscontroller:ste:1"/>
        </ind:yamlfilecontent_test>
        <unix:file_test id="oval:ssg-test_file_for_kubelet_configure_tls_cipher_suites_ingresscontroller:tst:1" check="all" check_existence="only_one_exists" comment="Find the file to be checked ('/apis/operator.openshift.io/v1/namespaces/openshift-ingress-operator/ingresscontrollers/default')." version="1">
          <unix:object object_ref="oval:ssg-object_file_for_kubelet_configure_tls_cipher_suites_ingresscontroller:obj:1"/>
        </unix:file_test>
      </oval-def:tests>
      <oval-def:objects>
        <unix:file_object id="oval:ssg-object_file_for_kubelet_configure_tls_cipher_suites_ingresscontroller:obj:1" version="1">
          <unix:filepath var_ref="oval:ssg-kubelet_configure_tls_cipher_suites_ingresscontroller_file_location:var:1"/>
        </unix:file_object>
        <ind:yamlfilecontent_object id="oval:ssg-object_kubelet_configure_tls_cipher_suites_ingresscontroller:obj:1" version="1">
          <ind:filepath var_ref="oval:ssg-kubelet_configure_tls_cipher_suites_ingresscontroller_file_location:var:1"/>
          <ind:yamlpath>.status.tlsProfile.ciphers[:]</ind:yamlpath>
        </ind:yamlfilecontent_object>
      </oval-def:objects>
      <oval-def:states>
        <ind:yamlfilecontent_state id="oval:ssg-state_kubelet_configure_tls_cipher_suites_ingresscontroller:ste:1" version="1">
          <ind:value datatype="record">
            <oval-def:field name="#" operation="pattern match">^(ECDHE-ECDSA-AES128-GCM-SHA256|ECDHE-RSA-AES128-GCM-SHA256|ECDHE-ECDSA-CHACHA20-POLY1305|ECDHE-RSA-AES256-GCM-SHA384|ECDHE-RSA-CHACHA20-POLY1305|ECDHE-ECDSA-AES256-GCM-SHA384|AES256-GCM-SHA384|AES128-GCM-SHA256)$</oval-def:field>
          </ind:value>
        </ind:yamlfilecontent_state>
      </oval-def:states>
      <oval-def:variables>
        <oval-def:local_variable id="oval:ssg-kubelet_configure_tls_cipher_suites_ingresscontroller_file_location:var:1" datatype="string" comment="The actual path of the file to scan." version="1">
          <oval-def:concat>
            <oval-def:variable_component var_ref="oval:ssg-ocp_data_root:var:1"/>
            <oval-def:literal_component>/apis/operator.openshift.io/v1/namespaces/openshift-ingress-operator/ingresscontrollers/default</oval-def:literal_component>
          </oval-def:concat>
        </oval-def:local_variable>
      </oval-def:variables>
    </oval-def:oval_definitions>
</ds:data-stream-collection>
evgenyz commented 3 years ago

Okay, so you want to count how many elements of the array were returned in the object oval:ssg-object_kubelet_configure_tls_cipher_suites_ingresscontroller:obj:1, right?

Here is the test (variable test oval:0:tst:6) which would make sure that record oval:ssg-object_kubelet_configure_tls_cipher_suites_ingresscontroller:obj:1 has 4 name="#" fields (see <ind-def:value>4</ind-def:value> in oval:0:ste:7).

I know that OVAL can be brain-damaging, don't hesitate to ask for explanation.

Also, are you sure that it is a good idea to count array elements?

    <ind-def:variable_test version="1" id="oval:0:tst:6" check="all">
      <ind-def:object object_ref="oval:0:obj:7"/>
      <ind-def:state state_ref="oval:0:ste:7"/>
    </ind-def:variable_test>

    <ind-def:variable_object version="1" id="oval:0:obj:7">
      <ind-def:var_ref>oval:0:var:4</ind-def:var_ref>
    </ind-def:variable_object>

    <ind-def:variable_state version="1" id="oval:0:ste:7">
      <ind-def:var_ref>oval:0:var:4</ind-def:var_ref>
      <ind-def:value>4</ind-def:value>
    </ind-def:variable_state>

    <local_variable comment="var" datatype="int" version="1" id="oval:0:var:4">
      <count>
        <object_component object_ref="oval:ssg-object_kubelet_configure_tls_cipher_suites_ingresscontroller:obj:1" item_field="value" record_field="#"/>
      </count>
    </local_variable>
tmishina commented 3 years ago

@evgenyz Thank you, I'm now clearer about how I can use the count function. Here let me use concrete example - section 5.1.3 of CIS Benchmark for OpenShift. It requires minimizing the number of wildcard roles. Example of a rule with wildcard is as follows (there are two rules containing *).

items:
- apiVersion: rbac.authorization.k8s.io/v1
  kind: ClusterRole
  metadata:
    name: admin
  rules:
  - apiGroups:
    - packages.operators.coreos.com
    resources:
    - packagemanifests
    verbs:
    - '*'
  - apiGroups:
    - admissionregistration.k8s.io
    resources:
    - mutatingwebhookconfigurations
    verbs:
    - '*'
...

I think the rule should count the number of wildcard rules and warn user if the number excesses some threshold. To count the wildcard roles with curl, we can use following command.

 $ curl -H "Authorization: Bearer $(oc whoami -t)"  "https://$(KUBE_API_SERVER)/apis/rbac.authorization.k8s.io/v1/clusterroles" | jq .items[].rules[].verbs[] | sort | uniq -c
  28 "*"
...

My strategy was:

  1. create a rule using yamlfile_value (without counting)
  2. build the rule
  3. modify the OVAL built by the script to add counting function

To get wildcard roles using yamlfile_value, the rule (without considering counting) would be as follows.

template:
  name: yamlfile_value
  vars:
    ocp_data: "true"
    filepath: 'apis/rbac.authorization.k8s.io/v1/clusterroles'
    yamlpath: ".items[:].rules[:].verbs[:]"
    values:
    - value: '\*'
      operation: 'pattern match'

Before adding counting function, I tried to run this check. Result is: the API resource collector fails to save multiple clusterrole resources. I imagine this kind of multiple resource collection has not implemented yet (there are some commented out rules using such multiple collection). Single resource check (like below) works file, but it does not achieve what we want to check.

template:
    name: yamlfile_value
    vars:
        ocp_data: "true"
        filepath: '/apis/rbac.authorization.k8s.io/v1/clusterroles/admin'
        yamlpath: ".rules[61].verbs[0]"
        values:
          - value: '\*'
            operation: 'pattern match'

Do you or your colleagues know how to solve this multiple-resource collection issue?

tmishina commented 3 years ago

There are two issues on Compliance Operator to implement section 5.x rules.

1. Error when multiple rules attempt to collect all resources of a type

Assume that there are two rules and they attempt to collect following resources.

When rule 1 is performed, the resource is stored in /kubernetes-api-resources/apis/rbac.authorization.k8s.io/v1/clusterroles/admin. And then rule 2 attempts to save a resource to /kubernetes-api-resources/apis/rbac.authorization.k8s.io/v1/clusterroles, it will fail because there already exists a directory. This error does not happen when only rule 2 is selected.

2. Error when null entry exists in map/list retrieval

With yamlfile_value template, multiple entries can be specified like yamlpath: ".items[:].rules[:].verbs[:]" (this path is to check the wildcard rule - 5.1.3). When null entry exists in the traversing, the check will fail. For example, following json returns error, not ["read"]. I think it is difficult to avoid this error as jq also returns error, but I'd like to get result ["read"].

{
  "items": [
    {
      "rules": [
        {
          "verbs": [
            "read"
          ]
        }
      ]
    },
    {
      "rules": null
    }
  ]
}

In my understanding, we need to update Compliance Operator. Does anyone know the way to avoid these errors? or do I need to propose enhancements to Compliance Operator?

evgenyz commented 3 years ago

In theory yaml probe could be adjusted to treat null as an empty array (or map, dep. on context), but I'm not sure if it won't bring other unwanted side-effects.

What are the results for .items[:].rules[:] path in that case?

tmishina commented 3 years ago

It may be an empty array (map) for null. In my case I want to enumerate verbs (e.g., ["delete", "create",...') and honestly I have not yet thought about the side-effect.

{
  "apiGroups": [
    "operators.coreos.com"
  ],
  "resources": [
    "subscriptions"
  ],
  "verbs": [
    "create",
    "update",
    "patch",
    "delete"
  ]
}
{
}
^^^^ empty 
...
Mab879 commented 3 weeks ago

@yuumasato is this still valid

yuumasato commented 3 days ago

Not sure if this exact issue is valid, but it is still relevant. CIS 1.6.0 sections 5.1.xand5.2.x are MANUAL. cc: @rhmdnd @Vincent056