Skip to content

Commit d07b202

Browse files
committed
Rework identification API to be asynchronous
User issues a POST request with payload to /identify, unique request_id is generated and returned to user. User then can issue GET request to /identify/<request_id> to get response. This allows to scan big accounts with a lot of resources and do not exceed 30 seconds response timeout for lambda integration with gateway. New DDB was added for tracking status of user requests. API lambda(entrypoint) now triggers existing describe lambdas to do a scan. Every describe lambda retrieves request_id from payload and updates corresponding record in requests table after it finishes the scan of the region. This allows to track the progress of scan, by comparing current progress and total number of scans (features * regions) and determine when the scan is finished. Remediation is not supported for now. Add "scan account <account id>" command to slack bot. Currently it supports only full account scan.
1 parent 1bd8d36 commit d07b202

22 files changed

Lines changed: 412 additions & 78 deletions

File tree

deployment/cf-templates/api.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@
209209
"HttpMethod": "ANY",
210210
"Integration": {
211211
"Type": "AWS_PROXY",
212-
"IntegrationHttpMethod": "ANY",
212+
"IntegrationHttpMethod": "POST",
213213
"Uri": {"Fn::Join": ["",
214214
["arn:aws:apigateway:", {"Ref": "AWS::Region"}, ":lambda:path/2015-03-31/functions/", {"Fn::GetAtt": ["LambdaApi", "Arn"]}, "/invocations"]
215215
]}

deployment/cf-templates/ddb.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,28 @@
428428
},
429429
"TableName": {"Fn::Join" : ["", [ { "Ref": "ResourcesPrefix" }, "rds-unencrypted" ] ]}
430430
}
431+
},
432+
"DynamoDBApiRequests": {
433+
"Type": "AWS::DynamoDB::Table",
434+
"Properties": {
435+
"AttributeDefinitions": [
436+
{
437+
"AttributeName": "request_id",
438+
"AttributeType": "S"
439+
}
440+
],
441+
"KeySchema": [
442+
{
443+
"AttributeName": "request_id",
444+
"KeyType": "HASH"
445+
}
446+
],
447+
"ProvisionedThroughput": {
448+
"ReadCapacityUnits": "10",
449+
"WriteCapacityUnits": "2"
450+
},
451+
"TableName": {"Fn::Join" : ["", [ { "Ref": "ResourcesPrefix" }, "credentials" ] ]}
452+
}
431453
}
432454
}
433455
}

deployment/configs/config.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,20 +40,25 @@
4040
"654321210987": "slave2"
4141
}
4242
},
43+
"api": {
44+
"ddb.table_name": "hammer-api-requests"
45+
},
4346
"whitelisting_procedure_url": "",
4447
"credentials": {
4548
"ddb.table_name": "hammer-credentials"
4649
},
4750
"s3_bucket_acl": {
4851
"enabled": true,
4952
"ddb.table_name": "hammer-s3-public-bucket-acl",
53+
"topic_name": "hammer-describe-s3-acl-lambda",
5054
"reporting": false,
5155
"remediation": false,
5256
"remediation_retention_period": 0
5357
},
5458
"secgrp_unrestricted_access": {
5559
"enabled": true,
5660
"ddb.table_name": "hammer-security-groups-unrestricted",
61+
"topic_name": "hammer-describe-security-groups-lambda",
5762
"restricted_ports": [
5863
21,
5964
22,
@@ -75,6 +80,7 @@
7580
"user_inactivekeys": {
7681
"enabled": true,
7782
"ddb.table_name": "hammer-iam-user-keys-inactive",
83+
"topic_name": "hammer-describe-iam-user-inactive-keys-lambda",
7884
"ignore_accounts": ["654321210987"],
7985
"inactive_criteria_days": 1,
8086
"reporting": false,
@@ -84,6 +90,7 @@
8490
"user_keysrotation": {
8591
"enabled": true,
8692
"ddb.table_name": "hammer-iam-user-keys-rotation",
93+
"topic_name": "hammer-describe-iam-user-keys-rotation-lambda",
8794
"rotation_criteria_days": 10,
8895
"reporting": false,
8996
"remediation": false,
@@ -92,52 +99,60 @@
9299
"s3_bucket_policy": {
93100
"enabled": true,
94101
"ddb.table_name": "hammer-s3-public-bucket-policy",
102+
"topic_name": "hammer-describe-s3-policy-lambda",
95103
"reporting": false,
96104
"remediation": false,
97105
"remediation_retention_period": 7
98106
},
99107
"cloudtrails": {
100108
"enabled": true,
101109
"ddb.table_name": "hammer-cloudtrails",
110+
"topic_name": "hammer-describe-cloudtrails-lambda",
102111
"reporting": false
103112
},
104113
"ebs_unencrypted_volume": {
105114
"enabled": true,
106115
"ddb.table_name": "hammer-ebs-volumes-unencrypted",
116+
"topic_name": "hammer-describe-ebs-unencrypted-volumes-lambda",
107117
"accounts": ["123456789012", "210987654321"],
108118
"reporting": false
109119
},
110120
"ebs_public_snapshot": {
111121
"enabled": true,
112122
"ddb.table_name": "hammer-ebs-snapshots-public",
123+
"topic_name": "hammer-describe-ebs-public-snapshots-lambda",
113124
"reporting": false,
114125
"remediation": false,
115126
"remediation_retention_period": 0
116127
},
117128
"rds_public_snapshot": {
118129
"enabled": true,
119130
"ddb.table_name": "hammer-rds-public-snapshots",
131+
"topic_name": "hammer-describe-rds-public-snapshots-lambda",
120132
"reporting": false,
121133
"remediation": false,
122134
"remediation_retention_period": 0
123135
},
124136
"sqs_public_access": {
125137
"enabled": true,
126138
"ddb.table_name": "hammer-sqs-public-access",
139+
"topic_name": "hammer-describe-sqs-public-policy-lambda",
127140
"reporting": true,
128141
"remediation": false,
129142
"remediation_retention_period": 0
130143
},
131144
"s3_encryption": {
132145
"enabled": true,
133146
"ddb.table_name": "hammer-s3-unencrypted",
147+
"topic_name": "hammer-describe-s3-encryption-lambda",
134148
"reporting": true,
135149
"remediation": false,
136150
"remediation_retention_period": 0
137151
},
138152
"rds_encryption": {
139153
"enabled": true,
140154
"ddb.table_name": "hammer-rds-unencrypted",
155+
"topic_name": "hammer-describe-rds-encryption-lambda",
141156
"reporting": true
142157
}
143158
}

hammer/identification/lambdas/api/authorizer.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ def lambda_handler(event, context):
2828
policy.restApiId = apiGatewayArnTmp[0]
2929
policy.region = tmp[3]
3030
policy.stage = apiGatewayArnTmp[1]
31+
# a quick hack to allow GET calls to /identify/{request_id}, request_id is hex string
32+
# rewrite this solution to more generic variant
33+
if len(apiGatewayArnTmp) == 5:
34+
full_path = '/identify/' + apiGatewayArnTmp[4]
35+
policy.allowMethod(HttpVerb.GET, full_path)
3136
policy.allowMethod(HttpVerb.POST, '/identify')
3237
policy.allowMethod(HttpVerb.POST, '/remediate')
3338

@@ -190,4 +195,4 @@ def build(self):
190195
policy['policyDocument']['Statement'].extend(self._getStatementForEffect('Allow', self.allowMethods))
191196
policy['policyDocument']['Statement'].extend(self._getStatementForEffect('Deny', self.denyMethods))
192197

193-
return policy
198+
return policy

0 commit comments

Comments
 (0)