mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 13:01:56 +00:00
feat(aws): add elbv2_listener_pqc_tls_enabled security check (#11254)
This commit is contained in:
committed by
GitHub
parent
24b670ac36
commit
8e9af708f8
@@ -78,6 +78,7 @@ The following list includes all the AWS checks with configurable variables that
|
||||
| `elasticache_redis_cluster_backup_enabled` | `minimum_snapshot_retention_period` | Integer | `7` |
|
||||
| `elb_is_in_multiple_az` | `elb_min_azs` | Integer | `2` |
|
||||
| `elbv2_is_in_multiple_az` | `elbv2_min_azs` | Integer | `2` |
|
||||
| `elbv2_listener_pqc_tls_enabled` | `elbv2_listener_pqc_tls_allowed_policies` | List of Strings | See `config.yaml` |
|
||||
| `eventbridge_bus_cross_account_access` | `trusted_account_ids` | List of Strings | `[]` |
|
||||
| `eventbridge_schema_registry_cross_account_access` | `trusted_account_ids` | List of Strings | `[]` |
|
||||
| `glue_etl_jobs_no_secrets_in_arguments` | `secrets_ignore_patterns` | List of Strings | `[]` |
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
`elbv2_listener_pqc_tls_enabled` check for AWS provider, verifying that ELBv2 listeners use post-quantum TLS policies
|
||||
@@ -1181,6 +1181,7 @@
|
||||
"elb_insecure_ssl_ciphers",
|
||||
"elb_ssl_listeners",
|
||||
"elbv2_insecure_ssl_ciphers",
|
||||
"elbv2_listener_pqc_tls_enabled",
|
||||
"cloudfront_distributions_pqc_tls_enabled",
|
||||
"apigateway_domain_name_pqc_tls_enabled",
|
||||
"transfer_server_pqc_ssh_kex_enabled",
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
"elb_insecure_ssl_ciphers",
|
||||
"elb_ssl_listeners",
|
||||
"elbv2_insecure_ssl_ciphers",
|
||||
"elbv2_listener_pqc_tls_enabled",
|
||||
"cloudfront_distributions_pqc_tls_enabled",
|
||||
"apigateway_domain_name_pqc_tls_enabled",
|
||||
"transfer_server_pqc_ssh_kex_enabled",
|
||||
|
||||
@@ -2495,6 +2495,7 @@
|
||||
],
|
||||
"Checks": [
|
||||
"elbv2_insecure_ssl_ciphers",
|
||||
"elbv2_listener_pqc_tls_enabled",
|
||||
"cloudfront_distributions_pqc_tls_enabled",
|
||||
"apigateway_domain_name_pqc_tls_enabled",
|
||||
"transfer_server_pqc_ssh_kex_enabled"
|
||||
@@ -2521,6 +2522,7 @@
|
||||
],
|
||||
"Checks": [
|
||||
"elbv2_insecure_ssl_ciphers",
|
||||
"elbv2_listener_pqc_tls_enabled",
|
||||
"cloudfront_distributions_pqc_tls_enabled",
|
||||
"apigateway_domain_name_pqc_tls_enabled",
|
||||
"transfer_server_pqc_ssh_kex_enabled"
|
||||
|
||||
@@ -1377,6 +1377,7 @@
|
||||
"Checks": [
|
||||
"apigateway_restapi_client_certificate_enabled",
|
||||
"elbv2_insecure_ssl_ciphers",
|
||||
"elbv2_listener_pqc_tls_enabled",
|
||||
"cloudfront_distributions_pqc_tls_enabled",
|
||||
"apigateway_domain_name_pqc_tls_enabled",
|
||||
"transfer_server_pqc_ssh_kex_enabled",
|
||||
@@ -1399,6 +1400,7 @@
|
||||
"Checks": [
|
||||
"apigateway_restapi_client_certificate_enabled",
|
||||
"elbv2_insecure_ssl_ciphers",
|
||||
"elbv2_listener_pqc_tls_enabled",
|
||||
"cloudfront_distributions_pqc_tls_enabled",
|
||||
"apigateway_domain_name_pqc_tls_enabled",
|
||||
"transfer_server_pqc_ssh_kex_enabled",
|
||||
|
||||
@@ -615,6 +615,7 @@
|
||||
"Checks": [
|
||||
"apigateway_restapi_client_certificate_enabled",
|
||||
"elbv2_insecure_ssl_ciphers",
|
||||
"elbv2_listener_pqc_tls_enabled",
|
||||
"cloudfront_distributions_pqc_tls_enabled",
|
||||
"apigateway_domain_name_pqc_tls_enabled",
|
||||
"transfer_server_pqc_ssh_kex_enabled",
|
||||
|
||||
@@ -266,6 +266,7 @@
|
||||
"ec2_ebs_default_encryption",
|
||||
"efs_encryption_at_rest_enabled",
|
||||
"elbv2_insecure_ssl_ciphers",
|
||||
"elbv2_listener_pqc_tls_enabled",
|
||||
"cloudfront_distributions_pqc_tls_enabled",
|
||||
"apigateway_domain_name_pqc_tls_enabled",
|
||||
"transfer_server_pqc_ssh_kex_enabled",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"Checks": [
|
||||
"elb_insecure_ssl_ciphers",
|
||||
"elbv2_insecure_ssl_ciphers",
|
||||
"elbv2_listener_pqc_tls_enabled",
|
||||
"cloudfront_distributions_pqc_tls_enabled",
|
||||
"apigateway_domain_name_pqc_tls_enabled",
|
||||
"transfer_server_pqc_ssh_kex_enabled"
|
||||
|
||||
@@ -2064,6 +2064,7 @@
|
||||
"elb_ssl_listeners",
|
||||
"elb_ssl_listeners_use_acm_certificate",
|
||||
"elbv2_insecure_ssl_ciphers",
|
||||
"elbv2_listener_pqc_tls_enabled",
|
||||
"cloudfront_distributions_pqc_tls_enabled",
|
||||
"apigateway_domain_name_pqc_tls_enabled",
|
||||
"transfer_server_pqc_ssh_kex_enabled",
|
||||
@@ -3142,6 +3143,7 @@
|
||||
"elb_ssl_listeners_use_acm_certificate",
|
||||
"elbv2_desync_mitigation_mode",
|
||||
"elbv2_insecure_ssl_ciphers",
|
||||
"elbv2_listener_pqc_tls_enabled",
|
||||
"cloudfront_distributions_pqc_tls_enabled",
|
||||
"apigateway_domain_name_pqc_tls_enabled",
|
||||
"transfer_server_pqc_ssh_kex_enabled",
|
||||
|
||||
@@ -2066,6 +2066,7 @@
|
||||
"elb_ssl_listeners",
|
||||
"elb_ssl_listeners_use_acm_certificate",
|
||||
"elbv2_insecure_ssl_ciphers",
|
||||
"elbv2_listener_pqc_tls_enabled",
|
||||
"cloudfront_distributions_pqc_tls_enabled",
|
||||
"apigateway_domain_name_pqc_tls_enabled",
|
||||
"transfer_server_pqc_ssh_kex_enabled",
|
||||
@@ -3145,6 +3146,7 @@
|
||||
"elb_ssl_listeners_use_acm_certificate",
|
||||
"elbv2_desync_mitigation_mode",
|
||||
"elbv2_insecure_ssl_ciphers",
|
||||
"elbv2_listener_pqc_tls_enabled",
|
||||
"cloudfront_distributions_pqc_tls_enabled",
|
||||
"apigateway_domain_name_pqc_tls_enabled",
|
||||
"transfer_server_pqc_ssh_kex_enabled",
|
||||
|
||||
@@ -717,6 +717,7 @@
|
||||
"apigateway_restapi_client_certificate_enabled",
|
||||
"ec2_ebs_volume_encryption",
|
||||
"elbv2_insecure_ssl_ciphers",
|
||||
"elbv2_listener_pqc_tls_enabled",
|
||||
"cloudfront_distributions_pqc_tls_enabled",
|
||||
"apigateway_domain_name_pqc_tls_enabled",
|
||||
"transfer_server_pqc_ssh_kex_enabled",
|
||||
|
||||
@@ -5592,6 +5592,7 @@
|
||||
"Checks": [
|
||||
"apigateway_restapi_client_certificate_enabled",
|
||||
"elbv2_insecure_ssl_ciphers",
|
||||
"elbv2_listener_pqc_tls_enabled",
|
||||
"cloudfront_distributions_pqc_tls_enabled",
|
||||
"apigateway_domain_name_pqc_tls_enabled",
|
||||
"transfer_server_pqc_ssh_kex_enabled",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"ec2_instance_public_ip",
|
||||
"efs_encryption_at_rest_enabled",
|
||||
"elbv2_insecure_ssl_ciphers",
|
||||
"elbv2_listener_pqc_tls_enabled",
|
||||
"cloudfront_distributions_pqc_tls_enabled",
|
||||
"apigateway_domain_name_pqc_tls_enabled",
|
||||
"transfer_server_pqc_ssh_kex_enabled",
|
||||
|
||||
@@ -490,6 +490,7 @@
|
||||
"elbv2_ssl_listeners",
|
||||
"elb_insecure_ssl_ciphers",
|
||||
"elbv2_insecure_ssl_ciphers",
|
||||
"elbv2_listener_pqc_tls_enabled",
|
||||
"cloudfront_distributions_pqc_tls_enabled",
|
||||
"apigateway_domain_name_pqc_tls_enabled",
|
||||
"transfer_server_pqc_ssh_kex_enabled",
|
||||
|
||||
@@ -424,6 +424,21 @@ aws:
|
||||
- "SecurityPolicy_TLS13_1_2_PFS_PQ_2025_09"
|
||||
- "SecurityPolicy_TLS13_1_2_PQ_2025_09"
|
||||
|
||||
# aws.elbv2_listener_pqc_tls_enabled
|
||||
# Allowed post-quantum TLS security policies for ELBv2 HTTPS/TLS listeners
|
||||
elbv2_listener_pqc_tls_allowed_policies:
|
||||
- "ELBSecurityPolicy-TLS13-1-2-PQ-2025-09"
|
||||
- "ELBSecurityPolicy-TLS13-1-2-Ext1-PQ-2025-09"
|
||||
- "ELBSecurityPolicy-TLS13-1-2-Ext2-PQ-2025-09"
|
||||
- "ELBSecurityPolicy-TLS13-1-2-Res-PQ-2025-09"
|
||||
- "ELBSecurityPolicy-TLS13-1-3-PQ-2025-09"
|
||||
- "ELBSecurityPolicy-TLS13-1-2-FIPS-PQ-2025-09"
|
||||
- "ELBSecurityPolicy-TLS13-1-2-Ext0-FIPS-PQ-2025-09"
|
||||
- "ELBSecurityPolicy-TLS13-1-2-Ext1-FIPS-PQ-2025-09"
|
||||
- "ELBSecurityPolicy-TLS13-1-2-Ext2-FIPS-PQ-2025-09"
|
||||
- "ELBSecurityPolicy-TLS13-1-2-Res-FIPS-PQ-2025-09"
|
||||
- "ELBSecurityPolicy-TLS13-1-3-FIPS-PQ-2025-09"
|
||||
|
||||
# aws.rolesanywhere_trust_anchor_pqc_pki
|
||||
# Allowed post-quantum key algorithms for AWS Private CAs backing IAM Roles Anywhere trust anchors
|
||||
rolesanywhere_pqc_pca_key_algorithms:
|
||||
|
||||
@@ -415,6 +415,10 @@ class AWSProviderConfig(ProviderConfigBase):
|
||||
le=6,
|
||||
description="Min AZs an Application/Network LB must span. Range: 1..6.",
|
||||
)
|
||||
elbv2_listener_pqc_tls_allowed_policies: Optional[list[str]] = Field(
|
||||
default=None,
|
||||
description="ELBv2 SSL policies that satisfy the PQ TLS listener check.",
|
||||
)
|
||||
|
||||
# --- ElastiCache -----------------------------------------------------
|
||||
minimum_snapshot_retention_period: Optional[int] = Field(
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "elbv2_listener_pqc_tls_enabled",
|
||||
"CheckTitle": "ELBv2 HTTPS/TLS listeners use a post-quantum TLS security policy",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices"
|
||||
],
|
||||
"ServiceName": "elbv2",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "low",
|
||||
"ResourceType": "AwsElbv2LoadBalancer",
|
||||
"ResourceGroup": "network",
|
||||
"Description": "**ELBv2 HTTPS and TLS listeners** are assessed for use of **post-quantum (PQ) TLS security policies**. Listeners whose `SslPolicy` is not in the approved PQ set lack hybrid key exchange (ML-KEM 768 + ECDHE), which can increase harvest-now-decrypt-later exposure for recorded traffic.",
|
||||
"Risk": "Without PQ-ready TLS policies, encrypted traffic captured today may be stored for future cryptanalysis if a **cryptographically relevant quantum computer** becomes available (**harvest-now, decrypt-later** attack). PQ-ready TLS policies reduce this long-term confidentiality risk for sensitive data, credentials, and session tokens transmitted through the load balancer.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://docs.aws.amazon.com/elasticloadbalancing/latest/application/describe-ssl-policies.html",
|
||||
"https://aws.amazon.com/security/post-quantum-cryptography/",
|
||||
"https://csrc.nist.gov/projects/post-quantum-cryptography"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws elbv2 modify-listener --listener-arn <listener_arn> --ssl-policy ELBSecurityPolicy-TLS13-1-2-PQ-2025-09",
|
||||
"NativeIaC": "```yaml\nResources:\n <example_resource_name>:\n Type: AWS::ElasticLoadBalancingV2::Listener\n Properties:\n LoadBalancerArn: <example_resource_arn>\n Protocol: HTTPS\n Port: 443\n DefaultActions:\n - Type: forward\n TargetGroupArn: <example_resource_arn>\n Certificates:\n - CertificateArn: <example_certificate_arn>\n SslPolicy: ELBSecurityPolicy-TLS13-1-2-PQ-2025-09 # FIX: uses a post-quantum TLS policy\n```",
|
||||
"Other": "1. In the AWS Console, go to EC2 > Load Balancers\n2. Select the load balancer and open the Listeners tab\n3. Select the HTTPS/TLS listener and choose Edit\n4. Set Security policy to ELBSecurityPolicy-TLS13-1-2-PQ-2025-09 (or any approved PQ policy)\n5. Save changes",
|
||||
"Terraform": "```hcl\nresource \"aws_lb_listener\" \"<example_resource_name>\" {\n load_balancer_arn = \"<example_resource_arn>\"\n port = 443\n protocol = \"HTTPS\"\n ssl_policy = \"ELBSecurityPolicy-TLS13-1-2-PQ-2025-09\" # FIX: post-quantum TLS policy\n certificate_arn = \"<example_certificate_arn>\"\n\n default_action {\n type = \"forward\"\n target_group_arn = \"<example_resource_arn>\"\n }\n}\n```"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Migrate all ELBv2 HTTPS and TLS listeners to a **post-quantum TLS policy** (`ELBSecurityPolicy-TLS13-*-PQ-2025-09` family) to enable hybrid key exchange (ML-KEM + ECDHE). Periodically review and update policies as AWS publishes new PQ-ready options.",
|
||||
"Url": "https://hub.prowler.com/check/elbv2_listener_pqc_tls_enabled"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"encryption"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [
|
||||
"elbv2_insecure_ssl_ciphers"
|
||||
],
|
||||
"Notes": ""
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
"""Check that ELBv2 HTTPS/TLS listeners use post-quantum TLS policies."""
|
||||
|
||||
from prowler.lib.check.models import Check, Check_Report_AWS
|
||||
from prowler.providers.aws.services.elbv2.elbv2_client import elbv2_client
|
||||
|
||||
PQ_TLS_POLICIES_DEFAULT = [
|
||||
"ELBSecurityPolicy-TLS13-1-2-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-2-Ext1-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-2-Ext2-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-2-Res-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-3-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-2-FIPS-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-2-Ext0-FIPS-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-2-Ext1-FIPS-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-2-Ext2-FIPS-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-2-Res-FIPS-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-3-FIPS-PQ-2025-09",
|
||||
]
|
||||
|
||||
|
||||
class elbv2_listener_pqc_tls_enabled(Check):
|
||||
"""Verify that every ELBv2 HTTPS or TLS listener uses a post-quantum TLS policy.
|
||||
|
||||
This check evaluates whether each HTTPS (ALB) or TLS (NLB) listener on an
|
||||
ELBv2 load balancer terminates TLS with a security policy that offers
|
||||
post-quantum (PQ) hybrid key exchange (ML-KEM 768 combined with ECDHE).
|
||||
- PASS: All HTTPS/TLS listeners on the load balancer use a PQ TLS policy.
|
||||
- FAIL: At least one HTTPS/TLS listener uses a non-PQ TLS policy.
|
||||
"""
|
||||
|
||||
def execute(self) -> list[Check_Report_AWS]:
|
||||
"""Execute the PQ TLS policy check for every ELBv2 load balancer.
|
||||
|
||||
Returns:
|
||||
A list of reports for load balancers with discovered listeners.
|
||||
"""
|
||||
findings = []
|
||||
pq_tls_policies = elbv2_client.audit_config.get(
|
||||
"elbv2_listener_pqc_tls_allowed_policies", PQ_TLS_POLICIES_DEFAULT
|
||||
)
|
||||
for lb in elbv2_client.loadbalancersv2.values():
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=lb)
|
||||
|
||||
if lb.listener_discovery_failed:
|
||||
continue
|
||||
|
||||
has_tls_listeners = False
|
||||
non_pq_listeners = []
|
||||
for listener_arn, listener in lb.listeners.items():
|
||||
if listener.protocol in ("HTTPS", "TLS"):
|
||||
has_tls_listeners = True
|
||||
if listener.ssl_policy not in pq_tls_policies:
|
||||
ssl_policy = listener.ssl_policy or "<none>"
|
||||
non_pq_listeners.append(
|
||||
f"{listener.protocol}:{listener.port} ({listener_arn}) uses {ssl_policy}"
|
||||
)
|
||||
|
||||
if not has_tls_listeners:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"ELBv2 {lb.name} has no HTTPS/TLS listeners."
|
||||
findings.append(report)
|
||||
continue
|
||||
|
||||
if non_pq_listeners:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"ELBv2 {lb.name} has HTTPS/TLS listeners without post-quantum TLS policy: {', '.join(non_pq_listeners)}."
|
||||
else:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"ELBv2 {lb.name} has all HTTPS/TLS listeners using a post-quantum TLS policy."
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -86,10 +86,14 @@ class ELBv2(AWSService):
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
else:
|
||||
load_balancer[1].listener_discovery_failed = True
|
||||
load_balancer[1].listener_discovery_error = str(error)
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
except Exception as error:
|
||||
load_balancer[1].listener_discovery_failed = True
|
||||
load_balancer[1].listener_discovery_error = str(error)
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
@@ -209,6 +213,8 @@ class LoadBalancerv2(BaseModel):
|
||||
drop_invalid_header_fields: Optional[str]
|
||||
cross_zone_load_balancing: Optional[str]
|
||||
listeners: Dict[str, Listenerv2] = {}
|
||||
listener_discovery_failed: bool = False
|
||||
listener_discovery_error: Optional[str] = None
|
||||
scheme: Optional[str]
|
||||
security_groups: list[str] = []
|
||||
# Key: ZoneName, Value: SubnetId
|
||||
|
||||
@@ -320,6 +320,19 @@ config_aws = {
|
||||
"minimum_snapshot_retention_period": 7,
|
||||
"elb_min_azs": 2,
|
||||
"elbv2_min_azs": 2,
|
||||
"elbv2_listener_pqc_tls_allowed_policies": [
|
||||
"ELBSecurityPolicy-TLS13-1-2-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-2-Ext1-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-2-Ext2-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-2-Res-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-3-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-2-FIPS-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-2-Ext0-FIPS-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-2-Ext1-FIPS-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-2-Ext2-FIPS-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-2-Res-FIPS-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-3-FIPS-PQ-2025-09",
|
||||
],
|
||||
"secrets_ignore_patterns": [],
|
||||
"max_days_secret_unused": 90,
|
||||
"max_days_secret_unrotated": 90,
|
||||
|
||||
@@ -362,6 +362,21 @@ aws:
|
||||
# Minimum number of Availability Zones that an ELBv2 must be in
|
||||
elbv2_min_azs: 2
|
||||
|
||||
# aws.elbv2_listener_pqc_tls_enabled
|
||||
# Allowed post-quantum TLS security policies for ELBv2 HTTPS/TLS listeners
|
||||
elbv2_listener_pqc_tls_allowed_policies:
|
||||
- "ELBSecurityPolicy-TLS13-1-2-PQ-2025-09"
|
||||
- "ELBSecurityPolicy-TLS13-1-2-Ext1-PQ-2025-09"
|
||||
- "ELBSecurityPolicy-TLS13-1-2-Ext2-PQ-2025-09"
|
||||
- "ELBSecurityPolicy-TLS13-1-2-Res-PQ-2025-09"
|
||||
- "ELBSecurityPolicy-TLS13-1-3-PQ-2025-09"
|
||||
- "ELBSecurityPolicy-TLS13-1-2-FIPS-PQ-2025-09"
|
||||
- "ELBSecurityPolicy-TLS13-1-2-Ext0-FIPS-PQ-2025-09"
|
||||
- "ELBSecurityPolicy-TLS13-1-2-Ext1-FIPS-PQ-2025-09"
|
||||
- "ELBSecurityPolicy-TLS13-1-2-Ext2-FIPS-PQ-2025-09"
|
||||
- "ELBSecurityPolicy-TLS13-1-2-Res-FIPS-PQ-2025-09"
|
||||
- "ELBSecurityPolicy-TLS13-1-3-FIPS-PQ-2025-09"
|
||||
|
||||
# AWS Elasticache Configuration
|
||||
# aws.elasticache_redis_cluster_backup_enabled
|
||||
# Minimum number of days that a Redis cluster must have backups retention period
|
||||
|
||||
@@ -176,6 +176,33 @@ class Test_AWS_Enums:
|
||||
assert _validate({"ecr_repository_vulnerability_minimum_severity": level}) == {}
|
||||
|
||||
|
||||
class TestAWSELBv2PQCTLSAllowedPolicies:
|
||||
def test_valid_policy_list_round_trips(self):
|
||||
policies = [
|
||||
"ELBSecurityPolicy-TLS13-1-2-Res-2021-06",
|
||||
"ELBSecurityPolicy-TLS13-1-3-2021-06",
|
||||
]
|
||||
|
||||
assert _validate({"elbv2_listener_pqc_tls_allowed_policies": policies}) == {
|
||||
"elbv2_listener_pqc_tls_allowed_policies": policies
|
||||
}
|
||||
|
||||
def test_key_is_exposed_in_scan_config_schema(self):
|
||||
aws_properties = SCAN_CONFIG_SCHEMA["properties"]["aws"]["properties"]
|
||||
|
||||
assert "elbv2_listener_pqc_tls_allowed_policies" in aws_properties
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value",
|
||||
[
|
||||
"ELBSecurityPolicy-TLS13-1-2-Res-2021-06",
|
||||
["ELBSecurityPolicy-TLS13-1-2-Res-2021-06", 123],
|
||||
],
|
||||
)
|
||||
def test_invalid_policy_values_are_dropped(self, value):
|
||||
assert _validate({"elbv2_listener_pqc_tls_allowed_policies": value}) == {}
|
||||
|
||||
|
||||
class Test_AWS_Secrets_Ignore_Files:
|
||||
def test_valid_file_patterns_round_trip(self):
|
||||
files = ["*.deps.json", "vendor/*.js"]
|
||||
|
||||
+673
@@ -0,0 +1,673 @@
|
||||
"""Tests for elbv2_listener_pqc_tls_enabled check."""
|
||||
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
from boto3 import client, resource
|
||||
from botocore.exceptions import ClientError
|
||||
from moto import mock_aws
|
||||
|
||||
from tests.providers.aws.utils import (
|
||||
AWS_REGION_EU_WEST_1,
|
||||
AWS_REGION_EU_WEST_1_AZA,
|
||||
AWS_REGION_EU_WEST_1_AZB,
|
||||
AWS_REGION_US_EAST_1,
|
||||
set_mocked_aws_provider,
|
||||
)
|
||||
|
||||
|
||||
class Test_elbv2_listener_pqc_tls_enabled:
|
||||
"""Test cases for the elbv2_listener_pqc_tls_enabled check."""
|
||||
|
||||
def _create_alb_infrastructure(self, region=AWS_REGION_EU_WEST_1):
|
||||
"""Helper to create VPC, subnets, security group, target group, and ALB.
|
||||
|
||||
Returns a tuple of (elbv2_client, lb_response, target_group_arn).
|
||||
"""
|
||||
conn = client("elbv2", region_name=region)
|
||||
ec2 = resource("ec2", region_name=region)
|
||||
|
||||
security_group = ec2.create_security_group(
|
||||
GroupName="a-security-group", Description="First One"
|
||||
)
|
||||
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
|
||||
subnet1 = ec2.create_subnet(
|
||||
VpcId=vpc.id,
|
||||
CidrBlock="172.28.7.192/26",
|
||||
AvailabilityZone=f"{region}a",
|
||||
)
|
||||
subnet2 = ec2.create_subnet(
|
||||
VpcId=vpc.id,
|
||||
CidrBlock="172.28.7.0/26",
|
||||
AvailabilityZone=f"{region}b",
|
||||
)
|
||||
|
||||
lb = conn.create_load_balancer(
|
||||
Name="my-lb",
|
||||
Subnets=[subnet1.id, subnet2.id],
|
||||
SecurityGroups=[security_group.id],
|
||||
Scheme="internal",
|
||||
Type="application",
|
||||
)["LoadBalancers"][0]
|
||||
|
||||
response = conn.create_target_group(
|
||||
Name="a-target",
|
||||
Protocol="HTTP",
|
||||
Port=8080,
|
||||
VpcId=vpc.id,
|
||||
HealthCheckProtocol="HTTP",
|
||||
HealthCheckPort="8080",
|
||||
HealthCheckPath="/",
|
||||
HealthCheckIntervalSeconds=5,
|
||||
HealthCheckTimeoutSeconds=3,
|
||||
HealthyThresholdCount=5,
|
||||
UnhealthyThresholdCount=2,
|
||||
Matcher={"HttpCode": "200"},
|
||||
)
|
||||
target_group_arn = response["TargetGroups"][0]["TargetGroupArn"]
|
||||
|
||||
return conn, lb, target_group_arn
|
||||
|
||||
def _create_nlb_infrastructure(self, region=AWS_REGION_EU_WEST_1):
|
||||
"""Helper to create VPC, subnets, target group, and NLB.
|
||||
|
||||
Returns a tuple of (elbv2_client, lb_response, target_group_arn).
|
||||
"""
|
||||
conn = client("elbv2", region_name=region)
|
||||
ec2 = resource("ec2", region_name=region)
|
||||
|
||||
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
|
||||
subnet1 = ec2.create_subnet(
|
||||
VpcId=vpc.id,
|
||||
CidrBlock="172.28.7.192/26",
|
||||
AvailabilityZone=AWS_REGION_EU_WEST_1_AZA,
|
||||
)
|
||||
subnet2 = ec2.create_subnet(
|
||||
VpcId=vpc.id,
|
||||
CidrBlock="172.28.7.0/26",
|
||||
AvailabilityZone=AWS_REGION_EU_WEST_1_AZB,
|
||||
)
|
||||
|
||||
lb = conn.create_load_balancer(
|
||||
Name="my-nlb",
|
||||
Subnets=[subnet1.id, subnet2.id],
|
||||
Scheme="internal",
|
||||
Type="network",
|
||||
)["LoadBalancers"][0]
|
||||
|
||||
response = conn.create_target_group(
|
||||
Name="a-target",
|
||||
Protocol="TCP",
|
||||
Port=8080,
|
||||
VpcId=vpc.id,
|
||||
)
|
||||
target_group_arn = response["TargetGroups"][0]["TargetGroupArn"]
|
||||
|
||||
return conn, lb, target_group_arn
|
||||
|
||||
def _mock_and_execute(self, audit_config=None):
|
||||
"""Helper to set up mocks and execute the check.
|
||||
|
||||
Must be called inside a @mock_aws decorated method, after AWS
|
||||
resources have been created with moto.
|
||||
"""
|
||||
from prowler.providers.aws.services.elbv2.elbv2_service import ELBv2
|
||||
|
||||
audit_config = audit_config or {}
|
||||
aws_provider = set_mocked_aws_provider(
|
||||
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1],
|
||||
create_default_organization=False,
|
||||
audit_config=audit_config,
|
||||
)
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_aws_provider(
|
||||
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1],
|
||||
audit_config=audit_config,
|
||||
),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.aws.services.elbv2.elbv2_listener_pqc_tls_enabled.elbv2_listener_pqc_tls_enabled.elbv2_client",
|
||||
new=ELBv2(aws_provider),
|
||||
),
|
||||
):
|
||||
from prowler.providers.aws.services.elbv2.elbv2_listener_pqc_tls_enabled.elbv2_listener_pqc_tls_enabled import (
|
||||
elbv2_listener_pqc_tls_enabled,
|
||||
)
|
||||
|
||||
check = elbv2_listener_pqc_tls_enabled()
|
||||
return check.execute()
|
||||
|
||||
def _assert_listener_arn_in_status(self, result, listener_arn):
|
||||
"""Assert that remediation details identify the affected listener ARN."""
|
||||
assert listener_arn in result[0].status_extended
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# No-resource scenarios
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
@mock_aws
|
||||
def test_no_load_balancers(self):
|
||||
"""Test that no findings are returned when there are no load balancers."""
|
||||
result = self._mock_and_execute()
|
||||
assert len(result) == 0
|
||||
|
||||
@mock_aws
|
||||
def test_lb_with_http_listener_only(self):
|
||||
"""Test PASS when a load balancer has no HTTPS/TLS listeners."""
|
||||
conn, lb, target_group_arn = self._create_alb_infrastructure()
|
||||
conn.create_listener(
|
||||
LoadBalancerArn=lb["LoadBalancerArn"],
|
||||
Protocol="HTTP",
|
||||
Port=80,
|
||||
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
|
||||
)
|
||||
|
||||
result = self._mock_and_execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert result[0].status_extended == "ELBv2 my-lb has no HTTPS/TLS listeners."
|
||||
assert result[0].resource_id == "my-lb"
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# PASS scenarios
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"ssl_policy",
|
||||
[
|
||||
"ELBSecurityPolicy-TLS13-1-2-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-2-Ext1-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-2-Ext2-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-2-Res-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-3-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-2-FIPS-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-2-Ext0-FIPS-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-2-Ext1-FIPS-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-2-Ext2-FIPS-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-2-Res-FIPS-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-3-FIPS-PQ-2025-09",
|
||||
],
|
||||
)
|
||||
@mock_aws
|
||||
def test_listener_with_pq_policy_pass(self, ssl_policy):
|
||||
"""Test PASS when HTTPS listener uses an allowed PQ TLS policy."""
|
||||
conn, lb, target_group_arn = self._create_alb_infrastructure()
|
||||
conn.create_listener(
|
||||
LoadBalancerArn=lb["LoadBalancerArn"],
|
||||
Protocol="HTTPS",
|
||||
Port=443,
|
||||
SslPolicy=ssl_policy,
|
||||
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
|
||||
)
|
||||
|
||||
result = self._mock_and_execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "ELBv2 my-lb has all HTTPS/TLS listeners using a post-quantum TLS policy."
|
||||
)
|
||||
assert result[0].resource_id == "my-lb"
|
||||
assert result[0].resource_arn == lb["LoadBalancerArn"]
|
||||
assert result[0].region == AWS_REGION_EU_WEST_1
|
||||
|
||||
@mock_aws
|
||||
def test_multiple_https_listeners_all_pq_pass(self):
|
||||
"""Test PASS when a LB has multiple HTTPS listeners all using PQ policies."""
|
||||
conn, lb, target_group_arn = self._create_alb_infrastructure()
|
||||
conn.create_listener(
|
||||
LoadBalancerArn=lb["LoadBalancerArn"],
|
||||
Protocol="HTTPS",
|
||||
Port=443,
|
||||
SslPolicy="ELBSecurityPolicy-TLS13-1-2-PQ-2025-09",
|
||||
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
|
||||
)
|
||||
conn.create_listener(
|
||||
LoadBalancerArn=lb["LoadBalancerArn"],
|
||||
Protocol="HTTPS",
|
||||
Port=8443,
|
||||
SslPolicy="ELBSecurityPolicy-TLS13-1-3-FIPS-PQ-2025-09",
|
||||
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
|
||||
)
|
||||
|
||||
result = self._mock_and_execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "ELBv2 my-lb has all HTTPS/TLS listeners using a post-quantum TLS policy."
|
||||
)
|
||||
assert result[0].resource_id == "my-lb"
|
||||
|
||||
@mock_aws
|
||||
def test_mixed_http_and_pq_https_listeners_pass(self):
|
||||
"""Test PASS when LB has both HTTP and HTTPS listeners, HTTPS using PQ policy."""
|
||||
conn, lb, target_group_arn = self._create_alb_infrastructure()
|
||||
# HTTP listener (out of scope)
|
||||
conn.create_listener(
|
||||
LoadBalancerArn=lb["LoadBalancerArn"],
|
||||
Protocol="HTTP",
|
||||
Port=80,
|
||||
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
|
||||
)
|
||||
# HTTPS listener with PQ policy
|
||||
conn.create_listener(
|
||||
LoadBalancerArn=lb["LoadBalancerArn"],
|
||||
Protocol="HTTPS",
|
||||
Port=443,
|
||||
SslPolicy="ELBSecurityPolicy-TLS13-1-2-PQ-2025-09",
|
||||
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
|
||||
)
|
||||
|
||||
result = self._mock_and_execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert result[0].resource_id == "my-lb"
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# FAIL scenarios
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
@mock_aws
|
||||
def test_listener_with_classical_tls_policy_fail(self):
|
||||
"""Test FAIL when HTTPS listener uses a classical (non-PQ) TLS policy."""
|
||||
conn, lb, target_group_arn = self._create_alb_infrastructure()
|
||||
listener = conn.create_listener(
|
||||
LoadBalancerArn=lb["LoadBalancerArn"],
|
||||
Protocol="HTTPS",
|
||||
Port=443,
|
||||
SslPolicy="ELBSecurityPolicy-TLS13-1-2-2021-06",
|
||||
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
|
||||
)["Listeners"][0]
|
||||
|
||||
result = self._mock_and_execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"ELBv2 my-lb has HTTPS/TLS listeners without post-quantum TLS policy: HTTPS:443 ({listener['ListenerArn']}) uses ELBSecurityPolicy-TLS13-1-2-2021-06."
|
||||
)
|
||||
self._assert_listener_arn_in_status(result, listener["ListenerArn"])
|
||||
assert result[0].resource_id == "my-lb"
|
||||
assert result[0].resource_arn == lb["LoadBalancerArn"]
|
||||
assert result[0].region == AWS_REGION_EU_WEST_1
|
||||
|
||||
@mock_aws
|
||||
def test_listener_with_tls_1_0_pq_policy_fails_by_default(self):
|
||||
"""Test FAIL when HTTPS listener uses a TLS 1.0-minimum PQ policy by default."""
|
||||
conn, lb, target_group_arn = self._create_alb_infrastructure()
|
||||
listener = conn.create_listener(
|
||||
LoadBalancerArn=lb["LoadBalancerArn"],
|
||||
Protocol="HTTPS",
|
||||
Port=443,
|
||||
SslPolicy="ELBSecurityPolicy-TLS13-1-0-PQ-2025-09",
|
||||
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
|
||||
)["Listeners"][0]
|
||||
|
||||
result = self._mock_and_execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"ELBv2 my-lb has HTTPS/TLS listeners without post-quantum TLS policy: HTTPS:443 ({listener['ListenerArn']}) uses ELBSecurityPolicy-TLS13-1-0-PQ-2025-09."
|
||||
)
|
||||
assert result[0].resource_id == "my-lb"
|
||||
|
||||
@mock_aws
|
||||
def test_listener_with_empty_ssl_policy_fail(self):
|
||||
"""Test FAIL when an HTTPS listener has no SSL policy value."""
|
||||
conn, lb, target_group_arn = self._create_alb_infrastructure()
|
||||
listener = conn.create_listener(
|
||||
LoadBalancerArn=lb["LoadBalancerArn"],
|
||||
Protocol="HTTPS",
|
||||
Port=443,
|
||||
SslPolicy="ELBSecurityPolicy-TLS13-1-2-2021-06",
|
||||
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
|
||||
)["Listeners"][0]
|
||||
|
||||
from prowler.providers.aws.services.elbv2.elbv2_service import ELBv2
|
||||
|
||||
aws_provider = set_mocked_aws_provider(
|
||||
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1],
|
||||
create_default_organization=False,
|
||||
)
|
||||
service = ELBv2(aws_provider)
|
||||
service.loadbalancersv2[lb["LoadBalancerArn"]].listeners[
|
||||
listener["ListenerArn"]
|
||||
].ssl_policy = ""
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.aws.services.elbv2.elbv2_listener_pqc_tls_enabled.elbv2_listener_pqc_tls_enabled.elbv2_client",
|
||||
new=service,
|
||||
),
|
||||
):
|
||||
from prowler.providers.aws.services.elbv2.elbv2_listener_pqc_tls_enabled.elbv2_listener_pqc_tls_enabled import (
|
||||
elbv2_listener_pqc_tls_enabled,
|
||||
)
|
||||
|
||||
result = elbv2_listener_pqc_tls_enabled().execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"ELBv2 my-lb has HTTPS/TLS listeners without post-quantum TLS policy: HTTPS:443 ({listener['ListenerArn']}) uses <none>."
|
||||
)
|
||||
assert result[0].resource_id == "my-lb"
|
||||
|
||||
@mock_aws
|
||||
def test_listener_with_legacy_policy_fail(self):
|
||||
"""Test FAIL when HTTPS listener uses a legacy TLS policy."""
|
||||
conn, lb, target_group_arn = self._create_alb_infrastructure()
|
||||
listener = conn.create_listener(
|
||||
LoadBalancerArn=lb["LoadBalancerArn"],
|
||||
Protocol="HTTPS",
|
||||
Port=443,
|
||||
SslPolicy="ELBSecurityPolicy-TLS-1-1-2017-01",
|
||||
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
|
||||
)["Listeners"][0]
|
||||
|
||||
result = self._mock_and_execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"ELBv2 my-lb has HTTPS/TLS listeners without post-quantum TLS policy: HTTPS:443 ({listener['ListenerArn']}) uses ELBSecurityPolicy-TLS-1-1-2017-01."
|
||||
)
|
||||
self._assert_listener_arn_in_status(result, listener["ListenerArn"])
|
||||
assert result[0].resource_id == "my-lb"
|
||||
assert result[0].resource_arn == lb["LoadBalancerArn"]
|
||||
|
||||
@mock_aws
|
||||
def test_mixed_pq_and_non_pq_listeners_fail(self):
|
||||
"""Test FAIL when LB has one PQ listener and one non-PQ listener."""
|
||||
conn, lb, target_group_arn = self._create_alb_infrastructure()
|
||||
# PQ listener
|
||||
conn.create_listener(
|
||||
LoadBalancerArn=lb["LoadBalancerArn"],
|
||||
Protocol="HTTPS",
|
||||
Port=443,
|
||||
SslPolicy="ELBSecurityPolicy-TLS13-1-2-PQ-2025-09",
|
||||
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
|
||||
)
|
||||
# Non-PQ listener
|
||||
conn.create_listener(
|
||||
LoadBalancerArn=lb["LoadBalancerArn"],
|
||||
Protocol="HTTPS",
|
||||
Port=8443,
|
||||
SslPolicy="ELBSecurityPolicy-TLS13-1-2-2021-06",
|
||||
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
|
||||
)
|
||||
|
||||
result = self._mock_and_execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert "ELBSecurityPolicy-TLS13-1-2-2021-06" in result[0].status_extended
|
||||
assert result[0].resource_id == "my-lb"
|
||||
|
||||
@mock_aws
|
||||
def test_multiple_non_pq_listeners_lists_all_policies_fail(self):
|
||||
"""Test FAIL lists all non-PQ policies when multiple listeners are non-compliant."""
|
||||
conn, lb, target_group_arn = self._create_alb_infrastructure()
|
||||
conn.create_listener(
|
||||
LoadBalancerArn=lb["LoadBalancerArn"],
|
||||
Protocol="HTTPS",
|
||||
Port=443,
|
||||
SslPolicy="ELBSecurityPolicy-TLS-1-1-2017-01",
|
||||
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
|
||||
)
|
||||
conn.create_listener(
|
||||
LoadBalancerArn=lb["LoadBalancerArn"],
|
||||
Protocol="HTTPS",
|
||||
Port=8443,
|
||||
SslPolicy="ELBSecurityPolicy-TLS13-1-2-2021-06",
|
||||
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
|
||||
)
|
||||
|
||||
result = self._mock_and_execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
# Both non-PQ policies should be mentioned
|
||||
assert "ELBSecurityPolicy-TLS-1-1-2017-01" in result[0].status_extended
|
||||
assert "ELBSecurityPolicy-TLS13-1-2-2021-06" in result[0].status_extended
|
||||
assert result[0].resource_id == "my-lb"
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Custom audit_config scenario
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
@mock_aws
|
||||
def test_custom_audit_config_narrows_allowlist(self):
|
||||
"""Test that a custom audit_config allowlist is honoured.
|
||||
|
||||
When elbv2_listener_pqc_tls_allowed_policies is overridden to only
|
||||
allow FIPS PQ policies, a non-FIPS PQ policy should FAIL.
|
||||
"""
|
||||
conn, lb, target_group_arn = self._create_alb_infrastructure()
|
||||
# Use a PQ policy that is in the default list but NOT in our custom list
|
||||
conn.create_listener(
|
||||
LoadBalancerArn=lb["LoadBalancerArn"],
|
||||
Protocol="HTTPS",
|
||||
Port=443,
|
||||
SslPolicy="ELBSecurityPolicy-TLS13-1-2-PQ-2025-09",
|
||||
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
|
||||
)
|
||||
|
||||
custom_config = {
|
||||
"elbv2_listener_pqc_tls_allowed_policies": [
|
||||
"ELBSecurityPolicy-TLS13-1-2-FIPS-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-3-FIPS-PQ-2025-09",
|
||||
]
|
||||
}
|
||||
|
||||
result = self._mock_and_execute(audit_config=custom_config)
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert "ELBSecurityPolicy-TLS13-1-2-PQ-2025-09" in result[0].status_extended
|
||||
|
||||
@mock_aws
|
||||
def test_custom_audit_config_fips_policy_pass(self):
|
||||
"""Test PASS when listener uses a FIPS PQ policy and custom config allows it."""
|
||||
conn, lb, target_group_arn = self._create_alb_infrastructure()
|
||||
conn.create_listener(
|
||||
LoadBalancerArn=lb["LoadBalancerArn"],
|
||||
Protocol="HTTPS",
|
||||
Port=443,
|
||||
SslPolicy="ELBSecurityPolicy-TLS13-1-2-FIPS-PQ-2025-09",
|
||||
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
|
||||
)
|
||||
|
||||
custom_config = {
|
||||
"elbv2_listener_pqc_tls_allowed_policies": [
|
||||
"ELBSecurityPolicy-TLS13-1-2-FIPS-PQ-2025-09",
|
||||
"ELBSecurityPolicy-TLS13-1-3-FIPS-PQ-2025-09",
|
||||
]
|
||||
}
|
||||
|
||||
result = self._mock_and_execute(audit_config=custom_config)
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "ELBv2 my-lb has all HTTPS/TLS listeners using a post-quantum TLS policy."
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_custom_audit_config_allows_tls_1_0_pq_policy(self):
|
||||
"""Test PASS when custom config explicitly allows a TLS 1.0-minimum PQ policy."""
|
||||
conn, lb, target_group_arn = self._create_alb_infrastructure()
|
||||
conn.create_listener(
|
||||
LoadBalancerArn=lb["LoadBalancerArn"],
|
||||
Protocol="HTTPS",
|
||||
Port=443,
|
||||
SslPolicy="ELBSecurityPolicy-TLS13-1-0-PQ-2025-09",
|
||||
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
|
||||
)
|
||||
|
||||
result = self._mock_and_execute(
|
||||
audit_config={
|
||||
"elbv2_listener_pqc_tls_allowed_policies": [
|
||||
"ELBSecurityPolicy-TLS13-1-0-PQ-2025-09"
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "ELBv2 my-lb has all HTTPS/TLS listeners using a post-quantum TLS policy."
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_empty_audit_config_allowlist_fails_tls_listener(self):
|
||||
"""Test an intentionally empty allowlist is honoured."""
|
||||
conn, lb, target_group_arn = self._create_alb_infrastructure()
|
||||
conn.create_listener(
|
||||
LoadBalancerArn=lb["LoadBalancerArn"],
|
||||
Protocol="HTTPS",
|
||||
Port=443,
|
||||
SslPolicy="ELBSecurityPolicy-TLS13-1-2-PQ-2025-09",
|
||||
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
|
||||
)
|
||||
|
||||
result = self._mock_and_execute(
|
||||
audit_config={"elbv2_listener_pqc_tls_allowed_policies": []}
|
||||
)
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert result[0].resource_id == "my-lb"
|
||||
assert "ELBSecurityPolicy-TLS13-1-2-PQ-2025-09" in result[0].status_extended
|
||||
|
||||
@mock_aws
|
||||
def test_tls_listener_with_pq_policy_pass(self):
|
||||
"""Test PASS when a TLS listener uses an allowed PQ TLS policy."""
|
||||
conn, lb, target_group_arn = self._create_nlb_infrastructure()
|
||||
conn.create_listener(
|
||||
LoadBalancerArn=lb["LoadBalancerArn"],
|
||||
Protocol="TLS",
|
||||
Port=443,
|
||||
SslPolicy="ELBSecurityPolicy-TLS13-1-2-PQ-2025-09",
|
||||
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
|
||||
)
|
||||
|
||||
result = self._mock_and_execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert result[0].resource_id == "my-nlb"
|
||||
assert result[0].resource_arn == lb["LoadBalancerArn"]
|
||||
|
||||
@mock_aws
|
||||
def test_nlb_with_tcp_listener_only(self):
|
||||
"""Test PASS when an NLB has no HTTPS/TLS listeners."""
|
||||
conn, lb, target_group_arn = self._create_nlb_infrastructure()
|
||||
conn.create_listener(
|
||||
LoadBalancerArn=lb["LoadBalancerArn"],
|
||||
Protocol="TCP",
|
||||
Port=80,
|
||||
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
|
||||
)
|
||||
|
||||
result = self._mock_and_execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert result[0].status_extended == "ELBv2 my-nlb has no HTTPS/TLS listeners."
|
||||
assert result[0].resource_id == "my-nlb"
|
||||
assert result[0].resource_arn == lb["LoadBalancerArn"]
|
||||
|
||||
@mock_aws
|
||||
def test_tls_listener_with_non_pq_policy_fail(self):
|
||||
"""Test FAIL when a TLS listener uses a non-PQ TLS policy."""
|
||||
conn, lb, target_group_arn = self._create_nlb_infrastructure()
|
||||
listener = conn.create_listener(
|
||||
LoadBalancerArn=lb["LoadBalancerArn"],
|
||||
Protocol="TLS",
|
||||
Port=443,
|
||||
SslPolicy="ELBSecurityPolicy-TLS13-1-2-2021-06",
|
||||
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
|
||||
)["Listeners"][0]
|
||||
|
||||
result = self._mock_and_execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"ELBv2 my-nlb has HTTPS/TLS listeners without post-quantum TLS policy: TLS:443 ({listener['ListenerArn']}) uses ELBSecurityPolicy-TLS13-1-2-2021-06."
|
||||
)
|
||||
self._assert_listener_arn_in_status(result, listener["ListenerArn"])
|
||||
assert result[0].resource_id == "my-nlb"
|
||||
assert result[0].resource_arn == lb["LoadBalancerArn"]
|
||||
|
||||
@mock_aws
|
||||
def test_listener_discovery_failure_returns_no_findings(self):
|
||||
"""Test no findings when listeners cannot be retrieved for a load balancer."""
|
||||
conn, lb, _ = self._create_alb_infrastructure()
|
||||
|
||||
from prowler.providers.aws.services.elbv2.elbv2_service import ELBv2
|
||||
|
||||
aws_provider = set_mocked_aws_provider(
|
||||
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1],
|
||||
create_default_organization=False,
|
||||
)
|
||||
|
||||
service = ELBv2(aws_provider)
|
||||
error = ClientError(
|
||||
{
|
||||
"Error": {
|
||||
"Code": "AccessDenied",
|
||||
"Message": "User is not authorized to perform: elasticloadbalancing:DescribeListeners",
|
||||
}
|
||||
},
|
||||
"DescribeListeners",
|
||||
)
|
||||
service.loadbalancersv2[lb["LoadBalancerArn"]].listeners = {}
|
||||
service.regional_clients[AWS_REGION_EU_WEST_1] = mock.MagicMock(
|
||||
region=AWS_REGION_EU_WEST_1,
|
||||
get_paginator=mock.MagicMock(side_effect=error),
|
||||
)
|
||||
service._describe_listeners(
|
||||
(lb["LoadBalancerArn"], service.loadbalancersv2[lb["LoadBalancerArn"]])
|
||||
)
|
||||
|
||||
assert service.loadbalancersv2[lb["LoadBalancerArn"]].listener_discovery_failed
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.aws.services.elbv2.elbv2_listener_pqc_tls_enabled.elbv2_listener_pqc_tls_enabled.elbv2_client",
|
||||
new=service,
|
||||
),
|
||||
):
|
||||
from prowler.providers.aws.services.elbv2.elbv2_listener_pqc_tls_enabled.elbv2_listener_pqc_tls_enabled import (
|
||||
elbv2_listener_pqc_tls_enabled,
|
||||
)
|
||||
|
||||
result = elbv2_listener_pqc_tls_enabled().execute()
|
||||
|
||||
assert len(result) == 0
|
||||
Reference in New Issue
Block a user