feat(securityhub_check): Add check and service for SecurityHub (#1360)

Co-authored-by: Toni de la Fuente <toni@blyx.com>
Co-authored-by: sergargar <sergio@verica.io>
Co-authored-by: Pepe Fagoaga <pepe@verica.io>
This commit is contained in:
Sergio Garcia
2022-10-19 15:21:07 +02:00
committed by GitHub
parent 6e73321a95
commit 5b5b0b0405
10 changed files with 290 additions and 39 deletions

View File

@@ -87,7 +87,6 @@ class AccessAnalyzer:
f"{regional_client.region} -- {error.__class__.__name__}: {error}" f"{regional_client.region} -- {error.__class__.__name__}: {error}"
) )
@dataclass @dataclass
class Analyzer: class Analyzer:
arn: str arn: str

View File

@@ -1,38 +0,0 @@
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# 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.
CHECK_ID_extra799="7.99"
CHECK_TITLE_extra799="[extra799] Check if Security Hub is enabled and its standard subscriptions"
CHECK_SCORED_extra799="NOT_SCORED"
CHECK_CIS_LEVEL_extra799="EXTRA"
CHECK_SEVERITY_extra799="High"
CHECK_ASFF_RESOURCE_TYPE_extra799="AwsSecurityHubHub"
CHECK_ALTERNATE_check799="extra799"
CHECK_SERVICENAME_extra799="securityhub"
CHECK_RISK_extra799='AWS Security Hub gives you a comprehensive view of your security alerts and security posture across your AWS accounts.'
CHECK_REMEDIATION_extra799='Security Hub is Regional. When you enable or disable a security standard; it is enabled or disabled only in the current Region or in the Region that you specify.'
CHECK_DOC_extra799='https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-standards-enable-disable.html'
CHECK_CAF_EPIC_extra799='Logging and Monitoring'
extra799(){
for regx in $REGIONS; do
# If command below fails get nothing then it there are no subscriptions and Security Hub is not enabled.
LIST_OF_SECHUB_SUBSCRIPTIONS=$($AWSCLI $PROFILE_OPT --region $regx securityhub get-enabled-standards --query 'StandardsSubscriptions[?StandardsStatus == `READY`].StandardsSubscriptionArn' --output json 2>/dev/null | awk -F "/" '{ print $2 }' | tr '\n' ' ' )
if [[ $LIST_OF_SECHUB_SUBSCRIPTIONS ]]; then
textPass "$regx: Security Hub is enabled with standards $LIST_OF_SECHUB_SUBSCRIPTIONS" "$regx"
else
textInfo "$regx: Security Hub is not enabled" "$regx"
#textFail "$regx: Security Hub is not enabled" "$regx"
fi
done
}

View File

@@ -0,0 +1,4 @@
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.securityhub.securityhub_service import SecurityHub
securityhub_client = SecurityHub(current_audit_info)

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "securityhub_enabled",
"CheckTitle": "Check if Security Hub is enabled and its standard subscriptions.",
"CheckType": ["Logging and Monitoring"],
"ServiceName": "securityhub",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:securityhub:region:account-id:hub/hub-id",
"Severity": "high",
"ResourceType": "AwsSecurityHubHub",
"Description": "Check if Security Hub is enabled and its standard subscriptions.",
"Risk": "AWS Security Hub gives you a comprehensive view of your security alerts and security posture across your AWS accounts.",
"RelatedUrl": "https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-standards-enable-disable.html",
"Remediation": {
"Code": {
"CLI": "aws securityhub enable-security-hub --enable-default-standards",
"NativeIaC": "",
"Other": "",
"Terraform": ""
},
"Recommendation": {
"Text": "Security Hub is Regional. When you enable or disable a security standard, it is enabled or disabled only in the current Region or in the Region that you specify.",
"Url": "https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-standards-enable-disable.html"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,23 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.securityhub.securityhub_client import securityhub_client
class securityhub_enabled(Check):
def execute(self):
findings = []
for securityhub in securityhub_client.securityhubs:
report = Check_Report(self.metadata)
report.region = securityhub.region
if securityhub.status == "ACTIVE":
report.status = "PASS"
report.status_extended = (
f"Security Hub is enabled with standards {securityhub.standards}"
)
else:
report.status = "FAIL"
report.status_extended = f"Security Hub is not enabled"
report.resource_id = securityhub.id
report.resource_arn = securityhub.arn
findings.append(report)
return findings

View File

@@ -0,0 +1,62 @@
from unittest import mock
from providers.aws.services.securityhub.securityhub_service import SecurityHubHub
class Test_accessanalyzer_enabled_without_findings:
def test_securityhub_hub_inactive(self):
securityhub_client = mock.MagicMock
securityhub_client.securityhubs = [
SecurityHubHub(
"",
"Security Hub",
"NOT_AVAILABLE",
"",
"eu-west-1",
)
]
with mock.patch(
"providers.aws.services.securityhub.securityhub_service.SecurityHub",
new=securityhub_client,
):
# Test Check
from providers.aws.services.securityhub.securityhub_enabled.securityhub_enabled import (
securityhub_enabled,
)
check = securityhub_enabled()
result = check.execute()
assert result[0].status == "FAIL"
assert result[0].status_extended == "Security Hub is not enabled"
assert result[0].resource_id == "Security Hub"
def test_securityhub_hub_active(self):
securityhub_client = mock.MagicMock
securityhub_client.securityhubs = [
SecurityHubHub(
"arn:aws:securityhub:us-east-1:0123456789012:hub/default",
"default",
"ACTIVE",
"cis-aws-foundations-benchmark/v/1.2.0",
"eu-west-1",
)
]
with mock.patch(
"providers.aws.services.securityhub.securityhub_service.SecurityHub",
new=securityhub_client,
):
# Test Check
from providers.aws.services.securityhub.securityhub_enabled.securityhub_enabled import (
securityhub_enabled,
)
check = securityhub_enabled()
result = check.execute()
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Security Hub is enabled with standards cis-aws-foundations-benchmark/v/1.2.0"
)
assert result[0].resource_id == "default"

View File

@@ -0,0 +1,91 @@
import threading
from dataclasses import dataclass
from lib.logger import logger
from providers.aws.aws_provider import generate_regional_clients
################## SecurityHub
class SecurityHub:
def __init__(self, audit_info):
self.service = "securityhub"
self.session = audit_info.audit_session
self.audited_account = audit_info.audited_account
self.regional_clients = generate_regional_clients(self.service, audit_info)
self.securityhubs = []
self.__threading_call__(self.__describe_hub__)
def __get_session__(self):
return self.session
def __threading_call__(self, call):
threads = []
for regional_client in self.regional_clients.values():
threads.append(threading.Thread(target=call, args=(regional_client,)))
for t in threads:
t.start()
for t in threads:
t.join()
def __describe_hub__(self, regional_client):
logger.info("SecurityHub - Describing Hub...")
try:
get_enabled_standards_paginator = regional_client.get_paginator(
"get_enabled_standards"
)
standards = ""
for page in get_enabled_standards_paginator.paginate():
for standard in page["StandardsSubscriptions"]:
standards += f" {standard['StandardsArn'].split('/')[1]}"
# Security Hub is not enabled in region
if standards == "":
self.securityhubs.append(
SecurityHubHub(
"",
"Security Hub",
"NOT_AVAILABLE",
"",
regional_client.region,
)
)
else:
# SecurityHub is active so get HubArn
hub_arn = regional_client.describe_hub()["HubArn"]
hub_id = hub_arn.split("/")[1]
self.securityhubs.append(
SecurityHubHub(
hub_arn,
hub_id,
"ACTIVE",
standards,
regional_client.region,
)
)
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
@dataclass
class SecurityHubHub:
arn: str
id: str
status: str
standards: str
region: str
def __init__(
self,
arn,
id,
status,
standards,
region,
):
self.arn = arn
self.id = id
self.status = status
self.standards = standards
self.region = region

View File

@@ -0,0 +1,75 @@
from unittest.mock import patch
import botocore
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.securityhub.securityhub_service import SecurityHub
# Mock Test Region
AWS_REGION = "eu-west-1"
# Mocking Access Analyzer Calls
make_api_call = botocore.client.BaseClient._make_api_call
# As you can see the operation_name has the list_analyzers snake_case form but
# we are using the ListAnalyzers form.
# Rationale -> https://github.com/boto/botocore/blob/develop/botocore/client.py#L810:L816
#
# We have to mock every AWS API call using Boto3
def mock_make_api_call(self, operation_name, kwarg):
if operation_name == "GetEnabledStandards":
return {
"StandardsSubscriptions": [
{
"StandardsArn": "arn:aws:securityhub:::ruleset/cis-aws-foundations-benchmark/v/1.2.0",
"StandardsSubscriptionArn": "arn:aws:securityhub:us-east-1:0123456789012:subscription/cis-aws-foundations-benchmark/v/1.2.0",
"StandardsInput": {"string": "string"},
"StandardsStatus": "READY",
},
]
}
if operation_name == "DescribeHub":
return {
"HubArn": "arn:aws:securityhub:us-east-1:0123456789012:hub/default",
}
return make_api_call(self, operation_name, kwarg)
# Mock generate_regional_clients()
def mock_generate_regional_clients(service, audit_info):
regional_client = audit_info.audit_session.client(service, region_name=AWS_REGION)
regional_client.region = AWS_REGION
return {AWS_REGION: regional_client}
# Patch every AWS call using Boto3 and generate_regional_clients to have 1 client
@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
@patch(
"providers.aws.services.securityhub.securityhub_service.generate_regional_clients",
new=mock_generate_regional_clients,
)
class Test_SecurityHub_Service:
# Test SecurityHub Client
def test__get_client__(self):
access_analyzer = SecurityHub(current_audit_info)
assert (
access_analyzer.regional_clients[AWS_REGION].__class__.__name__
== "SecurityHub"
)
# Test SecurityHub Session
def test__get_session__(self):
access_analyzer = SecurityHub(current_audit_info)
assert access_analyzer.session.__class__.__name__ == "Session"
def test__describe_hub__(self):
# Set partition for the service
current_audit_info.audited_partition = "aws"
securityhub = SecurityHub(current_audit_info)
assert len(securityhub.securityhubs) == 1
assert (
securityhub.securityhubs[0].arn
== "arn:aws:securityhub:us-east-1:0123456789012:hub/default"
)
assert securityhub.securityhubs[0].id == "default"
assert securityhub.securityhubs[0].standards == " cis-aws-foundations-benchmark"