-
Notifications
You must be signed in to change notification settings - Fork 10
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
Changes from 2 commits
170d233
1a36a85
f3a6fc3
f900056
527ff36
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the oscal_metadata.yaml different from the There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. broken link There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
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. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 2021 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
...again this is confusing. Is this a file or evidence gathered from the locker? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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", \ | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) |
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 | ||
|
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" | ||
} | ||
} | ||
] | ||
} | ||
] | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated as suggested.