Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new feature: create OSCAL json report from compliance operator evidence #50

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# [0.11.0](https://github.com/ComplianceAsCode/auditree-arboretum/releases/tag/v0.11.0)

- [ADDED] Kubernetes resources report added.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- [ADDED] Kubernetes resources report added.
- [ADDED] Kubernetes Compliance OSCAL Observations harvest report added.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated as suggested.


# [0.10.0](https://github.com/ComplianceAsCode/auditree-arboretum/releases/tag/v0.10.0)

- [ADDED] Organization repository direct collaborators check added to `permissions`.
Expand Down
2 changes: 1 addition & 1 deletion arboretum/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@
# limitations under the License.
"""Arboretum - Checking your compliance & security posture, continuously."""

__version__ = '0.10.0'
__version__ = '0.11.0'
23 changes: 23 additions & 0 deletions arboretum/kubernetes/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,29 @@ list of clusters. TTL is set to 1 day.

Checks coming soon...

## Reports

### Compliance OSCAL Observations

* Report: [compliance_oscal_observations][compliance-oscal-observations]
* Purpose: Create a JSON format report as a [NIST OSCAL Assessment Results][assessment-results] observations list from the kubernetes [OpenShift Compliance Operator][compliance-operator] data in the evidence locker.
* Behavior:
* A report is generated comprising a collection of observations, one for each [XCCDF][xccdf] rule/result pair discovered in the `cluster_resource.json` files with respect to the optional date range. Each observation may be enhanced in accordance with an optional `oscal_metadata.yaml` file.
* Data files required:
* `raw/kubernetes/cluster_resource.json`, created by the kubernetes provider [ClusterResourceFetcher][fetch-cluster-resource].
* Data files optional:
* `raw/kubernetes/oscal_metadata.json`, planted by the kubernetes provider account administrator.
Comment on lines +124 to +128
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the oscal_metadata.yaml different from the raw/kubernetes/oscal_metadata.json?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does whichever file that is going to be planted in the locker need to be treated as evidence? Planting it in the locker implies that it is auditable and will be treated as evidence with a time to live setting.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are the 2 files: cluster_resource.json and oscal_metadata.json. They go hand-in-hand. The latter is metadata about the former. If the inventory (e.g. name: ssg-ocp4-ds-cis-10.221.139.105-pod) of the former former adds/deletes/modifies a name, then the latter should change accordingly.

* Details/Config:

```shell
harvest reports arboretum --detail compliance_oscal_observations
```

[compliance-oscal-observations]: https://github.com/ComplianceAsCode/auditree-arboretum/blob/main/arboretum/kubernetes/reports/compliance_oscal_observations.py
[fetch-cluster-resource]: https://github.ibm.com/auditree/auditree-central/blob/master/auditree_central/provider/iks/fetchers/fetch_cluster_resource.py
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

broken link

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two broken links fixed.

[assessment-results]: https://pages.nist.gov/OSCAL/documentation/schema/assessment-results-layer/assessment-results/
[xccdf]: https://csrc.nist.gov/projects/security-content-automation-protocol/specifications/xccdf
[compliance-operator]: https://github.com/openshift/compliance-operator/blob/master/README.md
[auditree-framework]: https://github.com/ComplianceAsCode/auditree-framework
[auditree-framework documentation]: https://complianceascode.github.io/auditree-framework/
[usage]: https://github.com/ComplianceAsCode/auditree-arboretum#usage
Expand Down
227 changes: 227 additions & 0 deletions arboretum/kubernetes/reports/compliance_oscal_observations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
# -*- mode:python; coding:utf-8 -*-
# Copyright (c) 2020 IBM Corp. All rights reserved.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2021

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed copyright date.

#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
The compliance OSCAL observations report.
degenaro marked this conversation as resolved.
Show resolved Hide resolved

A json report comprising NIST OSCAL Assessment Results Observations generated
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of "comprising" here is confusing to me. Could this be rewritten a bit?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced "comprising" with "containing".

by processing compliance operator fetcher cluster_resource evidence. The
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
by processing compliance operator fetcher cluster_resource evidence. The
by processing Kubernetes stand-alone cluster resources evidence. The

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed "compliance operator fetcher cluster_resource" to "Kubernetes stand-alone cluster resources" as suggested.

embedded XML within the cluster_resource evidence is transformed to produce the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
embedded XML within the cluster_resource evidence is transformed to produce the
XML within the cluster resource evidence is transformed to produce the JSON

Copy link
Author

@degenaro degenaro Mar 16, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed "embedded", removed "_", added "JSON" as suggested.

report. If an optional oscal_metadata file is specified, then the report is
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
report. If an optional oscal_metadata file is specified, then the report is
report. If an optional OSCAL metadata file is specified, then the report is

...again this is confusing. Is this a file or evidence gathered from the locker?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed "oscal_metadata" to "OSCAL metadata". The metadata (if present) makes up for what is lacking in the cluster_resource evidence to produce a better OSCAL. As mentioned in a comment above, the metadata should change over time as the resources contained within the cluster change.

enhanced accordingly.

Provide the "start" and "end" optional configuration (--config) parameters
as a JSON string, in "YYYYMMDD" format to define a date range for the evidence
used to process the report. If omitted, the default value is the current date.

---------------
Example usages:
---------------

> harvest report my-repo arboretum compliance_oscal_observations

> harvest report my-repo arboretum compliance_oscal_observations \
--config '{ \
"oscal_metadata":"raw/kubernetes/oscal_metadata.yaml" \
}'

> harvest report my-repo arboretum compliance_oscal_observations \
--config '{ \
"cluster_resource":"raw/kubernetes/cluster_resource.json", \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(1) Why would someone need to supply this? AFAICT this evidence should always be processed by the report.
(2) Also, this isn't the evidence file name generated by the fetcher.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to allow flexibility in case the fetcher generated file name changes for any reason.

"oscal_metadata":"raw/kubernetes/oscal_metadata.yaml", \
"start":"20200901", \
"end":"20201231" \
}'

--------------------
oscal_metadata.yaml:
--------------------

The oscal_metadata.yaml file comprises one or more mappings. Below is shown the
format of a single mapping. The items in angle brackets are to be replaced with
desired values for augmenting the produced OSCAL.

The mapping whose <name> matches the [metadata][name] in the evidence for the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is confusing. Above you imply that this yaml is evidence as is the cluster resources evidence. If that's the case then you should be clear which evidence you're referring to here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to specify "cluster resources evidence" as suggested.

corresponding embedded XML, if any, is used for augmenting the produced OSCAL.

<name>:
namespace: <namespace>
subject-references:
component:
uuid-ref: <uuid-ref-component>
type: <component-type>
title: <component-title>
inventory-item:
uuid-ref: <uuid-ref-inventory-item>
type: <inventory-item-type>
title: <inventory-item-title>
properties:
target: <target>
cluster-name: <cluster-name>
cluster-type: <cluster-type>
cluster-region: <cluster-region>

A sample oscal_metadata.yaml file with 2 mappings is shown below.

ssg-ocp4-ds-cis-111.222.333.444-pod:
namespace: xccdf
subject-references:
component:
uuid-ref: 56666738-0f9a-4e38-9aac-c0fad00a5821
type: component
title: Red Hat OpenShift Kubernetes
inventory-item:
uuid-ref: 46aADFAC-A1fd-4Cf0-a6aA-d1AfAb3e0d3e
type: inventory-item
title: Pod
properties:
target: kube-br7qsa3d0vceu2so1a90-roksopensca-0000026b.iks.mycorp
cluster-name: ROKS-OpenSCAP-1
cluster-type: openshift
cluster-region: us-south
ssg-rhel7-ds-cis-111.222.333.444-pod:
namespace: xccdf
subject-references:
component:
uuid-ref: 89cfe7a7-ce6b-4699-aa7b-2f5739c72001
type: component
title: RedHat Enterprise Linux 7.8
inventory-item:
uuid-ref: 46aADFAC-A1fd-4Cf0-a6aA-d1AfAb3e0d3e
type: inventory-item
title: VM
properties:
target: kube-br7qsa3d0vceu2so1a90-roksopensca-0000026b.iks.mycorp
cluster-name: ROKS-OpenSCAP-1
cluster-type: openshift
cluster-region: us-south
"""

import json
from datetime import datetime, timedelta

from harvest.reporter import BaseReporter

from trestle.utils import osco

import yaml


class ComplianceOscalObservations(BaseReporter):
"""The compliance oscal observations class."""

@property
def report_filename(self):
"""Return the report filename."""
return 'compliance_oscal_observations.json'

def generate_report(self):
"""
Generate the compliance oscal observations report content.

:returns: stringified OSCAL json content
"""
# get required cluster resource path
path_cluster_resource = self.config.get(
'cluster_resource', 'raw/kubernetes/cluster_resource.json'
)
# get optional oscal_metadata path
path_oscal_metadata = self.config.get(
'oscal_metadata', 'raw/kubernetes/oscal_metadata.yaml'
)
# get start+end dates
start_dt = datetime.strptime(
self.config.get('start', datetime.today().strftime('%Y%m%d')),
'%Y%m%d'
)
end_dt = datetime.strptime(
self.config.get('end', datetime.today().strftime('%Y%m%d')),
'%Y%m%d'
)
if start_dt > end_dt:
raise ValueError('Cannot have start date before end date.')
current_dt = start_dt
previous = None
observation_list = []
# examine each day's evidence, if any
while current_dt <= end_dt:
try:
cluster_resource = json.loads(
self.get_file_content(path_cluster_resource, current_dt)
)
try:
oscal_metadata = yaml.load(
self.get_file_content(path_oscal_metadata, current_dt),
Loader=yaml.FullLoader
)
# add locker info to oscal metadata
for key in oscal_metadata.keys():
entry = oscal_metadata[key]
entry['locker'] = self.repo_url
except Exception:
oscal_metadata = None
# skip if no new evidence
if previous != cluster_resource:
previous = cluster_resource
# examine entries skipping those not relevant
for key in cluster_resource.keys():
for group in cluster_resource[key]:
for cluster in cluster_resource[key][group]:
for resource in cluster.get('resources', []):
self._update_observations(
observation_list,
resource,
oscal_metadata
)
except Exception:
pass
current_dt = current_dt + timedelta(days=1)
# create report
if len(observation_list) == 0:
raise RuntimeError('No report content.')
observation_dict = json.dumps(
{'observations': observation_list}, indent=2
)
report = str(observation_dict)
return report

def _update_observations(self, observation_list, resource, oscal_metadata):
"""Update observations list with additional observations."""
if resource.get('kind') != 'ConfigMap':
return
if 'data' not in resource.keys():
return
if 'results' not in resource['data'].keys():
return
if 'metadata' not in resource.keys():
return
if 'name' not in resource['metadata'].keys():
return
# assemble osco data for transformation
data = {'results': resource['data']['results']}
osco_data = {
'kind': resource['kind'],
'data': data,
'metadata': resource['metadata']
}
# get OSCAL Observation objects
arp, analysis = osco.get_observations(osco_data, oscal_metadata)
# convert Observation objects into json
for observation_model in arp.observations:
observation_json = json.loads(
observation_model.json(
exclude_none=True, by_alias=True, indent=2
)
)
observation_list.append(observation_json)
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ packages = find:
install_requires =
auditree-framework>=1.2.3
auditree-harvest>=1.0.0
compliance-trestle>=0.7.0

[options.packages.find]
exclude =
Expand Down
35 changes: 35 additions & 0 deletions test/fixtures/compliance_oscal_metadata.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@

ssg-ocp4-ds-cis-10.321.456.999-pod:
namespace: xccdf
subject-references:
component:
uuid-ref: 56666738-0f9a-4e38-9aac-c0fad00a5821
type: component
title: Red Hat OpenShift Kubernetes
inventory-item:
uuid-ref: 46aADFAC-A1fd-4Cf0-a6aA-d1AfAb3e0d3e
type: inventory-item
title: Pod
properties:
target: kube-br7qsa3d0vceu2so1a90-roksopensca-default-0000026b.iks.mycorp
cluster-name: ROKS-OpenSCAP-1
cluster-type: openshift
cluster-region: us-south

ssg-rhel7-ds-cis-111.222.333.444-pod:
namespace: xccdf
subject-references:
component:
uuid-ref: 89cfe7a7-ce6b-4699-aa7b-2f5739c72001
type: component
title: RedHat Enterprise Linux 7.8
inventory-item:
uuid-ref: 46aADFAC-A1fd-4Cf0-a6aA-d1AfAb3e0d3e
type: inventory-item
title: VM
properties:
target: kube-br7qsa3d0vceu2so1a90-roksopensca-default-0000026b.iks.mycorp
cluster-name: ROKS-OpenSCAP-1
cluster-type: openshift
cluster-region: us-south

38 changes: 38 additions & 0 deletions test/fixtures/kubernetes_cluster_resource.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"iks": {
"demo2020": [
{
"name": "compliance-dev-city10",
"region": "region2",
"type": "kubernetes",
"resources": [
{
"apiVersion": "v1",
"data": {
"exit-code": "2",
"results": "<?xml version=\"1.0\" encoding=\"UTF-8\"?> <TestResult xmlns=\"http://checklists.nist.gov/xccdf/1.2\" id=\"xccdf_org.open-scap_testresult_xccdf_org.ssgproject.content_profile_cis\" start-time=\"2020-08-03T02:26:31+00:00\" end-time=\"2020-08-03T02:26:31+00:00\" version=\"0.1.52\" test-system=\"cpe:/a:redhat:openscap:1.3.3\"> <benchmark href=\"/content/ssg-ocp4-ds.xml\" id=\"xccdf_org.ssgproject.content_benchmark_OCP-4\"/> <title>OSCAP Scan Result</title> <profile idref=\"xccdf_org.ssgproject.content_profile_cis\"/> <target>kube-roksopensca-default-00000123.iks.abc</target> <target-id-ref system=\"http://scap.nist.gov/schema/asset-identification/1.1\" name=\"asset0\" href=\"\"/> <platform idref=\"cpe:/a:redhat:openshift_container_platform:4.1\"/> <platform idref=\"cpe:/a:machine\"/> <set-value idref=\"xccdf_org.ssgproject.content_value_ocp_data_root\">/kubernetes-api-resources</set-value> <set-value idref=\"xccdf_org.ssgproject.content_value_var_kube_authorization_mode\">Webhook</set-value> <set-value idref=\"xccdf_org.ssgproject.content_value_var_streaming_connection_timeouts\">5m</set-value> <rule-result idref=\"xccdf_org.ssgproject.content_rule_ocp_idp_no_htpasswd\" time=\"2020-08-03T02:26:31+00:00\" severity=\"medium\" weight=\"1.000000\"> <result>notselected</result> <ident system=\"https://nvd.nist.gov/cce/index.cfm\">CCE-84209-6</ident> </rule-result> <rule-result idref=\"xccdf_org.ssgproject.content_rule_controller_use_service_account\" time=\"2020-08-03T02:26:31+00:00\" severity=\"medium\" weight=\"1.000000\"> <result>pass</result> <check system=\"http://oval.mitre.org/XMLSchema/oval-definitions-5\"> <check-content-ref name=\"oval:ssg-controller_use_service_account:def:1\" href=\"#oval0\"/> </check> </rule-result> <rule-result idref=\"xccdf_org.ssgproject.content_rule_controller_use_service_account\" time=\"2020-08-03T02:26:31+00:00\" severity=\"medium\" weight=\"1.000000\"> <result>pass</result> <check system=\"http://oval.mitre.org/XMLSchema/oval-definitions-5\"> <check-content-ref name=\"oval:ssg-controller_use_service_account:def:1\" href=\"#oval0\"/> </check> </rule-result> <rule-result idref=\"xccdf_org.ssgproject.content_rule_controller_rotate_kubelet_server_certs\" time=\"2020-08-03T02:26:31+00:00\" severity=\"medium\" weight=\"1.000000\"> <result>fail</result> <check system=\"http://oval.mitre.org/XMLSchema/oval-definitions-5\"> <check-content-ref name=\"oval:ssg-controller_rotate_kubelet_server_certs:def:1\" href=\"#oval0\"/> </check> </rule-result> <rule-result idref=\"xccdf_org.ssgproject.content_rule_scc_limit_root_containers\" time=\"2020-08-03T02:26:31+00:00\" severity=\"medium\" weight=\"1.000000\"> <result>notchecked</result> <message severity=\"info\">No candidate or applicable check found.</message> </rule-result> </TestResult>"
},
"kind": "ConfigMap",
"metadata": {
"annotations": {
"compliance-remediations/processed": "",
"compliance.openshift.io/scan-error-msg": "",
"compliance.openshift.io/scan-result": "NON-COMPLIANT",
"openscap-scan-result/node": "10.321.456.999"
},
"creationTimestamp": "2020-08-03T02:26:38Z",
"labels": {
"compliance-scan": "ssg-ocp4-ds-cis"
},
"name": "ssg-ocp4-ds-cis-10.321.456.999-pod",
"namespace": "openshift-compliance",
"resourceVersion": "22693331",
"selfLink": "/api/v1/namespaces/openshift-compliance/configmaps/ssg-ocp4-ds-cis-10.321.456.999-pod",
"uid": "dba4a77e-5eab-4df8-8258-64305f6a1768"
}
}
]
}
]
}
}
Loading