mirror of
https://github.com/ghndrx/prowler.git
synced 2026-02-10 14:55:00 +00:00
feat(ssm): Service and checks (#1496)
This commit is contained in:
0
providers/aws/services/ssm/__init__.py
Normal file
0
providers/aws/services/ssm/__init__.py
Normal file
@@ -1,52 +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_extra7124="7.124"
|
||||
CHECK_TITLE_extra7124="[extra7124] Check if EC2 instances are managed by Systems Manager."
|
||||
CHECK_SCORED_extra7124="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra7124="EXTRA"
|
||||
CHECK_SEVERITY_extra7124="Medium"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra7124="AwsEc2Instance"
|
||||
CHECK_ALTERNATE_check7124="extra7124"
|
||||
CHECK_ASFF_COMPLIANCE_TYPE_extra7124="ens-op.exp.1.aws.sys.1 ens-op.acc.4.aws.sys.1"
|
||||
CHECK_SERVICENAME_extra7124="ssm"
|
||||
CHECK_RISK_extra7124='AWS Config provides AWS Managed Rules; which are predefined; customizable rules that AWS Config uses to evaluate whether your AWS resource configurations comply with common best practices.'
|
||||
CHECK_REMEDIATION_extra7124='Verify and apply Systems Manager Prerequisites.'
|
||||
CHECK_DOC_extra7124='https://docs.aws.amazon.com/systems-manager/latest/userguide/managed_instances.html'
|
||||
CHECK_CAF_EPIC_extra7124='Infrastructure Security'
|
||||
|
||||
extra7124(){
|
||||
for regx in $REGIONS; do
|
||||
# Filters running instances only
|
||||
LIST_EC2_INSTANCES=$($AWSCLI ec2 describe-instances $PROFILE_OPT --query 'Reservations[*].Instances[*].[InstanceId]' --filters Name=instance-state-name,Values=running --region $regx --output text 2>&1)
|
||||
if [[ $(echo "$LIST_EC2_INSTANCES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "$regx: Access Denied trying to describe instances" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $LIST_EC2_INSTANCES ]]; then
|
||||
LIST_SSM_MANAGED_INSTANCES=$($AWSCLI ssm describe-instance-information $PROFILE_OPT --query "InstanceInformationList[].InstanceId" --region $regx | jq -r '.[]')
|
||||
LIST_EC2_UNMANAGED=$(echo ${LIST_SSM_MANAGED_INSTANCES[@]} ${LIST_EC2_INSTANCES[@]} | tr ' ' '\n' | sort | uniq -u)
|
||||
if [[ $LIST_EC2_UNMANAGED ]]; then
|
||||
for instance in $LIST_EC2_UNMANAGED; do
|
||||
textFail "$regx: EC2 instance $instance is not managed by Systems Manager" "$regx" "$instance"
|
||||
done
|
||||
fi
|
||||
if [[ $LIST_SSM_MANAGED_INSTANCES ]]; then
|
||||
for instance in $LIST_SSM_MANAGED_INSTANCES; do
|
||||
textPass "$regx: EC2 instance $instance is managed by Systems Manager" "$regx" "$instance"
|
||||
done
|
||||
fi
|
||||
else
|
||||
textInfo "$regx: No EC2 instances running found" "$regx"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -1,51 +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_extra7127="7.127"
|
||||
CHECK_TITLE_extra7127="[extra7127] Check if EC2 instances managed by Systems Manager are compliant with patching requirements"
|
||||
CHECK_SCORED_extra7127="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra7127="EXTRA"
|
||||
CHECK_SEVERITY_extra7127="High"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra7127="AwsEc2Instance"
|
||||
CHECK_ASFF_TYPE_extra7127="Software and Configuration Checks/ENS op.exp.4.aws.sys.1"
|
||||
CHECK_ALTERNATE_check7127="extra7127"
|
||||
CHECK_ASFF_COMPLIANCE_TYPE_extra7127="ens-op.exp.1.aws.sys.1 ens-op.exp.4.aws.sys.1"
|
||||
CHECK_SERVICENAME_extra7127="ssm"
|
||||
CHECK_RISK_extra7127='Without the most recent security patches your system is potentially vulnerable to cyberattacks. Even the best-designed software can not anticipate every future threat to cybersecurity. Poor patch management can leave an organizations data exposed subjecting them to malware and ransomware attacks.'
|
||||
CHECK_REMEDIATION_extra7127='Consider using SSM in all accounts and services to at least monitor for missing patches on servers. Use a robust process to apply security fixes as soon as they are made available. Patch compliance data from Patch Manager can be sent to AWS Security Hub to centralize security issues.'
|
||||
CHECK_DOC_extra7127='https://docs.aws.amazon.com/systems-manager/latest/userguide/patch-compliance-identify.html'
|
||||
CHECK_CAF_EPIC_extra7127='Infrastructure Security'
|
||||
|
||||
extra7127(){
|
||||
for regx in $REGIONS; do
|
||||
NON_COMPLIANT_SSM_MANAGED_INSTANCES=$($AWSCLI ssm list-resource-compliance-summaries $PROFILE_OPT --region $regx --filters Key=Status,Values=NON_COMPLIANT --query ResourceComplianceSummaryItems[].ResourceId --output text 2>&1)
|
||||
if [[ $(echo "$NON_COMPLIANT_SSM_MANAGED_INSTANCES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "$regx: Access Denied trying to list resource compliance summaries" "$regx"
|
||||
continue
|
||||
fi
|
||||
COMPLIANT_SSM_MANAGED_INSTANCES=$($AWSCLI ssm list-resource-compliance-summaries $PROFILE_OPT --region $regx --filters Key=Status,Values=COMPLIANT --query ResourceComplianceSummaryItems[].ResourceId --output text)
|
||||
if [[ $NON_COMPLIANT_SSM_MANAGED_INSTANCES || $COMPLIANT_SSM_MANAGED_INSTANCES ]]; then
|
||||
if [[ $NON_COMPLIANT_SSM_MANAGED_INSTANCES ]]; then
|
||||
for instance in $NON_COMPLIANT_SSM_MANAGED_INSTANCES; do
|
||||
textFail "$regx: EC2 managed instance $instance is non-compliant" "$regx" "$instance"
|
||||
done
|
||||
fi
|
||||
if [[ $COMPLIANT_SSM_MANAGED_INSTANCES ]]; then
|
||||
for instance in $COMPLIANT_SSM_MANAGED_INSTANCES; do
|
||||
textPass "$regx: EC2 managed instance $instance is compliant" "$regx" "$instance"
|
||||
done
|
||||
fi
|
||||
else
|
||||
textInfo "$regx: No EC2 managed instances found" "$regx"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Prowler - the handy cloud security tool (copyright 2019) 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_extra7140="7.140"
|
||||
CHECK_TITLE_extra7140="[extra7140] Check if there are SSM Documents set as public"
|
||||
CHECK_SCORED_extra7140="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra7140="EXTRA"
|
||||
CHECK_SEVERITY_extra7140="High"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra7140="AwsSsmDocument"
|
||||
CHECK_ALTERNATE_check7140="extra7140"
|
||||
CHECK_SERVICENAME_extra7140="ssm"
|
||||
CHECK_RISK_extra7140='SSM Documents may contain private information or even secrets and tokens.'
|
||||
CHECK_REMEDIATION_extra7140='Carefully review the contents of the document before is shared. Enable SSM Block public sharing for documents.'
|
||||
CHECK_DOC_extra7140='https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-before-you-share.html'
|
||||
CHECK_CAF_EPIC_extra7140='Data Protection'
|
||||
extra7140(){
|
||||
|
||||
for regx in $REGIONS; do
|
||||
SSM_DOCS=$($AWSCLI $PROFILE_OPT --region $regx ssm list-documents --filters Key=Owner,Values=Self --query DocumentIdentifiers[].Name --output text 2>&1)
|
||||
if [[ $(echo "$SSM_DOCS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "$regx: Access Denied trying to list documents" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $SSM_DOCS ]];then
|
||||
for ssmdoc in $SSM_DOCS; do
|
||||
SSM_DOC_SHARED_ALL=$($AWSCLI $PROFILE_OPT --region $regx ssm describe-document-permission --name "$ssmdoc" --permission-type "Share" --query AccountIds[] --output text | grep all)
|
||||
if [[ $SSM_DOC_SHARED_ALL ]];then
|
||||
textFail "$regx: SSM Document $ssmdoc is public." "$regx" "$ssmdoc"
|
||||
else
|
||||
textPass "$regx: SSM Document $ssmdoc is not public." "$regx" "$ssmdoc"
|
||||
fi
|
||||
done
|
||||
else
|
||||
textInfo "$regx: No SSM Document found." "$regx"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2019) 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_extra7141="7.141"
|
||||
CHECK_TITLE_extra7141="[extra7141] Find secrets in SSM Documents"
|
||||
CHECK_SCORED_extra7141="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra7141="EXTRA"
|
||||
CHECK_SEVERITY_extra7141="Critical"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra7141="AwsSsmDocument"
|
||||
CHECK_ALTERNATE_check7141="extra7141"
|
||||
CHECK_SERVICENAME_extra7141="ssm"
|
||||
CHECK_RISK_extra7141='Secrets hardcoded into SSM Documents by malware and bad actors to gain lateral access to other services.'
|
||||
CHECK_REMEDIATION_extra7141='Implement automated detective control (e.g. using tools like Prowler) to scan accounts for passwords and secrets. Use Secrets Manager service to store and retrieve passwords and secrets.'
|
||||
CHECK_DOC_extra7141='https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-secretsmanager-secret-generatesecretstring.html'
|
||||
CHECK_CAF_EPIC_extra7141='IAM'
|
||||
|
||||
extra7141(){
|
||||
SECRETS_TEMP_FOLDER="$PROWLER_DIR/secrets-$ACCOUNT_NUM-$PROWLER_START_TIME"
|
||||
if [[ ! -d "${SECRETS_TEMP_FOLDER}" ]]; then
|
||||
# this folder is deleted once this check is finished
|
||||
mkdir "${SECRETS_TEMP_FOLDER}"
|
||||
fi
|
||||
|
||||
for regx in ${REGIONS}; do
|
||||
CHECK_DETECT_SECRETS_INSTALLATION=$(secretsDetector)
|
||||
if [[ $? -eq 241 ]]; then
|
||||
textInfo "$regx: python library detect-secrets not found. Make sure it is installed correctly." "$regx"
|
||||
else
|
||||
SSM_DOCS=$("${AWSCLI}" ${PROFILE_OPT} --region "${regx}" ssm list-documents --filters 'Key=Owner,Values=Self' --query 'DocumentIdentifiers[].Name' --output text 2>&1)
|
||||
if [[ $(echo "${SSM_DOCS}" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "${regx}: Access Denied trying to list documents" "${regx}"
|
||||
continue
|
||||
fi
|
||||
if [[ ${SSM_DOCS} ]];then
|
||||
for ssmdoc in ${SSM_DOCS}; do
|
||||
SSM_DOC_FILE="${SECRETS_TEMP_FOLDER}/extra7141-${ssmdoc}-${regx}-content.txt"
|
||||
"${AWSCLI}" ${PROFILE_OPT} --region "${regx}" ssm get-document --name "${ssmdoc}" --output text --document-format JSON > "${SSM_DOC_FILE}" 2>&1
|
||||
if [[ $(grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' "${SSM_DOC_FILE}") ]]; then
|
||||
textInfo "${regx}: Access Denied trying to get document" "${regx}"
|
||||
continue
|
||||
fi
|
||||
FINDINGS=$(secretsDetector file "${SSM_DOC_FILE}")
|
||||
if [[ "${FINDINGS}" -eq 0 ]]; then
|
||||
textPass "${regx}: No secrets found in SSM Document ${ssmdoc}" "${regx}" "${ssmdoc}"
|
||||
# delete file if nothing interesting is there
|
||||
rm -f "${SSM_DOC_FILE}"
|
||||
else
|
||||
textFail "${regx}: Potential secret found SSM Document ${ssmdoc}" "${regx}" "${ssmdoc}"
|
||||
# delete file to not leave trace, user must look at the CFN Stack
|
||||
rm -f "${SSM_DOC_FILE}"
|
||||
fi
|
||||
done
|
||||
else
|
||||
textInfo "${regx}: No SSM Document found." "${regx}"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
rm -rf "${SECRETS_TEMP_FOLDER}"
|
||||
}
|
||||
4
providers/aws/services/ssm/ssm_client.py
Normal file
4
providers/aws/services/ssm/ssm_client.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.ssm.ssm_service import SSM
|
||||
|
||||
ssm_client = SSM(current_audit_info)
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ssm_document_secrets",
|
||||
"CheckTitle": "Find secrets in SSM Documents.",
|
||||
"CheckType": [],
|
||||
"ServiceName": "ssm",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "arn:aws:ssm:region:account-id:document/document-name",
|
||||
"Severity": "critical",
|
||||
"ResourceType": "AwsSsmDocument",
|
||||
"Description": "Find secrets in SSM Documents.",
|
||||
"Risk": "Secrets hardcoded into SSM Documents by malware and bad actors to gain lateral access to other services.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-secretsmanager-secret-generatesecretstring.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Implement automated detective control (e.g. using tools like Prowler) to scan accounts for passwords and secrets. Use Secrets Manager service to store and retrieve passwords and secrets.",
|
||||
"Url": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-secretsmanager-secret-generatesecretstring.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import json
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
from detect_secrets import SecretsCollection
|
||||
from detect_secrets.settings import default_settings
|
||||
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.ssm.ssm_client import ssm_client
|
||||
|
||||
|
||||
class ssm_document_secrets(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for document in ssm_client.documents.values():
|
||||
report = Check_Report(self.metadata)
|
||||
report.region = document.region
|
||||
report.resource_arn = f"arn:aws:ssm:{document.region}:{ssm_client.audited_account}:document/{document.name}"
|
||||
report.resource_id = document.name
|
||||
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"No secrets found in SSM Document {document.name}"
|
||||
|
||||
if document.content:
|
||||
temp_env_data_file = tempfile.NamedTemporaryFile(delete=False)
|
||||
temp_env_data_file.write(
|
||||
bytes(json.dumps(document.content), encoding="raw_unicode_escape")
|
||||
)
|
||||
temp_env_data_file.close()
|
||||
secrets = SecretsCollection()
|
||||
with default_settings():
|
||||
secrets.scan_file(temp_env_data_file.name)
|
||||
|
||||
if secrets.json():
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"Potential secret found in SSM Document {document.name}"
|
||||
)
|
||||
|
||||
os.remove(temp_env_data_file.name)
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -0,0 +1,100 @@
|
||||
from unittest import mock
|
||||
|
||||
from moto.core import DEFAULT_ACCOUNT_ID
|
||||
|
||||
from providers.aws.services.ssm.ssm_service import Document
|
||||
|
||||
AWS_REGION = "eu-west-1"
|
||||
|
||||
|
||||
class Test_ssm_documents_secrets:
|
||||
def test_no_documents(self):
|
||||
ssm_client = mock.MagicMock
|
||||
ssm_client.documents = {}
|
||||
with mock.patch(
|
||||
"providers.aws.services.ssm.ssm_service.SSM",
|
||||
new=ssm_client,
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ssm.ssm_document_secrets.ssm_document_secrets import (
|
||||
ssm_document_secrets,
|
||||
)
|
||||
|
||||
check = ssm_document_secrets()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
def test_document_with_secrets(self):
|
||||
ssm_client = mock.MagicMock
|
||||
document_name = "test-document"
|
||||
document_arn = (
|
||||
f"arn:aws:ssm:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:document/{document_name}"
|
||||
)
|
||||
ssm_client.audited_account = DEFAULT_ACCOUNT_ID
|
||||
ssm_client.documents = {
|
||||
document_name: Document(
|
||||
name=document_name,
|
||||
region=AWS_REGION,
|
||||
content={"db_password": "test-password"},
|
||||
account_owners=[],
|
||||
)
|
||||
}
|
||||
with mock.patch(
|
||||
"providers.aws.services.ssm.ssm_service.SSM",
|
||||
new=ssm_client,
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ssm.ssm_document_secrets.ssm_document_secrets import (
|
||||
ssm_document_secrets,
|
||||
)
|
||||
|
||||
check = ssm_document_secrets()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].region == AWS_REGION
|
||||
assert result[0].resource_id == document_name
|
||||
assert result[0].resource_arn == document_arn
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Potential secret found in SSM Document {document_name}"
|
||||
)
|
||||
|
||||
def test_document_no_secrets(self):
|
||||
ssm_client = mock.MagicMock
|
||||
document_name = "test-document"
|
||||
document_arn = (
|
||||
f"arn:aws:ssm:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:document/{document_name}"
|
||||
)
|
||||
ssm_client.audited_account = DEFAULT_ACCOUNT_ID
|
||||
ssm_client.documents = {
|
||||
document_name: Document(
|
||||
name=document_name,
|
||||
region=AWS_REGION,
|
||||
content={"profile": "test"},
|
||||
account_owners=[],
|
||||
)
|
||||
}
|
||||
with mock.patch(
|
||||
"providers.aws.services.ssm.ssm_service.SSM",
|
||||
new=ssm_client,
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ssm.ssm_document_secrets.ssm_document_secrets import (
|
||||
ssm_document_secrets,
|
||||
)
|
||||
|
||||
check = ssm_document_secrets()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].region == AWS_REGION
|
||||
assert result[0].resource_id == document_name
|
||||
assert result[0].resource_arn == document_arn
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"No secrets found in SSM Document {document_name}"
|
||||
)
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ssm_documents_set_as_public",
|
||||
"CheckTitle": "Check if there are SSM Documents set as public.",
|
||||
"CheckType": [],
|
||||
"ServiceName": "ssm",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "arn:aws:ssm:region:account-id:document/document-name",
|
||||
"Severity": "high",
|
||||
"ResourceType": "AwsSsmDocument",
|
||||
"Description": "Check if there are SSM Documents set as public.",
|
||||
"Risk": "SSM Documents may contain private information or even secrets and tokens.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-before-you-share.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "https://github.com/cloudmatos/matos/tree/master/remediations/aws/ssm/ssm-doc-block",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Carefully review the contents of the document before is shared. Enable SSM Block public sharing for documents.",
|
||||
"Url": "https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-before-you-share.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.ssm.ssm_client import ssm_client
|
||||
|
||||
|
||||
class ssm_documents_set_as_public(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for document in ssm_client.documents.values():
|
||||
report = Check_Report(self.metadata)
|
||||
report.region = document.region
|
||||
report.resource_arn = f"arn:aws:ssm:{document.region}:{ssm_client.audited_account}:document/{document.name}"
|
||||
report.resource_id = document.name
|
||||
|
||||
if document.account_owners:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"SSM Document {document.name} is public"
|
||||
else:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"SSM Document {document.name} is not public"
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -0,0 +1,99 @@
|
||||
from unittest import mock
|
||||
|
||||
from moto.core import DEFAULT_ACCOUNT_ID
|
||||
|
||||
from providers.aws.services.ssm.ssm_service import Document
|
||||
|
||||
AWS_REGION = "eu-west-1"
|
||||
|
||||
|
||||
class Test_ssm_documents_set_as_public:
|
||||
def test_no_documents(self):
|
||||
ssm_client = mock.MagicMock
|
||||
ssm_client.documents = {}
|
||||
with mock.patch(
|
||||
"providers.aws.services.ssm.ssm_service.SSM",
|
||||
new=ssm_client,
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ssm.ssm_documents_set_as_public.ssm_documents_set_as_public import (
|
||||
ssm_documents_set_as_public,
|
||||
)
|
||||
|
||||
check = ssm_documents_set_as_public()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
def test_document_public(self):
|
||||
ssm_client = mock.MagicMock
|
||||
document_name = "test-document"
|
||||
document_arn = (
|
||||
f"arn:aws:ssm:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:document/{document_name}"
|
||||
)
|
||||
ssm_client.audited_account = DEFAULT_ACCOUNT_ID
|
||||
ssm_client.documents = {
|
||||
document_name: Document(
|
||||
name=document_name,
|
||||
region=AWS_REGION,
|
||||
content="",
|
||||
account_owners=["111111111111", "111111222222"],
|
||||
)
|
||||
}
|
||||
with mock.patch(
|
||||
"providers.aws.services.ssm.ssm_service.SSM",
|
||||
new=ssm_client,
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ssm.ssm_documents_set_as_public.ssm_documents_set_as_public import (
|
||||
ssm_documents_set_as_public,
|
||||
)
|
||||
|
||||
check = ssm_documents_set_as_public()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].region == AWS_REGION
|
||||
assert result[0].resource_id == document_name
|
||||
assert result[0].resource_arn == document_arn
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended == f"SSM Document {document_name} is public"
|
||||
)
|
||||
|
||||
def test_document_not_public(self):
|
||||
ssm_client = mock.MagicMock
|
||||
document_name = "test-document"
|
||||
document_arn = (
|
||||
f"arn:aws:ssm:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:document/{document_name}"
|
||||
)
|
||||
ssm_client.audited_account = DEFAULT_ACCOUNT_ID
|
||||
ssm_client.documents = {
|
||||
document_name: Document(
|
||||
name=document_name,
|
||||
region=AWS_REGION,
|
||||
content="",
|
||||
account_owners=[],
|
||||
)
|
||||
}
|
||||
with mock.patch(
|
||||
"providers.aws.services.ssm.ssm_service.SSM",
|
||||
new=ssm_client,
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ssm.ssm_documents_set_as_public.ssm_documents_set_as_public import (
|
||||
ssm_documents_set_as_public,
|
||||
)
|
||||
|
||||
check = ssm_documents_set_as_public()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].region == AWS_REGION
|
||||
assert result[0].resource_id == document_name
|
||||
assert result[0].resource_arn == document_arn
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"SSM Document {document_name} is not public"
|
||||
)
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ssm_managed_compliant_patching",
|
||||
"CheckTitle": "Check if EC2 instances managed by Systems Manager are compliant with patching requirements.",
|
||||
"CheckType": [],
|
||||
"ServiceName": "ssm",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "arn:aws:ec2:region:account-id:instance/instance-id",
|
||||
"Severity": "high",
|
||||
"ResourceType": "AwsEc2Instance",
|
||||
"Description": "Check if EC2 instances managed by Systems Manager are compliant with patching requirements.",
|
||||
"Risk": "Without the most recent security patches your system is potentially vulnerable to cyberattacks. Even the best-designed software can not anticipate every future threat to cybersecurity. Poor patch management can leave an organizations data exposed subjecting them to malware and ransomware attacks.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/systems-manager/latest/userguide/patch-compliance-identify.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Consider using SSM in all accounts and services to at least monitor for missing patches on servers. Use a robust process to apply security fixes as soon as they are made available. Patch compliance data from Patch Manager can be sent to AWS Security Hub to centralize security issues.",
|
||||
"Url": "https://docs.aws.amazon.com/systems-manager/latest/userguide/patch-compliance-identify.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.ssm.ssm_client import ssm_client
|
||||
from providers.aws.services.ssm.ssm_service import ResourceStatus
|
||||
|
||||
|
||||
class ssm_managed_compliant_patching(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for resource in ssm_client.compliance_resources.values():
|
||||
report = Check_Report(self.metadata)
|
||||
report.region = resource.region
|
||||
report.resource_arn = f"arn:aws:ec2:{resource.region}:{ssm_client.audited_account}:instance/{resource.id}"
|
||||
report.resource_id = resource.id
|
||||
|
||||
if resource.status == ResourceStatus.COMPLIANT:
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"EC2 managed instance {resource.id} is compliant"
|
||||
)
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"EC2 managed instance {resource.id} is non-compliant"
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -0,0 +1,100 @@
|
||||
from unittest import mock
|
||||
|
||||
from moto.core import DEFAULT_ACCOUNT_ID
|
||||
|
||||
from providers.aws.services.ssm.ssm_service import ComplianceResource, ResourceStatus
|
||||
|
||||
AWS_REGION = "eu-west-1"
|
||||
|
||||
|
||||
class Test_ssm_managed_compliant_patching:
|
||||
def test_no_compliance_resources(self):
|
||||
ssm_client = mock.MagicMock
|
||||
ssm_client.compliance_resources = {}
|
||||
with mock.patch(
|
||||
"providers.aws.services.ssm.ssm_service.SSM",
|
||||
new=ssm_client,
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ssm.ssm_managed_compliant_patching.ssm_managed_compliant_patching import (
|
||||
ssm_managed_compliant_patching,
|
||||
)
|
||||
|
||||
check = ssm_managed_compliant_patching()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
def test_compliance_resources_compliant(self):
|
||||
ssm_client = mock.MagicMock
|
||||
instance_id = "i-1234567890abcdef0"
|
||||
instance_arn = (
|
||||
f"arn:aws:ec2:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:instance/{instance_id}"
|
||||
)
|
||||
ssm_client.audited_account = DEFAULT_ACCOUNT_ID
|
||||
ssm_client.compliance_resources = {
|
||||
instance_id: ComplianceResource(
|
||||
id="i-1234567890abcdef0",
|
||||
region=AWS_REGION,
|
||||
status=ResourceStatus.COMPLIANT,
|
||||
)
|
||||
}
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ssm.ssm_service.SSM",
|
||||
new=ssm_client,
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ssm.ssm_managed_compliant_patching.ssm_managed_compliant_patching import (
|
||||
ssm_managed_compliant_patching,
|
||||
)
|
||||
|
||||
check = ssm_managed_compliant_patching()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].region == AWS_REGION
|
||||
assert result[0].resource_id == instance_id
|
||||
assert result[0].resource_arn == instance_arn
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"EC2 managed instance {instance_id} is compliant"
|
||||
)
|
||||
|
||||
def test_compliance_resources_non_compliant(self):
|
||||
ssm_client = mock.MagicMock
|
||||
instance_id = "i-1234567890abcdef0"
|
||||
instance_arn = (
|
||||
f"arn:aws:ec2:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:instance/{instance_id}"
|
||||
)
|
||||
ssm_client.audited_account = DEFAULT_ACCOUNT_ID
|
||||
ssm_client.compliance_resources = {
|
||||
instance_id: ComplianceResource(
|
||||
id="i-1234567890abcdef0",
|
||||
region=AWS_REGION,
|
||||
status=ResourceStatus.NON_COMPLIANT,
|
||||
)
|
||||
}
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.ssm.ssm_service.SSM",
|
||||
new=ssm_client,
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.ssm.ssm_managed_compliant_patching.ssm_managed_compliant_patching import (
|
||||
ssm_managed_compliant_patching,
|
||||
)
|
||||
|
||||
check = ssm_managed_compliant_patching()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].region == AWS_REGION
|
||||
assert result[0].resource_id == instance_id
|
||||
assert result[0].resource_arn == instance_arn
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"EC2 managed instance {instance_id} is non-compliant"
|
||||
)
|
||||
144
providers/aws/services/ssm/ssm_service.py
Normal file
144
providers/aws/services/ssm/ssm_service.py
Normal file
@@ -0,0 +1,144 @@
|
||||
import json
|
||||
import threading
|
||||
from enum import Enum
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from lib.logger import logger
|
||||
from providers.aws.aws_provider import generate_regional_clients
|
||||
|
||||
|
||||
################## SSM
|
||||
class SSM:
|
||||
def __init__(self, audit_info):
|
||||
self.service = "ssm"
|
||||
self.session = audit_info.audit_session
|
||||
self.audited_account = audit_info.audited_account
|
||||
self.regional_clients = generate_regional_clients(self.service, audit_info)
|
||||
self.documents = {}
|
||||
self.compliance_resources = {}
|
||||
self.__threading_call__(self.__list_documents__)
|
||||
self.__threading_call__(self.__get_document__)
|
||||
self.__threading_call__(self.__describe_document_permission__)
|
||||
self.__threading_call__(self.__list_resource_compliance_summaries__)
|
||||
|
||||
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 __list_documents__(self, regional_client):
|
||||
logger.info("SSM - Listing Documents...")
|
||||
try:
|
||||
# To retrieve only the documents owned by the account
|
||||
list_documents_parameters = {
|
||||
"Filters": [
|
||||
{
|
||||
"Key": "Owner",
|
||||
"Values": [
|
||||
"Self",
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
list_documents_paginator = regional_client.get_paginator("list_documents")
|
||||
for page in list_documents_paginator.paginate(**list_documents_parameters):
|
||||
for document in page["DocumentIdentifiers"]:
|
||||
document_name = document["Name"]
|
||||
|
||||
self.documents[document_name] = Document(
|
||||
name=document_name,
|
||||
region=regional_client.region,
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{regional_client.region} --"
|
||||
f" {error.__class__.__name__}[{error.__traceback__.tb_lineno}]:"
|
||||
f" {error}"
|
||||
)
|
||||
|
||||
def __get_document__(self, regional_client):
|
||||
logger.info("SSM - Getting Document...")
|
||||
try:
|
||||
for document in self.documents.values():
|
||||
if document.region == regional_client.region:
|
||||
document_info = regional_client.get_document(Name=document.name)
|
||||
self.documents[document.name].content = json.loads(
|
||||
document_info["Content"]
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{regional_client.region} --"
|
||||
f" {error.__class__.__name__}[{error.__traceback__.tb_lineno}]:"
|
||||
f" {error}"
|
||||
)
|
||||
|
||||
def __describe_document_permission__(self, regional_client):
|
||||
logger.info("SSM - Describing Document Permission...")
|
||||
try:
|
||||
for document in self.documents.values():
|
||||
if document.region == regional_client.region:
|
||||
document_permissions = regional_client.describe_document_permission(
|
||||
Name=document.name, PermissionType="Share"
|
||||
)
|
||||
self.documents[document.name].account_owners = document_permissions[
|
||||
"AccountIds"
|
||||
]
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{regional_client.region} --"
|
||||
f" {error.__class__.__name__}[{error.__traceback__.tb_lineno}]:"
|
||||
f" {error}"
|
||||
)
|
||||
|
||||
def __list_resource_compliance_summaries__(self, regional_client):
|
||||
logger.info("SSM - List Resources Compliance Summaries...")
|
||||
try:
|
||||
list_resource_compliance_summaries_paginator = (
|
||||
regional_client.get_paginator("list_resource_compliance_summaries")
|
||||
)
|
||||
for page in list_resource_compliance_summaries_paginator.paginate():
|
||||
for item in page["ResourceComplianceSummaryItems"]:
|
||||
resource_id = item["ResourceId"]
|
||||
resource_status = item["Status"]
|
||||
|
||||
self.compliance_resources[resource_id] = ComplianceResource(
|
||||
id=resource_id,
|
||||
status=resource_status,
|
||||
region=regional_client.region,
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{regional_client.region} --"
|
||||
f" {error.__class__.__name__}[{error.__traceback__.tb_lineno}]:"
|
||||
f" {error}"
|
||||
)
|
||||
|
||||
|
||||
class ResourceStatus(Enum):
|
||||
COMPLIANT = "COMPLIANT"
|
||||
NON_COMPLIANT = "NON_COMPLIANT"
|
||||
|
||||
|
||||
class ComplianceResource(BaseModel):
|
||||
id: str
|
||||
region: str
|
||||
status: ResourceStatus
|
||||
|
||||
|
||||
class Document(BaseModel):
|
||||
name: str
|
||||
region: str
|
||||
content: dict = None
|
||||
account_owners: list[str] = None
|
||||
204
providers/aws/services/ssm/ssm_service_test.py
Normal file
204
providers/aws/services/ssm/ssm_service_test.py
Normal file
@@ -0,0 +1,204 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
import botocore
|
||||
import yaml
|
||||
from boto3 import client, session
|
||||
from moto import mock_ssm
|
||||
from moto.core import DEFAULT_ACCOUNT_ID
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import AWS_Audit_Info
|
||||
from providers.aws.services.ssm.ssm_service import SSM, ResourceStatus
|
||||
|
||||
# Mock Test Region
|
||||
AWS_REGION = "eu-west-1"
|
||||
|
||||
# Mocking Access Analyzer Calls
|
||||
make_api_call = botocore.client.BaseClient._make_api_call
|
||||
|
||||
|
||||
def mock_make_api_call(self, operation_name, kwarg):
|
||||
"""We have to mock every AWS API call using Boto3"""
|
||||
if operation_name == "ListResourceComplianceSummaries":
|
||||
return {
|
||||
"ResourceComplianceSummaryItems": [
|
||||
{
|
||||
"ComplianceType": "Association",
|
||||
"ResourceType": "ManagedInstance",
|
||||
"ResourceId": "i-1234567890abcdef0",
|
||||
"Status": "COMPLIANT",
|
||||
"OverallSeverity": "UNSPECIFIED",
|
||||
"ExecutionSummary": {"ExecutionTime": 1550509273.0},
|
||||
"CompliantSummary": {
|
||||
"CompliantCount": 2,
|
||||
"SeveritySummary": {
|
||||
"CriticalCount": 0,
|
||||
"HighCount": 0,
|
||||
"MediumCount": 0,
|
||||
"LowCount": 0,
|
||||
"InformationalCount": 0,
|
||||
"UnspecifiedCount": 2,
|
||||
},
|
||||
},
|
||||
"NonCompliantSummary": {
|
||||
"NonCompliantCount": 0,
|
||||
"SeveritySummary": {
|
||||
"CriticalCount": 0,
|
||||
"HighCount": 0,
|
||||
"MediumCount": 0,
|
||||
"LowCount": 0,
|
||||
"InformationalCount": 0,
|
||||
"UnspecifiedCount": 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
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}
|
||||
|
||||
|
||||
# SSM Document YAML Template
|
||||
ssm_document_yaml = """
|
||||
schemaVersion: "2.2"
|
||||
description: "Sample Yaml"
|
||||
parameters:
|
||||
Parameter1:
|
||||
type: "Integer"
|
||||
default: 3
|
||||
description: "Command Duration."
|
||||
allowedValues: [1,2,3,4]
|
||||
Parameter2:
|
||||
type: "String"
|
||||
default: "def"
|
||||
description:
|
||||
allowedValues: ["abc", "def", "ghi"]
|
||||
allowedPattern: r"^[a-zA-Z0-9_-.]{3,128}$"
|
||||
Parameter3:
|
||||
type: "Boolean"
|
||||
default: false
|
||||
description: "A boolean"
|
||||
allowedValues: [True, False]
|
||||
Parameter4:
|
||||
type: "StringList"
|
||||
default: ["abc", "def"]
|
||||
description: "A string list"
|
||||
Parameter5:
|
||||
type: "StringMap"
|
||||
default:
|
||||
NotificationType: Command
|
||||
NotificationEvents:
|
||||
- Failed
|
||||
NotificationArn: "$dependency.topicArn"
|
||||
description:
|
||||
Parameter6:
|
||||
type: "MapList"
|
||||
default:
|
||||
- DeviceName: "/dev/sda1"
|
||||
Ebs:
|
||||
VolumeSize: '50'
|
||||
- DeviceName: "/dev/sdm"
|
||||
Ebs:
|
||||
VolumeSize: '100'
|
||||
description:
|
||||
mainSteps:
|
||||
- action: "aws:runShellScript"
|
||||
name: "sampleCommand"
|
||||
inputs:
|
||||
runCommand:
|
||||
- "echo hi"
|
||||
"""
|
||||
|
||||
|
||||
# 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.ssm.ssm_service.generate_regional_clients",
|
||||
new=mock_generate_regional_clients,
|
||||
)
|
||||
class Test_SSM_Service:
|
||||
# Mocked Audit Info
|
||||
def set_mocked_audit_info(self):
|
||||
audit_info = AWS_Audit_Info(
|
||||
original_session=None,
|
||||
audit_session=session.Session(
|
||||
profile_name=None,
|
||||
botocore_session=None,
|
||||
),
|
||||
audited_account=None,
|
||||
audited_user_id=None,
|
||||
audited_partition="aws",
|
||||
audited_identity_arn=None,
|
||||
profile=None,
|
||||
profile_region=AWS_REGION,
|
||||
credentials=None,
|
||||
assumed_role_info=None,
|
||||
audited_regions=None,
|
||||
organizations_metadata=None,
|
||||
)
|
||||
return audit_info
|
||||
|
||||
# Test SSM Client
|
||||
@mock_ssm
|
||||
def test__get_client__(self):
|
||||
ssm = SSM(self.set_mocked_audit_info())
|
||||
assert ssm.regional_clients[AWS_REGION].__class__.__name__ == "SSM"
|
||||
|
||||
# Test SSM Session
|
||||
@mock_ssm
|
||||
def test__get_session__(self):
|
||||
ssm = SSM(self.set_mocked_audit_info())
|
||||
assert ssm.session.__class__.__name__ == "Session"
|
||||
|
||||
# Test SSM Service
|
||||
@mock_ssm
|
||||
def test__get_service__(self):
|
||||
ssm = SSM(self.set_mocked_audit_info())
|
||||
assert ssm.service == "ssm"
|
||||
|
||||
@mock_ssm
|
||||
def test__list_documents__(self):
|
||||
# Create SSM Document
|
||||
ssm_client = client("ssm", region_name=AWS_REGION)
|
||||
ssm_document_name = "test-document"
|
||||
_ = ssm_client.create_document(
|
||||
Content=ssm_document_yaml,
|
||||
Name=ssm_document_name,
|
||||
DocumentType="Command",
|
||||
DocumentFormat="YAML",
|
||||
)
|
||||
# Add permissions
|
||||
ssm_client.modify_document_permission(
|
||||
Name=ssm_document_name,
|
||||
PermissionType="Share",
|
||||
AccountIdsToAdd=[DEFAULT_ACCOUNT_ID],
|
||||
)
|
||||
|
||||
ssm = SSM(self.set_mocked_audit_info())
|
||||
|
||||
assert len(ssm.documents) == 1
|
||||
assert ssm.documents
|
||||
assert ssm.documents[ssm_document_name]
|
||||
assert ssm.documents[ssm_document_name].name == ssm_document_name
|
||||
assert ssm.documents[ssm_document_name].region == AWS_REGION
|
||||
assert ssm.documents[ssm_document_name].content == yaml.safe_load(
|
||||
ssm_document_yaml
|
||||
)
|
||||
assert ssm.documents[ssm_document_name].account_owners == [DEFAULT_ACCOUNT_ID]
|
||||
|
||||
@mock_ssm
|
||||
def test__list_resource_compliance_summaries__(self):
|
||||
ssm = SSM(self.set_mocked_audit_info())
|
||||
instance_id = "i-1234567890abcdef0"
|
||||
assert len(ssm.compliance_resources) == 1
|
||||
assert ssm.compliance_resources
|
||||
assert ssm.compliance_resources[instance_id]
|
||||
assert ssm.compliance_resources[instance_id].id == instance_id
|
||||
assert ssm.compliance_resources[instance_id].region == AWS_REGION
|
||||
assert ssm.compliance_resources[instance_id].status == ResourceStatus.COMPLIANT
|
||||
Reference in New Issue
Block a user