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
@@ -0,0 +1,339 @@
1
+ """
2
+ VPC Endpoints security compliance test.
3
+
4
+ Checks that VPC Endpoints use secure configurations and policies.
5
+
6
+ ISO 27001 Control: A.8.22 - Network segregation
7
+ Requirement: VPC Endpoints should use restrictive policies and private DNS
8
+
9
+ Example:
10
+ >>> from complio.connectors.aws.client import AWSConnector
11
+ >>> from complio.tests_library.network.vpc_endpoints_security import VPCEndpointsSecurityTest
12
+ >>>
13
+ >>> connector = AWSConnector("production", "us-east-1")
14
+ >>> connector.connect()
15
+ >>>
16
+ >>> test = VPCEndpointsSecurityTest(connector)
17
+ >>> result = test.run()
18
+ >>> print(f"Passed: {result.passed}, Score: {result.score}")
19
+ """
20
+
21
+ from typing import Any, Dict
22
+ import json
23
+
24
+ from botocore.exceptions import ClientError
25
+
26
+ from complio.connectors.aws.client import AWSConnector
27
+ from complio.tests_library.base import (
28
+ ComplianceTest,
29
+ Severity,
30
+ TestResult,
31
+ TestStatus,
32
+ )
33
+
34
+
35
+ class VPCEndpointsSecurityTest(ComplianceTest):
36
+ """Test for VPC Endpoints security compliance.
37
+
38
+ Verifies that VPC Endpoints use secure configurations:
39
+ - Interface endpoints should have Private DNS enabled
40
+ - Endpoint policies should not be overly permissive (not full access)
41
+ - Gateway endpoints (S3, DynamoDB) should use restrictive policies
42
+
43
+ Compliance Requirements:
44
+ - Interface endpoints should enable Private DNS
45
+ - Endpoint policies should follow least privilege
46
+ - Avoid "*" in policy statements
47
+
48
+ Scoring:
49
+ - 100% if all VPC Endpoints follow security best practices
50
+ - Proportional score based on compliant/total ratio
51
+ - 100% if no VPC Endpoints exist
52
+
53
+ Example:
54
+ >>> test = VPCEndpointsSecurityTest(connector)
55
+ >>> result = test.execute()
56
+ >>> for finding in result.findings:
57
+ ... print(f"{finding.resource_id}: {finding.title}")
58
+ """
59
+
60
+ def __init__(self, connector: AWSConnector) -> None:
61
+ """Initialize VPC Endpoints security test.
62
+
63
+ Args:
64
+ connector: AWS connector instance
65
+ """
66
+ super().__init__(
67
+ test_id="vpc_endpoints_security",
68
+ test_name="VPC Endpoints Security Check",
69
+ description="Verify VPC Endpoints use secure configurations and policies",
70
+ control_id="A.8.22",
71
+ connector=connector,
72
+ scope="regional",
73
+ )
74
+
75
+ def execute(self) -> TestResult:
76
+ """Execute VPC Endpoints security compliance test.
77
+
78
+ Returns:
79
+ TestResult with findings for insecure VPC Endpoints
80
+
81
+ Example:
82
+ >>> test = VPCEndpointsSecurityTest(connector)
83
+ >>> result = test.execute()
84
+ >>> print(result.score)
85
+ 100.0
86
+ """
87
+ result = TestResult(
88
+ test_id=self.test_id,
89
+ test_name=self.test_name,
90
+ status=TestStatus.PASSED,
91
+ passed=True,
92
+ score=100.0,
93
+ )
94
+
95
+ try:
96
+ # Get EC2 client
97
+ ec2_client = self.connector.get_client("ec2")
98
+
99
+ # List all VPC Endpoints
100
+ self.logger.info("listing_vpc_endpoints")
101
+ endpoints_response = ec2_client.describe_vpc_endpoints()
102
+ vpc_endpoints = endpoints_response.get("VpcEndpoints", [])
103
+
104
+ if not vpc_endpoints:
105
+ self.logger.info("no_vpc_endpoints_found")
106
+ result.metadata["message"] = "No VPC Endpoints found in region"
107
+ return result
108
+
109
+ self.logger.info("vpc_endpoints_found", count=len(vpc_endpoints))
110
+
111
+ # Check security for each VPC Endpoint
112
+ secure_endpoint_count = 0
113
+
114
+ for endpoint in vpc_endpoints:
115
+ endpoint_id = endpoint["VpcEndpointId"]
116
+ endpoint_type = endpoint.get("VpcEndpointType", "")
117
+ service_name = endpoint.get("ServiceName", "")
118
+ vpc_id = endpoint.get("VpcId", "")
119
+ state = endpoint.get("State", "")
120
+
121
+ # Skip endpoints that are being deleted
122
+ if state in ["deleted", "deleting", "failed"]:
123
+ continue
124
+
125
+ result.resources_scanned += 1
126
+
127
+ # Determine security issues
128
+ issues = []
129
+ severity = Severity.MEDIUM
130
+
131
+ # Check for Interface endpoints
132
+ if endpoint_type == "Interface":
133
+ private_dns_enabled = endpoint.get("PrivateDnsEnabled", False)
134
+ if not private_dns_enabled:
135
+ issues.append("Private DNS not enabled for interface endpoint")
136
+ severity = Severity.MEDIUM
137
+
138
+ # Check endpoint policy
139
+ policy_document = endpoint.get("PolicyDocument")
140
+ if policy_document:
141
+ try:
142
+ policy = json.loads(policy_document)
143
+ # Check for overly permissive policies
144
+ if isinstance(policy, dict):
145
+ statements = policy.get("Statement", [])
146
+ for statement in statements:
147
+ if isinstance(statement, dict):
148
+ effect = statement.get("Effect", "")
149
+ action = statement.get("Action", "")
150
+ resource = statement.get("Resource", "")
151
+ principal = statement.get("Principal", "")
152
+
153
+ # Check for full access
154
+ if effect == "Allow" and action == "*" and (resource == "*" or resource == ["*"]):
155
+ issues.append("Endpoint policy allows all actions on all resources (too permissive)")
156
+ severity = Severity.HIGH
157
+
158
+ # Check for any principal
159
+ if effect == "Allow" and principal == "*":
160
+ issues.append("Endpoint policy allows any principal (too permissive)")
161
+ severity = Severity.HIGH
162
+
163
+ except json.JSONDecodeError:
164
+ issues.append("Endpoint policy is not valid JSON")
165
+
166
+ # Create evidence
167
+ evidence = self.create_evidence(
168
+ resource_id=endpoint_id,
169
+ resource_type="vpc_endpoint",
170
+ data={
171
+ "vpc_endpoint_id": endpoint_id,
172
+ "vpc_id": vpc_id,
173
+ "type": endpoint_type,
174
+ "service_name": service_name,
175
+ "state": state,
176
+ "private_dns_enabled": endpoint.get("PrivateDnsEnabled"),
177
+ "has_issues": len(issues) > 0,
178
+ "issues": issues,
179
+ }
180
+ )
181
+ result.add_evidence(evidence)
182
+
183
+ if len(issues) == 0:
184
+ secure_endpoint_count += 1
185
+ self.logger.debug(
186
+ "vpc_endpoint_secure",
187
+ endpoint_id=endpoint_id
188
+ )
189
+ else:
190
+ # Create finding for insecure VPC Endpoint
191
+ finding = self.create_finding(
192
+ resource_id=endpoint_id,
193
+ resource_type="vpc_endpoint",
194
+ severity=severity,
195
+ title="VPC Endpoint has security configuration issues",
196
+ description=f"VPC Endpoint '{endpoint_id}' (service: {service_name}) in VPC '{vpc_id}' has "
197
+ f"security configuration issues: {'; '.join(issues)}. VPC Endpoints should enable "
198
+ "Private DNS for interface endpoints and use least privilege policies to control "
199
+ "access. ISO 27001 A.8.22 requires proper network segregation and access controls.",
200
+ remediation=(
201
+ f"Improve VPC Endpoint '{endpoint_id}' security:\n\n"
202
+ "1. Enable Private DNS (for interface endpoints):\n"
203
+ f"aws ec2 modify-vpc-endpoint \\\n"
204
+ f" --vpc-endpoint-id {endpoint_id} \\\n"
205
+ " --private-dns-enabled\n\n"
206
+ "2. Apply restrictive endpoint policy:\n"
207
+ "Create policy.json with least privilege access:\n"
208
+ "{\n"
209
+ ' "Statement": [\n'
210
+ ' {\n'
211
+ ' "Effect": "Allow",\n'
212
+ ' "Principal": {"AWS": "arn:aws:iam::ACCOUNT-ID:root"},\n'
213
+ ' "Action": [\n'
214
+ ' "s3:GetObject",\n'
215
+ ' "s3:PutObject"\n'
216
+ ' ],\n'
217
+ ' "Resource": "arn:aws:s3:::my-bucket/*"\n'
218
+ ' }\n'
219
+ ' ]\n'
220
+ '}\n\n'
221
+ f"aws ec2 modify-vpc-endpoint \\\n"
222
+ f" --vpc-endpoint-id {endpoint_id} \\\n"
223
+ " --policy-document file://policy.json\n\n"
224
+ "Or use AWS Console:\n"
225
+ "1. Go to VPC console → Endpoints\n"
226
+ f"2. Select endpoint '{endpoint_id}'\n"
227
+ "3. Actions → Modify endpoint\n"
228
+ "4. For interface endpoints:\n"
229
+ " - Enable 'Enable Private DNS Name'\n"
230
+ "5. Edit policy:\n"
231
+ " - Click 'Custom' policy\n"
232
+ " - Define specific actions and resources\n"
233
+ " - Specify principal (avoid using '*')\n"
234
+ "6. Click 'Save'\n\n"
235
+ "Security best practices:\n"
236
+ "- Use interface endpoints for AWS services (more secure than internet)\n"
237
+ "- Enable Private DNS for seamless application integration\n"
238
+ "- Apply least privilege endpoint policies\n"
239
+ "- Use security groups to control access to interface endpoints\n"
240
+ "- Use VPC endpoint policies to restrict S3/DynamoDB access\n"
241
+ "- Monitor VPC endpoint usage with VPC Flow Logs\n"
242
+ "- Tag endpoints for easy management\n"
243
+ "- Regularly review and audit endpoint policies\n\n"
244
+ "Example restrictive policies:\n"
245
+ "S3 Gateway Endpoint (read-only for specific bucket):\n"
246
+ "{\n"
247
+ ' "Statement": [{\n'
248
+ ' "Effect": "Allow",\n'
249
+ ' "Principal": "*",\n'
250
+ ' "Action": "s3:GetObject",\n'
251
+ ' "Resource": "arn:aws:s3:::my-bucket/*"\n'
252
+ ' }]\n'
253
+ '}\n\n'
254
+ "DynamoDB Gateway Endpoint (specific table access):\n"
255
+ "{\n"
256
+ ' "Statement": [{\n'
257
+ ' "Effect": "Allow",\n'
258
+ ' "Principal": "*",\n'
259
+ ' "Action": ["dynamodb:GetItem", "dynamodb:PutItem"],\n'
260
+ ' "Resource": "arn:aws:dynamodb:region:account:table/MyTable"\n'
261
+ ' }]\n'
262
+ '}'
263
+ ),
264
+ evidence=evidence
265
+ )
266
+ result.add_finding(finding)
267
+
268
+ self.logger.warning(
269
+ "vpc_endpoint_insecure",
270
+ endpoint_id=endpoint_id,
271
+ issues=issues
272
+ )
273
+
274
+ # Calculate compliance score
275
+ result.score = (secure_endpoint_count / len(vpc_endpoints)) * 100
276
+
277
+ # Determine pass/fail
278
+ result.passed = secure_endpoint_count == len(vpc_endpoints)
279
+ result.status = TestStatus.PASSED if result.passed else TestStatus.FAILED
280
+
281
+ # Add metadata
282
+ result.metadata = {
283
+ "total_vpc_endpoints": len(vpc_endpoints),
284
+ "secure_vpc_endpoints": secure_endpoint_count,
285
+ "insecure_vpc_endpoints": len(vpc_endpoints) - secure_endpoint_count,
286
+ "compliance_percentage": result.score,
287
+ }
288
+
289
+ self.logger.info(
290
+ "vpc_endpoints_security_test_completed",
291
+ total_vpc_endpoints=len(vpc_endpoints),
292
+ secure=secure_endpoint_count,
293
+ score=result.score,
294
+ passed=result.passed
295
+ )
296
+
297
+ except ClientError as e:
298
+ error_code = e.response.get("Error", {}).get("Code")
299
+ self.logger.error("vpc_endpoints_security_test_error", error_code=error_code, error=str(e))
300
+ result.status = TestStatus.ERROR
301
+ result.passed = False
302
+ result.score = 0.0
303
+ result.error_message = f"AWS API Error: {error_code} - {str(e)}"
304
+
305
+ except Exception as e:
306
+ self.logger.error("vpc_endpoints_security_test_error", error=str(e))
307
+ result.status = TestStatus.ERROR
308
+ result.passed = False
309
+ result.score = 0.0
310
+ result.error_message = str(e)
311
+
312
+ return result
313
+
314
+
315
+ # ============================================================================
316
+ # CONVENIENCE FUNCTION
317
+ # ============================================================================
318
+
319
+
320
+ def run_vpc_endpoints_security_test(connector: AWSConnector) -> TestResult:
321
+ """Run VPC Endpoints security compliance test.
322
+
323
+ Convenience function for running the test.
324
+
325
+ Args:
326
+ connector: AWS connector
327
+
328
+ Returns:
329
+ TestResult
330
+
331
+ Example:
332
+ >>> from complio.connectors.aws.client import AWSConnector
333
+ >>> connector = AWSConnector("production", "us-east-1")
334
+ >>> connector.connect()
335
+ >>> result = run_vpc_endpoints_security_test(connector)
336
+ >>> print(f"Score: {result.score}%")
337
+ """
338
+ test = VPCEndpointsSecurityTest(connector)
339
+ return test.execute()
@@ -0,0 +1,333 @@
1
+ """
2
+ VPN connection security compliance test.
3
+
4
+ Checks that all VPN connections use secure configurations.
5
+
6
+ ISO 27001 Control: A.8.22 - Network segregation
7
+ Requirement: VPN connections must use secure encryption and tunnel configurations
8
+
9
+ Example:
10
+ >>> from complio.connectors.aws.client import AWSConnector
11
+ >>> from complio.tests_library.network.vpn_security import VPNSecurityTest
12
+ >>>
13
+ >>> connector = AWSConnector("production", "us-east-1")
14
+ >>> connector.connect()
15
+ >>>
16
+ >>> test = VPNSecurityTest(connector)
17
+ >>> result = test.run()
18
+ >>> print(f"Passed: {result.passed}, Score: {result.score}")
19
+ """
20
+
21
+ from typing import Any, Dict
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 VPNSecurityTest(ComplianceTest):
35
+ """Test for VPN connection security compliance.
36
+
37
+ Verifies that all VPN connections use secure tunnel configurations
38
+ including proper encryption and integrity algorithms.
39
+
40
+ Compliance Requirements:
41
+ - All VPN tunnels must be UP (active)
42
+ - Use strong encryption (AES-256 recommended)
43
+ - Use strong integrity algorithms (SHA-256 or better)
44
+ - Use IKEv2 protocol when possible
45
+
46
+ Scoring:
47
+ - 100% if all VPN connections have secure configurations
48
+ - Proportional score based on compliant/total ratio
49
+ - 100% if no VPN connections exist
50
+
51
+ Example:
52
+ >>> test = VPNSecurityTest(connector)
53
+ >>> result = test.execute()
54
+ >>> for finding in result.findings:
55
+ ... print(f"{finding.resource_id}: {finding.title}")
56
+ """
57
+
58
+ def __init__(self, connector: AWSConnector) -> None:
59
+ """Initialize VPN security test.
60
+
61
+ Args:
62
+ connector: AWS connector instance
63
+ """
64
+ super().__init__(
65
+ test_id="vpn_security",
66
+ test_name="VPN Connection Security Check",
67
+ description="Verify all VPN connections use secure tunnel configurations",
68
+ control_id="A.8.22",
69
+ connector=connector,
70
+ scope="regional",
71
+ )
72
+
73
+ def execute(self) -> TestResult:
74
+ """Execute VPN connection security compliance test.
75
+
76
+ Returns:
77
+ TestResult with findings for insecure VPN configurations
78
+
79
+ Example:
80
+ >>> test = VPNSecurityTest(connector)
81
+ >>> result = test.execute()
82
+ >>> print(result.score)
83
+ 100.0
84
+ """
85
+ result = TestResult(
86
+ test_id=self.test_id,
87
+ test_name=self.test_name,
88
+ status=TestStatus.PASSED,
89
+ passed=True,
90
+ score=100.0,
91
+ )
92
+
93
+ try:
94
+ # Get EC2 client
95
+ ec2_client = self.connector.get_client("ec2")
96
+
97
+ # List all VPN connections
98
+ self.logger.info("listing_vpn_connections")
99
+ vpn_response = ec2_client.describe_vpn_connections()
100
+ vpn_connections = vpn_response.get("VpnConnections", [])
101
+
102
+ if not vpn_connections:
103
+ self.logger.info("no_vpn_connections_found")
104
+ result.metadata["message"] = "No VPN connections found in region"
105
+ return result
106
+
107
+ self.logger.info("vpn_connections_found", count=len(vpn_connections))
108
+
109
+ # Check security for each VPN connection
110
+ secure_vpn_count = 0
111
+
112
+ for vpn_connection in vpn_connections:
113
+ vpn_id = vpn_connection["VpnConnectionId"]
114
+ vpn_state = vpn_connection.get("State", "")
115
+ result.resources_scanned += 1
116
+
117
+ # Skip deleted/deleting VPN connections
118
+ if vpn_state in ["deleted", "deleting"]:
119
+ continue
120
+
121
+ # Check tunnel details
122
+ vgw_telemetry = vpn_connection.get("VgwTelemetry", [])
123
+ tunnel_options = vpn_connection.get("Options", {}).get("TunnelOptions", [])
124
+
125
+ # Initialize security check variables
126
+ all_tunnels_secure = True
127
+ security_issues = []
128
+
129
+ # Check each tunnel
130
+ for idx, telemetry in enumerate(vgw_telemetry):
131
+ tunnel_status = telemetry.get("Status", "DOWN")
132
+
133
+ # Check if tunnel is up
134
+ if tunnel_status != "UP":
135
+ all_tunnels_secure = False
136
+ security_issues.append(f"Tunnel {idx + 1} is {tunnel_status} (not UP)")
137
+
138
+ # Check tunnel options if available
139
+ for idx, tunnel_option in enumerate(tunnel_options):
140
+ phase1_encryption = tunnel_option.get("Phase1EncryptionAlgorithms", [])
141
+ phase1_integrity = tunnel_option.get("Phase1IntegrityAlgorithms", [])
142
+ phase2_encryption = tunnel_option.get("Phase2EncryptionAlgorithms", [])
143
+ phase2_integrity = tunnel_option.get("Phase2IntegrityAlgorithms", [])
144
+ ike_versions = tunnel_option.get("IKEVersions", [])
145
+
146
+ # Check for weak encryption (prefer AES-256)
147
+ weak_encryption_found = False
148
+ for enc_alg in phase1_encryption + phase2_encryption:
149
+ if isinstance(enc_alg, dict):
150
+ value = enc_alg.get("Value", "")
151
+ if value and "AES128" in value:
152
+ weak_encryption_found = True
153
+
154
+ if weak_encryption_found:
155
+ security_issues.append(f"Tunnel {idx + 1} uses AES-128 (AES-256 recommended)")
156
+
157
+ # Check for weak integrity (prefer SHA-256 or better)
158
+ weak_integrity_found = False
159
+ for int_alg in phase1_integrity + phase2_integrity:
160
+ if isinstance(int_alg, dict):
161
+ value = int_alg.get("Value", "")
162
+ if value and "SHA1" in value:
163
+ weak_integrity_found = True
164
+
165
+ if weak_integrity_found:
166
+ security_issues.append(f"Tunnel {idx + 1} uses SHA-1 (SHA-256+ recommended)")
167
+
168
+ # Check IKE version (IKEv2 preferred)
169
+ if ike_versions:
170
+ has_ikev2 = any(
171
+ v.get("Value") == "ikev2" if isinstance(v, dict) else False
172
+ for v in ike_versions
173
+ )
174
+ if not has_ikev2:
175
+ security_issues.append(f"Tunnel {idx + 1} does not use IKEv2")
176
+
177
+ # Create evidence
178
+ evidence = self.create_evidence(
179
+ resource_id=vpn_id,
180
+ resource_type="vpn_connection",
181
+ data={
182
+ "vpn_connection_id": vpn_id,
183
+ "state": vpn_state,
184
+ "type": vpn_connection.get("Type"),
185
+ "category": vpn_connection.get("Category"),
186
+ "tunnel_count": len(vgw_telemetry),
187
+ "tunnel_status": [t.get("Status") for t in vgw_telemetry],
188
+ "security_issues": security_issues,
189
+ }
190
+ )
191
+ result.add_evidence(evidence)
192
+
193
+ if all_tunnels_secure and not security_issues:
194
+ secure_vpn_count += 1
195
+ self.logger.debug(
196
+ "vpn_connection_secure",
197
+ vpn_id=vpn_id
198
+ )
199
+ else:
200
+ # Determine severity based on issues
201
+ severity = Severity.HIGH if "DOWN" in str(security_issues) else Severity.MEDIUM
202
+
203
+ # Create finding for insecure VPN connection
204
+ finding = self.create_finding(
205
+ resource_id=vpn_id,
206
+ resource_type="vpn_connection",
207
+ severity=severity,
208
+ title="VPN connection has security issues",
209
+ description=f"VPN connection '{vpn_id}' has security configuration issues: "
210
+ f"{', '.join(security_issues)}. "
211
+ "VPN connections should use strong encryption (AES-256), strong "
212
+ "integrity algorithms (SHA-256 or better), IKEv2 protocol, and "
213
+ "all tunnels should be in UP status for high availability. "
214
+ "ISO 27001 A.8.22 requires secure network segregation and "
215
+ "encryption of data in transit.",
216
+ remediation=(
217
+ f"Improve VPN connection '{vpn_id}' security:\n\n"
218
+ "1. Use strong encryption and integrity algorithms:\n"
219
+ " When creating/modifying VPN connection, specify tunnel options:\n"
220
+ " - Phase1EncryptionAlgorithms: AES256, AES256-GCM-16\n"
221
+ " - Phase1IntegrityAlgorithms: SHA2-256, SHA2-384, SHA2-512\n"
222
+ " - Phase2EncryptionAlgorithms: AES256, AES256-GCM-16\n"
223
+ " - Phase2IntegrityAlgorithms: SHA2-256, SHA2-384, SHA2-512\n"
224
+ " - IKEVersions: ikev2\n\n"
225
+ "2. Ensure both tunnels are UP:\n"
226
+ " - Check customer gateway configuration\n"
227
+ " - Verify routing and firewall rules\n"
228
+ " - Check VPN connection logs in CloudWatch\n\n"
229
+ "3. Create new VPN connection with secure settings:\n"
230
+ " aws ec2 create-vpn-connection \\\n"
231
+ " --type ipsec.1 \\\n"
232
+ " --customer-gateway-id <cgw-id> \\\n"
233
+ " --vpn-gateway-id <vgw-id> \\\n"
234
+ " --options TunnelOptions='[{\n"
235
+ ' "Phase1EncryptionAlgorithms":[{"Value":"AES256"}],\n'
236
+ ' "Phase1IntegrityAlgorithms":[{"Value":"SHA2-256"}],\n'
237
+ ' "Phase2EncryptionAlgorithms":[{"Value":"AES256"}],\n'
238
+ ' "Phase2IntegrityAlgorithms":[{"Value":"SHA2-256"}],\n'
239
+ ' "IKEVersions":[{"Value":"ikev2"}]\n'
240
+ " }]'\n\n"
241
+ "Or use AWS Console:\n"
242
+ "1. Go to VPC console → Site-to-Site VPN Connections\n"
243
+ "2. Create new VPN connection or modify existing\n"
244
+ "3. Under 'Tunnel Options', configure:\n"
245
+ " - Phase 1 Encryption: AES-256\n"
246
+ " - Phase 1 Integrity: SHA2-256 or better\n"
247
+ " - Phase 2 Encryption: AES-256\n"
248
+ " - Phase 2 Integrity: SHA2-256 or better\n"
249
+ " - IKE Version: IKEv2\n"
250
+ "4. Update customer gateway configuration accordingly\n\n"
251
+ "Security best practices:\n"
252
+ "- Enable VPN CloudWatch logs for monitoring\n"
253
+ "- Use DPD (Dead Peer Detection) timeout\n"
254
+ "- Configure tunnel inside CIDR appropriately\n"
255
+ "- Regularly rotate pre-shared keys\n"
256
+ "- Monitor tunnel status with CloudWatch alarms"
257
+ ),
258
+ evidence=evidence
259
+ )
260
+ result.add_finding(finding)
261
+
262
+ self.logger.warning(
263
+ "vpn_connection_insecure",
264
+ vpn_id=vpn_id,
265
+ issues=security_issues
266
+ )
267
+
268
+ # Calculate compliance score
269
+ result.score = (secure_vpn_count / len(vpn_connections)) * 100
270
+
271
+ # Determine pass/fail
272
+ result.passed = secure_vpn_count == len(vpn_connections)
273
+ result.status = TestStatus.PASSED if result.passed else TestStatus.FAILED
274
+
275
+ # Add metadata
276
+ result.metadata = {
277
+ "total_vpn_connections": len(vpn_connections),
278
+ "secure_vpn_connections": secure_vpn_count,
279
+ "insecure_vpn_connections": len(vpn_connections) - secure_vpn_count,
280
+ "compliance_percentage": result.score,
281
+ }
282
+
283
+ self.logger.info(
284
+ "vpn_security_test_completed",
285
+ total_vpn_connections=len(vpn_connections),
286
+ secure=secure_vpn_count,
287
+ score=result.score,
288
+ passed=result.passed
289
+ )
290
+
291
+ except ClientError as e:
292
+ error_code = e.response.get("Error", {}).get("Code")
293
+ self.logger.error("vpn_security_test_error", error_code=error_code, error=str(e))
294
+ result.status = TestStatus.ERROR
295
+ result.passed = False
296
+ result.score = 0.0
297
+ result.error_message = f"AWS API Error: {error_code} - {str(e)}"
298
+
299
+ except Exception as e:
300
+ self.logger.error("vpn_security_test_error", error=str(e))
301
+ result.status = TestStatus.ERROR
302
+ result.passed = False
303
+ result.score = 0.0
304
+ result.error_message = str(e)
305
+
306
+ return result
307
+
308
+
309
+ # ============================================================================
310
+ # CONVENIENCE FUNCTION
311
+ # ============================================================================
312
+
313
+
314
+ def run_vpn_security_test(connector: AWSConnector) -> TestResult:
315
+ """Run VPN connection security compliance test.
316
+
317
+ Convenience function for running the test.
318
+
319
+ Args:
320
+ connector: AWS connector
321
+
322
+ Returns:
323
+ TestResult
324
+
325
+ Example:
326
+ >>> from complio.connectors.aws.client import AWSConnector
327
+ >>> connector = AWSConnector("production", "us-east-1")
328
+ >>> connector.connect()
329
+ >>> result = run_vpn_security_test(connector)
330
+ >>> print(f"Score: {result.score}%")
331
+ """
332
+ test = VPNSecurityTest(connector)
333
+ return test.execute()