mirror of
https://github.com/ghndrx/prowler.git
synced 2026-02-10 14:55:00 +00:00
feat(apigateway): Service and checks for APIGW v1 and v2 (#1415)
This commit is contained in:
0
providers/aws/services/apigateway/__init__.py
Normal file
0
providers/aws/services/apigateway/__init__.py
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "apigateway_authorizers_enabled",
|
||||
"CheckTitle": "Check if API Gateway has configured authorizers.",
|
||||
"CheckType": ["IAM"],
|
||||
"ServiceName": "apigateway",
|
||||
"SubServiceName": "rest_api",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsApiGatewayRestApi",
|
||||
"Description": "Check if API Gateway has configured authorizers.",
|
||||
"Risk": "If no authorizer is enabled anyone can use the service.",
|
||||
"RelatedUrl": "",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "https://docs.bridgecrew.io/docs/public_6-api-gateway-authorizer-set#cloudformation",
|
||||
"Other": "",
|
||||
"Terraform": "https://docs.bridgecrew.io/docs/public_6-api-gateway-authorizer-set#terraform"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Implement Amazon Cognito or a Lambda function to control access to your API.",
|
||||
"Url": "https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.apigateway.apigateway_client import apigateway_client
|
||||
|
||||
|
||||
class apigateway_authorizers_enabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for rest_api in apigateway_client.rest_apis:
|
||||
report = Check_Report(self.metadata)
|
||||
report.region = rest_api.region
|
||||
if rest_api.authorizer:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"API Gateway {rest_api.name} ID {rest_api.id} has authorizer configured."
|
||||
report.resource_id = rest_api.name
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
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)
|
||||
|
||||
return findings
|
||||
@@ -0,0 +1,119 @@
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_apigateway, mock_iam, mock_lambda
|
||||
from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_apigateway_authorizers_enabled:
|
||||
@mock_apigateway
|
||||
def test_apigateway_no_rest_apis(self):
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.apigateway.apigateway_service import APIGateway
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.apigateway.apigateway_authorizers_enabled.apigateway_authorizers_enabled.apigateway_client",
|
||||
new=APIGateway(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.apigateway.apigateway_authorizers_enabled.apigateway_authorizers_enabled import (
|
||||
apigateway_authorizers_enabled,
|
||||
)
|
||||
|
||||
check = apigateway_authorizers_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
@mock_apigateway
|
||||
@mock_iam
|
||||
@mock_lambda
|
||||
def test_apigateway_one_rest_api_with_lambda_authorizer(self):
|
||||
# Create APIGateway Mocked Resources
|
||||
apigateway_client = client("apigateway", region_name=AWS_REGION)
|
||||
lambda_client = client("lambda", region_name=AWS_REGION)
|
||||
iam_client = client("iam")
|
||||
# Create APIGateway Rest API
|
||||
role_arn = iam_client.create_role(
|
||||
RoleName="my-role",
|
||||
AssumeRolePolicyDocument="some policy",
|
||||
)["Role"]["Arn"]
|
||||
rest_api = apigateway_client.create_rest_api(
|
||||
name="test-rest-api",
|
||||
)
|
||||
authorizer = lambda_client.create_function(
|
||||
FunctionName="lambda-authorizer",
|
||||
Runtime="python3.7",
|
||||
Role=role_arn,
|
||||
Handler="lambda_function.lambda_handler",
|
||||
Code={
|
||||
"ImageUri": "123456789012.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest"
|
||||
},
|
||||
)
|
||||
apigateway_client.create_authorizer(
|
||||
name="test",
|
||||
restApiId=rest_api["id"],
|
||||
type="TOKEN",
|
||||
authorizerUri=f"arn:aws:apigateway:{apigateway_client.meta.region_name}:lambda:path/2015-03-31/functions/arn:aws:lambda:{apigateway_client.meta.region_name}:{ACCOUNT_ID}:function:{authorizer['FunctionName']}/invocations",
|
||||
)
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.apigateway.apigateway_service import APIGateway
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.apigateway.apigateway_authorizers_enabled.apigateway_authorizers_enabled.apigateway_client",
|
||||
new=APIGateway(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.apigateway.apigateway_authorizers_enabled.apigateway_authorizers_enabled import (
|
||||
apigateway_authorizers_enabled,
|
||||
)
|
||||
|
||||
check = apigateway_authorizers_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert result[0].status == "PASS"
|
||||
assert len(result) == 1
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"API Gateway test-rest-api ID {rest_api['id']} has authorizer configured."
|
||||
)
|
||||
assert result[0].resource_id == "test-rest-api"
|
||||
|
||||
@mock_apigateway
|
||||
def test_apigateway_one_rest_api_without_lambda_authorizer(self):
|
||||
# Create APIGateway Mocked Resources
|
||||
apigateway_client = client("apigateway", region_name=AWS_REGION)
|
||||
# Create APIGateway Rest API
|
||||
rest_api = apigateway_client.create_rest_api(
|
||||
name="test-rest-api",
|
||||
)
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.apigateway.apigateway_service import APIGateway
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.apigateway.apigateway_authorizers_enabled.apigateway_authorizers_enabled.apigateway_client",
|
||||
new=APIGateway(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.apigateway.apigateway_authorizers_enabled.apigateway_authorizers_enabled import (
|
||||
apigateway_authorizers_enabled,
|
||||
)
|
||||
|
||||
check = apigateway_authorizers_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert result[0].status == "FAIL"
|
||||
assert len(result) == 1
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"API Gateway test-rest-api ID {rest_api['id']} has not authorizer configured."
|
||||
)
|
||||
assert result[0].resource_id == "test-rest-api"
|
||||
4
providers/aws/services/apigateway/apigateway_client.py
Normal file
4
providers/aws/services/apigateway/apigateway_client.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.apigateway.apigateway_service import APIGateway
|
||||
|
||||
apigateway_client = APIGateway(current_audit_info)
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "apigateway_client_certificate_enabled",
|
||||
"CheckTitle": "Check if API Gateway has client certificate enabled to access your backend endpoint.",
|
||||
"CheckType": ["Data Protection"],
|
||||
"ServiceName": "apigateway",
|
||||
"SubServiceName": "rest_api",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsApiGatewayRestApi",
|
||||
"Description": "Check if API Gateway has client certificate enabled to access your backend endpoint.",
|
||||
"Risk": "Possible man in the middle attacks and other similar risks.",
|
||||
"RelatedUrl": "",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enable client certificate. Mutual TLS is recommended and commonly used for business-to-business (B2B) applications. It iss used in standards such as Open Banking. API Gateway now provides integrated mutual TLS authentication at no additional cost.",
|
||||
"Url": "https://aws.amazon.com/blogs/compute/introducing-mutual-tls-authentication-for-amazon-api-gateway/"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.apigateway.apigateway_client import apigateway_client
|
||||
|
||||
|
||||
class apigateway_client_certificate_enabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for rest_api in apigateway_client.rest_apis:
|
||||
for stage in rest_api.stages:
|
||||
report = Check_Report(self.metadata)
|
||||
if stage.client_certificate:
|
||||
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.resource_id = rest_api.name
|
||||
report.region = rest_api.region
|
||||
else:
|
||||
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.resource_id = rest_api.name
|
||||
report.region = rest_api.region
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -0,0 +1,140 @@
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_apigateway
|
||||
|
||||
from providers.aws.services.apigateway.apigateway_service import Stage
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_apigateway_client_certificate_enabled:
|
||||
@mock_apigateway
|
||||
def test_apigateway_no_stages(self):
|
||||
# Create APIGateway Mocked Resources
|
||||
apigateway_client = client("apigateway", region_name=AWS_REGION)
|
||||
# Create APIGateway Rest API
|
||||
apigateway_client.create_rest_api(
|
||||
name="test-rest-api",
|
||||
)
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.apigateway.apigateway_service import APIGateway
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.apigateway.apigateway_client_certificate_enabled.apigateway_client_certificate_enabled.apigateway_client",
|
||||
new=APIGateway(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.apigateway.apigateway_client_certificate_enabled.apigateway_client_certificate_enabled import (
|
||||
apigateway_client_certificate_enabled,
|
||||
)
|
||||
|
||||
check = apigateway_client_certificate_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
@mock_apigateway
|
||||
def test_apigateway_one_stage_without_certificate(self):
|
||||
# Create APIGateway Mocked Resources
|
||||
apigateway_client = client("apigateway", region_name=AWS_REGION)
|
||||
# Create APIGateway Deployment Stage
|
||||
rest_api = apigateway_client.create_rest_api(
|
||||
name="test-rest-api",
|
||||
)
|
||||
# Get the rest api's root id
|
||||
root_resource_id = apigateway_client.get_resources(restApiId=rest_api["id"])[
|
||||
"items"
|
||||
][0]["id"]
|
||||
resource = apigateway_client.create_resource(
|
||||
restApiId=rest_api["id"],
|
||||
parentId=root_resource_id,
|
||||
pathPart="test-path",
|
||||
)
|
||||
apigateway_client.put_method(
|
||||
restApiId=rest_api["id"],
|
||||
resourceId=resource["id"],
|
||||
httpMethod="GET",
|
||||
authorizationType="NONE",
|
||||
)
|
||||
apigateway_client.put_integration(
|
||||
restApiId=rest_api["id"],
|
||||
resourceId=resource["id"],
|
||||
httpMethod="GET",
|
||||
type="HTTP",
|
||||
integrationHttpMethod="POST",
|
||||
uri="http://test.com",
|
||||
)
|
||||
apigateway_client.create_deployment(
|
||||
restApiId=rest_api["id"],
|
||||
stageName="test",
|
||||
)
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.apigateway.apigateway_service import APIGateway
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.apigateway.apigateway_client_certificate_enabled.apigateway_client_certificate_enabled.apigateway_client",
|
||||
new=APIGateway(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.apigateway.apigateway_client_certificate_enabled.apigateway_client_certificate_enabled import (
|
||||
apigateway_client_certificate_enabled,
|
||||
)
|
||||
|
||||
check = apigateway_client_certificate_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== 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"
|
||||
|
||||
@mock_apigateway
|
||||
def test_apigateway_one_stage_with_certificate(self):
|
||||
|
||||
# Create APIGateway Mocked Resources
|
||||
apigateway_client = client("apigateway", region_name=AWS_REGION)
|
||||
# Create APIGateway Deployment Stage
|
||||
rest_api = apigateway_client.create_rest_api(
|
||||
name="test-rest-api",
|
||||
)
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.apigateway.apigateway_service import APIGateway
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.apigateway.apigateway_client_certificate_enabled.apigateway_client_certificate_enabled.apigateway_client",
|
||||
new=APIGateway(current_audit_info),
|
||||
) as service_client:
|
||||
# Test Check
|
||||
from providers.aws.services.apigateway.apigateway_client_certificate_enabled.apigateway_client_certificate_enabled import (
|
||||
apigateway_client_certificate_enabled,
|
||||
)
|
||||
|
||||
service_client.rest_apis[0].stages.append(
|
||||
Stage(
|
||||
"test",
|
||||
logging=True,
|
||||
client_certificate=True,
|
||||
waf=True,
|
||||
)
|
||||
)
|
||||
|
||||
check = apigateway_client_certificate_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== 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"
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "apigateway_endpoint_public",
|
||||
"CheckTitle": "Check if API Gateway endpoint is public or private.",
|
||||
"CheckType": ["Infrastructure Security"],
|
||||
"ServiceName": "apigateway",
|
||||
"SubServiceName": "rest_api",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsApiGatewayRestApi",
|
||||
"Description": "Check if API Gateway endpoint is public or private.",
|
||||
"Risk": "If accessible from internet without restrictions opens up attack / abuse surface for any malicious user.",
|
||||
"RelatedUrl": "",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Verify that any public Api Gateway is protected and audited. Detective controls for common risks should be implemented.",
|
||||
"Url": "https://d1.awsstatic.com/whitepapers/api-gateway-security.pdf?svrd_sip6"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.apigateway.apigateway_client import apigateway_client
|
||||
|
||||
|
||||
class apigateway_endpoint_public(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for rest_api in apigateway_client.rest_apis:
|
||||
report = Check_Report(self.metadata)
|
||||
report.region = rest_api.region
|
||||
if rest_api.public_endpoint:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"API Gateway {rest_api.name} ID {rest_api.id} is internet accesible."
|
||||
report.resource_id = rest_api.name
|
||||
else:
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"API Gateway {rest_api.name} ID {rest_api.id} is private."
|
||||
)
|
||||
report.resource_id = rest_api.name
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -0,0 +1,105 @@
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_apigateway
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_apigateway_endpoint_public:
|
||||
@mock_apigateway
|
||||
def test_apigateway_no_rest_apis(self):
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.apigateway.apigateway_service import APIGateway
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.apigateway.apigateway_endpoint_public.apigateway_endpoint_public.apigateway_client",
|
||||
new=APIGateway(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.apigateway.apigateway_endpoint_public.apigateway_endpoint_public import (
|
||||
apigateway_endpoint_public,
|
||||
)
|
||||
|
||||
check = apigateway_endpoint_public()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
@mock_apigateway
|
||||
def test_apigateway_one_private_rest_api(self):
|
||||
# Create APIGateway Mocked Resources
|
||||
apigateway_client = client("apigateway", region_name=AWS_REGION)
|
||||
# Create APIGateway Deployment Stage
|
||||
rest_api = apigateway_client.create_rest_api(
|
||||
name="test-rest-api",
|
||||
endpointConfiguration={
|
||||
"types": [
|
||||
"PRIVATE",
|
||||
]
|
||||
},
|
||||
)
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.apigateway.apigateway_service import APIGateway
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.apigateway.apigateway_endpoint_public.apigateway_endpoint_public.apigateway_client",
|
||||
new=APIGateway(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.apigateway.apigateway_endpoint_public.apigateway_endpoint_public import (
|
||||
apigateway_endpoint_public,
|
||||
)
|
||||
|
||||
check = apigateway_endpoint_public()
|
||||
result = check.execute()
|
||||
|
||||
assert result[0].status == "PASS"
|
||||
assert len(result) == 1
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"API Gateway test-rest-api ID {rest_api['id']} is private."
|
||||
)
|
||||
assert result[0].resource_id == "test-rest-api"
|
||||
|
||||
@mock_apigateway
|
||||
def test_apigateway_one_public_rest_api(self):
|
||||
# Create APIGateway Mocked Resources
|
||||
apigateway_client = client("apigateway", region_name=AWS_REGION)
|
||||
# Create APIGateway Deployment Stage
|
||||
rest_api = apigateway_client.create_rest_api(
|
||||
name="test-rest-api",
|
||||
endpointConfiguration={
|
||||
"types": [
|
||||
"EDGE",
|
||||
]
|
||||
},
|
||||
)
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.apigateway.apigateway_service import APIGateway
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.apigateway.apigateway_endpoint_public.apigateway_endpoint_public.apigateway_client",
|
||||
new=APIGateway(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.apigateway.apigateway_endpoint_public.apigateway_endpoint_public import (
|
||||
apigateway_endpoint_public,
|
||||
)
|
||||
|
||||
check = apigateway_endpoint_public()
|
||||
result = check.execute()
|
||||
|
||||
assert result[0].status == "FAIL"
|
||||
assert len(result) == 1
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"API Gateway test-rest-api ID {rest_api['id']} is internet accesible."
|
||||
)
|
||||
assert result[0].resource_id == "test-rest-api"
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "apigateway_logging_enabled",
|
||||
"CheckTitle": "Check if API Gateway has logging enabled.",
|
||||
"CheckType": ["Logging and Monitoring"],
|
||||
"ServiceName": "apigateway",
|
||||
"SubServiceName": "rest_api",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsApiGatewayRestApi",
|
||||
"Description": "Check if API Gateway 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.",
|
||||
"RelatedUrl": "",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": "https://docs.bridgecrew.io/docs/ensure-api-gateway-stage-have-logging-level-defined-as-appropiate#terraform"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Monitoring is an important part of maintaining the reliability, availability and performance of API Gateway and your AWS solutions. You should collect monitoring data from all of the parts of your AWS solution. CloudTrail provides a record of actions taken by a user, role, or an AWS service in API Gateway. Using the information collected by CloudTrail, you can determine the request that was made to API Gateway, the IP address from which the request was made, who made the request, etc.",
|
||||
"Url": "https://docs.aws.amazon.com/apigateway/latest/developerguide/security-monitoring.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.apigateway.apigateway_client import apigateway_client
|
||||
|
||||
|
||||
class apigateway_logging_enabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for rest_api in apigateway_client.rest_apis:
|
||||
report = Check_Report(self.metadata)
|
||||
report.region = rest_api.region
|
||||
for stage in rest_api.stages:
|
||||
if stage.logging:
|
||||
report.status = "PASS"
|
||||
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:
|
||||
report.status = "FAIL"
|
||||
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)
|
||||
|
||||
return findings
|
||||
@@ -0,0 +1,160 @@
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_apigateway
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_apigateway_logging_enabled:
|
||||
@mock_apigateway
|
||||
def test_apigateway_no_rest_apis(self):
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.apigateway.apigateway_service import APIGateway
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.apigateway.apigateway_logging_enabled.apigateway_logging_enabled.apigateway_client",
|
||||
new=APIGateway(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.apigateway.apigateway_logging_enabled.apigateway_logging_enabled import (
|
||||
apigateway_logging_enabled,
|
||||
)
|
||||
|
||||
check = apigateway_logging_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
@mock_apigateway
|
||||
def test_apigateway_one_rest_api_with_logging(self):
|
||||
# Create APIGateway Mocked Resources
|
||||
apigateway_client = client("apigateway", region_name=AWS_REGION)
|
||||
rest_api = apigateway_client.create_rest_api(
|
||||
name="test-rest-api",
|
||||
)
|
||||
# Get the rest api's root id
|
||||
root_resource_id = apigateway_client.get_resources(restApiId=rest_api["id"])[
|
||||
"items"
|
||||
][0]["id"]
|
||||
resource = apigateway_client.create_resource(
|
||||
restApiId=rest_api["id"],
|
||||
parentId=root_resource_id,
|
||||
pathPart="test-path",
|
||||
)
|
||||
apigateway_client.put_method(
|
||||
restApiId=rest_api["id"],
|
||||
resourceId=resource["id"],
|
||||
httpMethod="GET",
|
||||
authorizationType="NONE",
|
||||
)
|
||||
apigateway_client.put_integration(
|
||||
restApiId=rest_api["id"],
|
||||
resourceId=resource["id"],
|
||||
httpMethod="GET",
|
||||
type="HTTP",
|
||||
integrationHttpMethod="POST",
|
||||
uri="http://test.com",
|
||||
)
|
||||
apigateway_client.create_deployment(
|
||||
restApiId=rest_api["id"],
|
||||
stageName="test",
|
||||
)
|
||||
apigateway_client.update_stage(
|
||||
restApiId=rest_api["id"],
|
||||
stageName="test",
|
||||
patchOperations=[
|
||||
{
|
||||
"op": "replace",
|
||||
"path": "/*/*/logging/loglevel",
|
||||
"value": "INFO",
|
||||
},
|
||||
],
|
||||
)
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.apigateway.apigateway_service import APIGateway
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.apigateway.apigateway_logging_enabled.apigateway_logging_enabled.apigateway_client",
|
||||
new=APIGateway(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.apigateway.apigateway_logging_enabled.apigateway_logging_enabled import (
|
||||
apigateway_logging_enabled,
|
||||
)
|
||||
|
||||
check = apigateway_logging_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert result[0].status == "PASS"
|
||||
assert len(result) == 1
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"API Gateway test-rest-api ID {rest_api['id']} in stage test has logging enabled."
|
||||
)
|
||||
assert result[0].resource_id == "test-rest-api"
|
||||
|
||||
@mock_apigateway
|
||||
def test_apigateway_one_rest_api_without_logging(self):
|
||||
# Create APIGateway Mocked Resources
|
||||
apigateway_client = client("apigateway", region_name=AWS_REGION)
|
||||
# Create APIGateway Rest API
|
||||
rest_api = apigateway_client.create_rest_api(
|
||||
name="test-rest-api",
|
||||
)
|
||||
# Get the rest api's root id
|
||||
root_resource_id = apigateway_client.get_resources(restApiId=rest_api["id"])[
|
||||
"items"
|
||||
][0]["id"]
|
||||
resource = apigateway_client.create_resource(
|
||||
restApiId=rest_api["id"],
|
||||
parentId=root_resource_id,
|
||||
pathPart="test-path",
|
||||
)
|
||||
apigateway_client.put_method(
|
||||
restApiId=rest_api["id"],
|
||||
resourceId=resource["id"],
|
||||
httpMethod="GET",
|
||||
authorizationType="NONE",
|
||||
)
|
||||
apigateway_client.put_integration(
|
||||
restApiId=rest_api["id"],
|
||||
resourceId=resource["id"],
|
||||
httpMethod="GET",
|
||||
type="HTTP",
|
||||
integrationHttpMethod="POST",
|
||||
uri="http://test.com",
|
||||
)
|
||||
apigateway_client.create_deployment(
|
||||
restApiId=rest_api["id"],
|
||||
stageName="test",
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.apigateway.apigateway_service import APIGateway
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.apigateway.apigateway_logging_enabled.apigateway_logging_enabled.apigateway_client",
|
||||
new=APIGateway(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.apigateway.apigateway_logging_enabled.apigateway_logging_enabled import (
|
||||
apigateway_logging_enabled,
|
||||
)
|
||||
|
||||
check = apigateway_logging_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert result[0].status == "FAIL"
|
||||
assert len(result) == 1
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"API Gateway test-rest-api ID {rest_api['id']} in stage test has logging disabled."
|
||||
)
|
||||
assert result[0].resource_id == "test-rest-api"
|
||||
144
providers/aws/services/apigateway/apigateway_service.py
Normal file
144
providers/aws/services/apigateway/apigateway_service.py
Normal file
@@ -0,0 +1,144 @@
|
||||
import threading
|
||||
from dataclasses import dataclass
|
||||
|
||||
from lib.logger import logger
|
||||
from providers.aws.aws_provider import generate_regional_clients
|
||||
|
||||
|
||||
################## APIGateway
|
||||
class APIGateway:
|
||||
def __init__(self, audit_info):
|
||||
self.service = "apigateway"
|
||||
self.session = audit_info.audit_session
|
||||
self.audited_account = audit_info.audited_account
|
||||
self.regional_clients = generate_regional_clients(self.service, audit_info)
|
||||
self.rest_apis = []
|
||||
self.__threading_call__(self.__get_rest_apis__)
|
||||
self.__get_authorizers__()
|
||||
self.__get_rest_api__()
|
||||
self.__get_stages__()
|
||||
|
||||
def __get_session__(self):
|
||||
return self.session
|
||||
|
||||
def __threading_call__(self, call):
|
||||
threads = []
|
||||
for regional_client in self.regional_clients.values():
|
||||
threads.append(threading.Thread(target=call, args=(regional_client,)))
|
||||
for t in threads:
|
||||
t.start()
|
||||
for t in threads:
|
||||
t.join()
|
||||
|
||||
def __get_rest_apis__(self, regional_client):
|
||||
logger.info("APIGateway - Getting Rest APIs...")
|
||||
try:
|
||||
get_rest_apis_paginator = regional_client.get_paginator("get_rest_apis")
|
||||
for page in get_rest_apis_paginator.paginate():
|
||||
for apigw in page["items"]:
|
||||
self.rest_apis.append(
|
||||
RestAPI(
|
||||
apigw["id"],
|
||||
regional_client.region,
|
||||
apigw["name"],
|
||||
)
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}: {error}"
|
||||
)
|
||||
|
||||
def __get_authorizers__(self):
|
||||
logger.info("APIGateway - Getting Rest APIs authorizer...")
|
||||
try:
|
||||
for rest_api in self.rest_apis:
|
||||
regional_client = self.regional_clients[rest_api.region]
|
||||
authorizers = regional_client.get_authorizers(restApiId=rest_api.id)[
|
||||
"items"
|
||||
]
|
||||
if authorizers:
|
||||
rest_api.authorizer = True
|
||||
except Exception as error:
|
||||
logger.error(f"{error.__class__.__name__}: {error}")
|
||||
|
||||
def __get_rest_api__(self):
|
||||
logger.info("APIGateway - Describing Rest API...")
|
||||
try:
|
||||
for rest_api in self.rest_apis:
|
||||
regional_client = self.regional_clients[rest_api.region]
|
||||
rest_api_info = regional_client.get_rest_api(restApiId=rest_api.id)
|
||||
if rest_api_info["endpointConfiguration"]["types"] == ["PRIVATE"]:
|
||||
rest_api.public_endpoint = False
|
||||
except Exception as error:
|
||||
logger.error(f"{error.__class__.__name__}: {error}")
|
||||
|
||||
def __get_stages__(self):
|
||||
logger.info("APIGateway - Getting stages for Rest APIs...")
|
||||
try:
|
||||
for rest_api in self.rest_apis:
|
||||
regional_client = self.regional_clients[rest_api.region]
|
||||
stages = regional_client.get_stages(restApiId=rest_api.id)
|
||||
for stage in stages["item"]:
|
||||
waf = None
|
||||
logging = False
|
||||
client_certificate = False
|
||||
if "webAclArn" in stage:
|
||||
waf = stage["webAclArn"]
|
||||
if "methodSettings" in stage:
|
||||
if stage["methodSettings"]:
|
||||
logging = True
|
||||
if "clientCertificateId" in stage:
|
||||
client_certificate = True
|
||||
rest_api.stages.append(
|
||||
Stage(
|
||||
stage["stageName"],
|
||||
logging,
|
||||
client_certificate,
|
||||
waf,
|
||||
)
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(f"{error.__class__.__name__}: {error}")
|
||||
|
||||
|
||||
@dataclass
|
||||
class Stage:
|
||||
name: str
|
||||
logging: bool
|
||||
client_certificate: bool
|
||||
waf: str
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name,
|
||||
logging,
|
||||
client_certificate,
|
||||
waf,
|
||||
):
|
||||
self.name = name
|
||||
self.logging = logging
|
||||
self.client_certificate = client_certificate
|
||||
self.waf = waf
|
||||
|
||||
|
||||
@dataclass
|
||||
class RestAPI:
|
||||
id: str
|
||||
region: str
|
||||
name: str
|
||||
authorizer: bool
|
||||
public_endpoint: bool
|
||||
stages: list[Stage]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id,
|
||||
region,
|
||||
name,
|
||||
):
|
||||
self.id = id
|
||||
self.region = region
|
||||
self.name = name
|
||||
self.authorizer = False
|
||||
self.public_endpoint = True
|
||||
self.stages = []
|
||||
165
providers/aws/services/apigateway/apigateway_service_test.py
Normal file
165
providers/aws/services/apigateway/apigateway_service_test.py
Normal file
@@ -0,0 +1,165 @@
|
||||
from boto3 import client, session
|
||||
from moto import mock_apigateway
|
||||
|
||||
from providers.aws.lib.audit_info.models import AWS_Audit_Info
|
||||
from providers.aws.services.apigateway.apigateway_service import APIGateway
|
||||
|
||||
AWS_ACCOUNT_NUMBER = 123456789012
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_APIGateway_Service:
|
||||
# Mocked Audit Info
|
||||
def set_mocked_audit_info(self):
|
||||
audit_info = AWS_Audit_Info(
|
||||
original_session=None,
|
||||
audit_session=session.Session(
|
||||
profile_name=None,
|
||||
botocore_session=None,
|
||||
),
|
||||
audited_account=AWS_ACCOUNT_NUMBER,
|
||||
audited_user_id=None,
|
||||
audited_partition="aws",
|
||||
audited_identity_arn=None,
|
||||
profile=None,
|
||||
profile_region=None,
|
||||
credentials=None,
|
||||
assumed_role_info=None,
|
||||
audited_regions=None,
|
||||
organizations_metadata=None,
|
||||
)
|
||||
return audit_info
|
||||
|
||||
# Test APIGateway Service
|
||||
@mock_apigateway
|
||||
def test_service(self):
|
||||
# APIGateway client for this test class
|
||||
audit_info = self.set_mocked_audit_info()
|
||||
apigateway = APIGateway(audit_info)
|
||||
assert apigateway.service == "apigateway"
|
||||
|
||||
# Test APIGateway Client
|
||||
@mock_apigateway
|
||||
def test_client(self):
|
||||
# APIGateway client for this test class
|
||||
audit_info = self.set_mocked_audit_info()
|
||||
apigateway = APIGateway(audit_info)
|
||||
for client in apigateway.regional_clients.values():
|
||||
assert client.__class__.__name__ == "APIGateway"
|
||||
|
||||
# Test APIGateway Session
|
||||
@mock_apigateway
|
||||
def test__get_session__(self):
|
||||
# APIGateway client for this test class
|
||||
audit_info = self.set_mocked_audit_info()
|
||||
apigateway = APIGateway(audit_info)
|
||||
assert apigateway.session.__class__.__name__ == "Session"
|
||||
|
||||
# Test APIGateway Session
|
||||
@mock_apigateway
|
||||
def test_audited_account(self):
|
||||
# APIGateway client for this test class
|
||||
audit_info = self.set_mocked_audit_info()
|
||||
apigateway = APIGateway(audit_info)
|
||||
assert apigateway.audited_account == AWS_ACCOUNT_NUMBER
|
||||
|
||||
# Test APIGateway Get Rest APIs
|
||||
@mock_apigateway
|
||||
def test__get_rest_apis__(self):
|
||||
# Generate APIGateway Client
|
||||
apigateway_client = client("apigateway", region_name=AWS_REGION)
|
||||
# Create APIGateway Rest API
|
||||
apigateway_client.create_rest_api(
|
||||
name="test-rest-api",
|
||||
)
|
||||
# APIGateway client for this test class
|
||||
audit_info = self.set_mocked_audit_info()
|
||||
apigateway = APIGateway(audit_info)
|
||||
assert len(apigateway.rest_apis) == len(
|
||||
apigateway_client.get_rest_apis()["items"]
|
||||
)
|
||||
|
||||
# Test APIGateway Get Authorizers
|
||||
@mock_apigateway
|
||||
def test__get_authorizers__(self):
|
||||
# Generate APIGateway Client
|
||||
apigateway_client = client("apigateway", region_name=AWS_REGION)
|
||||
# Create APIGateway Rest API
|
||||
rest_api = apigateway_client.create_rest_api(
|
||||
name="test-rest-api",
|
||||
)
|
||||
# Create authorizer
|
||||
apigateway_client.create_authorizer(
|
||||
name="test-authorizer",
|
||||
restApiId=rest_api["id"],
|
||||
type="TOKEN",
|
||||
)
|
||||
# APIGateway client for this test class
|
||||
audit_info = self.set_mocked_audit_info()
|
||||
apigateway = APIGateway(audit_info)
|
||||
assert apigateway.rest_apis[0].authorizer == True
|
||||
|
||||
# Test APIGateway Get Rest API
|
||||
@mock_apigateway
|
||||
def test__get_rest_api__(self):
|
||||
# Generate APIGateway Client
|
||||
apigateway_client = client("apigateway", region_name=AWS_REGION)
|
||||
# Create private APIGateway Rest API
|
||||
apigateway_client.create_rest_api(
|
||||
name="test-rest-api", endpointConfiguration={"types": ["PRIVATE"]}
|
||||
)
|
||||
# APIGateway client for this test class
|
||||
audit_info = self.set_mocked_audit_info()
|
||||
apigateway = APIGateway(audit_info)
|
||||
assert apigateway.rest_apis[0].public_endpoint == False
|
||||
|
||||
# Test APIGateway Get Stages
|
||||
@mock_apigateway
|
||||
def test__get_stages__(self):
|
||||
# Generate APIGateway Client
|
||||
apigateway_client = client("apigateway", region_name=AWS_REGION)
|
||||
# Create APIGateway Rest API and a deployment stage
|
||||
rest_api = apigateway_client.create_rest_api(
|
||||
name="test-rest-api",
|
||||
)
|
||||
# Get the rest api's root id
|
||||
root_resource_id = apigateway_client.get_resources(restApiId=rest_api["id"])[
|
||||
"items"
|
||||
][0]["id"]
|
||||
resource = apigateway_client.create_resource(
|
||||
restApiId=rest_api["id"],
|
||||
parentId=root_resource_id,
|
||||
pathPart="test-path",
|
||||
)
|
||||
apigateway_client.put_method(
|
||||
restApiId=rest_api["id"],
|
||||
resourceId=resource["id"],
|
||||
httpMethod="GET",
|
||||
authorizationType="NONE",
|
||||
)
|
||||
apigateway_client.put_integration(
|
||||
restApiId=rest_api["id"],
|
||||
resourceId=resource["id"],
|
||||
httpMethod="GET",
|
||||
type="HTTP",
|
||||
integrationHttpMethod="POST",
|
||||
uri="http://test.com",
|
||||
)
|
||||
apigateway_client.create_deployment(
|
||||
restApiId=rest_api["id"],
|
||||
stageName="test",
|
||||
)
|
||||
apigateway_client.update_stage(
|
||||
restApiId=rest_api["id"],
|
||||
stageName="test",
|
||||
patchOperations=[
|
||||
{
|
||||
"op": "replace",
|
||||
"path": "/*/*/logging/loglevel",
|
||||
"value": "INFO",
|
||||
},
|
||||
],
|
||||
)
|
||||
audit_info = self.set_mocked_audit_info()
|
||||
apigateway = APIGateway(audit_info)
|
||||
assert apigateway.rest_apis[0].stages[0].logging == True
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "apigateway_waf_acl_attached",
|
||||
"CheckTitle": "Check if API Gateway has a WAF ACL attached.",
|
||||
"CheckType": ["Infrastructure Security"],
|
||||
"ServiceName": "apigateway",
|
||||
"SubServiceName": "rest_api",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsApiGatewayRestApi",
|
||||
"Description": "Check if API Gateway has a WAF ACL attached.",
|
||||
"Risk": "Potential attacks and / or abuse of service, more even for even for internet reachable services.",
|
||||
"RelatedUrl": "",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Use AWS WAF to protect your API Gateway API from common web exploits, such as SQL injection and cross-site scripting (XSS) attacks. These could affect API availability and performance, compromise security or consume excessive resources.",
|
||||
"Url": "https://docs.aws.amazon.com/apigateway/latest/developerguide/security-monitoring.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "",
|
||||
"Compliance": []
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.apigateway.apigateway_client import apigateway_client
|
||||
|
||||
|
||||
class apigateway_waf_acl_attached(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for rest_api in apigateway_client.rest_apis:
|
||||
report = Check_Report(self.metadata)
|
||||
report.region = rest_api.region
|
||||
for stage in rest_api.stages:
|
||||
if stage.waf:
|
||||
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.resource_id = rest_api.name
|
||||
else:
|
||||
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.resource_id = rest_api.name
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -0,0 +1,166 @@
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_apigateway, mock_wafv2
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
|
||||
|
||||
class Test_apigateway_waf_acl_attached:
|
||||
@mock_apigateway
|
||||
def test_apigateway_no_rest_apis(self):
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.apigateway.apigateway_service import APIGateway
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.apigateway.apigateway_waf_acl_attached.apigateway_waf_acl_attached.apigateway_client",
|
||||
new=APIGateway(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.apigateway.apigateway_waf_acl_attached.apigateway_waf_acl_attached import (
|
||||
apigateway_waf_acl_attached,
|
||||
)
|
||||
|
||||
check = apigateway_waf_acl_attached()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
@mock_apigateway
|
||||
@mock_wafv2
|
||||
def test_apigateway_one_rest_api_with_waf(self):
|
||||
# Create APIGateway Mocked Resources
|
||||
apigateway_client = client("apigateway", region_name=AWS_REGION)
|
||||
waf_client = client("wafv2", region_name=AWS_REGION)
|
||||
rest_api = apigateway_client.create_rest_api(
|
||||
name="test-rest-api",
|
||||
)
|
||||
# Get the rest api's root id
|
||||
root_resource_id = apigateway_client.get_resources(restApiId=rest_api["id"])[
|
||||
"items"
|
||||
][0]["id"]
|
||||
resource = apigateway_client.create_resource(
|
||||
restApiId=rest_api["id"],
|
||||
parentId=root_resource_id,
|
||||
pathPart="test-path",
|
||||
)
|
||||
apigateway_client.put_method(
|
||||
restApiId=rest_api["id"],
|
||||
resourceId=resource["id"],
|
||||
httpMethod="GET",
|
||||
authorizationType="NONE",
|
||||
)
|
||||
apigateway_client.put_integration(
|
||||
restApiId=rest_api["id"],
|
||||
resourceId=resource["id"],
|
||||
httpMethod="GET",
|
||||
type="HTTP",
|
||||
integrationHttpMethod="POST",
|
||||
uri="http://test.com",
|
||||
)
|
||||
apigateway_client.create_deployment(
|
||||
restApiId=rest_api["id"],
|
||||
stageName="test",
|
||||
)
|
||||
waf_arn = waf_client.create_web_acl(
|
||||
Name="test",
|
||||
Scope="REGIONAL",
|
||||
DefaultAction={"Allow": {}},
|
||||
VisibilityConfig={
|
||||
"SampledRequestsEnabled": False,
|
||||
"CloudWatchMetricsEnabled": False,
|
||||
"MetricName": "idk",
|
||||
},
|
||||
)["Summary"]["ARN"]
|
||||
waf_client.associate_web_acl(
|
||||
WebACLArn=waf_arn,
|
||||
ResourceArn=f"arn:aws:apigateway:{apigateway_client.meta.region_name}::/restapis/{rest_api['id']}/stages/test",
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.apigateway.apigateway_service import APIGateway
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.apigateway.apigateway_waf_acl_attached.apigateway_waf_acl_attached.apigateway_client",
|
||||
new=APIGateway(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.apigateway.apigateway_waf_acl_attached.apigateway_waf_acl_attached import (
|
||||
apigateway_waf_acl_attached,
|
||||
)
|
||||
|
||||
check = apigateway_waf_acl_attached()
|
||||
result = check.execute()
|
||||
|
||||
assert result[0].status == "PASS"
|
||||
assert len(result) == 1
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== 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"
|
||||
|
||||
@mock_apigateway
|
||||
def test_apigateway_one_rest_api_without_waf(self):
|
||||
# Create APIGateway Mocked Resources
|
||||
apigateway_client = client("apigateway", region_name=AWS_REGION)
|
||||
# Create APIGateway Rest API
|
||||
rest_api = apigateway_client.create_rest_api(
|
||||
name="test-rest-api",
|
||||
)
|
||||
# Get the rest api's root id
|
||||
root_resource_id = apigateway_client.get_resources(restApiId=rest_api["id"])[
|
||||
"items"
|
||||
][0]["id"]
|
||||
resource = apigateway_client.create_resource(
|
||||
restApiId=rest_api["id"],
|
||||
parentId=root_resource_id,
|
||||
pathPart="test-path",
|
||||
)
|
||||
apigateway_client.put_method(
|
||||
restApiId=rest_api["id"],
|
||||
resourceId=resource["id"],
|
||||
httpMethod="GET",
|
||||
authorizationType="NONE",
|
||||
)
|
||||
apigateway_client.put_integration(
|
||||
restApiId=rest_api["id"],
|
||||
resourceId=resource["id"],
|
||||
httpMethod="GET",
|
||||
type="HTTP",
|
||||
integrationHttpMethod="POST",
|
||||
uri="http://test.com",
|
||||
)
|
||||
apigateway_client.create_deployment(
|
||||
restApiId=rest_api["id"],
|
||||
stageName="test",
|
||||
)
|
||||
|
||||
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||
from providers.aws.services.apigateway.apigateway_service import APIGateway
|
||||
|
||||
current_audit_info.audited_partition = "aws"
|
||||
|
||||
with mock.patch(
|
||||
"providers.aws.services.apigateway.apigateway_waf_acl_attached.apigateway_waf_acl_attached.apigateway_client",
|
||||
new=APIGateway(current_audit_info),
|
||||
):
|
||||
# Test Check
|
||||
from providers.aws.services.apigateway.apigateway_waf_acl_attached.apigateway_waf_acl_attached import (
|
||||
apigateway_waf_acl_attached,
|
||||
)
|
||||
|
||||
check = apigateway_waf_acl_attached()
|
||||
result = check.execute()
|
||||
|
||||
assert result[0].status == "FAIL"
|
||||
assert len(result) == 1
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== 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"
|
||||
@@ -1,57 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
|
||||
CHECK_ID_extra7156="7.156"
|
||||
CHECK_TITLE_extra7156="[extra7156] Checks if API Gateway V2 has Access Logging enabled"
|
||||
CHECK_SCORED_extra7156="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra7156="EXTRA"
|
||||
CHECK_SEVERITY_extra7156="Medium"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra7156="AwsApiGatewayV2Api"
|
||||
CHECK_ALTERNATE_check7156="extra7156"
|
||||
CHECK_SERVICENAME_extra7156="apigateway"
|
||||
CHECK_RISK_extra7156="If not enabled the logging of API calls is not possible. This information is important for monitoring API access."
|
||||
CHECK_REMEDIATION_extra7156="Enable Access Logging in the API stage."
|
||||
CHECK_DOC_extra7156="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apigatewayv2-stage-accesslogsettings.html"
|
||||
CHECK_CAF_EPIC_extra7156="Logging and Monitoring"
|
||||
|
||||
extra7156(){
|
||||
|
||||
# "Check if API Gateway V2 has Access Logging enabled "
|
||||
for regx in $REGIONS; do
|
||||
LIST_OF_API_GW=$($AWSCLI apigatewayv2 get-apis $PROFILE_OPT --region $regx --query Items[*].ApiId --output text 2>&1)
|
||||
if [[ $(echo "$LIST_OF_API_GW" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "$regx: Access Denied trying to get APIs" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $LIST_OF_API_GW ]];then
|
||||
for apigwid in $LIST_OF_API_GW;do
|
||||
API_GW_NAME=$($AWSCLI apigatewayv2 get-apis $PROFILE_OPT --region $regx --query "Items[?ApiId==\`$apigwid\`].Name" --output text)
|
||||
CHECK_STAGES_NAME=$($AWSCLI apigatewayv2 get-stages $PROFILE_OPT --region $regx --api-id $apigwid --query "Items[*].StageName" --output text)
|
||||
if [[ $CHECK_STAGES_NAME ]];then
|
||||
for stagename in $CHECK_STAGES_NAME;do
|
||||
CHECK_STAGE_METHOD_LOGGING=$($AWSCLI apigatewayv2 get-stages $PROFILE_OPT --region $regx --api-id $apigwid --query "Items[?StageName == \`$stagename\` ].AccessLogSettings.DestinationArn" --output text)
|
||||
if [[ $CHECK_STAGE_METHOD_LOGGING ]];then
|
||||
textPass "$regx: API Gateway V2 $API_GW_NAME ID: $apigwid with stage: $stagename has access logging enabled to $CHECK_STAGE_METHOD_LOGGING" "$regx" "$API_GW_NAME"
|
||||
else
|
||||
textFail "$regx: API Gateway V2 $API_GW_NAME ID: $apigwid with stage: $stagename has access logging disabled" "$regx" "$API_GW_NAME"
|
||||
fi
|
||||
done
|
||||
else
|
||||
textFail "$regx: No Stage name found for $API_GW_NAME" "$regx" "$API_GW_NAME"
|
||||
fi
|
||||
done
|
||||
else
|
||||
textInfo "$regx: No API Gateway found" "$regx"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
CHECK_ID_extra7157="7.157"
|
||||
CHECK_TITLE_extra7157="[extra7157] Check if API Gateway V2 has configured authorizers"
|
||||
CHECK_SCORED_extra7157="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra7157="EXTRA"
|
||||
CHECK_SEVERITY_extra7157="Medium"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra7157="AwsApiGatewayV2Api"
|
||||
CHECK_ALTERNATE_check746="extra7157"
|
||||
CHECK_SERVICENAME_extra7157="apigateway"
|
||||
CHECK_RISK_extra7157='If no authorizer is enabled anyone can use the service.'
|
||||
CHECK_REMEDIATION_extra7157='Implement JWT or Lambda Function to control access to your API.'
|
||||
CHECK_DOC_extra7157='https://docs.aws.amazon.com/apigatewayv2/latest/api-reference/apis-apiid-authorizers.html'
|
||||
CHECK_CAF_EPIC_extra7157='IAM'
|
||||
|
||||
extra7157(){
|
||||
for regx in $REGIONS; do
|
||||
LIST_OF_API_GW=$($AWSCLI apigatewayv2 get-apis $PROFILE_OPT --region $regx --query "Items[*].ApiId" --output text 2>&1)
|
||||
if [[ $(echo "$LIST_OF_API_GW" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "$regx: Access Denied trying to get APIs" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $LIST_OF_API_GW ]];then
|
||||
for api in $LIST_OF_API_GW; do
|
||||
API_GW_NAME=$($AWSCLI apigatewayv2 get-apis $PROFILE_OPT --region $regx --query "Items[?ApiId==\`$api\`].Name" --output text)
|
||||
AUTHORIZER_CONFIGURED=$($AWSCLI apigatewayv2 --region $regx get-authorizers --api-id $api --query "Items[*].AuthorizerType" --output text)
|
||||
if [[ $AUTHORIZER_CONFIGURED ]]; then
|
||||
textPass "$regx: API Gateway V2 $API_GW_NAME ID $api has authorizer configured" "$regx" "$API_GW_NAME"
|
||||
else
|
||||
textFail "$regx: API Gateway V2 $API_GW_NAME ID $api has no authorizer configured" "$regx" "$API_GW_NAME"
|
||||
fi
|
||||
done
|
||||
else
|
||||
textInfo "$regx: No API Gateways found" "$regx"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
CHECK_ID_extra722="7.22"
|
||||
CHECK_TITLE_extra722="[extra722] Check if API Gateway has logging enabled"
|
||||
CHECK_SCORED_extra722="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra722="EXTRA"
|
||||
CHECK_SEVERITY_extra722="Medium"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra722="AwsApiGatewayRestApi"
|
||||
CHECK_ALTERNATE_check722="extra722"
|
||||
CHECK_SERVICENAME_extra722="apigateway"
|
||||
CHECK_RISK_extra722='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.'
|
||||
CHECK_REMEDIATION_extra722='Monitoring is an important part of maintaining the reliability; availability; and performance of API Gateway and your AWS solutions. You should collect monitoring data from all of the parts of your AWS solution. CloudTrail provides a record of actions taken by a user; role; or an AWS service in API Gateway. Using the information collected by CloudTrail; you can determine the request that was made to API Gateway; the IP address from which the request was made; who made the request; etc.'
|
||||
CHECK_DOC_extra722='https://docs.aws.amazon.com/apigateway/latest/developerguide/security-monitoring.html'
|
||||
CHECK_CAF_EPIC_extra722='Logging and Monitoring'
|
||||
|
||||
extra722(){
|
||||
# "Check if API Gateway has logging enabled "
|
||||
for regx in $REGIONS; do
|
||||
LIST_OF_API_GW=$($AWSCLI apigateway get-rest-apis $PROFILE_OPT --region $regx --query items[*].id --output text 2>&1)
|
||||
if [[ $(echo "$LIST_OF_API_GW" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
|
||||
textInfo "$regx: Access Denied trying to get rest APIs" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $LIST_OF_API_GW ]];then
|
||||
for apigwid in $LIST_OF_API_GW;do
|
||||
API_GW_NAME=$($AWSCLI apigateway get-rest-apis $PROFILE_OPT --region $regx --query "items[?id==\`$apigwid\`].name" --output text)
|
||||
CHECK_STAGES_NAME=$($AWSCLI apigateway get-stages $PROFILE_OPT --region $regx --rest-api-id $apigwid --query "item[*].stageName" --output text)
|
||||
if [[ $CHECK_STAGES_NAME ]];then
|
||||
for stagname in $CHECK_STAGES_NAME;do
|
||||
CHECK_STAGE_METHOD_LOGGING=$($AWSCLI apigateway get-stages $PROFILE_OPT --region $regx --rest-api-id $apigwid --query "item[?stageName == \`$stagname\` ].methodSettings" --output text |awk '{ print $6 }' |egrep 'ERROR|INFO')
|
||||
if [[ $CHECK_STAGE_METHOD_LOGGING ]];then
|
||||
textPass "$regx: API Gateway $API_GW_NAME ID $apigwid in $stagname has logging enabled as $CHECK_STAGE_METHOD_LOGGING" "$regx" "$API_GW_NAME"
|
||||
else
|
||||
textFail "$regx: API Gateway $API_GW_NAME ID $apigwid in $stagname has logging disabled" "$regx" "$API_GW_NAME"
|
||||
fi
|
||||
done
|
||||
else
|
||||
textFail "$regx: No Stage name found for $API_GW_NAME" "$regx" "$API_GW_NAME"
|
||||
fi
|
||||
done
|
||||
else
|
||||
textInfo "$regx: No API Gateway found" "$regx"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
CHECK_ID_extra743="7.43"
|
||||
CHECK_TITLE_extra743="[extra743] Check if API Gateway has client certificate enabled to access your backend endpoint"
|
||||
CHECK_SCORED_extra743="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra743="EXTRA"
|
||||
CHECK_SEVERITY_extra743="Medium"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra743="AwsApiGatewayRestApi"
|
||||
CHECK_ALTERNATE_check743="extra743"
|
||||
CHECK_SERVICENAME_extra743="apigateway"
|
||||
CHECK_RISK_extra743='Possible man in the middle attacks and other similar risks.'
|
||||
CHECK_REMEDIATION_extra743='Enable client certificate. Mutual TLS is recommended and commonly used for business-to-business (B2B) applications. It’s used in standards such as Open Banking. API Gateway now provides integrated mutual TLS authentication at no additional cost.'
|
||||
CHECK_DOC_extra743='https://aws.amazon.com/blogs/compute/introducing-mutual-tls-authentication-for-amazon-api-gateway/'
|
||||
CHECK_CAF_EPIC_extra743='Data Protection'
|
||||
|
||||
extra743(){
|
||||
for regx in $REGIONS; do
|
||||
LIST_OF_REST_APIS=$($AWSCLI $PROFILE_OPT --region $regx apigateway get-rest-apis --query 'items[*].id' --output text 2>&1)
|
||||
if [[ $(echo "$LIST_OF_REST_APIS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "$regx: Access Denied trying to get rest APIs" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $LIST_OF_REST_APIS ]];then
|
||||
for api in $LIST_OF_REST_APIS; do
|
||||
API_GW_NAME=$($AWSCLI apigateway get-rest-apis $PROFILE_OPT --region $regx --query "items[?id==\`$api\`].name" --output text)
|
||||
LIST_OF_STAGES=$($AWSCLI $PROFILE_OPT --region $regx apigateway get-stages --rest-api-id $api --query 'item[*].stageName' --output text)
|
||||
if [[ $LIST_OF_STAGES ]]; then
|
||||
for stage in $LIST_OF_STAGES; do
|
||||
CHECK_CERTIFICATE=$($AWSCLI $PROFILE_OPT --region $regx apigateway get-stages --rest-api-id $api --query "item[?stageName==\`$stage\`].clientCertificateId" --output text)
|
||||
if [[ $CHECK_CERTIFICATE ]]; then
|
||||
textPass "$regx: API Gateway $API_GW_NAME ID $api in $stage has client certificate enabled" "$regx" "$API_GW_NAME"
|
||||
else
|
||||
textFail "$regx: API Gateway $API_GW_NAME ID $api in $stage has not client certificate enabled" "$regx" "$API_GW_NAME"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
done
|
||||
else
|
||||
textInfo "$regx: No API Gateways found" "$regx"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
CHECK_ID_extra744="7.44"
|
||||
CHECK_TITLE_extra744="[extra744] Check if API Gateway has a WAF ACL attached"
|
||||
CHECK_SCORED_extra744="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra744="EXTRA"
|
||||
CHECK_SEVERITY_extra744="Medium"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra744="AwsApiGatewayRestApi"
|
||||
CHECK_ALTERNATE_check744="extra744"
|
||||
CHECK_ASFF_COMPLIANCE_TYPE_extra744="ens-mp.s.2.aws.waf.2"
|
||||
CHECK_SERVICENAME_extra744="apigateway"
|
||||
CHECK_RISK_extra744='Potential attacks and / or abuse of service; more even for even for internet reachable services.'
|
||||
CHECK_REMEDIATION_extra744='Use AWS WAF to protect your API Gateway API from common web exploits; such as SQL injection and cross-site scripting (XSS) attacks. These could affect API availability and performance; compromise security; or consume excessive resources.'
|
||||
CHECK_DOC_extra744='https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-control-access-aws-waf.html'
|
||||
CHECK_CAF_EPIC_extra744='Infrastructure Security'
|
||||
|
||||
extra744(){
|
||||
for regx in $REGIONS; do
|
||||
LIST_OF_REST_APIS=$($AWSCLI $PROFILE_OPT --region $regx apigateway get-rest-apis --query 'items[*].id' --output text 2>&1)
|
||||
if [[ $(echo "$LIST_OF_REST_APIS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "$regx: Access Denied trying to get rest APIs" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $LIST_OF_REST_APIS ]];then
|
||||
for api in $LIST_OF_REST_APIS; do
|
||||
API_GW_NAME=$($AWSCLI apigateway get-rest-apis $PROFILE_OPT --region $regx --query "items[?id==\`$api\`].name" --output text)
|
||||
LIST_OF_STAGES=$($AWSCLI $PROFILE_OPT --region $regx apigateway get-stages --rest-api-id $api --query 'item[*].stageName' --output text)
|
||||
if [[ $LIST_OF_STAGES ]]; then
|
||||
for stage in $LIST_OF_STAGES; do
|
||||
CHECK_WAFACL=$($AWSCLI $PROFILE_OPT --region $regx apigateway get-stages --rest-api-id $api --query "item[?stageName==\`$stage\`].webAclArn" --output text)
|
||||
if [[ $CHECK_WAFACL ]]; then
|
||||
textPass "$regx: API Gateway $API_GW_NAME ID $api in $stage has $CHECK_WAFACL WAF ACL attached" "$regx" "$API_GW_NAME"
|
||||
else
|
||||
textFail "$regx: API Gateway $API_GW_NAME ID $api in $stage has not WAF ACL attached" "$regx" "$API_GW_NAME"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
done
|
||||
else
|
||||
textInfo "$regx: No API Gateways found" "$regx"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
CHECK_ID_extra745="7.45"
|
||||
CHECK_TITLE_extra745="[extra745] Check if API Gateway endpoint is public or private"
|
||||
CHECK_SCORED_extra745="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra745="EXTRA"
|
||||
CHECK_SEVERITY_extra745="Medium"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra745="AwsApiGatewayRestApi"
|
||||
CHECK_ALTERNATE_check745="extra745"
|
||||
CHECK_SERVICENAME_extra745="apigateway"
|
||||
CHECK_RISK_extra745='If accessible from internet without restrictions opens up attack / abuse surface for any malicious user.'
|
||||
CHECK_REMEDIATION_extra745='Verify that any public Api Gateway is protected and audited. Detective controls for common risks should be implemented.'
|
||||
CHECK_DOC_extra745='https://d1.awsstatic.com/whitepapers/api-gateway-security.pdf?svrd_sip6'
|
||||
CHECK_CAF_EPIC_extra745='Infrastructure Security'
|
||||
|
||||
extra745(){
|
||||
for regx in $REGIONS; do
|
||||
LIST_OF_REST_APIS=$($AWSCLI $PROFILE_OPT --region $regx apigateway get-rest-apis --query 'items[*].id' --output text 2>&1)
|
||||
if [[ $(echo "$LIST_OF_REST_APIS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "$regx: Access Denied trying to get rest APIs" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $LIST_OF_REST_APIS ]];then
|
||||
for api in $LIST_OF_REST_APIS; do
|
||||
API_GW_NAME=$($AWSCLI apigateway get-rest-apis $PROFILE_OPT --region $regx --query "items[?id==\`$api\`].name" --output text)
|
||||
ENDPOINT_CONFIG_TYPE=$($AWSCLI $PROFILE_OPT --region $regx apigateway get-rest-api --rest-api-id $api --query endpointConfiguration.types --output text)
|
||||
if [[ $ENDPOINT_CONFIG_TYPE ]]; then
|
||||
case $ENDPOINT_CONFIG_TYPE in
|
||||
PRIVATE )
|
||||
textPass "$regx: API Gateway $API_GW_NAME ID $api is set as $ENDPOINT_CONFIG_TYPE" "$regx" "$API_GW_NAME"
|
||||
;;
|
||||
REGIONAL )
|
||||
textFail "$regx: API Gateway $API_GW_NAME ID $api is internet accesible as $ENDPOINT_CONFIG_TYPE" "$regx" "$API_GW_NAME"
|
||||
;;
|
||||
EDGE )
|
||||
textFail "$regx: API Gateway $API_GW_NAME ID $api is internet accesible as $ENDPOINT_CONFIG_TYPE" "$regx" "$API_GW_NAME"
|
||||
esac
|
||||
fi
|
||||
done
|
||||
else
|
||||
textInfo "$regx: No API Gateways found" "$regx"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
CHECK_ID_extra746="7.46"
|
||||
CHECK_TITLE_extra746="[extra746] Check if API Gateway has configured authorizers"
|
||||
CHECK_SCORED_extra746="NOT_SCORED"
|
||||
CHECK_CIS_LEVEL_extra746="EXTRA"
|
||||
CHECK_SEVERITY_extra746="Medium"
|
||||
CHECK_ASFF_RESOURCE_TYPE_extra746="AwsApiGatewayRestApi"
|
||||
CHECK_ALTERNATE_check746="extra746"
|
||||
CHECK_SERVICENAME_extra746="apigateway"
|
||||
CHECK_RISK_extra746='If no authorizer is enabled anyone can use the service.'
|
||||
CHECK_REMEDIATION_extra746='Implement Amazon Cognito or a Lambda function to control access to your API.'
|
||||
CHECK_DOC_extra746='https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html'
|
||||
CHECK_CAF_EPIC_extra746='IAM'
|
||||
|
||||
extra746(){
|
||||
for regx in $REGIONS; do
|
||||
LIST_OF_REST_APIS=$($AWSCLI $PROFILE_OPT --region $regx apigateway get-rest-apis --query 'items[*].id' --output text 2>&1)
|
||||
if [[ $(echo "$LIST_OF_REST_APIS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
|
||||
textInfo "$regx: Access Denied trying to get rest APIs" "$regx"
|
||||
continue
|
||||
fi
|
||||
if [[ $LIST_OF_REST_APIS ]];then
|
||||
for api in $LIST_OF_REST_APIS; do
|
||||
API_GW_NAME=$($AWSCLI apigateway get-rest-apis $PROFILE_OPT --region $regx --query "items[?id==\`$api\`].name" --output text)
|
||||
AUTHORIZER_CONFIGURED=$($AWSCLI $PROFILE_OPT --region $regx apigateway get-authorizers --rest-api-id $api --query items[*].type --output text)
|
||||
if [[ $AUTHORIZER_CONFIGURED ]]; then
|
||||
textPass "$regx: API Gateway $API_GW_NAME ID $api has authorizer configured" "$regx" "$API_GW_NAME"
|
||||
else
|
||||
textFail "$regx: API Gateway $API_GW_NAME ID $api has not authorizer configured" "$regx" "$API_GW_NAME"
|
||||
fi
|
||||
done
|
||||
else
|
||||
textInfo "$regx: No API Gateways found" "$regx"
|
||||
fi
|
||||
done
|
||||
}
|
||||
Reference in New Issue
Block a user