Skip to content

Commit

Permalink
Merge branch 'master' into PRWLR-4859-review-logic-for-public-resources
Browse files Browse the repository at this point in the history
  • Loading branch information
sergargar authored Oct 14, 2024
2 parents 88a8941 + 7fd0798 commit fff4e45
Show file tree
Hide file tree
Showing 35 changed files with 1,447 additions and 638 deletions.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "codebuild_project_s3_logs_encrypted",
"CheckTitle": "Ensure S3 Logs for CodeBuild Projects are encrypted at rest.",
"CheckType": [
"Effects/Data Exposure"
],
"ServiceName": "codebuild",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "low",
"ResourceType": "AwsCodeBuildProject",
"Description": "Ensure that the S3 logs for CodeBuild projects are encrypted at rest.",
"Risk": "If the logs are not encrypted, sensitive information could be exposed to unauthorized users.",
"RelatedUrl": "https://docs.aws.amazon.com/codebuild/latest/userguide/change-project.html#change-project-console-logs",
"Remediation": {
"Code": {
"CLI": "aws codebuild update-project --name <project-name> --logs-config \"s3Logs={status=ENABLED, location=<bucket-name>/<path>, encryptionDisabled=false\"}",
"NativeIaC": "",
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/codebuild-controls.html#codebuild-3",
"Terraform": ""
},
"Recommendation": {
"Text": "Ensure that the CodeBuild project's S3 logs are encrypted at rest by setting the `encryptionDisabled` parameter to `false` in the `s3Logs` configuration.",
"Url": "https://docs.aws.amazon.com/codebuild/latest/userguide/change-project.html#change-project-console-logs"
}
},
"Categories": [
"encryption",
"logging"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.codebuild.codebuild_client import codebuild_client


class codebuild_project_s3_logs_encrypted(Check):
def execute(self):
findings = []
for project in codebuild_client.projects.values():
if project.s3_logs.enabled:
report = Check_Report_AWS(self.metadata())
report.resource_id = project.name
report.resource_arn = project.arn
report.region = project.region
report.resource_tags = project.tags
report.status = "PASS"
report.status_extended = f"CodeBuild project {project.name} has encrypted S3 logs stored in {project.s3_logs.bucket_location}."
if not project.s3_logs.encrypted:
report.status = "FAIL"
report.status_extended = f"CodeBuild project {project.name} does not have encrypted S3 logs stored in {project.s3_logs.bucket_location}."

findings.append(report)

return findings
15 changes: 15 additions & 0 deletions prowler/providers/aws/services/codebuild/codebuild_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,14 @@ def _batch_get_projects(self, project):
EnvironmentVariable(**var) for var in env_vars
]
project.buildspec = project_info.get("source", {}).get("buildspec", "")
s3_logs = project_info.get("logsConfig", {}).get("s3Logs", {})
project.s3_logs = s3Logs(
enabled=(
True if s3_logs.get("status", "DISABLED") == "ENABLED" else False
),
bucket_location=s3_logs.get("location", ""),
encrypted=(not s3_logs.get("encryptionDisabled", False)),
)
project.tags = project_info.get("tags", [])
except Exception as error:
logger.error(
Expand All @@ -115,6 +123,12 @@ class EnvironmentVariable(BaseModel):
type: str


class s3Logs(BaseModel):
enabled: bool
bucket_location: str
encrypted: bool


class Project(BaseModel):
name: str
arn: str
Expand All @@ -125,4 +139,5 @@ class Project(BaseModel):
source: Optional[Source]
secondary_sources: Optional[list[Source]] = []
environment_variables: Optional[List[EnvironmentVariable]]
s3_logs: Optional[s3Logs]
tags: Optional[list]
201 changes: 101 additions & 100 deletions prowler/providers/aws/services/opensearch/opensearch_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,145 +8,144 @@
from prowler.providers.aws.lib.service.service import AWSService


################################ OpenSearch
class OpenSearchService(AWSService):
def __init__(self, provider):
# Call AWSService's __init__
super().__init__("opensearch", provider)
self.opensearch_domains = []
self.opensearch_domains = {}
self.__threading_call__(self._list_domain_names)
self._describe_domain_config(self.regional_clients)
self._describe_domain(self.regional_clients)
self._list_tags()
self.__threading_call__(
self._describe_domain_config, self.opensearch_domains.values()
)
self.__threading_call__(self._describe_domain, self.opensearch_domains.values())
self.__threading_call__(self._list_tags, self.opensearch_domains.values())

def _list_domain_names(self, regional_client):
logger.info("OpenSearch - listing domain names...")
try:
domains = regional_client.list_domain_names()
for domain in domains["DomainNames"]:
arn = f"arn:{self.audited_partition}:opensearch:{regional_client.region}:{self.audited_account}:domain/{domain['DomainName']}"
arn = f"arn:{self.audited_partition}:es:{regional_client.region}:{self.audited_account}:domain/{domain['DomainName']}"
if not self.audit_resources or (
is_resource_filtered(arn, self.audit_resources)
):
self.opensearch_domains.append(
OpenSearchDomain(
arn=arn,
name=domain["DomainName"],
region=regional_client.region,
)
self.opensearch_domains[arn] = OpenSearchDomain(
arn=arn,
name=domain["DomainName"],
region=regional_client.region,
)
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)

def _describe_domain_config(self, regional_clients):
def _describe_domain_config(self, domain):
logger.info("OpenSearch - describing domain configurations...")
try:
for domain in self.opensearch_domains:
regional_client = regional_clients[domain.region]
describe_domain = regional_client.describe_domain_config(
DomainName=domain.name
)
for logging_key in [
"SEARCH_SLOW_LOGS",
"INDEX_SLOW_LOGS",
"AUDIT_LOGS",
]:
if logging_key in describe_domain["DomainConfig"].get(
"LogPublishingOptions", {}
).get("Options", {}):
domain.logging.append(
PublishingLoggingOption(
name=logging_key,
enabled=describe_domain["DomainConfig"][
"LogPublishingOptions"
]["Options"][logging_key]["Enabled"],
)
regional_client = self.regional_clients[domain.region]
describe_domain = regional_client.describe_domain_config(
DomainName=domain.name
)
for logging_key in [
"SEARCH_SLOW_LOGS",
"INDEX_SLOW_LOGS",
"AUDIT_LOGS",
]:
if logging_key in describe_domain["DomainConfig"].get(
"LogPublishingOptions", {}
).get("Options", {}):
domain.logging.append(
PublishingLoggingOption(
name=logging_key,
enabled=describe_domain["DomainConfig"][
"LogPublishingOptions"
]["Options"][logging_key]["Enabled"],
)
try:
domain.access_policy = loads(
describe_domain["DomainConfig"]["AccessPolicies"]["Options"]
)
except JSONDecodeError as error:
logger.warning(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
continue
try:
domain.access_policy = loads(
describe_domain["DomainConfig"]["AccessPolicies"]["Options"]
)
except JSONDecodeError as error:
logger.warning(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)

except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)

def _describe_domain(self, regional_clients):
def _describe_domain(self, domain):
logger.info("OpenSearch - describing domain configurations...")
try:
for domain in self.opensearch_domains:
regional_client = regional_clients[domain.region]
describe_domain = regional_client.describe_domain(
DomainName=domain.name
)
domain.arn = describe_domain["DomainStatus"]["ARN"]
if "Endpoints" in describe_domain["DomainStatus"]:
if "vpc" in describe_domain["DomainStatus"]["Endpoints"]:
domain.vpc_endpoints = [
vpc
for vpc in describe_domain["DomainStatus"][
"Endpoints"
].values()
]

domain.vpc_id = (
describe_domain["DomainStatus"]
.get("VPCOptions", {})
.get("VPCId", "")
)
domain.cognito_options = describe_domain["DomainStatus"][
"CognitoOptions"
]["Enabled"]
domain.encryption_at_rest = describe_domain["DomainStatus"][
"EncryptionAtRestOptions"
]["Enabled"]
domain.node_to_node_encryption = describe_domain["DomainStatus"][
"NodeToNodeEncryptionOptions"
]["Enabled"]
domain.enforce_https = describe_domain["DomainStatus"][
"DomainEndpointOptions"
]["EnforceHTTPS"]
domain.internal_user_database = describe_domain["DomainStatus"][
"AdvancedSecurityOptions"
]["InternalUserDatabaseEnabled"]
domain.saml_enabled = (
describe_domain["DomainStatus"]["AdvancedSecurityOptions"]
.get("SAMLOptions", {})
.get("Enabled", False)
regional_client = self.regional_clients[domain.region]
describe_domain = regional_client.describe_domain(DomainName=domain.name)
domain.arn = describe_domain["DomainStatus"]["ARN"]
if "Endpoints" in describe_domain["DomainStatus"]:
if "vpc" in describe_domain["DomainStatus"]["Endpoints"]:
domain.vpc_endpoints = [
vpc
for vpc in describe_domain["DomainStatus"]["Endpoints"].values()
]
)
domain.update_available = describe_domain["DomainStatus"][
"ServiceSoftwareOptions"
]["UpdateAvailable"]
domain.version = describe_domain["DomainStatus"]["EngineVersion"]
domain.advanced_settings_enabled = describe_domain["DomainStatus"][
"AdvancedSecurityOptions"
]["Enabled"]
domain.vpc_id = (
describe_domain["DomainStatus"]
.get("VPCOptions", {})
.get("VPCId", "")
)
domain.cognito_options = describe_domain["DomainStatus"][
"CognitoOptions"
].get("Enabled", False)
domain.encryption_at_rest = describe_domain["DomainStatus"][
"EncryptionAtRestOptions"
].get("Enabled", False)
domain.node_to_node_encryption = describe_domain["DomainStatus"][
"NodeToNodeEncryptionOptions"
].get("Enabled", False)
domain.enforce_https = describe_domain["DomainStatus"][
"DomainEndpointOptions"
].get("EnforceHTTPS", False)
domain.internal_user_database = describe_domain["DomainStatus"][
"AdvancedSecurityOptions"
].get("InternalUserDatabaseEnabled", False)
domain.saml_enabled = (
describe_domain["DomainStatus"]["AdvancedSecurityOptions"]
.get("SAMLOptions", {})
.get("Enabled", False)
)
domain.update_available = (
describe_domain["DomainStatus"]
.get("ServiceSoftwareOptions", {"UpdateAvailable": False})
.get("UpdateAvailable", False)
)
domain.version = describe_domain["DomainStatus"].get("EngineVersion", None)
domain.advanced_settings_enabled = describe_domain["DomainStatus"][
"AdvancedSecurityOptions"
].get("Enabled", False)
domain.instance_count = describe_domain["DomainStatus"][
"ClusterConfig"
].get("InstanceCount", None)
domain.zone_awareness_enabled = describe_domain["DomainStatus"][
"ClusterConfig"
].get("ZoneAwarenessEnabled", False)
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)

def _list_tags(self):
def _list_tags(self, domain):
logger.info("OpenSearch - List Tags...")
for domain in self.opensearch_domains:
try:
regional_client = self.regional_clients[domain.region]
response = regional_client.list_tags(
ARN=domain.arn,
)["TagList"]
domain.tags = response
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
try:
regional_client = self.regional_clients[domain.region]
response = regional_client.list_tags(
ARN=domain.arn,
)["TagList"]
domain.tags = response
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)


class PublishingLoggingOption(BaseModel):
Expand All @@ -170,5 +169,7 @@ class OpenSearchDomain(BaseModel):
saml_enabled: bool = None
update_available: bool = None
version: str = None
instance_count: Optional[int]
zone_awareness_enabled: Optional[bool]
tags: Optional[list] = []
advanced_settings_enabled: bool = None
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
class opensearch_service_domains_access_control_enabled(Check):
def execute(self):
findings = []
for domain in opensearch_client.opensearch_domains:
for domain in opensearch_client.opensearch_domains.values():
report = Check_Report_AWS(self.metadata())
report.region = domain.region
report.resource_id = domain.name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
class opensearch_service_domains_audit_logging_enabled(Check):
def execute(self):
findings = []
for domain in opensearch_client.opensearch_domains:
for domain in opensearch_client.opensearch_domains.values():
report = Check_Report_AWS(self.metadata())
report.region = domain.region
report.resource_id = domain.name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
class opensearch_service_domains_cloudwatch_logging_enabled(Check):
def execute(self):
findings = []
for domain in opensearch_client.opensearch_domains:
for domain in opensearch_client.opensearch_domains.values():
report = Check_Report_AWS(self.metadata())
report.region = domain.region
report.resource_id = domain.name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
class opensearch_service_domains_encryption_at_rest_enabled(Check):
def execute(self):
findings = []
for domain in opensearch_client.opensearch_domains:
for domain in opensearch_client.opensearch_domains.values():
report = Check_Report_AWS(self.metadata())
report.region = domain.region
report.resource_id = domain.name
Expand Down
Loading

0 comments on commit fff4e45

Please sign in to comment.