Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions contrib/k8s/helm/prowler-api/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,11 @@ mainConfig:
# Minimum number of Availability Zones that an ELBv2 must be in
elbv2_min_azs: 2

# AWS Post-Quantum TLS Configuration
# aws.apigateway_domain_name_pqc_tls_enabled
apigateway_pqc_tls_allowed_policies:
- "SecurityPolicy_TLS13_1_3_2025_09"


# AWS Secrets Configuration
# Patterns to ignore in the secrets checks
Expand Down
1 change: 1 addition & 0 deletions docs/user-guide/cli/tutorials/configuration_file.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ The following list includes all the AWS checks with configurable variables that
| `elasticache_redis_cluster_backup_enabled` | `minimum_snapshot_retention_period` | Integer |
| `elb_is_in_multiple_az` | `elb_min_azs` | Integer |
| `elbv2_is_in_multiple_az` | `elbv2_min_azs` | Integer |
| `apigateway_domain_name_pqc_tls_enabled` | `apigateway_pqc_tls_allowed_policies` | List of Strings |
| `guardduty_is_enabled` | `mute_non_default_regions` | Boolean |
| `iam_user_access_not_stale_to_sagemaker` | `max_unused_sagemaker_access_days` | Integer |
| `iam_user_accesskey_unused` | `max_unused_access_keys_days` | Integer |
Expand Down
3 changes: 2 additions & 1 deletion permissions/prowler-additions-policy.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@
],
"Resource": [
"arn:*:apigateway:*::/restapis/*",
"arn:*:apigateway:*::/apis/*"
"arn:*:apigateway:*::/apis/*",
"arn:*:apigateway:*::/domainnames/*"
],
"Sid": "AllowAPIGatewayReadOnly"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ Resources:
Resource:
- "arn:*:apigateway:*::/restapis/*"
- "arn:*:apigateway:*::/apis/*"
- "arn:*:apigateway:*::/domainnames/*"
- !If
- OrganizationsEnabled
- PolicyName: ProwlerOrganizations
Expand Down
1 change: 1 addition & 0 deletions prowler/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ All notable changes to the **Prowler SDK** are documented in this file.

### 🚀 Added

- `apigateway_domain_name_pqc_tls_enabled` check for AWS provider to verify API Gateway custom domain names use a post-quantum TLS security policy [(#11316)](https://github.com/prowler-cloud/prowler/pull/11316)
- Sites, Additional Google services, and Marketplace checks for Google Workspace provider using the Cloud Identity Policy API [(#11281)](https://github.com/prowler-cloud/prowler/pull/11281)
- `entra_app_registration_client_secret_unused` check for M365 provider [(#11232)](https://github.com/prowler-cloud/prowler/pull/11232)
- `cloudsql_instance_cmek_encryption_enabled` check for GCP provider [(#11023)](https://github.com/prowler-cloud/prowler/pull/11023)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1151,6 +1151,7 @@
"elb_insecure_ssl_ciphers",
"elb_ssl_listeners",
"elbv2_insecure_ssl_ciphers",
"apigateway_domain_name_pqc_tls_enabled",
"elbv2_ssl_listeners",
"s3_bucket_secure_transport_policy"
]
Expand Down
1 change: 1 addition & 0 deletions prowler/compliance/aws/ccc_aws.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"elb_insecure_ssl_ciphers",
"elb_ssl_listeners",
"elbv2_insecure_ssl_ciphers",
"apigateway_domain_name_pqc_tls_enabled",
"elbv2_ssl_listeners",
"elbv2_nlb_tls_termination_enabled",
"s3_bucket_secure_transport_policy",
Expand Down
2 changes: 2 additions & 0 deletions prowler/compliance/aws/csa_ccm_4.0_aws.json
Original file line number Diff line number Diff line change
Expand Up @@ -1289,6 +1289,7 @@
"elb_ssl_listeners",
"elbv2_ssl_listeners",
"elbv2_insecure_ssl_ciphers",
"apigateway_domain_name_pqc_tls_enabled",
"elbv2_nlb_tls_termination_enabled",
"cloudfront_distributions_https_enabled",
"cloudfront_distributions_origin_traffic_encrypted",
Expand Down Expand Up @@ -1442,6 +1443,7 @@
"acm_certificates_with_secure_key_algorithms",
"elb_insecure_ssl_ciphers",
"elbv2_insecure_ssl_ciphers",
"apigateway_domain_name_pqc_tls_enabled",
"cloudfront_distributions_using_deprecated_ssl_protocols"
]
},
Expand Down
6 changes: 4 additions & 2 deletions prowler/compliance/aws/ens_rd2022_aws.json
Original file line number Diff line number Diff line change
Expand Up @@ -2366,7 +2366,8 @@
}
],
"Checks": [
"elbv2_insecure_ssl_ciphers"
"elbv2_insecure_ssl_ciphers",
"apigateway_domain_name_pqc_tls_enabled"
]
},
{
Expand All @@ -2389,7 +2390,8 @@
}
],
"Checks": [
"elbv2_insecure_ssl_ciphers"
"elbv2_insecure_ssl_ciphers",
"apigateway_domain_name_pqc_tls_enabled"
]
},
{
Expand Down
2 changes: 2 additions & 0 deletions prowler/compliance/aws/fedramp_moderate_revision_4_aws.json
Original file line number Diff line number Diff line change
Expand Up @@ -1145,6 +1145,7 @@
"Checks": [
"apigateway_restapi_client_certificate_enabled",
"elbv2_insecure_ssl_ciphers",
"apigateway_domain_name_pqc_tls_enabled",
"elb_ssl_listeners",
"opensearch_service_domains_node_to_node_encryption_enabled",
"s3_bucket_secure_transport_policy"
Expand All @@ -1164,6 +1165,7 @@
"Checks": [
"apigateway_restapi_client_certificate_enabled",
"elbv2_insecure_ssl_ciphers",
"apigateway_domain_name_pqc_tls_enabled",
"elb_ssl_listeners",
"opensearch_service_domains_node_to_node_encryption_enabled",
"s3_bucket_secure_transport_policy"
Expand Down
1 change: 1 addition & 0 deletions prowler/compliance/aws/ffiec_aws.json
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,7 @@
"Checks": [
"apigateway_restapi_client_certificate_enabled",
"elbv2_insecure_ssl_ciphers",
"apigateway_domain_name_pqc_tls_enabled",
"elb_ssl_listeners",
"s3_bucket_secure_transport_policy"
]
Expand Down
1 change: 1 addition & 0 deletions prowler/compliance/aws/gxp_21_cfr_part_11_aws.json
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@
"ec2_ebs_default_encryption",
"efs_encryption_at_rest_enabled",
"elbv2_insecure_ssl_ciphers",
"apigateway_domain_name_pqc_tls_enabled",
"elb_ssl_listeners",
"opensearch_service_domains_encryption_at_rest_enabled",
"opensearch_service_domains_node_to_node_encryption_enabled",
Expand Down
3 changes: 2 additions & 1 deletion prowler/compliance/aws/iso27001_2013_aws.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
],
"Checks": [
"elb_insecure_ssl_ciphers",
"elbv2_insecure_ssl_ciphers"
"elbv2_insecure_ssl_ciphers",
"apigateway_domain_name_pqc_tls_enabled"
]
},
{
Expand Down
2 changes: 2 additions & 0 deletions prowler/compliance/aws/kisa_isms_p_2023_aws.json
Original file line number Diff line number Diff line change
Expand Up @@ -2040,6 +2040,7 @@
"elb_ssl_listeners",
"elb_ssl_listeners_use_acm_certificate",
"elbv2_insecure_ssl_ciphers",
"apigateway_domain_name_pqc_tls_enabled",
"elbv2_nlb_tls_termination_enabled",
"elbv2_ssl_listeners",
"glue_data_catalogs_connection_passwords_encryption_enabled",
Expand Down Expand Up @@ -3090,6 +3091,7 @@
"elb_ssl_listeners_use_acm_certificate",
"elbv2_desync_mitigation_mode",
"elbv2_insecure_ssl_ciphers",
"apigateway_domain_name_pqc_tls_enabled",
"elbv2_internet_facing",
"elbv2_listeners_underneath",
"elbv2_logging_enabled",
Expand Down
2 changes: 2 additions & 0 deletions prowler/compliance/aws/kisa_isms_p_2023_korean_aws.json
Original file line number Diff line number Diff line change
Expand Up @@ -2042,6 +2042,7 @@
"elb_ssl_listeners",
"elb_ssl_listeners_use_acm_certificate",
"elbv2_insecure_ssl_ciphers",
"apigateway_domain_name_pqc_tls_enabled",
"elbv2_nlb_tls_termination_enabled",
"elbv2_ssl_listeners",
"glue_data_catalogs_connection_passwords_encryption_enabled",
Expand Down Expand Up @@ -3093,6 +3094,7 @@
"elb_ssl_listeners_use_acm_certificate",
"elbv2_desync_mitigation_mode",
"elbv2_insecure_ssl_ciphers",
"apigateway_domain_name_pqc_tls_enabled",
"elbv2_internet_facing",
"elbv2_listeners_underneath",
"elbv2_logging_enabled",
Expand Down
1 change: 1 addition & 0 deletions prowler/compliance/aws/nist_800_171_revision_2_aws.json
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,7 @@
"apigateway_restapi_client_certificate_enabled",
"ec2_ebs_volume_encryption",
"elbv2_insecure_ssl_ciphers",
"apigateway_domain_name_pqc_tls_enabled",
"opensearch_service_domains_node_to_node_encryption_enabled",
"s3_bucket_default_encryption",
"s3_bucket_secure_transport_policy"
Expand Down
2 changes: 2 additions & 0 deletions prowler/compliance/aws/nist_800_53_revision_5_aws.json
Original file line number Diff line number Diff line change
Expand Up @@ -5262,6 +5262,7 @@
"Checks": [
"apigateway_restapi_client_certificate_enabled",
"elbv2_insecure_ssl_ciphers",
"apigateway_domain_name_pqc_tls_enabled",
"elb_ssl_listeners",
"opensearch_service_domains_node_to_node_encryption_enabled",
"s3_bucket_secure_transport_policy"
Expand Down Expand Up @@ -5549,6 +5550,7 @@
],
"Checks": [
"elbv2_insecure_ssl_ciphers",
"apigateway_domain_name_pqc_tls_enabled",
"elb_ssl_listeners"
]
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"ec2_instance_public_ip",
"efs_encryption_at_rest_enabled",
"elbv2_insecure_ssl_ciphers",
"apigateway_domain_name_pqc_tls_enabled",
"elb_ssl_listeners",
"ec2_ebs_default_encryption",
"emr_cluster_master_nodes_no_public_ip",
Expand Down
1 change: 1 addition & 0 deletions prowler/compliance/aws/secnumcloud_3.2_aws.json
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,7 @@
"elbv2_ssl_listeners",
"elb_insecure_ssl_ciphers",
"elbv2_insecure_ssl_ciphers",
"apigateway_domain_name_pqc_tls_enabled",
"redshift_cluster_in_transit_encryption_enabled",
"elasticache_redis_cluster_in_transit_encryption_enabled",
"dynamodb_accelerator_cluster_in_transit_encryption_enabled",
Expand Down
6 changes: 6 additions & 0 deletions prowler/config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,12 @@ aws:
# Minimum number of Availability Zones that an ELBv2 must be in
elbv2_min_azs: 2

# AWS Post-Quantum TLS Configuration
# aws.apigateway_domain_name_pqc_tls_enabled
# Allowed post-quantum TLS security policies for API Gateway custom domain names
apigateway_pqc_tls_allowed_policies:
- "SecurityPolicy_TLS13_1_3_2025_09"

# AWS Elasticache Configuration
# aws.elasticache_redis_cluster_backup_enabled
# Minimum number of days that a Redis cluster must have backups retention period
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"Provider": "aws",
"CheckID": "apigateway_domain_name_pqc_tls_enabled",
"CheckTitle": "API Gateway custom domain names use a post-quantum TLS security policy",
"CheckType": [
"Software and Configuration Checks/AWS Security Best Practices"
],
"ServiceName": "apigateway",
"SubServiceName": "",
"ResourceIdTemplate": "",
"Severity": "low",
"ResourceType": "AwsApiGatewayDomainName",
"ResourceGroup": "network",
"Description": "**API Gateway custom domain names** for REST APIs are assessed for use of a **post-quantum (PQ) TLS security policy** (`SecurityPolicy_TLS13_*_2025_09`). Custom domains with legacy policies such as `TLS_1_0` or `TLS_1_2` lack hybrid ML-KEM key exchange, leaving captured traffic vulnerable to future quantum decryption.",
"Risk": "Without a PQ-ready TLS policy, traffic to API Gateway custom domains captured today can be decrypted once a **cryptographically relevant quantum computer** exists (**harvest-now, decrypt-later** attack). This threatens long-term **confidentiality** of API payloads, credentials, and bearer tokens.",
"RelatedUrl": "",
"AdditionalURLs": [
"https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-custom-domain-tls-version.html",
"https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-security-policies-list.html",
"https://aws.amazon.com/security/post-quantum-cryptography/",
"https://csrc.nist.gov/projects/post-quantum-cryptography"
],
"Remediation": {
"Code": {
"CLI": "aws apigateway update-domain-name --domain-name <domain_name> --patch-operations op=replace,path=/securityPolicy,value=SecurityPolicy_TLS13_1_3_2025_09",
"NativeIaC": "```yaml\nResources:\n <example_resource_name>:\n Type: AWS::ApiGateway::DomainName\n Properties:\n DomainName: api.example.com\n RegionalCertificateArn: <example_certificate_arn>\n SecurityPolicy: SecurityPolicy_TLS13_1_3_2025_09 # FIX: enhanced post-quantum security policy\n EndpointConfiguration:\n Types:\n - REGIONAL\n```",
"Other": "1. In the AWS Console, go to API Gateway > Custom domain names\n2. Select the custom domain and choose Edit on Domain name configurations\n3. Set Security policy to SecurityPolicy_TLS13_1_3_2025_09 (post-quantum) and Endpoint access mode to Strict\n4. Save the changes",
"Terraform": "```hcl\nresource \"aws_api_gateway_domain_name\" \"<example_resource_name>\" {\n domain_name = \"api.example.com\"\n regional_certificate_arn = \"<example_certificate_arn>\"\n security_policy = \"SecurityPolicy_TLS13_1_3_2025_09\" # FIX: enhanced post-quantum security policy\n endpoint_configuration {\n types = [\"REGIONAL\"]\n }\n}\n```"
},
"Recommendation": {
"Text": "Migrate every API Gateway custom domain name to an **enhanced post-quantum TLS policy** (`SecurityPolicy_TLS13_1_3_2025_09`) that enables hybrid ML-KEM key exchange. Note that you must also enable Strict endpoint access mode and that mutual TLS is not supported on enhanced policies. Review allowed policies regularly as AWS publishes new PQ-ready options.",
"Url": "https://hub.prowler.com/check/apigateway_domain_name_pqc_tls_enabled"
}
},
"Categories": [
"encryption"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": "API Gateway HTTP and WebSocket APIs only support the legacy TLS_1_2 security policy and therefore cannot use post-quantum TLS today; this check evaluates REST API custom domain names only."
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.apigateway.apigateway_client import (
apigateway_client,
)

PQC_APIGATEWAY_POLICIES_DEFAULT = [
"SecurityPolicy_TLS13_1_3_2025_09",
]


class apigateway_domain_name_pqc_tls_enabled(Check):
"""Verify that every API Gateway custom domain name uses a post-quantum TLS policy.

A custom domain name PASSES when its ``securityPolicy`` belongs to the
configured allowlist of enhanced post-quantum policies.
"""

def execute(self) -> list[Check_Report_AWS]:
findings = []
pqc_policies = apigateway_client.audit_config.get(
"apigateway_pqc_tls_allowed_policies", PQC_APIGATEWAY_POLICIES_DEFAULT
)
for domain in apigateway_client.domain_names:
report = Check_Report_AWS(metadata=self.metadata(), resource=domain)
policy = domain.security_policy or "<none>"
if domain.security_policy in pqc_policies:
report.status = "PASS"
report.status_extended = (
f"API Gateway custom domain {domain.name} uses post-quantum "
f"TLS policy {policy}."
)
else:
report.status = "FAIL"
report.status_extended = (
f"API Gateway custom domain {domain.name} uses TLS policy "
f"{policy}, which is not in the post-quantum allowlist."
)
findings.append(report)

return findings
35 changes: 35 additions & 0 deletions prowler/providers/aws/services/apigateway/apigateway_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,39 @@ def __init__(self, provider):
# Call AWSService's __init__
super().__init__(__class__.__name__, provider)
self.rest_apis = []
self.domain_names = []
self.__threading_call__(self._get_rest_apis)
self.__threading_call__(self._get_domain_names)
self._get_authorizers()
self._get_rest_api()
self._get_stages()
self._get_resources()

def _get_domain_names(self, regional_client):
logger.info("APIGateway - Getting custom domain names...")
try:
paginator = regional_client.get_paginator("get_domain_names")
for page in paginator.paginate():
for item in page.get("items", []):
domain_name = item.get("domainName", "")
arn = f"arn:{self.audited_partition}:apigateway:{regional_client.region}::/domainnames/{domain_name}"
if not self.audit_resources or (
is_resource_filtered(arn, self.audit_resources)
):
self.domain_names.append(
DomainName(
name=domain_name,
arn=arn,
region=regional_client.region,
security_policy=item.get("securityPolicy", ""),
tags=[item.get("tags", {})],
)
)
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)

def _get_rest_apis(self, regional_client):
logger.info("APIGateway - Getting Rest APIs...")
try:
Expand Down Expand Up @@ -249,3 +276,11 @@ class RestAPI(BaseModel):
stages: list[Stage] = []
tags: Optional[list] = []
resources: list[PathResourceMethods] = []


class DomainName(BaseModel):
name: str
arn: str
region: str
security_policy: str = ""
tags: Optional[list] = []
Loading
Loading