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,332 @@
1
+ """
2
+ CloudFront HTTPS enforcement compliance test.
3
+
4
+ Checks that CloudFront distributions enforce HTTPS for secure content delivery.
5
+
6
+ ISO 27001 Control: A.8.24 - Use of cryptography
7
+ Requirement: CloudFront distributions must use HTTPS to encrypt data in transit
8
+
9
+ Example:
10
+ >>> from complio.connectors.aws.client import AWSConnector
11
+ >>> from complio.tests_library.network.cloudfront_https import CloudFrontHTTPSTest
12
+ >>>
13
+ >>> connector = AWSConnector("production", "us-east-1")
14
+ >>> connector.connect()
15
+ >>>
16
+ >>> test = CloudFrontHTTPSTest(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 CloudFrontHTTPSTest(ComplianceTest):
35
+ """Test for CloudFront HTTPS enforcement compliance.
36
+
37
+ Verifies that CloudFront distributions use HTTPS for secure content delivery:
38
+ - Viewer protocol policy should redirect HTTP to HTTPS or HTTPS-only
39
+ - Origin protocol policy should use HTTPS when possible
40
+ - Minimum TLS version should be TLS 1.2 or higher
41
+ - Should use modern security policy
42
+
43
+ Compliance Requirements:
44
+ - ViewerProtocolPolicy must be 'redirect-to-https' or 'https-only'
45
+ - MinimumProtocolVersion should be TLSv1.2_2021 or higher
46
+ - Origin should use HTTPS when communicating with backend
47
+ - SSL/TLS certificate should be valid
48
+
49
+ Scoring:
50
+ - 100% if all distributions enforce HTTPS properly
51
+ - Proportional score based on compliant/total ratio
52
+ - 100% if no distributions exist
53
+
54
+ Example:
55
+ >>> test = CloudFrontHTTPSTest(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 CloudFront HTTPS test.
63
+
64
+ Args:
65
+ connector: AWS connector instance
66
+ """
67
+ super().__init__(
68
+ test_id="cloudfront_https",
69
+ test_name="CloudFront HTTPS Enforcement Check",
70
+ description="Verify CloudFront distributions enforce HTTPS for secure content delivery",
71
+ control_id="A.8.24",
72
+ connector=connector,
73
+ scope="global", # CloudFront is a global service
74
+ )
75
+
76
+ def execute(self) -> TestResult:
77
+ """Execute CloudFront HTTPS compliance test.
78
+
79
+ Returns:
80
+ TestResult with findings for insecure distributions
81
+
82
+ Example:
83
+ >>> test = CloudFrontHTTPSTest(connector)
84
+ >>> result = test.execute()
85
+ >>> print(result.score)
86
+ 100.0
87
+ """
88
+ result = TestResult(
89
+ test_id=self.test_id,
90
+ test_name=self.test_name,
91
+ status=TestStatus.PASSED,
92
+ passed=True,
93
+ score=100.0,
94
+ )
95
+
96
+ try:
97
+ # Get CloudFront client (global service, no region needed)
98
+ cloudfront_client = self.connector.get_client("cloudfront")
99
+
100
+ # List all distributions
101
+ self.logger.info("listing_cloudfront_distributions")
102
+ distributions = []
103
+
104
+ paginator = cloudfront_client.get_paginator("list_distributions")
105
+ for page in paginator.paginate():
106
+ dist_list = page.get("DistributionList", {})
107
+ items = dist_list.get("Items", [])
108
+ distributions.extend(items)
109
+
110
+ if not distributions:
111
+ self.logger.info("no_cloudfront_distributions_found")
112
+ result.metadata["message"] = "No CloudFront distributions found"
113
+ return result
114
+
115
+ self.logger.info("cloudfront_distributions_found", count=len(distributions))
116
+
117
+ # Check HTTPS configuration for each distribution
118
+ https_enforced_count = 0
119
+
120
+ for dist_summary in distributions:
121
+ dist_id = dist_summary["Id"]
122
+ dist_domain = dist_summary.get("DomainName", "")
123
+ dist_status = dist_summary.get("Status", "")
124
+
125
+ result.resources_scanned += 1
126
+
127
+ # Get full distribution configuration
128
+ dist_response = cloudfront_client.get_distribution(Id=dist_id)
129
+ dist_config = dist_response.get("Distribution", {}).get("DistributionConfig", {})
130
+
131
+ # Check viewer protocol policy in default cache behavior
132
+ default_cache_behavior = dist_config.get("DefaultCacheBehavior", {})
133
+ viewer_protocol_policy = default_cache_behavior.get("ViewerProtocolPolicy", "allow-all")
134
+
135
+ # Check minimum protocol version
136
+ viewer_certificate = dist_config.get("ViewerCertificate", {})
137
+ min_protocol_version = viewer_certificate.get("MinimumProtocolVersion", "SSLv3")
138
+
139
+ # Check origin protocol policy
140
+ origins = dist_config.get("Origins", {}).get("Items", [])
141
+ insecure_origins = []
142
+ for origin in origins:
143
+ custom_origin_config = origin.get("CustomOriginConfig", {})
144
+ if custom_origin_config:
145
+ origin_protocol_policy = custom_origin_config.get("OriginProtocolPolicy", "http-only")
146
+ if origin_protocol_policy == "http-only":
147
+ insecure_origins.append(origin.get("Id", "unknown"))
148
+
149
+ # Determine if configuration is secure
150
+ issues = []
151
+ severity = Severity.MEDIUM
152
+
153
+ # Check viewer protocol policy
154
+ if viewer_protocol_policy not in ["redirect-to-https", "https-only"]:
155
+ issues.append(f"Viewer protocol allows HTTP (policy: {viewer_protocol_policy})")
156
+ severity = Severity.HIGH
157
+
158
+ # Check minimum TLS version
159
+ if min_protocol_version not in ["TLSv1.2_2021", "TLSv1.2_2019", "TLSv1.2_2018", "TLSv1.1_2016"]:
160
+ if "TLSv1.2" not in min_protocol_version and "TLSv1.3" not in min_protocol_version:
161
+ issues.append(f"Outdated minimum TLS version: {min_protocol_version}")
162
+ severity = Severity.HIGH
163
+
164
+ # Check origin protocol
165
+ if insecure_origins:
166
+ issues.append(f"Origins using HTTP-only: {', '.join(insecure_origins)}")
167
+ severity = Severity.MEDIUM
168
+
169
+ # Create evidence
170
+ evidence = self.create_evidence(
171
+ resource_id=dist_id,
172
+ resource_type="cloudfront_distribution",
173
+ data={
174
+ "distribution_id": dist_id,
175
+ "domain_name": dist_domain,
176
+ "status": dist_status,
177
+ "viewer_protocol_policy": viewer_protocol_policy,
178
+ "minimum_protocol_version": min_protocol_version,
179
+ "insecure_origins": insecure_origins,
180
+ "has_issues": len(issues) > 0,
181
+ "issues": issues,
182
+ }
183
+ )
184
+ result.add_evidence(evidence)
185
+
186
+ if len(issues) == 0:
187
+ https_enforced_count += 1
188
+ self.logger.debug(
189
+ "cloudfront_https_enforced",
190
+ distribution_id=dist_id
191
+ )
192
+ else:
193
+ # Create finding for insecure distribution
194
+ finding = self.create_finding(
195
+ resource_id=dist_id,
196
+ resource_type="cloudfront_distribution",
197
+ severity=severity,
198
+ title="CloudFront distribution not properly enforcing HTTPS",
199
+ description=f"CloudFront distribution '{dist_id}' (domain: {dist_domain}) has HTTPS "
200
+ f"enforcement issues: {'; '.join(issues)}. CloudFront distributions should "
201
+ "enforce HTTPS to encrypt data in transit, use modern TLS versions (1.2+), "
202
+ "and communicate with origins securely. ISO 27001 A.8.24 requires "
203
+ "cryptographic controls for data in transit.",
204
+ remediation=(
205
+ f"Improve CloudFront distribution '{dist_id}' HTTPS configuration:\n\n"
206
+ "1. Enforce HTTPS for viewers:\n"
207
+ f"aws cloudfront get-distribution-config --id {dist_id} > dist-config.json\n"
208
+ "# Edit dist-config.json:\n"
209
+ "# Set DefaultCacheBehavior.ViewerProtocolPolicy to 'redirect-to-https' or 'https-only'\n"
210
+ f"aws cloudfront update-distribution \\\n"
211
+ f" --id {dist_id} \\\n"
212
+ " --distribution-config file://dist-config.json \\\n"
213
+ " --if-match <ETag-from-get-distribution-config>\n\n"
214
+ "2. Update minimum TLS version:\n"
215
+ "# In dist-config.json:\n"
216
+ "# Set ViewerCertificate.MinimumProtocolVersion to 'TLSv1.2_2021'\n\n"
217
+ "3. Use HTTPS for origin communication:\n"
218
+ "# In dist-config.json:\n"
219
+ "# For each CustomOriginConfig:\n"
220
+ "# Set OriginProtocolPolicy to 'https-only' or 'match-viewer'\n\n"
221
+ "Or use AWS Console:\n"
222
+ "1. Go to CloudFront console\n"
223
+ f"2. Select distribution '{dist_id}'\n"
224
+ "3. Go to 'Behaviors' tab\n"
225
+ "4. Edit default behavior:\n"
226
+ " - Viewer Protocol Policy: 'Redirect HTTP to HTTPS'\n"
227
+ "5. Go to 'General' tab → Edit:\n"
228
+ " - Custom SSL Certificate (if using custom domain)\n"
229
+ " - Security Policy: TLSv1.2_2021 (recommended)\n"
230
+ "6. Go to 'Origins' tab\n"
231
+ "7. Edit each origin:\n"
232
+ " - Protocol: HTTPS only\n"
233
+ " - Minimum Origin SSL Protocol: TLSv1.2\n\n"
234
+ "Recommended configurations:\n"
235
+ "Viewer Protocol Policy:\n"
236
+ "- redirect-to-https (recommended for public sites)\n"
237
+ "- https-only (for APIs or sensitive content)\n\n"
238
+ "Security Policy (Minimum TLS Version):\n"
239
+ "- TLSv1.2_2021 (recommended, supports TLS 1.2 and 1.3)\n"
240
+ "- TLSv1.2_2019\n"
241
+ "- TLSv1.2_2018\n\n"
242
+ "Origin Protocol Policy:\n"
243
+ "- https-only (most secure)\n"
244
+ "- match-viewer (flexible, but ensure viewers use HTTPS)\n\n"
245
+ "Additional security best practices:\n"
246
+ "- Use AWS Certificate Manager (ACM) for SSL/TLS certificates\n"
247
+ "- Enable automatic certificate renewal\n"
248
+ "- Configure custom SSL certificate for custom domains\n"
249
+ "- Enable AWS WAF for web application protection\n"
250
+ "- Enable access logging for audit trail\n"
251
+ "- Use Origin Access Identity (OAI) for S3 origins\n"
252
+ "- Enable field-level encryption for sensitive data\n"
253
+ "- Configure security headers (HSTS, CSP, etc.)\n"
254
+ "- Monitor CloudFront metrics in CloudWatch\n"
255
+ "- Use signed URLs/cookies for private content"
256
+ ),
257
+ evidence=evidence
258
+ )
259
+ result.add_finding(finding)
260
+
261
+ self.logger.warning(
262
+ "cloudfront_https_not_enforced",
263
+ distribution_id=dist_id,
264
+ issues=issues
265
+ )
266
+
267
+ # Calculate compliance score
268
+ result.score = (https_enforced_count / len(distributions)) * 100
269
+
270
+ # Determine pass/fail
271
+ result.passed = https_enforced_count == len(distributions)
272
+ result.status = TestStatus.PASSED if result.passed else TestStatus.FAILED
273
+
274
+ # Add metadata
275
+ result.metadata = {
276
+ "total_distributions": len(distributions),
277
+ "https_enforced": https_enforced_count,
278
+ "insecure_distributions": len(distributions) - https_enforced_count,
279
+ "compliance_percentage": result.score,
280
+ }
281
+
282
+ self.logger.info(
283
+ "cloudfront_https_test_completed",
284
+ total_distributions=len(distributions),
285
+ https_enforced=https_enforced_count,
286
+ score=result.score,
287
+ passed=result.passed
288
+ )
289
+
290
+ except ClientError as e:
291
+ error_code = e.response.get("Error", {}).get("Code")
292
+ self.logger.error("cloudfront_https_test_error", error_code=error_code, error=str(e))
293
+ result.status = TestStatus.ERROR
294
+ result.passed = False
295
+ result.score = 0.0
296
+ result.error_message = f"AWS API Error: {error_code} - {str(e)}"
297
+
298
+ except Exception as e:
299
+ self.logger.error("cloudfront_https_test_error", error=str(e))
300
+ result.status = TestStatus.ERROR
301
+ result.passed = False
302
+ result.score = 0.0
303
+ result.error_message = str(e)
304
+
305
+ return result
306
+
307
+
308
+ # ============================================================================
309
+ # CONVENIENCE FUNCTION
310
+ # ============================================================================
311
+
312
+
313
+ def run_cloudfront_https_test(connector: AWSConnector) -> TestResult:
314
+ """Run CloudFront HTTPS enforcement compliance test.
315
+
316
+ Convenience function for running the test.
317
+
318
+ Args:
319
+ connector: AWS connector
320
+
321
+ Returns:
322
+ TestResult
323
+
324
+ Example:
325
+ >>> from complio.connectors.aws.client import AWSConnector
326
+ >>> connector = AWSConnector("production", "us-east-1")
327
+ >>> connector.connect()
328
+ >>> result = run_cloudfront_https_test(connector)
329
+ >>> print(f"Score: {result.score}%")
330
+ """
331
+ test = CloudFrontHTTPSTest(connector)
332
+ return test.execute()
@@ -0,0 +1,343 @@
1
+ """
2
+ AWS Direct Connect security compliance test.
3
+
4
+ Checks that Direct Connect connections use secure configurations.
5
+
6
+ ISO 27001 Control: A.8.22 - Network segregation
7
+ Requirement: Direct Connect connections should use encryption and proper configurations
8
+
9
+ Example:
10
+ >>> from complio.connectors.aws.client import AWSConnector
11
+ >>> from complio.tests_library.network.direct_connect_security import DirectConnectSecurityTest
12
+ >>>
13
+ >>> connector = AWSConnector("production", "us-east-1")
14
+ >>> connector.connect()
15
+ >>>
16
+ >>> test = DirectConnectSecurityTest(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 DirectConnectSecurityTest(ComplianceTest):
35
+ """Test for AWS Direct Connect security compliance.
36
+
37
+ Verifies that Direct Connect connections use secure configurations:
38
+ - Virtual interfaces should use MACsec encryption when possible
39
+ - Connection state should be available (not down)
40
+ - Should have proper tagging for management
41
+
42
+ Compliance Requirements:
43
+ - Use MACsec encryption for Layer 2 security (when supported)
44
+ - Connections should be in available state
45
+ - Virtual interfaces properly configured
46
+
47
+ Scoring:
48
+ - 100% if all Direct Connect resources follow security best practices
49
+ - Proportional score based on compliant/total ratio
50
+ - 100% if no Direct Connect connections exist
51
+
52
+ Example:
53
+ >>> test = DirectConnectSecurityTest(connector)
54
+ >>> result = test.execute()
55
+ >>> for finding in result.findings:
56
+ ... print(f"{finding.resource_id}: {finding.title}")
57
+ """
58
+
59
+ def __init__(self, connector: AWSConnector) -> None:
60
+ """Initialize Direct Connect security test.
61
+
62
+ Args:
63
+ connector: AWS connector instance
64
+ """
65
+ super().__init__(
66
+ test_id="direct_connect_security",
67
+ test_name="AWS Direct Connect Security Check",
68
+ description="Verify Direct Connect connections use secure configurations",
69
+ control_id="A.8.22",
70
+ connector=connector,
71
+ scope="regional",
72
+ )
73
+
74
+ def execute(self) -> TestResult:
75
+ """Execute Direct Connect security compliance test.
76
+
77
+ Returns:
78
+ TestResult with findings for insecure Direct Connect configurations
79
+
80
+ Example:
81
+ >>> test = DirectConnectSecurityTest(connector)
82
+ >>> result = test.execute()
83
+ >>> print(result.score)
84
+ 100.0
85
+ """
86
+ result = TestResult(
87
+ test_id=self.test_id,
88
+ test_name=self.test_name,
89
+ status=TestStatus.PASSED,
90
+ passed=True,
91
+ score=100.0,
92
+ )
93
+
94
+ try:
95
+ # Get Direct Connect client
96
+ dx_client = self.connector.get_client("directconnect")
97
+
98
+ # List all Direct Connect connections
99
+ self.logger.info("listing_direct_connect_connections")
100
+ connections_response = dx_client.describe_connections()
101
+ connections = connections_response.get("connections", [])
102
+
103
+ if not connections:
104
+ self.logger.info("no_direct_connect_connections_found")
105
+ result.metadata["message"] = "No Direct Connect connections found in region"
106
+ return result
107
+
108
+ self.logger.info("direct_connect_connections_found", count=len(connections))
109
+
110
+ # Check security for each connection
111
+ secure_connection_count = 0
112
+
113
+ for connection in connections:
114
+ connection_id = connection.get("connectionId", "")
115
+ connection_name = connection.get("connectionName", "")
116
+ connection_state = connection.get("connectionState", "")
117
+ location = connection.get("location", "")
118
+ bandwidth = connection.get("bandwidth", "")
119
+
120
+ # Skip deleted connections
121
+ if connection_state in ["deleted", "deleting"]:
122
+ continue
123
+
124
+ result.resources_scanned += 1
125
+
126
+ # Check for security issues
127
+ issues = []
128
+ severity = Severity.MEDIUM
129
+
130
+ # Check connection state
131
+ if connection_state not in ["available", "requested", "pending"]:
132
+ issues.append(f"Connection not in healthy state: {connection_state}")
133
+ severity = Severity.HIGH
134
+
135
+ # Check if MACsec is supported and enabled
136
+ has_mac_sec_capability = connection.get("hasLogicalRedundancy", False)
137
+ mac_sec_keys = connection.get("macSecKeys", [])
138
+
139
+ # MACsec provides Layer 2 encryption
140
+ if has_mac_sec_capability and len(mac_sec_keys) == 0:
141
+ issues.append("MACsec capable but not configured (encryption not enabled)")
142
+ severity = Severity.MEDIUM
143
+
144
+ # Get associated virtual interfaces
145
+ vifs_response = dx_client.describe_virtual_interfaces(connectionId=connection_id)
146
+ virtual_interfaces = vifs_response.get("virtualInterfaces", [])
147
+
148
+ # Check virtual interface configurations
149
+ vif_issues = []
150
+ for vif in virtual_interfaces:
151
+ vif_state = vif.get("virtualInterfaceState", "")
152
+ if vif_state == "down":
153
+ vif_issues.append(f"Virtual interface {vif.get('virtualInterfaceId')} is down")
154
+
155
+ if vif_issues:
156
+ issues.extend(vif_issues)
157
+ severity = Severity.HIGH
158
+
159
+ # Create evidence
160
+ evidence = self.create_evidence(
161
+ resource_id=connection_id,
162
+ resource_type="direct_connect_connection",
163
+ data={
164
+ "connection_id": connection_id,
165
+ "connection_name": connection_name,
166
+ "connection_state": connection_state,
167
+ "location": location,
168
+ "bandwidth": bandwidth,
169
+ "has_macsec_capability": has_mac_sec_capability,
170
+ "macsec_configured": len(mac_sec_keys) > 0,
171
+ "virtual_interfaces_count": len(virtual_interfaces),
172
+ "has_issues": len(issues) > 0,
173
+ "issues": issues,
174
+ }
175
+ )
176
+ result.add_evidence(evidence)
177
+
178
+ if len(issues) == 0:
179
+ secure_connection_count += 1
180
+ self.logger.debug(
181
+ "direct_connect_secure",
182
+ connection_id=connection_id
183
+ )
184
+ else:
185
+ # Create finding for insecure Direct Connect connection
186
+ finding = self.create_finding(
187
+ resource_id=connection_id,
188
+ resource_type="direct_connect_connection",
189
+ severity=severity,
190
+ title="Direct Connect connection has security issues",
191
+ description=f"Direct Connect connection '{connection_name}' ({connection_id}) at location "
192
+ f"'{location}' has security issues: {'; '.join(issues)}. Direct Connect "
193
+ "connections should use MACsec encryption when available, maintain healthy "
194
+ "connection states, and have properly configured virtual interfaces. "
195
+ "ISO 27001 A.8.22 requires secure network connections and encryption.",
196
+ remediation=(
197
+ f"Improve Direct Connect connection '{connection_id}' security:\n\n"
198
+ "1. Enable MACsec encryption (if supported):\n"
199
+ "# First, associate a MACsec secret key\n"
200
+ "aws directconnect associate-mac-sec-key \\\n"
201
+ f" --connection-id {connection_id} \\\n"
202
+ " --secret-arn <SECRETS-MANAGER-ARN>\n\n"
203
+ "# Verify MACsec is active\n"
204
+ f"aws directconnect describe-connections \\\n"
205
+ f" --connection-id {connection_id}\n\n"
206
+ "2. Check connection health:\n"
207
+ f"aws directconnect describe-connections \\\n"
208
+ f" --connection-id {connection_id}\n\n"
209
+ "If connection is down, contact AWS Support:\n"
210
+ "- Check Letter of Authorization (LOA) status\n"
211
+ "- Verify physical connectivity at colocation\n"
212
+ "- Check BGP configuration\n\n"
213
+ "3. Configure virtual interfaces properly:\n"
214
+ "# List all virtual interfaces\n"
215
+ f"aws directconnect describe-virtual-interfaces \\\n"
216
+ f" --connection-id {connection_id}\n\n"
217
+ "# For down virtual interfaces, check:\n"
218
+ "- BGP peering status\n"
219
+ "- VLAN configuration\n"
220
+ "- Route propagation\n"
221
+ "- Security group rules\n\n"
222
+ "Or use AWS Console:\n"
223
+ "1. Go to Direct Connect console\n"
224
+ "2. Select Connections\n"
225
+ f"3. Choose connection '{connection_name}'\n"
226
+ "4. Enable MACsec:\n"
227
+ " - Select 'Actions' → 'Associate MACsec key'\n"
228
+ " - Choose secret from Secrets Manager\n"
229
+ " - Verify MACsec status shows 'enabled'\n"
230
+ "5. Check virtual interfaces:\n"
231
+ " - Go to 'Virtual interfaces' tab\n"
232
+ " - Verify each VIF is in 'available' state\n"
233
+ " - Check BGP session status\n\n"
234
+ "Security best practices:\n"
235
+ "- Enable MACsec for Layer 2 encryption (10 Gbps and above)\n"
236
+ "- Use redundant connections for high availability\n"
237
+ "- Implement BGP authentication (MD5)\n"
238
+ "- Use VPN over Direct Connect for additional encryption\n"
239
+ "- Configure proper route filtering and BGP communities\n"
240
+ "- Enable Connection Health monitoring\n"
241
+ "- Use AWS Transit Gateway with Direct Connect Gateway\n"
242
+ "- Implement least privilege routing\n"
243
+ "- Tag connections for cost allocation and management\n"
244
+ "- Monitor with CloudWatch metrics:\n"
245
+ " • ConnectionState\n"
246
+ " • ConnectionBpsEgress/Ingress\n"
247
+ " • ConnectionPpsEgress/Ingress\n"
248
+ " • ConnectionLightLevelTx/Rx\n\n"
249
+ "MACsec encryption requirements:\n"
250
+ "- Supported on 10 Gbps and 100 Gbps connections\n"
251
+ "- Requires MACsec capable device at customer end\n"
252
+ "- Uses AES-256 GCM encryption\n"
253
+ "- Store keys in AWS Secrets Manager\n"
254
+ "- Rotate keys regularly\n\n"
255
+ "High availability setup:\n"
256
+ "- Deploy connections in multiple locations\n"
257
+ "- Use LAG (Link Aggregation Groups) when possible\n"
258
+ "- Configure BFD (Bidirectional Forwarding Detection)\n"
259
+ "- Implement active-active or active-passive failover\n"
260
+ "- Test failover scenarios regularly\n\n"
261
+ "Additional security layers:\n"
262
+ "- Use Site-to-Site VPN as backup\n"
263
+ "- Implement IPsec over Direct Connect for end-to-end encryption\n"
264
+ "- Use AWS PrivateLink for service-level isolation\n"
265
+ "- Configure network ACLs and security groups\n"
266
+ "- Enable VPC Flow Logs for traffic analysis"
267
+ ),
268
+ evidence=evidence
269
+ )
270
+ result.add_finding(finding)
271
+
272
+ self.logger.warning(
273
+ "direct_connect_insecure",
274
+ connection_id=connection_id,
275
+ issues=issues
276
+ )
277
+
278
+ # Calculate compliance score
279
+ result.score = (secure_connection_count / len(connections)) * 100
280
+
281
+ # Determine pass/fail
282
+ result.passed = secure_connection_count == len(connections)
283
+ result.status = TestStatus.PASSED if result.passed else TestStatus.FAILED
284
+
285
+ # Add metadata
286
+ result.metadata = {
287
+ "total_connections": len(connections),
288
+ "secure_connections": secure_connection_count,
289
+ "insecure_connections": len(connections) - secure_connection_count,
290
+ "compliance_percentage": result.score,
291
+ }
292
+
293
+ self.logger.info(
294
+ "direct_connect_security_test_completed",
295
+ total_connections=len(connections),
296
+ secure=secure_connection_count,
297
+ score=result.score,
298
+ passed=result.passed
299
+ )
300
+
301
+ except ClientError as e:
302
+ error_code = e.response.get("Error", {}).get("Code")
303
+ self.logger.error("direct_connect_security_test_error", error_code=error_code, error=str(e))
304
+ result.status = TestStatus.ERROR
305
+ result.passed = False
306
+ result.score = 0.0
307
+ result.error_message = f"AWS API Error: {error_code} - {str(e)}"
308
+
309
+ except Exception as e:
310
+ self.logger.error("direct_connect_security_test_error", error=str(e))
311
+ result.status = TestStatus.ERROR
312
+ result.passed = False
313
+ result.score = 0.0
314
+ result.error_message = str(e)
315
+
316
+ return result
317
+
318
+
319
+ # ============================================================================
320
+ # CONVENIENCE FUNCTION
321
+ # ============================================================================
322
+
323
+
324
+ def run_direct_connect_security_test(connector: AWSConnector) -> TestResult:
325
+ """Run AWS Direct Connect security compliance test.
326
+
327
+ Convenience function for running the test.
328
+
329
+ Args:
330
+ connector: AWS connector
331
+
332
+ Returns:
333
+ TestResult
334
+
335
+ Example:
336
+ >>> from complio.connectors.aws.client import AWSConnector
337
+ >>> connector = AWSConnector("production", "us-east-1")
338
+ >>> connector.connect()
339
+ >>> result = run_direct_connect_security_test(connector)
340
+ >>> print(f"Score: {result.score}%")
341
+ """
342
+ test = DirectConnectSecurityTest(connector)
343
+ return test.execute()