complio 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. CHANGELOG.md +208 -0
  2. README.md +343 -0
  3. complio/__init__.py +48 -0
  4. complio/cli/__init__.py +0 -0
  5. complio/cli/banner.py +87 -0
  6. complio/cli/commands/__init__.py +0 -0
  7. complio/cli/commands/history.py +439 -0
  8. complio/cli/commands/scan.py +700 -0
  9. complio/cli/main.py +115 -0
  10. complio/cli/output.py +338 -0
  11. complio/config/__init__.py +17 -0
  12. complio/config/settings.py +333 -0
  13. complio/connectors/__init__.py +9 -0
  14. complio/connectors/aws/__init__.py +0 -0
  15. complio/connectors/aws/client.py +342 -0
  16. complio/connectors/base.py +135 -0
  17. complio/core/__init__.py +10 -0
  18. complio/core/registry.py +228 -0
  19. complio/core/runner.py +351 -0
  20. complio/py.typed +0 -0
  21. complio/reporters/__init__.py +7 -0
  22. complio/reporters/generator.py +417 -0
  23. complio/tests_library/__init__.py +0 -0
  24. complio/tests_library/base.py +492 -0
  25. complio/tests_library/identity/__init__.py +0 -0
  26. complio/tests_library/identity/access_key_rotation.py +302 -0
  27. complio/tests_library/identity/mfa_enforcement.py +327 -0
  28. complio/tests_library/identity/root_account_protection.py +470 -0
  29. complio/tests_library/infrastructure/__init__.py +0 -0
  30. complio/tests_library/infrastructure/cloudtrail_encryption.py +286 -0
  31. complio/tests_library/infrastructure/cloudtrail_log_validation.py +274 -0
  32. complio/tests_library/infrastructure/cloudtrail_logging.py +400 -0
  33. complio/tests_library/infrastructure/ebs_encryption.py +244 -0
  34. complio/tests_library/infrastructure/ec2_security_groups.py +321 -0
  35. complio/tests_library/infrastructure/iam_password_policy.py +460 -0
  36. complio/tests_library/infrastructure/nacl_security.py +356 -0
  37. complio/tests_library/infrastructure/rds_encryption.py +252 -0
  38. complio/tests_library/infrastructure/s3_encryption.py +301 -0
  39. complio/tests_library/infrastructure/s3_public_access.py +369 -0
  40. complio/tests_library/infrastructure/secrets_manager_encryption.py +248 -0
  41. complio/tests_library/infrastructure/vpc_flow_logs.py +287 -0
  42. complio/tests_library/logging/__init__.py +0 -0
  43. complio/tests_library/logging/cloudwatch_alarms.py +354 -0
  44. complio/tests_library/logging/cloudwatch_logs_encryption.py +281 -0
  45. complio/tests_library/logging/cloudwatch_retention.py +252 -0
  46. complio/tests_library/logging/config_enabled.py +393 -0
  47. complio/tests_library/logging/eventbridge_rules.py +460 -0
  48. complio/tests_library/logging/guardduty_enabled.py +436 -0
  49. complio/tests_library/logging/security_hub_enabled.py +416 -0
  50. complio/tests_library/logging/sns_encryption.py +273 -0
  51. complio/tests_library/network/__init__.py +0 -0
  52. complio/tests_library/network/alb_nlb_security.py +421 -0
  53. complio/tests_library/network/api_gateway_security.py +452 -0
  54. complio/tests_library/network/cloudfront_https.py +332 -0
  55. complio/tests_library/network/direct_connect_security.py +343 -0
  56. complio/tests_library/network/nacl_configuration.py +367 -0
  57. complio/tests_library/network/network_firewall.py +355 -0
  58. complio/tests_library/network/transit_gateway_security.py +318 -0
  59. complio/tests_library/network/vpc_endpoints_security.py +339 -0
  60. complio/tests_library/network/vpn_security.py +333 -0
  61. complio/tests_library/network/waf_configuration.py +428 -0
  62. complio/tests_library/security/__init__.py +0 -0
  63. complio/tests_library/security/kms_key_rotation.py +314 -0
  64. complio/tests_library/storage/__init__.py +0 -0
  65. complio/tests_library/storage/backup_encryption.py +288 -0
  66. complio/tests_library/storage/dynamodb_encryption.py +280 -0
  67. complio/tests_library/storage/efs_encryption.py +257 -0
  68. complio/tests_library/storage/elasticache_encryption.py +370 -0
  69. complio/tests_library/storage/redshift_encryption.py +252 -0
  70. complio/tests_library/storage/s3_versioning.py +264 -0
  71. complio/utils/__init__.py +26 -0
  72. complio/utils/errors.py +179 -0
  73. complio/utils/exceptions.py +151 -0
  74. complio/utils/history.py +243 -0
  75. complio/utils/logger.py +391 -0
  76. complio-0.1.1.dist-info/METADATA +385 -0
  77. complio-0.1.1.dist-info/RECORD +79 -0
  78. complio-0.1.1.dist-info/WHEEL +4 -0
  79. complio-0.1.1.dist-info/entry_points.txt +3 -0
File without changes
@@ -0,0 +1,421 @@
1
+ """
2
+ Application Load Balancer and Network Load Balancer security compliance test.
3
+
4
+ Checks that ALBs and NLBs use secure configurations including HTTPS listeners.
5
+
6
+ ISO 27001 Control: A.8.22 - Network segregation
7
+ Requirement: Load balancers must use secure protocols and configurations
8
+
9
+ Example:
10
+ >>> from complio.connectors.aws.client import AWSConnector
11
+ >>> from complio.tests_library.network.alb_nlb_security import ALBNLBSecurityTest
12
+ >>>
13
+ >>> connector = AWSConnector("production", "us-east-1")
14
+ >>> connector.connect()
15
+ >>>
16
+ >>> test = ALBNLBSecurityTest(connector)
17
+ >>> result = test.run()
18
+ >>> print(f"Passed: {result.passed}, Score: {result.score}")
19
+ """
20
+
21
+ from typing import Any, Dict, List
22
+
23
+ from botocore.exceptions import ClientError
24
+
25
+ from complio.connectors.aws.client import AWSConnector
26
+ from complio.tests_library.base import (
27
+ ComplianceTest,
28
+ Severity,
29
+ TestResult,
30
+ TestStatus,
31
+ )
32
+
33
+
34
+ class ALBNLBSecurityTest(ComplianceTest):
35
+ """Test for ALB/NLB security compliance.
36
+
37
+ Verifies that Application and Network Load Balancers use secure configurations:
38
+ - ALBs should use HTTPS listeners
39
+ - HTTPS listeners should use modern TLS versions (TLS 1.2+)
40
+ - Should have access logging enabled
41
+ - Should have deletion protection enabled (recommended)
42
+
43
+ Compliance Requirements:
44
+ - ALBs should have at least one HTTPS listener
45
+ - HTTPS listeners must use TLS 1.2 or higher
46
+ - Access logging should be enabled for audit trail
47
+ - Deletion protection recommended for production
48
+
49
+ Scoring:
50
+ - 100% if all load balancers meet security requirements
51
+ - Proportional score based on compliant/total ratio
52
+ - 100% if no load balancers exist
53
+
54
+ Example:
55
+ >>> test = ALBNLBSecurityTest(connector)
56
+ >>> result = test.execute()
57
+ >>> for finding in result.findings:
58
+ ... print(f"{finding.resource_id}: {finding.title}")
59
+ """
60
+
61
+ def __init__(self, connector: AWSConnector) -> None:
62
+ """Initialize ALB/NLB security test.
63
+
64
+ Args:
65
+ connector: AWS connector instance
66
+ """
67
+ super().__init__(
68
+ test_id="alb_nlb_security",
69
+ test_name="ALB/NLB Security Check",
70
+ description="Verify Application and Network Load Balancers use secure configurations",
71
+ control_id="A.8.22",
72
+ connector=connector,
73
+ scope="regional",
74
+ )
75
+
76
+ def _check_alb_security(self, lb_arn: str, lb_name: str, listeners: List[Dict], attributes: Dict) -> tuple:
77
+ """Check ALB security configuration.
78
+
79
+ Args:
80
+ lb_arn: Load balancer ARN
81
+ lb_name: Load balancer name
82
+ listeners: List of listener configurations
83
+ attributes: Load balancer attributes
84
+
85
+ Returns:
86
+ Tuple of (has_issues, issues_list, severity)
87
+ """
88
+ issues = []
89
+ severity = Severity.MEDIUM
90
+
91
+ # Check if there's at least one HTTPS listener
92
+ https_listeners = [l for l in listeners if l.get("Protocol") == "HTTPS"]
93
+ http_listeners = [l for l in listeners if l.get("Protocol") == "HTTP"]
94
+
95
+ if len(https_listeners) == 0 and len(http_listeners) > 0:
96
+ issues.append("No HTTPS listeners configured (only HTTP)")
97
+ severity = Severity.HIGH
98
+
99
+ # Check HTTPS listener SSL policies
100
+ for listener in https_listeners:
101
+ ssl_policy = listener.get("SslPolicy", "")
102
+ # Check for outdated SSL policies
103
+ if ssl_policy and any(weak in ssl_policy for weak in ["ELBSecurityPolicy-2016", "ELBSecurityPolicy-TLS"]):
104
+ if "TLS-1-2" not in ssl_policy:
105
+ issues.append(f"HTTPS listener uses outdated SSL policy: {ssl_policy}")
106
+ severity = Severity.HIGH
107
+
108
+ # Check if access logging is enabled
109
+ access_logs_enabled = False
110
+ for attr in attributes:
111
+ if attr.get("Key") == "access_logs.s3.enabled":
112
+ access_logs_enabled = attr.get("Value") == "true"
113
+ break
114
+
115
+ if not access_logs_enabled:
116
+ issues.append("Access logging not enabled")
117
+ if severity == Severity.MEDIUM:
118
+ severity = Severity.MEDIUM
119
+
120
+ # Check deletion protection (best practice, not critical)
121
+ deletion_protection = False
122
+ for attr in attributes:
123
+ if attr.get("Key") == "deletion_protection.enabled":
124
+ deletion_protection = attr.get("Value") == "true"
125
+ break
126
+
127
+ if not deletion_protection:
128
+ issues.append("Deletion protection not enabled (recommended for production)")
129
+
130
+ return len(issues) > 0, issues, severity
131
+
132
+ def _check_nlb_security(self, lb_arn: str, lb_name: str, listeners: List[Dict], attributes: Dict) -> tuple:
133
+ """Check NLB security configuration.
134
+
135
+ Args:
136
+ lb_arn: Load balancer ARN
137
+ lb_name: Load balancer name
138
+ listeners: List of listener configurations
139
+ attributes: Load balancer attributes
140
+
141
+ Returns:
142
+ Tuple of (has_issues, issues_list, severity)
143
+ """
144
+ issues = []
145
+ severity = Severity.MEDIUM
146
+
147
+ # NLBs can use TLS listeners for secure traffic
148
+ tls_listeners = [l for l in listeners if l.get("Protocol") == "TLS"]
149
+ tcp_listeners = [l for l in listeners if l.get("Protocol") == "TCP"]
150
+
151
+ # Check if TLS is used where appropriate
152
+ if len(tls_listeners) == 0 and len(tcp_listeners) > 0:
153
+ # Check if any TCP listener is on standard HTTPS port (443)
154
+ https_tcp_listeners = [l for l in tcp_listeners if l.get("Port") == 443]
155
+ if https_tcp_listeners:
156
+ issues.append("TCP listener on port 443 without TLS (should use TLS protocol)")
157
+ severity = Severity.HIGH
158
+
159
+ # Check TLS listener SSL policies
160
+ for listener in tls_listeners:
161
+ ssl_policy = listener.get("SslPolicy", "")
162
+ if ssl_policy and "TLS-1-2" not in ssl_policy:
163
+ issues.append(f"TLS listener uses outdated SSL policy: {ssl_policy}")
164
+ severity = Severity.HIGH
165
+
166
+ # Check if access logging is enabled (NLB supports this via S3)
167
+ access_logs_enabled = False
168
+ for attr in attributes:
169
+ if attr.get("Key") == "access_logs.s3.enabled":
170
+ access_logs_enabled = attr.get("Value") == "true"
171
+ break
172
+
173
+ if not access_logs_enabled:
174
+ issues.append("Access logging not enabled")
175
+
176
+ # Check deletion protection
177
+ deletion_protection = False
178
+ for attr in attributes:
179
+ if attr.get("Key") == "deletion_protection.enabled":
180
+ deletion_protection = attr.get("Value") == "true"
181
+ break
182
+
183
+ if not deletion_protection:
184
+ issues.append("Deletion protection not enabled (recommended for production)")
185
+
186
+ return len(issues) > 0, issues, severity
187
+
188
+ def execute(self) -> TestResult:
189
+ """Execute ALB/NLB security compliance test.
190
+
191
+ Returns:
192
+ TestResult with findings for insecure load balancers
193
+
194
+ Example:
195
+ >>> test = ALBNLBSecurityTest(connector)
196
+ >>> result = test.execute()
197
+ >>> print(result.score)
198
+ 90.0
199
+ """
200
+ result = TestResult(
201
+ test_id=self.test_id,
202
+ test_name=self.test_name,
203
+ status=TestStatus.PASSED,
204
+ passed=True,
205
+ score=100.0,
206
+ )
207
+
208
+ try:
209
+ # Get ELBv2 client
210
+ elbv2_client = self.connector.get_client("elbv2")
211
+
212
+ # List all load balancers (ALB and NLB)
213
+ self.logger.info("listing_load_balancers")
214
+ lbs_response = elbv2_client.describe_load_balancers()
215
+ load_balancers = lbs_response.get("LoadBalancers", [])
216
+
217
+ if not load_balancers:
218
+ self.logger.info("no_load_balancers_found")
219
+ result.metadata["message"] = "No load balancers found in region"
220
+ return result
221
+
222
+ self.logger.info("load_balancers_found", count=len(load_balancers))
223
+
224
+ # Check security for each load balancer
225
+ secure_lb_count = 0
226
+
227
+ for lb in load_balancers:
228
+ lb_arn = lb["LoadBalancerArn"]
229
+ lb_name = lb["LoadBalancerName"]
230
+ lb_type = lb.get("Type", "application") # application or network
231
+ lb_scheme = lb.get("Scheme", "internet-facing")
232
+
233
+ result.resources_scanned += 1
234
+
235
+ # Get listeners for this load balancer
236
+ listeners_response = elbv2_client.describe_listeners(LoadBalancerArn=lb_arn)
237
+ listeners = listeners_response.get("Listeners", [])
238
+
239
+ # Get load balancer attributes
240
+ attributes_response = elbv2_client.describe_load_balancer_attributes(LoadBalancerArn=lb_arn)
241
+ attributes = attributes_response.get("Attributes", [])
242
+
243
+ # Check security based on type
244
+ if lb_type == "application":
245
+ has_issues, issues_list, severity = self._check_alb_security(
246
+ lb_arn, lb_name, listeners, attributes
247
+ )
248
+ elif lb_type == "network":
249
+ has_issues, issues_list, severity = self._check_nlb_security(
250
+ lb_arn, lb_name, listeners, attributes
251
+ )
252
+ else:
253
+ # Gateway load balancers - skip for now
254
+ continue
255
+
256
+ # Create evidence
257
+ evidence = self.create_evidence(
258
+ resource_id=lb_arn,
259
+ resource_type="load_balancer",
260
+ data={
261
+ "load_balancer_arn": lb_arn,
262
+ "load_balancer_name": lb_name,
263
+ "type": lb_type,
264
+ "scheme": lb_scheme,
265
+ "listener_count": len(listeners),
266
+ "has_issues": has_issues,
267
+ "issues": issues_list,
268
+ }
269
+ )
270
+ result.add_evidence(evidence)
271
+
272
+ if not has_issues:
273
+ secure_lb_count += 1
274
+ self.logger.debug(
275
+ "load_balancer_secure",
276
+ lb_name=lb_name,
277
+ lb_type=lb_type
278
+ )
279
+ else:
280
+ # Create finding for insecure load balancer
281
+ finding = self.create_finding(
282
+ resource_id=lb_arn,
283
+ resource_type="load_balancer",
284
+ severity=severity,
285
+ title=f"{lb_type.upper()} has security configuration issues",
286
+ description=f"{lb_type.upper()} '{lb_name}' has security configuration issues: "
287
+ f"{'; '.join(issues_list)}. Load balancers should use secure protocols "
288
+ "(HTTPS/TLS), modern SSL/TLS policies (TLS 1.2+), enable access logging "
289
+ "for audit trails, and enable deletion protection for production workloads. "
290
+ "ISO 27001 A.8.22 requires secure network segregation and encryption.",
291
+ remediation=(
292
+ f"Improve {lb_type.upper()} '{lb_name}' security:\n\n"
293
+ "1. Add HTTPS/TLS listener:\n"
294
+ f"aws elbv2 create-listener \\\n"
295
+ f" --load-balancer-arn {lb_arn} \\\n"
296
+ " --protocol {'HTTPS' if lb_type == 'application' else 'TLS'} \\\n"
297
+ " --port 443 \\\n"
298
+ " --certificates CertificateArn=<ACM-CERT-ARN> \\\n"
299
+ " --default-actions Type=forward,TargetGroupArn=<TG-ARN> \\\n"
300
+ " --ssl-policy ELBSecurityPolicy-TLS13-1-2-2021-06\n\n"
301
+ "2. Enable access logging:\n"
302
+ f"aws elbv2 modify-load-balancer-attributes \\\n"
303
+ f" --load-balancer-arn {lb_arn} \\\n"
304
+ " --attributes \\\n"
305
+ " Key=access_logs.s3.enabled,Value=true \\\n"
306
+ " Key=access_logs.s3.bucket,Value=<S3-BUCKET-NAME> \\\n"
307
+ " Key=access_logs.s3.prefix,Value=<PREFIX>\n\n"
308
+ "3. Enable deletion protection:\n"
309
+ f"aws elbv2 modify-load-balancer-attributes \\\n"
310
+ f" --load-balancer-arn {lb_arn} \\\n"
311
+ " --attributes Key=deletion_protection.enabled,Value=true\n\n"
312
+ "4. Update SSL policy to modern version:\n"
313
+ f"aws elbv2 modify-listener \\\n"
314
+ " --listener-arn <LISTENER-ARN> \\\n"
315
+ " --ssl-policy ELBSecurityPolicy-TLS13-1-2-2021-06\n\n"
316
+ "Or use AWS Console:\n"
317
+ "1. Go to EC2 console → Load Balancers\n"
318
+ f"2. Select '{lb_name}'\n"
319
+ "3. Add HTTPS/TLS listener:\n"
320
+ " - Click 'Add listener'\n"
321
+ " - Select protocol: {'HTTPS' if lb_type == 'application' else 'TLS'}\n"
322
+ " - Port: 443\n"
323
+ " - Default SSL certificate: Select from ACM\n"
324
+ " - Security policy: ELBSecurityPolicy-TLS13-1-2-2021-06\n"
325
+ "4. Enable access logs:\n"
326
+ " - Go to 'Attributes' tab\n"
327
+ " - Edit 'Access logs'\n"
328
+ " - Enable and specify S3 bucket\n"
329
+ "5. Enable deletion protection:\n"
330
+ " - Edit 'Deletion protection'\n"
331
+ " - Enable\n\n"
332
+ "Recommended SSL/TLS policies (in order of preference):\n"
333
+ "- ELBSecurityPolicy-TLS13-1-2-2021-06 (TLS 1.3 + 1.2)\n"
334
+ "- ELBSecurityPolicy-TLS13-1-2-Res-2021-06 (Restricted)\n"
335
+ "- ELBSecurityPolicy-TLS-1-2-2017-01 (TLS 1.2 minimum)\n\n"
336
+ "Security best practices:\n"
337
+ "- Use AWS Certificate Manager (ACM) for SSL/TLS certificates\n"
338
+ "- Enable automatic certificate renewal\n"
339
+ "- Configure HTTP to HTTPS redirect for ALBs\n"
340
+ "- Use security groups to restrict access\n"
341
+ "- Enable WAF for ALBs handling web traffic\n"
342
+ "- Monitor access logs for suspicious activity\n"
343
+ "- Use CloudWatch alarms for unhealthy targets"
344
+ ),
345
+ evidence=evidence
346
+ )
347
+ result.add_finding(finding)
348
+
349
+ self.logger.warning(
350
+ "load_balancer_security_issues",
351
+ lb_name=lb_name,
352
+ lb_type=lb_type,
353
+ issues=issues_list
354
+ )
355
+
356
+ # Calculate compliance score
357
+ result.score = (secure_lb_count / len(load_balancers)) * 100
358
+
359
+ # Determine pass/fail
360
+ result.passed = secure_lb_count == len(load_balancers)
361
+ result.status = TestStatus.PASSED if result.passed else TestStatus.FAILED
362
+
363
+ # Add metadata
364
+ result.metadata = {
365
+ "total_load_balancers": len(load_balancers),
366
+ "secure_load_balancers": secure_lb_count,
367
+ "insecure_load_balancers": len(load_balancers) - secure_lb_count,
368
+ "compliance_percentage": result.score,
369
+ }
370
+
371
+ self.logger.info(
372
+ "alb_nlb_security_test_completed",
373
+ total_load_balancers=len(load_balancers),
374
+ secure=secure_lb_count,
375
+ score=result.score,
376
+ passed=result.passed
377
+ )
378
+
379
+ except ClientError as e:
380
+ error_code = e.response.get("Error", {}).get("Code")
381
+ self.logger.error("alb_nlb_security_test_error", error_code=error_code, error=str(e))
382
+ result.status = TestStatus.ERROR
383
+ result.passed = False
384
+ result.score = 0.0
385
+ result.error_message = f"AWS API Error: {error_code} - {str(e)}"
386
+
387
+ except Exception as e:
388
+ self.logger.error("alb_nlb_security_test_error", error=str(e))
389
+ result.status = TestStatus.ERROR
390
+ result.passed = False
391
+ result.score = 0.0
392
+ result.error_message = str(e)
393
+
394
+ return result
395
+
396
+
397
+ # ============================================================================
398
+ # CONVENIENCE FUNCTION
399
+ # ============================================================================
400
+
401
+
402
+ def run_alb_nlb_security_test(connector: AWSConnector) -> TestResult:
403
+ """Run ALB/NLB security compliance test.
404
+
405
+ Convenience function for running the test.
406
+
407
+ Args:
408
+ connector: AWS connector
409
+
410
+ Returns:
411
+ TestResult
412
+
413
+ Example:
414
+ >>> from complio.connectors.aws.client import AWSConnector
415
+ >>> connector = AWSConnector("production", "us-east-1")
416
+ >>> connector.connect()
417
+ >>> result = run_alb_nlb_security_test(connector)
418
+ >>> print(f"Score: {result.score}%")
419
+ """
420
+ test = ALBNLBSecurityTest(connector)
421
+ return test.execute()