-
Notifications
You must be signed in to change notification settings - Fork 936
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9e2d4be
commit 15d4b60
Showing
11 changed files
with
242 additions
and
278 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 12 additions & 0 deletions
12
cloudtrail-lambda-dynamo-cdk/src/lib/lambda/object_tag_checker/Event.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
class Event: | ||
def __init__(self, object_arn, bucket_name, is_compliant, object_key): | ||
self.object_arn = object_arn | ||
self.bucket_name = bucket_name | ||
self.is_compliant = is_compliant | ||
self.object_key = object_key | ||
self.tags = None | ||
|
||
def validate_compliance(self, required_keys): | ||
result = self.tags.difference(required_keys) | ||
if result: | ||
return result |
93 changes: 93 additions & 0 deletions
93
cloudtrail-lambda-dynamo-cdk/src/lib/lambda/object_tag_checker/index.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import boto3 | ||
import os | ||
from objects_db import Objects | ||
from Event import Event | ||
from botocore.exceptions import ClientError | ||
from boto3.dynamodb.types import TypeDeserializer | ||
|
||
deserializer = TypeDeserializer() | ||
dynamodb = boto3.resource("dynamodb") | ||
s3_client = boto3.client("s3") | ||
table_name = os.environ["TABLE_NAME"] | ||
|
||
REQUIRED_KEYS = { | ||
"Key1", | ||
"Key2", | ||
"Key3", | ||
"Key4", | ||
} # replace with required resource keys | ||
|
||
|
||
def pass_items(event): | ||
objects_to_check = [] | ||
|
||
for record in event["Records"]: | ||
new_image = record["dynamodb"].get("NewImage") | ||
python_data = new_image | ||
print(python_data) | ||
for key, value in python_data.items(): | ||
if "S" in value: | ||
python_data[key] = value["S"] | ||
elif "BOOL" in value: | ||
python_data[key] = value["BOOL"] | ||
|
||
python_data = Event(**python_data) | ||
|
||
objects_to_check.append(python_data) | ||
|
||
return objects_to_check | ||
|
||
|
||
def add_to_set(tags: list): | ||
tags_set = set() | ||
for value in tags: | ||
tags_set.add(value["Key"].strip()) | ||
return tags_set | ||
|
||
|
||
def update_keys(item): | ||
resource_instance = Objects(dynamodb, table_name) | ||
attributes = {"is_compliant": item.is_compliant} | ||
resource_instance.add_key(item, attributes) | ||
|
||
|
||
def check_compliance(objects): | ||
for i in objects: | ||
try: | ||
tags_set = set() | ||
print(i.bucket_name) | ||
|
||
response = s3_client.get_object_tagging( | ||
Bucket=i.bucket_name, Key=i.object_key | ||
) | ||
|
||
tags = response["TagSet"] | ||
|
||
if tags: | ||
tags_set = add_to_set(tags) | ||
|
||
i.tags = tags_set | ||
is_not_compliant = i.validate_compliance(required_keys=REQUIRED_KEYS) | ||
|
||
if is_not_compliant or not i.tags: | ||
# further action may be taken here if not compliant (EX: notify admins using SNS) | ||
|
||
i.is_compliant = False | ||
print(i.object_key, "is not compliant") | ||
else: | ||
i.is_compliant = True | ||
update_keys(i) | ||
print(i.object_key, "is now compliant") | ||
|
||
except ClientError as err: | ||
if err.response["Error"]["Code"] == "NoSuchTagSet": | ||
print(i.resource_name, "has no tags or does not exist") | ||
else: | ||
print(err) | ||
except Exception as err: | ||
print(err) | ||
|
||
|
||
def lambda_handler(event, context): | ||
objects = pass_items(event) | ||
return check_compliance(objects) |
30 changes: 30 additions & 0 deletions
30
cloudtrail-lambda-dynamo-cdk/src/lib/lambda/object_tag_checker/objects_db.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
from botocore.exceptions import ClientError | ||
|
||
|
||
class Objects: | ||
def __init__(self, db_client, table_name): | ||
self.dyn_client = db_client | ||
self.table = self.dyn_client.Table(table_name) | ||
|
||
def add_key(self, item, attributes): | ||
update_expression = "SET {}".format( | ||
",".join(f"#{key}=:{key}" for key in attributes) | ||
) | ||
attribute_values = {f":{key}": value for key, value in attributes.items()} | ||
attribute_names = {f"#{key}": key for key in attributes} | ||
print(attribute_values) | ||
|
||
try: | ||
response = self.table.update_item( | ||
Key={ | ||
"object_arn": item.object_arn, | ||
}, | ||
UpdateExpression=update_expression, | ||
ExpressionAttributeValues=attribute_values, | ||
ExpressionAttributeNames=attribute_names, | ||
) | ||
return response | ||
|
||
except ClientError as err: | ||
print(err) | ||
raise |
108 changes: 40 additions & 68 deletions
108
cloudtrail-lambda-dynamo-cdk/src/lib/lambda/populate_dynamo/index.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,87 +1,59 @@ | ||
import boto3 | ||
import os | ||
import json | ||
import gzip | ||
from botocore.exceptions import ClientError | ||
import gzip | ||
import json | ||
import os | ||
|
||
dynamodb = boto3.resource('dynamodb') | ||
table_name = os.environ['TABLE_NAME'] | ||
dynamodb = boto3.resource("dynamodb") | ||
table_name = os.environ["TABLE_NAME"] | ||
table = dynamodb.Table(table_name) | ||
s3_client = boto3.client('s3') | ||
s3_client = boto3.client("s3") | ||
|
||
|
||
def lambda_handler(event, context): | ||
print(event) | ||
|
||
items_to_add = [] | ||
bucket_name = os.environ['BUCKET_NAME'] | ||
for record in event['Records']: | ||
cloudtrail_bucket = os.environ["CLOUDTRAIL_BUCKET_NAME"] | ||
|
||
for record in event["Records"]: | ||
try: | ||
if record['s3']['bucket']['name'] != bucket_name: | ||
bucket_name = record["s3"]["bucket"]["name"] | ||
|
||
if bucket_name != cloudtrail_bucket: | ||
continue | ||
|
||
object_key = record['s3']['object']['key'] | ||
|
||
response = s3_client.get_object(Bucket=bucket_name, Key=object_key) | ||
log_data = gzip.decompress(response['Body'].read()).decode('utf-8') | ||
cloudtrail_logs = json.loads(log_data)['Records'] | ||
|
||
|
||
object_key = record["s3"]["object"]["key"] | ||
response = s3_client.get_object(Bucket=cloudtrail_bucket, Key=object_key) | ||
log_data = gzip.decompress(response["Body"].read()).decode("utf-8") | ||
cloudtrail_logs = json.loads(log_data)["Records"] | ||
|
||
for log in cloudtrail_logs: | ||
event_name = log['eventName'] | ||
event_id = log['eventID'] | ||
event_source = log['eventSource'] | ||
|
||
if event_name == 'CreateBucket': | ||
bucket_name = log['requestParameters']['bucketName'] | ||
bucket_arn = f'arn:aws:s3:::{bucket_name}' | ||
|
||
item = { | ||
'resource_arn': bucket_arn, | ||
'event_id': event_id, | ||
'event_source': event_source, | ||
'resource_name': bucket_name, | ||
'is_compliant': False | ||
} | ||
items_to_add.append(item) | ||
|
||
if event_name == 'CreateCluster': | ||
cluster_arn = log['responseElements']['cluster']['clusterArn'] | ||
cluster_name = log['requestParameters']['clusterName'] | ||
|
||
event_name = log["eventName"] | ||
user_identity = log["userIdentity"] | ||
|
||
if ( | ||
user_identity.get("type") == "AWSService" | ||
and user_identity.get("invokedBy") == "cloudtrail.amazonaws.com" | ||
): | ||
continue | ||
|
||
if event_name == "PutObject": | ||
bucket_name = log["requestParameters"]["bucketName"] | ||
key = log["requestParameters"]["key"] | ||
object_arn = f"arn:aws:s3:::{bucket_name}/{key}" | ||
item = { | ||
'resource_arn': cluster_arn, | ||
'event_id': event_id, | ||
'event_source': event_source, | ||
'resource_name': cluster_name, | ||
'is_compliant': False | ||
"object_arn": object_arn, | ||
"object_key": key, | ||
"bucket_name": bucket_name, | ||
"is_compliant": False, | ||
} | ||
items_to_add.append(item) | ||
|
||
if event_name.startswith('CreateFunction'): | ||
function_name = log['requestParameters']['functionName'] | ||
region = log['awsRegion'] | ||
account_id = log['userIdentity']['accountId'] | ||
function_arn = f'arn:aws:lambda:{region}:{account_id}:function:{function_name}' | ||
|
||
|
||
item = { | ||
'resource_arn': function_arn, | ||
'event_id': event_id, | ||
'event_source': event_source, | ||
'resource_name': function_name, | ||
'is_compliant': False | ||
} | ||
|
||
if not any(i['resource_arn'] == function_arn for i in items_to_add): | ||
items_to_add.append(item) | ||
|
||
except ClientError as err: | ||
print(err) | ||
|
||
if items_to_add: | ||
with table.batch_writer() as batch: | ||
for item in items_to_add: | ||
batch.put_item(Item=item) | ||
|
||
return { | ||
'statusCode': 200, | ||
'body': json.dumps('Success') | ||
} | ||
return {"statusCode": 200, "body": json.dumps("Success")} |
15 changes: 0 additions & 15 deletions
15
cloudtrail-lambda-dynamo-cdk/src/lib/lambda/resource_tag_checker/Event.py
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.