fix(apigateway): Add ApiGateway ResourceArn and check fixes (#1707)

Co-authored-by: sergargar <sergio@verica.io>
This commit is contained in:
Gabriel Soltz
2023-01-16 10:23:14 +01:00
committed by GitHub
parent a69c28713a
commit 64090474e1
14 changed files with 76 additions and 25 deletions

View File

@@ -10,14 +10,14 @@ class apigateway_authorizers_enabled(Check):
for rest_api in apigateway_client.rest_apis: for rest_api in apigateway_client.rest_apis:
report = Check_Report_AWS(self.metadata()) report = Check_Report_AWS(self.metadata())
report.region = rest_api.region report.region = rest_api.region
report.resource_id = rest_api.name
report.resource_arn = rest_api.arn
if rest_api.authorizer: if rest_api.authorizer:
report.status = "PASS" report.status = "PASS"
report.status_extended = f"API Gateway {rest_api.name} ID {rest_api.id} has authorizer configured." report.status_extended = f"API Gateway {rest_api.name} ID {rest_api.id} has authorizer configured."
report.resource_id = rest_api.name
else: else:
report.status = "FAIL" report.status = "FAIL"
report.status_extended = f"API Gateway {rest_api.name} ID {rest_api.id} has not authorizer configured." report.status_extended = f"API Gateway {rest_api.name} ID {rest_api.id} has not authorizer configured."
report.resource_id = rest_api.name
findings.append(report) findings.append(report)
return findings return findings

View File

@@ -1,7 +1,7 @@
{ {
"Provider": "aws", "Provider": "aws",
"CheckID": "apigateway_client_certificate_enabled", "CheckID": "apigateway_client_certificate_enabled",
"CheckTitle": "Check if API Gateway has client certificate enabled to access your backend endpoint.", "CheckTitle": "Check if API Gateway Stage has client certificate enabled to access your backend endpoint.",
"CheckType": [ "CheckType": [
"Data Protection" "Data Protection"
], ],
@@ -9,8 +9,8 @@
"SubServiceName": "rest_api", "SubServiceName": "rest_api",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id", "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "medium", "Severity": "medium",
"ResourceType": "AwsApiGatewayRestApi", "ResourceType": "AwsApiGatewayStage",
"Description": "Check if API Gateway has client certificate enabled to access your backend endpoint.", "Description": "Check if API Gateway Stage has client certificate enabled to access your backend endpoint.",
"Risk": "Possible man in the middle attacks and other similar risks.", "Risk": "Possible man in the middle attacks and other similar risks.",
"RelatedUrl": "", "RelatedUrl": "",
"Remediation": { "Remediation": {

View File

@@ -10,16 +10,15 @@ class apigateway_client_certificate_enabled(Check):
for rest_api in apigateway_client.rest_apis: for rest_api in apigateway_client.rest_apis:
for stage in rest_api.stages: for stage in rest_api.stages:
report = Check_Report_AWS(self.metadata()) report = Check_Report_AWS(self.metadata())
report.resource_id = rest_api.name
report.region = rest_api.region
report.resource_arn = stage.arn
if stage.client_certificate: if stage.client_certificate:
report.status = "PASS" report.status = "PASS"
report.status_extended = f"API Gateway {rest_api.name} ID {rest_api.id} in stage {stage.name} has client certificate enabled." report.status_extended = f"API Gateway {rest_api.name} ID {rest_api.id} in stage {stage.name} has client certificate enabled."
report.resource_id = rest_api.name
report.region = rest_api.region
else: else:
report.status = "FAIL" report.status = "FAIL"
report.status_extended = f"API Gateway {rest_api.name} ID {rest_api.id} in stage {stage.name} has not client certificate enabled." report.status_extended = f"API Gateway {rest_api.name} ID {rest_api.id} in stage {stage.name} has not client certificate enabled."
report.resource_id = rest_api.name
report.region = rest_api.region
findings.append(report) findings.append(report)
return findings return findings

View File

@@ -10,16 +10,16 @@ class apigateway_endpoint_public(Check):
for rest_api in apigateway_client.rest_apis: for rest_api in apigateway_client.rest_apis:
report = Check_Report_AWS(self.metadata()) report = Check_Report_AWS(self.metadata())
report.region = rest_api.region report.region = rest_api.region
report.resource_id = rest_api.name
report.resource_arn = rest_api.arn
if rest_api.public_endpoint: if rest_api.public_endpoint:
report.status = "FAIL" report.status = "FAIL"
report.status_extended = f"API Gateway {rest_api.name} ID {rest_api.id} is internet accesible." report.status_extended = f"API Gateway {rest_api.name} ID {rest_api.id} is internet accesible."
report.resource_id = rest_api.name
else: else:
report.status = "PASS" report.status = "PASS"
report.status_extended = ( report.status_extended = (
f"API Gateway {rest_api.name} ID {rest_api.id} is private." f"API Gateway {rest_api.name} ID {rest_api.id} is private."
) )
report.resource_id = rest_api.name
findings.append(report) findings.append(report)
return findings return findings

View File

@@ -1,7 +1,7 @@
{ {
"Provider": "aws", "Provider": "aws",
"CheckID": "apigateway_logging_enabled", "CheckID": "apigateway_logging_enabled",
"CheckTitle": "Check if API Gateway has logging enabled.", "CheckTitle": "Check if API Gateway Stage has logging enabled.",
"CheckType": [ "CheckType": [
"Logging and Monitoring" "Logging and Monitoring"
], ],
@@ -9,8 +9,8 @@
"SubServiceName": "rest_api", "SubServiceName": "rest_api",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id", "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "medium", "Severity": "medium",
"ResourceType": "AwsApiGatewayRestApi", "ResourceType": "AwsApiGatewayStage",
"Description": "Check if API Gateway has logging enabled.", "Description": "Check if API Gateway Stage has logging enabled.",
"Risk": "If not enabled, monitoring of service use is not possible. Real-time monitoring of API calls can be achieved by directing CloudTrail Logs to CloudWatch Logs and establishing corresponding metric filters and alarms.", "Risk": "If not enabled, monitoring of service use is not possible. Real-time monitoring of API calls can be achieved by directing CloudTrail Logs to CloudWatch Logs and establishing corresponding metric filters and alarms.",
"RelatedUrl": "", "RelatedUrl": "",
"Remediation": { "Remediation": {

View File

@@ -8,17 +8,17 @@ class apigateway_logging_enabled(Check):
def execute(self): def execute(self):
findings = [] findings = []
for rest_api in apigateway_client.rest_apis: for rest_api in apigateway_client.rest_apis:
report = Check_Report_AWS(self.metadata())
report.region = rest_api.region
for stage in rest_api.stages: for stage in rest_api.stages:
report = Check_Report_AWS(self.metadata())
report.region = rest_api.region
report.resource_id = rest_api.name
report.resource_arn = stage.arn
if stage.logging: if stage.logging:
report.status = "PASS" report.status = "PASS"
report.status_extended = f"API Gateway {rest_api.name} ID {rest_api.id} in stage {stage.name} has logging enabled." report.status_extended = f"API Gateway {rest_api.name} ID {rest_api.id} in stage {stage.name} has logging enabled."
report.resource_id = rest_api.name
else: else:
report.status = "FAIL" report.status = "FAIL"
report.status_extended = f"API Gateway {rest_api.name} ID {rest_api.id} in stage {stage.name} has logging disabled." report.status_extended = f"API Gateway {rest_api.name} ID {rest_api.id} in stage {stage.name} has logging disabled."
report.resource_id = rest_api.name
findings.append(report) findings.append(report)
return findings return findings

View File

@@ -11,6 +11,7 @@ class APIGateway:
self.service = "apigateway" self.service = "apigateway"
self.session = audit_info.audit_session self.session = audit_info.audit_session
self.audited_account = audit_info.audited_account self.audited_account = audit_info.audited_account
self.audited_partition = audit_info.audited_partition
self.regional_clients = generate_regional_clients(self.service, audit_info) self.regional_clients = generate_regional_clients(self.service, audit_info)
self.rest_apis = [] self.rest_apis = []
self.__threading_call__(self.__get_rest_apis__) self.__threading_call__(self.__get_rest_apis__)
@@ -36,9 +37,11 @@ class APIGateway:
get_rest_apis_paginator = regional_client.get_paginator("get_rest_apis") get_rest_apis_paginator = regional_client.get_paginator("get_rest_apis")
for page in get_rest_apis_paginator.paginate(): for page in get_rest_apis_paginator.paginate():
for apigw in page["items"]: for apigw in page["items"]:
arn = f"arn:{self.audited_partition}:apigateway:{regional_client.region}::/apis/{apigw['id']}"
self.rest_apis.append( self.rest_apis.append(
RestAPI( RestAPI(
apigw["id"], apigw["id"],
arn,
regional_client.region, regional_client.region,
apigw["name"], apigw["name"],
) )
@@ -89,9 +92,11 @@ class APIGateway:
logging = True logging = True
if "clientCertificateId" in stage: if "clientCertificateId" in stage:
client_certificate = True client_certificate = True
arn = f"arn:{self.audited_partition}:apigateway:{regional_client.region}::/apis/{rest_api.id}/stages/{stage['stageName']}"
rest_api.stages.append( rest_api.stages.append(
Stage( Stage(
stage["stageName"], stage["stageName"],
arn,
logging, logging,
client_certificate, client_certificate,
waf, waf,
@@ -104,6 +109,7 @@ class APIGateway:
@dataclass @dataclass
class Stage: class Stage:
name: str name: str
arn: str
logging: bool logging: bool
client_certificate: bool client_certificate: bool
waf: str waf: str
@@ -111,11 +117,13 @@ class Stage:
def __init__( def __init__(
self, self,
name, name,
arn,
logging, logging,
client_certificate, client_certificate,
waf, waf,
): ):
self.name = name self.name = name
self.arn = arn
self.logging = logging self.logging = logging
self.client_certificate = client_certificate self.client_certificate = client_certificate
self.waf = waf self.waf = waf
@@ -124,6 +132,7 @@ class Stage:
@dataclass @dataclass
class RestAPI: class RestAPI:
id: str id: str
arn: str
region: str region: str
name: str name: str
authorizer: bool authorizer: bool
@@ -133,10 +142,12 @@ class RestAPI:
def __init__( def __init__(
self, self,
id, id,
arn,
region, region,
name, name,
): ):
self.id = id self.id = id
self.arn = arn
self.region = region self.region = region
self.name = name self.name = name
self.authorizer = False self.authorizer = False

View File

@@ -1,7 +1,7 @@
{ {
"Provider": "aws", "Provider": "aws",
"CheckID": "apigateway_waf_acl_attached", "CheckID": "apigateway_waf_acl_attached",
"CheckTitle": "Check if API Gateway has a WAF ACL attached.", "CheckTitle": "Check if API Gateway Stage has a WAF ACL attached.",
"CheckType": [ "CheckType": [
"Infrastructure Security" "Infrastructure Security"
], ],
@@ -9,8 +9,8 @@
"SubServiceName": "rest_api", "SubServiceName": "rest_api",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id", "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "medium", "Severity": "medium",
"ResourceType": "AwsApiGatewayRestApi", "ResourceType": "AwsApiGatewayStage",
"Description": "Check if API Gateway has a WAF ACL attached.", "Description": "Check if API Gateway Stage has a WAF ACL attached.",
"Risk": "Potential attacks and / or abuse of service, more even for even for internet reachable services.", "Risk": "Potential attacks and / or abuse of service, more even for even for internet reachable services.",
"RelatedUrl": "", "RelatedUrl": "",
"Remediation": { "Remediation": {

View File

@@ -8,17 +8,17 @@ class apigateway_waf_acl_attached(Check):
def execute(self): def execute(self):
findings = [] findings = []
for rest_api in apigateway_client.rest_apis: for rest_api in apigateway_client.rest_apis:
report = Check_Report_AWS(self.metadata())
report.region = rest_api.region
for stage in rest_api.stages: for stage in rest_api.stages:
report = Check_Report_AWS(self.metadata())
report.region = rest_api.region
report.resource_id = rest_api.name
report.resource_arn = stage.arn
if stage.waf: if stage.waf:
report.status = "PASS" report.status = "PASS"
report.status_extended = f"API Gateway {rest_api.name} ID {rest_api.id} in stage {stage.name} has {stage.waf} WAF ACL attached." report.status_extended = f"API Gateway {rest_api.name} ID {rest_api.id} in stage {stage.name} has {stage.waf} WAF ACL attached."
report.resource_id = rest_api.name
else: else:
report.status = "FAIL" report.status = "FAIL"
report.status_extended = f"API Gateway {rest_api.name} ID {rest_api.id} in stage {stage.name} has not WAF ACL attached." report.status_extended = f"API Gateway {rest_api.name} ID {rest_api.id} in stage {stage.name} has not WAF ACL attached."
report.resource_id = rest_api.name
findings.append(report) findings.append(report)
return findings return findings

View File

@@ -88,6 +88,10 @@ class Test_apigateway_authorizers_enabled:
== f"API Gateway test-rest-api ID {rest_api['id']} has authorizer configured." == f"API Gateway test-rest-api ID {rest_api['id']} has authorizer configured."
) )
assert result[0].resource_id == "test-rest-api" assert result[0].resource_id == "test-rest-api"
assert (
result[0].resource_arn
== f"arn:{current_audit_info.audited_partition}:apigateway:{AWS_REGION}::/apis/{rest_api['id']}"
)
@mock_apigateway @mock_apigateway
def test_apigateway_one_rest_api_without_lambda_authorizer(self): def test_apigateway_one_rest_api_without_lambda_authorizer(self):
@@ -123,3 +127,7 @@ class Test_apigateway_authorizers_enabled:
== f"API Gateway test-rest-api ID {rest_api['id']} has not authorizer configured." == f"API Gateway test-rest-api ID {rest_api['id']} has not authorizer configured."
) )
assert result[0].resource_id == "test-rest-api" assert result[0].resource_id == "test-rest-api"
assert (
result[0].resource_arn
== f"arn:{current_audit_info.audited_partition}:apigateway:{AWS_REGION}::/apis/{rest_api['id']}"
)

View File

@@ -99,6 +99,10 @@ class Test_apigateway_client_certificate_enabled:
== f"API Gateway test-rest-api ID {rest_api['id']} in stage test has not client certificate enabled." == f"API Gateway test-rest-api ID {rest_api['id']} in stage test has not client certificate enabled."
) )
assert result[0].resource_id == "test-rest-api" assert result[0].resource_id == "test-rest-api"
assert (
result[0].resource_arn
== f"arn:{current_audit_info.audited_partition}:apigateway:{AWS_REGION}::/apis/{rest_api['id']}/stages/test"
)
@mock_apigateway @mock_apigateway
def test_apigateway_one_stage_with_certificate(self): def test_apigateway_one_stage_with_certificate(self):
@@ -128,6 +132,7 @@ class Test_apigateway_client_certificate_enabled:
service_client.rest_apis[0].stages.append( service_client.rest_apis[0].stages.append(
Stage( Stage(
"test", "test",
f"arn:{current_audit_info.audited_partition}:apigateway:{AWS_REGION}::/apis/test-rest-api/stages/test",
logging=True, logging=True,
client_certificate=True, client_certificate=True,
waf=True, waf=True,
@@ -144,3 +149,7 @@ class Test_apigateway_client_certificate_enabled:
== f"API Gateway test-rest-api ID {rest_api['id']} in stage test has client certificate enabled." == f"API Gateway test-rest-api ID {rest_api['id']} in stage test has client certificate enabled."
) )
assert result[0].resource_id == "test-rest-api" assert result[0].resource_id == "test-rest-api"
assert (
result[0].resource_arn
== f"arn:{current_audit_info.audited_partition}:apigateway:{AWS_REGION}::/apis/test-rest-api/stages/test"
)

View File

@@ -69,6 +69,10 @@ class Test_apigateway_endpoint_public:
== f"API Gateway test-rest-api ID {rest_api['id']} is private." == f"API Gateway test-rest-api ID {rest_api['id']} is private."
) )
assert result[0].resource_id == "test-rest-api" assert result[0].resource_id == "test-rest-api"
assert (
result[0].resource_arn
== f"arn:{current_audit_info.audited_partition}:apigateway:{AWS_REGION}::/apis/{rest_api['id']}"
)
@mock_apigateway @mock_apigateway
def test_apigateway_one_public_rest_api(self): def test_apigateway_one_public_rest_api(self):
@@ -109,3 +113,7 @@ class Test_apigateway_endpoint_public:
== f"API Gateway test-rest-api ID {rest_api['id']} is internet accesible." == f"API Gateway test-rest-api ID {rest_api['id']} is internet accesible."
) )
assert result[0].resource_id == "test-rest-api" assert result[0].resource_id == "test-rest-api"
assert (
result[0].resource_arn
== f"arn:{current_audit_info.audited_partition}:apigateway:{AWS_REGION}::/apis/{rest_api['id']}"
)

View File

@@ -101,6 +101,10 @@ class Test_apigateway_logging_enabled:
== f"API Gateway test-rest-api ID {rest_api['id']} in stage test has logging enabled." == f"API Gateway test-rest-api ID {rest_api['id']} in stage test has logging enabled."
) )
assert result[0].resource_id == "test-rest-api" assert result[0].resource_id == "test-rest-api"
assert (
result[0].resource_arn
== f"arn:{current_audit_info.audited_partition}:apigateway:{AWS_REGION}::/apis/{rest_api['id']}/stages/test"
)
@mock_apigateway @mock_apigateway
def test_apigateway_one_rest_api_without_logging(self): def test_apigateway_one_rest_api_without_logging(self):
@@ -164,3 +168,7 @@ class Test_apigateway_logging_enabled:
== f"API Gateway test-rest-api ID {rest_api['id']} in stage test has logging disabled." == f"API Gateway test-rest-api ID {rest_api['id']} in stage test has logging disabled."
) )
assert result[0].resource_id == "test-rest-api" assert result[0].resource_id == "test-rest-api"
assert (
result[0].resource_arn
== f"arn:{current_audit_info.audited_partition}:apigateway:{AWS_REGION}::/apis/{rest_api['id']}/stages/test"
)

View File

@@ -107,6 +107,10 @@ class Test_apigateway_waf_acl_attached:
== f"API Gateway test-rest-api ID {rest_api['id']} in stage test has {waf_arn} WAF ACL attached." == f"API Gateway test-rest-api ID {rest_api['id']} in stage test has {waf_arn} WAF ACL attached."
) )
assert result[0].resource_id == "test-rest-api" assert result[0].resource_id == "test-rest-api"
assert (
result[0].resource_arn
== f"arn:{current_audit_info.audited_partition}:apigateway:{AWS_REGION}::/apis/{rest_api['id']}/stages/test"
)
@mock_apigateway @mock_apigateway
def test_apigateway_one_rest_api_without_waf(self): def test_apigateway_one_rest_api_without_waf(self):
@@ -170,3 +174,7 @@ class Test_apigateway_waf_acl_attached:
== f"API Gateway test-rest-api ID {rest_api['id']} in stage test has not WAF ACL attached." == f"API Gateway test-rest-api ID {rest_api['id']} in stage test has not WAF ACL attached."
) )
assert result[0].resource_id == "test-rest-api" assert result[0].resource_id == "test-rest-api"
assert (
result[0].resource_arn
== f"arn:{current_audit_info.audited_partition}:apigateway:{AWS_REGION}::/apis/{rest_api['id']}/stages/test"
)