mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 13:01:56 +00:00
feat(glue): add new check glue_etl_jobs_logging_enabled (#5581)
Co-authored-by: Sergio <sergio@prowler.com>
This commit is contained in:
committed by
GitHub
parent
239b248935
commit
056d482023
+32
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "glue_etl_jobs_logging_enabled",
|
||||
"CheckTitle": "Check if Glue ETL Jobs have logging enabled.",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/Industry and Regulatory Standards/AWS Foundational Security Best Practices"
|
||||
],
|
||||
"ServiceName": "glue",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "arn:partition:glue:region:account-id:job/job-name",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsGlueJob",
|
||||
"Description": "Ensure that Glue ETL Jobs have CloudWatch logs enabled.",
|
||||
"Risk": "Without logging enabled, AWS Glue jobs lack visibility into job activities and failures, making it difficult to detect unauthorized access, troubleshoot issues, and ensure compliance. This may result in untracked security incidents or operational issues that affect data processing.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/glue/latest/dg/monitor-continuous-logging.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws glue update-job --job-name <job-name> --job-update \"Command={DefaultArguments={--enable-continuous-cloudwatch-log=true}}\"",
|
||||
"NativeIaC": "",
|
||||
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/glue-controls.html#glue-2",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enable logging for AWS Glue jobs to capture and monitor job events. Logging allows for better visibility into job performance, error detection, and security oversight.",
|
||||
"Url": "https://docs.aws.amazon.com/glue/latest/dg/monitor-continuous-logging-enable.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
from typing import List
|
||||
|
||||
from prowler.lib.check.models import Check, Check_Report_AWS
|
||||
from prowler.providers.aws.services.glue.glue_client import glue_client
|
||||
|
||||
|
||||
class glue_etl_jobs_logging_enabled(Check):
|
||||
"""Check if Glue ETL jobs have logging enabled.
|
||||
|
||||
This check will return FAIL if the Glue ETL job does not have logging enabled.
|
||||
"""
|
||||
|
||||
def execute(self) -> List[Check_Report_AWS]:
|
||||
"""Execute the Glue ETL jobs logging enabled check.
|
||||
|
||||
Iterate over all Glue ETL jobs and check if they have logging enabled.
|
||||
|
||||
Returns:
|
||||
List[Check_Report_AWS]: A list of report objects with the results of the check.
|
||||
"""
|
||||
findings = []
|
||||
for job in glue_client.jobs:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.resource_id = job.name
|
||||
report.resource_arn = job.arn
|
||||
report.region = job.region
|
||||
report.resource_tags = job.tags
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"Glue job {job.name} does not have logging enabled."
|
||||
)
|
||||
|
||||
if (
|
||||
job.arguments.get("--enable-continuous-cloudwatch-log", "false")
|
||||
== "true"
|
||||
):
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Glue job {job.name} have logging enabled."
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -1,8 +1,8 @@
|
||||
import json
|
||||
from typing import Optional
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from botocore.exceptions import ClientError
|
||||
from pydantic import BaseModel
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.lib.scan_filters.scan_filters import is_resource_filtered
|
||||
@@ -112,7 +112,7 @@ class Glue(AWSService):
|
||||
name=job["Name"],
|
||||
arn=arn,
|
||||
security=job.get("SecurityConfiguration"),
|
||||
arguments=job.get("DefaultArguments"),
|
||||
arguments=job.get("DefaultArguments", {}),
|
||||
region=regional_client.region,
|
||||
)
|
||||
)
|
||||
@@ -277,7 +277,7 @@ class Connection(BaseModel):
|
||||
type: str
|
||||
properties: dict
|
||||
region: str
|
||||
tags: Optional[list]
|
||||
tags: Optional[List[Dict[str, str]]] = Field(default_factory=list)
|
||||
|
||||
|
||||
class Table(BaseModel):
|
||||
@@ -286,6 +286,7 @@ class Table(BaseModel):
|
||||
database: str
|
||||
catalog: Optional[str]
|
||||
region: str
|
||||
tags: Optional[List[Dict[str, str]]] = Field(default_factory=list)
|
||||
|
||||
|
||||
class CatalogEncryptionSetting(BaseModel):
|
||||
@@ -300,16 +301,16 @@ class DevEndpoint(BaseModel):
|
||||
arn: str
|
||||
security: Optional[str]
|
||||
region: str
|
||||
tags: Optional[list]
|
||||
tags: Optional[List[Dict[str, str]]] = Field(default_factory=list)
|
||||
|
||||
|
||||
class Job(BaseModel):
|
||||
arn: str
|
||||
name: str
|
||||
security: Optional[str]
|
||||
arguments: Optional[dict]
|
||||
arguments: Optional[Dict[str, str]] = Field(default_factory=dict)
|
||||
region: str
|
||||
tags: Optional[list]
|
||||
tags: Optional[List[Dict[str, str]]] = Field(default_factory=list)
|
||||
|
||||
|
||||
class SecurityConfig(BaseModel):
|
||||
@@ -321,6 +322,7 @@ class SecurityConfig(BaseModel):
|
||||
jb_encryption: str
|
||||
jb_key_arn: Optional[str]
|
||||
region: str
|
||||
tags: Optional[List[Dict[str, str]]] = Field(default_factory=list)
|
||||
|
||||
|
||||
class MLTransform(BaseModel):
|
||||
@@ -329,7 +331,7 @@ class MLTransform(BaseModel):
|
||||
name: str
|
||||
user_data_encryption: str
|
||||
region: str
|
||||
tags: Optional[list]
|
||||
tags: Optional[List[Dict[str, str]]] = Field(default_factory=list)
|
||||
|
||||
|
||||
class DataCatalog(BaseModel):
|
||||
@@ -337,3 +339,4 @@ class DataCatalog(BaseModel):
|
||||
region: str
|
||||
encryption_settings: Optional[CatalogEncryptionSetting]
|
||||
policy: Optional[dict]
|
||||
tags: Optional[List[Dict[str, str]]] = Field(default_factory=list)
|
||||
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_aws
|
||||
|
||||
from tests.providers.aws.utils import (
|
||||
AWS_ACCOUNT_NUMBER,
|
||||
AWS_REGION_US_EAST_1,
|
||||
set_mocked_aws_provider,
|
||||
)
|
||||
|
||||
|
||||
class Test_glue_etl_jobs_logging_enabled:
|
||||
@mock_aws
|
||||
def test_glue_no_jobs(self):
|
||||
from prowler.providers.aws.services.glue.glue_service import Glue
|
||||
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
):
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.glue.glue_etl_jobs_logging_enabled.glue_etl_jobs_logging_enabled.glue_client",
|
||||
new=Glue(aws_provider),
|
||||
):
|
||||
from prowler.providers.aws.services.glue.glue_etl_jobs_logging_enabled.glue_etl_jobs_logging_enabled import (
|
||||
glue_etl_jobs_logging_enabled,
|
||||
)
|
||||
|
||||
check = glue_etl_jobs_logging_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
@mock_aws
|
||||
def test_glue_job_logging_enabled(self):
|
||||
glue_client = client("glue", region_name=AWS_REGION_US_EAST_1)
|
||||
job_name = "test-job"
|
||||
job_arn = (
|
||||
f"arn:aws:glue:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:job/{job_name}"
|
||||
)
|
||||
glue_client.create_job(
|
||||
Name=job_name,
|
||||
Role="role_test",
|
||||
Command={"Name": "name_test", "ScriptLocation": "script_test"},
|
||||
DefaultArguments={"--enable-continuous-cloudwatch-log": "true"},
|
||||
Tags={"key_test": "value_test"},
|
||||
GlueVersion="1.0",
|
||||
MaxCapacity=0.0625,
|
||||
MaxRetries=0,
|
||||
Timeout=10,
|
||||
NumberOfWorkers=2,
|
||||
WorkerType="G.1X",
|
||||
SecurityConfiguration="sec_config",
|
||||
NotificationProperty={"NotifyDelayAfter": 1},
|
||||
)
|
||||
|
||||
from prowler.providers.aws.services.glue.glue_service import Glue
|
||||
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
):
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.glue.glue_etl_jobs_logging_enabled.glue_etl_jobs_logging_enabled.glue_client",
|
||||
new=Glue(aws_provider),
|
||||
):
|
||||
from prowler.providers.aws.services.glue.glue_etl_jobs_logging_enabled.glue_etl_jobs_logging_enabled import (
|
||||
glue_etl_jobs_logging_enabled,
|
||||
)
|
||||
|
||||
check = glue_etl_jobs_logging_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Glue job {job_name} have logging enabled."
|
||||
)
|
||||
assert result[0].resource_id == job_name
|
||||
assert result[0].resource_arn == job_arn
|
||||
assert result[0].resource_tags == [{"key_test": "value_test"}]
|
||||
|
||||
@mock_aws
|
||||
def test_glue_job_logging_disabled(self):
|
||||
glue_client = client("glue", region_name=AWS_REGION_US_EAST_1)
|
||||
job_name = "test-job"
|
||||
job_arn = (
|
||||
f"arn:aws:glue:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:job/{job_name}"
|
||||
)
|
||||
glue_client.create_job(
|
||||
Name=job_name,
|
||||
Role="role_test",
|
||||
Command={"Name": "name_test", "ScriptLocation": "script_test"},
|
||||
DefaultArguments={},
|
||||
Tags={"key_test": "value_test"},
|
||||
GlueVersion="1.0",
|
||||
MaxCapacity=0.0625,
|
||||
MaxRetries=0,
|
||||
Timeout=10,
|
||||
NumberOfWorkers=2,
|
||||
WorkerType="G.1X",
|
||||
SecurityConfiguration="sec_config",
|
||||
)
|
||||
|
||||
from prowler.providers.aws.services.glue.glue_service import Glue
|
||||
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
):
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.glue.glue_etl_jobs_logging_enabled.glue_etl_jobs_logging_enabled.glue_client",
|
||||
new=Glue(aws_provider),
|
||||
):
|
||||
from prowler.providers.aws.services.glue.glue_etl_jobs_logging_enabled.glue_etl_jobs_logging_enabled import (
|
||||
glue_etl_jobs_logging_enabled,
|
||||
)
|
||||
|
||||
check = glue_etl_jobs_logging_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Glue job {job_name} does not have logging enabled."
|
||||
)
|
||||
assert result[0].resource_id == job_name
|
||||
assert result[0].resource_arn == job_arn
|
||||
assert result[0].resource_tags == [{"key_test": "value_test"}]
|
||||
Reference in New Issue
Block a user