mcp-security-framework 1.1.0__py3-none-any.whl → 1.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.
- mcp_security_framework/__init__.py +26 -15
- mcp_security_framework/cli/__init__.py +1 -1
- mcp_security_framework/cli/cert_cli.py +233 -197
- mcp_security_framework/cli/security_cli.py +324 -234
- mcp_security_framework/constants.py +21 -27
- mcp_security_framework/core/auth_manager.py +41 -22
- mcp_security_framework/core/cert_manager.py +210 -147
- mcp_security_framework/core/permission_manager.py +9 -9
- mcp_security_framework/core/rate_limiter.py +2 -2
- mcp_security_framework/core/security_manager.py +284 -229
- mcp_security_framework/examples/__init__.py +6 -0
- mcp_security_framework/examples/comprehensive_example.py +349 -279
- mcp_security_framework/examples/django_example.py +247 -206
- mcp_security_framework/examples/fastapi_example.py +315 -283
- mcp_security_framework/examples/flask_example.py +274 -203
- mcp_security_framework/examples/gateway_example.py +304 -237
- mcp_security_framework/examples/microservice_example.py +258 -189
- mcp_security_framework/examples/standalone_example.py +255 -230
- mcp_security_framework/examples/test_all_examples.py +151 -135
- mcp_security_framework/middleware/__init__.py +46 -55
- mcp_security_framework/middleware/auth_middleware.py +62 -63
- mcp_security_framework/middleware/fastapi_auth_middleware.py +119 -118
- mcp_security_framework/middleware/fastapi_middleware.py +156 -148
- mcp_security_framework/middleware/flask_auth_middleware.py +160 -147
- mcp_security_framework/middleware/flask_middleware.py +183 -157
- mcp_security_framework/middleware/mtls_middleware.py +106 -117
- mcp_security_framework/middleware/rate_limit_middleware.py +105 -101
- mcp_security_framework/middleware/security_middleware.py +109 -124
- mcp_security_framework/schemas/config.py +2 -1
- mcp_security_framework/schemas/models.py +18 -6
- mcp_security_framework/utils/cert_utils.py +14 -8
- mcp_security_framework/utils/datetime_compat.py +116 -0
- {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/METADATA +2 -1
- mcp_security_framework-1.1.1.dist-info/RECORD +84 -0
- tests/conftest.py +63 -66
- tests/test_cli/test_cert_cli.py +184 -146
- tests/test_cli/test_security_cli.py +274 -247
- tests/test_core/test_cert_manager.py +24 -10
- tests/test_core/test_security_manager.py +2 -2
- tests/test_examples/test_comprehensive_example.py +190 -137
- tests/test_examples/test_fastapi_example.py +124 -101
- tests/test_examples/test_flask_example.py +124 -101
- tests/test_examples/test_standalone_example.py +73 -80
- tests/test_integration/test_auth_flow.py +213 -197
- tests/test_integration/test_certificate_flow.py +180 -149
- tests/test_integration/test_fastapi_integration.py +108 -111
- tests/test_integration/test_flask_integration.py +141 -140
- tests/test_integration/test_standalone_integration.py +290 -259
- tests/test_middleware/test_fastapi_auth_middleware.py +195 -174
- tests/test_middleware/test_fastapi_middleware.py +147 -132
- tests/test_middleware/test_flask_auth_middleware.py +260 -202
- tests/test_middleware/test_flask_middleware.py +201 -179
- tests/test_middleware/test_security_middleware.py +145 -130
- tests/test_utils/test_datetime_compat.py +147 -0
- mcp_security_framework-1.1.0.dist-info/RECORD +0 -82
- {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/WHEEL +0 -0
- {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/entry_points.txt +0 -0
- {mcp_security_framework-1.1.0.dist-info → mcp_security_framework-1.1.1.dist-info}/top_level.txt +0 -0
@@ -25,13 +25,13 @@ import ssl
|
|
25
25
|
from abc import ABC, abstractmethod
|
26
26
|
from typing import Any, Dict, List, Optional, Union
|
27
27
|
|
28
|
+
from ..schemas.models import AuthMethod, AuthResult, AuthStatus
|
28
29
|
from .security_middleware import SecurityMiddleware, SecurityMiddlewareError
|
29
|
-
from ..schemas.models import AuthResult, AuthStatus, AuthMethod
|
30
30
|
|
31
31
|
|
32
32
|
class MTLSMiddlewareError(SecurityMiddlewareError):
|
33
33
|
"""Raised when mTLS middleware encounters an error."""
|
34
|
-
|
34
|
+
|
35
35
|
def __init__(self, message: str, error_code: int = -32037):
|
36
36
|
self.message = message
|
37
37
|
self.error_code = error_code
|
@@ -41,18 +41,18 @@ class MTLSMiddlewareError(SecurityMiddlewareError):
|
|
41
41
|
class MTLSMiddleware(SecurityMiddleware):
|
42
42
|
"""
|
43
43
|
mTLS (Mutual TLS) Middleware Class
|
44
|
-
|
44
|
+
|
45
45
|
This class provides mTLS-specific middleware that focuses on mutual
|
46
46
|
TLS authentication and certificate validation. It's designed for
|
47
47
|
scenarios where client certificates are used for authentication.
|
48
|
-
|
48
|
+
|
49
49
|
The MTLSMiddleware implements:
|
50
50
|
- mTLS authentication processing
|
51
51
|
- Client certificate validation
|
52
52
|
- Certificate chain verification
|
53
53
|
- Certificate-based role extraction
|
54
54
|
- mTLS event logging
|
55
|
-
|
55
|
+
|
56
56
|
Key Responsibilities:
|
57
57
|
- Process requests through mTLS authentication pipeline
|
58
58
|
- Validate client certificates
|
@@ -60,82 +60,82 @@ class MTLSMiddleware(SecurityMiddleware):
|
|
60
60
|
- Extract roles and permissions from certificates
|
61
61
|
- Handle mTLS-specific error scenarios
|
62
62
|
- Log mTLS authentication events
|
63
|
-
|
63
|
+
|
64
64
|
Attributes:
|
65
65
|
Inherits all attributes from SecurityMiddleware
|
66
66
|
_certificate_cache (Dict): Cache for certificate validation results
|
67
67
|
_ca_certificates (List): List of trusted CA certificates
|
68
|
-
|
68
|
+
|
69
69
|
Example:
|
70
70
|
>>> from mcp_security_framework.middleware import MTLSMiddleware
|
71
|
-
>>>
|
71
|
+
>>>
|
72
72
|
>>> security_manager = SecurityManager(config)
|
73
73
|
>>> mtls_middleware = MTLSMiddleware(security_manager)
|
74
74
|
>>> app.add_middleware(mtls_middleware)
|
75
|
-
|
75
|
+
|
76
76
|
Note:
|
77
77
|
This middleware requires proper SSL/TLS configuration on the
|
78
78
|
server to handle client certificates.
|
79
79
|
"""
|
80
|
-
|
80
|
+
|
81
81
|
def __init__(self, security_manager):
|
82
82
|
"""
|
83
83
|
Initialize mTLS Middleware.
|
84
|
-
|
84
|
+
|
85
85
|
Args:
|
86
86
|
security_manager: Security manager instance containing
|
87
87
|
all security components and configuration.
|
88
|
-
|
88
|
+
|
89
89
|
Raises:
|
90
90
|
MTLSMiddlewareError: If initialization fails
|
91
91
|
"""
|
92
92
|
super().__init__(security_manager)
|
93
93
|
self.logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}")
|
94
|
-
|
94
|
+
|
95
95
|
# Initialize certificate cache
|
96
96
|
self._certificate_cache: Dict[str, Dict[str, Any]] = {}
|
97
|
-
|
97
|
+
|
98
98
|
# Load CA certificates if configured
|
99
99
|
self._ca_certificates = self._load_ca_certificates()
|
100
|
-
|
100
|
+
|
101
101
|
self.logger.info(
|
102
102
|
"mTLS middleware initialized",
|
103
|
-
extra={"ca_certificates_count": len(self._ca_certificates)}
|
103
|
+
extra={"ca_certificates_count": len(self._ca_certificates)},
|
104
104
|
)
|
105
|
-
|
105
|
+
|
106
106
|
@abstractmethod
|
107
107
|
def __call__(self, request: Any, call_next: Any) -> Any:
|
108
108
|
"""
|
109
109
|
Process request through mTLS middleware.
|
110
|
-
|
110
|
+
|
111
111
|
This method implements the mTLS authentication processing
|
112
112
|
pipeline, focusing on client certificate validation.
|
113
|
-
|
113
|
+
|
114
114
|
Args:
|
115
115
|
request: Framework-specific request object
|
116
116
|
call_next: Framework-specific call_next function
|
117
|
-
|
117
|
+
|
118
118
|
Returns:
|
119
119
|
Framework-specific response object
|
120
|
-
|
120
|
+
|
121
121
|
Raises:
|
122
122
|
MTLSMiddlewareError: If mTLS processing fails
|
123
123
|
"""
|
124
124
|
pass
|
125
|
-
|
125
|
+
|
126
126
|
def _authenticate_mtls(self, request: Any) -> AuthResult:
|
127
127
|
"""
|
128
128
|
Perform mTLS authentication.
|
129
|
-
|
129
|
+
|
130
130
|
This method handles mutual TLS authentication by validating
|
131
131
|
client certificates and extracting user information.
|
132
|
-
|
132
|
+
|
133
133
|
Args:
|
134
134
|
request: Framework-specific request object
|
135
|
-
|
135
|
+
|
136
136
|
Returns:
|
137
137
|
AuthResult: Authentication result with user information
|
138
|
-
|
138
|
+
|
139
139
|
Raises:
|
140
140
|
MTLSMiddlewareError: If mTLS authentication fails
|
141
141
|
"""
|
@@ -150,9 +150,9 @@ class MTLSMiddleware(SecurityMiddleware):
|
|
150
150
|
roles=[],
|
151
151
|
auth_method=AuthMethod.CERTIFICATE,
|
152
152
|
error_code=-32038,
|
153
|
-
error_message="No client certificate provided"
|
153
|
+
error_message="No client certificate provided",
|
154
154
|
)
|
155
|
-
|
155
|
+
|
156
156
|
# Validate client certificate
|
157
157
|
cert_validation = self._validate_client_certificate(client_cert)
|
158
158
|
if not cert_validation["is_valid"]:
|
@@ -163,119 +163,113 @@ class MTLSMiddleware(SecurityMiddleware):
|
|
163
163
|
roles=[],
|
164
164
|
auth_method=AuthMethod.CERTIFICATE,
|
165
165
|
error_code=-32039,
|
166
|
-
error_message=cert_validation["error_message"]
|
166
|
+
error_message=cert_validation["error_message"],
|
167
167
|
)
|
168
|
-
|
168
|
+
|
169
169
|
# Extract user information from certificate
|
170
170
|
user_info = self._extract_user_info_from_certificate(client_cert)
|
171
|
-
|
171
|
+
|
172
172
|
# Extract roles from certificate
|
173
173
|
roles = self._extract_roles_from_certificate(client_cert)
|
174
|
-
|
174
|
+
|
175
175
|
self.logger.info(
|
176
176
|
"mTLS authentication successful",
|
177
177
|
extra={
|
178
178
|
"username": user_info["username"],
|
179
179
|
"subject": user_info["subject"],
|
180
180
|
"issuer": user_info["issuer"],
|
181
|
-
"roles": roles
|
182
|
-
}
|
181
|
+
"roles": roles,
|
182
|
+
},
|
183
183
|
)
|
184
|
-
|
184
|
+
|
185
185
|
return AuthResult(
|
186
186
|
is_valid=True,
|
187
187
|
status=AuthStatus.SUCCESS,
|
188
188
|
username=user_info["username"],
|
189
189
|
roles=roles,
|
190
|
-
auth_method=AuthMethod.CERTIFICATE
|
190
|
+
auth_method=AuthMethod.CERTIFICATE,
|
191
191
|
)
|
192
|
-
|
192
|
+
|
193
193
|
except Exception as e:
|
194
194
|
self.logger.error(
|
195
|
-
"mTLS authentication failed",
|
196
|
-
extra={"error": str(e)},
|
197
|
-
exc_info=True
|
195
|
+
"mTLS authentication failed", extra={"error": str(e)}, exc_info=True
|
198
196
|
)
|
199
197
|
raise MTLSMiddlewareError(
|
200
|
-
f"mTLS authentication failed: {str(e)}",
|
201
|
-
error_code=-32040
|
198
|
+
f"mTLS authentication failed: {str(e)}", error_code=-32040
|
202
199
|
)
|
203
|
-
|
200
|
+
|
204
201
|
def _get_client_certificate(self, request: Any) -> Optional[str]:
|
205
202
|
"""
|
206
203
|
Get client certificate from request.
|
207
|
-
|
204
|
+
|
208
205
|
Args:
|
209
206
|
request: Framework-specific request object
|
210
|
-
|
207
|
+
|
211
208
|
Returns:
|
212
209
|
Optional[str]: Client certificate in PEM format if available
|
213
210
|
"""
|
214
211
|
# This should be implemented by framework-specific subclasses
|
215
212
|
# to extract the client certificate from the request
|
216
213
|
return None
|
217
|
-
|
214
|
+
|
218
215
|
def _validate_client_certificate(self, cert_pem: str) -> Dict[str, Any]:
|
219
216
|
"""
|
220
217
|
Validate client certificate.
|
221
|
-
|
218
|
+
|
222
219
|
Args:
|
223
220
|
cert_pem (str): Client certificate in PEM format
|
224
|
-
|
221
|
+
|
225
222
|
Returns:
|
226
223
|
Dict[str, Any]: Validation result with status and details
|
227
224
|
"""
|
228
225
|
try:
|
229
226
|
# Use security manager's certificate validation
|
230
227
|
is_valid = self.security_manager.cert_manager.validate_certificate_chain(
|
231
|
-
cert_pem,
|
232
|
-
self.config.ssl.ca_cert_file if self.config.ssl else None
|
228
|
+
cert_pem, self.config.ssl.ca_cert_file if self.config.ssl else None
|
233
229
|
)
|
234
|
-
|
230
|
+
|
235
231
|
if is_valid:
|
236
|
-
return {
|
237
|
-
"is_valid": True,
|
238
|
-
"error_message": ""
|
239
|
-
}
|
232
|
+
return {"is_valid": True, "error_message": ""}
|
240
233
|
else:
|
241
234
|
return {
|
242
235
|
"is_valid": False,
|
243
|
-
"error_message": "Certificate validation failed"
|
236
|
+
"error_message": "Certificate validation failed",
|
244
237
|
}
|
245
|
-
|
238
|
+
|
246
239
|
except Exception as e:
|
247
240
|
return {
|
248
241
|
"is_valid": False,
|
249
|
-
"error_message": f"Certificate validation error: {str(e)}"
|
242
|
+
"error_message": f"Certificate validation error: {str(e)}",
|
250
243
|
}
|
251
|
-
|
244
|
+
|
252
245
|
def _extract_user_info_from_certificate(self, cert_pem: str) -> Dict[str, str]:
|
253
246
|
"""
|
254
247
|
Extract user information from certificate.
|
255
|
-
|
248
|
+
|
256
249
|
Args:
|
257
250
|
cert_pem (str): Client certificate in PEM format
|
258
|
-
|
251
|
+
|
259
252
|
Returns:
|
260
253
|
Dict[str, str]: User information extracted from certificate
|
261
254
|
"""
|
262
255
|
try:
|
263
256
|
# Use security manager's certificate utilities
|
264
|
-
cert_info = self.security_manager.cert_manager.get_certificate_info(
|
265
|
-
|
257
|
+
cert_info = self.security_manager.cert_manager.get_certificate_info(
|
258
|
+
cert_pem
|
259
|
+
)
|
260
|
+
|
266
261
|
return {
|
267
262
|
"username": cert_info.get("common_name", "unknown"),
|
268
263
|
"subject": cert_info.get("subject", ""),
|
269
264
|
"issuer": cert_info.get("issuer", ""),
|
270
265
|
"serial_number": cert_info.get("serial_number", ""),
|
271
266
|
"valid_from": str(cert_info.get("valid_from", "")),
|
272
|
-
"valid_until": str(cert_info.get("valid_until", ""))
|
267
|
+
"valid_until": str(cert_info.get("valid_until", "")),
|
273
268
|
}
|
274
|
-
|
269
|
+
|
275
270
|
except Exception as e:
|
276
271
|
self.logger.error(
|
277
|
-
"Failed to extract user info from certificate",
|
278
|
-
extra={"error": str(e)}
|
272
|
+
"Failed to extract user info from certificate", extra={"error": str(e)}
|
279
273
|
)
|
280
274
|
return {
|
281
275
|
"username": "unknown",
|
@@ -283,61 +277,59 @@ class MTLSMiddleware(SecurityMiddleware):
|
|
283
277
|
"issuer": "",
|
284
278
|
"serial_number": "",
|
285
279
|
"valid_from": "",
|
286
|
-
"valid_until": ""
|
280
|
+
"valid_until": "",
|
287
281
|
}
|
288
|
-
|
282
|
+
|
289
283
|
def _extract_roles_from_certificate(self, cert_pem: str) -> List[str]:
|
290
284
|
"""
|
291
285
|
Extract roles from certificate.
|
292
|
-
|
286
|
+
|
293
287
|
Args:
|
294
288
|
cert_pem (str): Client certificate in PEM format
|
295
|
-
|
289
|
+
|
296
290
|
Returns:
|
297
291
|
List[str]: List of roles extracted from certificate
|
298
292
|
"""
|
299
293
|
try:
|
300
294
|
# Use security manager's certificate utilities
|
301
|
-
roles = self.security_manager.cert_manager.extract_roles_from_certificate(
|
295
|
+
roles = self.security_manager.cert_manager.extract_roles_from_certificate(
|
296
|
+
cert_pem
|
297
|
+
)
|
302
298
|
return roles
|
303
|
-
|
299
|
+
|
304
300
|
except Exception as e:
|
305
301
|
self.logger.error(
|
306
|
-
"Failed to extract roles from certificate",
|
307
|
-
extra={"error": str(e)}
|
302
|
+
"Failed to extract roles from certificate", extra={"error": str(e)}
|
308
303
|
)
|
309
304
|
return []
|
310
|
-
|
305
|
+
|
311
306
|
def _load_ca_certificates(self) -> List[str]:
|
312
307
|
"""
|
313
308
|
Load trusted CA certificates.
|
314
|
-
|
309
|
+
|
315
310
|
Returns:
|
316
311
|
List[str]: List of CA certificates in PEM format
|
317
312
|
"""
|
318
313
|
ca_certs = []
|
319
|
-
|
314
|
+
|
320
315
|
try:
|
321
316
|
if self.config.ssl and self.config.ssl.ca_cert_file:
|
322
317
|
# Load CA certificate from file
|
323
|
-
with open(self.config.ssl.ca_cert_file,
|
318
|
+
with open(self.config.ssl.ca_cert_file, "r") as f:
|
324
319
|
ca_certs.append(f.read())
|
325
|
-
|
320
|
+
|
326
321
|
except Exception as e:
|
327
|
-
self.logger.error(
|
328
|
-
|
329
|
-
extra={"error": str(e)}
|
330
|
-
)
|
331
|
-
|
322
|
+
self.logger.error("Failed to load CA certificates", extra={"error": str(e)})
|
323
|
+
|
332
324
|
return ca_certs
|
333
|
-
|
325
|
+
|
334
326
|
def _verify_certificate_chain(self, cert_pem: str) -> bool:
|
335
327
|
"""
|
336
328
|
Verify certificate chain against trusted CAs.
|
337
|
-
|
329
|
+
|
338
330
|
Args:
|
339
331
|
cert_pem (str): Client certificate in PEM format
|
340
|
-
|
332
|
+
|
341
333
|
Returns:
|
342
334
|
bool: True if chain is valid, False otherwise
|
343
335
|
"""
|
@@ -345,21 +337,20 @@ class MTLSMiddleware(SecurityMiddleware):
|
|
345
337
|
# This would implement certificate chain verification
|
346
338
|
# against the loaded CA certificates
|
347
339
|
return True
|
348
|
-
|
340
|
+
|
349
341
|
except Exception as e:
|
350
342
|
self.logger.error(
|
351
|
-
"Certificate chain verification failed",
|
352
|
-
extra={"error": str(e)}
|
343
|
+
"Certificate chain verification failed", extra={"error": str(e)}
|
353
344
|
)
|
354
345
|
return False
|
355
|
-
|
346
|
+
|
356
347
|
def _check_certificate_revocation(self, cert_pem: str) -> bool:
|
357
348
|
"""
|
358
349
|
Check if certificate is revoked.
|
359
|
-
|
350
|
+
|
360
351
|
Args:
|
361
352
|
cert_pem (str): Client certificate in PEM format
|
362
|
-
|
353
|
+
|
363
354
|
Returns:
|
364
355
|
bool: True if certificate is not revoked, False otherwise
|
365
356
|
"""
|
@@ -367,19 +358,22 @@ class MTLSMiddleware(SecurityMiddleware):
|
|
367
358
|
# This would implement CRL or OCSP checking
|
368
359
|
# For now, assume certificate is not revoked
|
369
360
|
return True
|
370
|
-
|
361
|
+
|
371
362
|
except Exception as e:
|
372
363
|
self.logger.error(
|
373
|
-
"Certificate revocation check failed",
|
374
|
-
extra={"error": str(e)}
|
364
|
+
"Certificate revocation check failed", extra={"error": str(e)}
|
375
365
|
)
|
376
366
|
return False
|
377
|
-
|
378
|
-
def _log_mtls_event(
|
379
|
-
|
367
|
+
|
368
|
+
def _log_mtls_event(
|
369
|
+
self,
|
370
|
+
event_type: str,
|
371
|
+
cert_info: Dict[str, Any],
|
372
|
+
request_details: Dict[str, Any],
|
373
|
+
) -> None:
|
380
374
|
"""
|
381
375
|
Log mTLS event.
|
382
|
-
|
376
|
+
|
383
377
|
Args:
|
384
378
|
event_type (str): Type of mTLS event
|
385
379
|
cert_info (Dict[str, Any]): Certificate information
|
@@ -393,47 +387,42 @@ class MTLSMiddleware(SecurityMiddleware):
|
|
393
387
|
"subject": cert_info.get("subject", ""),
|
394
388
|
"issuer": cert_info.get("issuer", ""),
|
395
389
|
"serial_number": cert_info.get("serial_number", ""),
|
396
|
-
**request_details
|
397
|
-
}
|
390
|
+
**request_details,
|
391
|
+
},
|
398
392
|
)
|
399
|
-
|
393
|
+
|
400
394
|
def get_certificate_info(self, cert_pem: str) -> Dict[str, Any]:
|
401
395
|
"""
|
402
396
|
Get detailed certificate information.
|
403
|
-
|
397
|
+
|
404
398
|
Args:
|
405
399
|
cert_pem (str): Certificate in PEM format
|
406
|
-
|
400
|
+
|
407
401
|
Returns:
|
408
402
|
Dict[str, Any]: Detailed certificate information
|
409
403
|
"""
|
410
404
|
try:
|
411
405
|
return self.security_manager.cert_manager.get_certificate_info(cert_pem)
|
412
406
|
except Exception as e:
|
413
|
-
self.logger.error(
|
414
|
-
"Failed to get certificate info",
|
415
|
-
extra={"error": str(e)}
|
416
|
-
)
|
407
|
+
self.logger.error("Failed to get certificate info", extra={"error": str(e)})
|
417
408
|
return {"error": str(e)}
|
418
|
-
|
409
|
+
|
419
410
|
def validate_certificate_chain(self, cert_pem: str) -> bool:
|
420
411
|
"""
|
421
412
|
Validate certificate chain.
|
422
|
-
|
413
|
+
|
423
414
|
Args:
|
424
415
|
cert_pem (str): Certificate in PEM format
|
425
|
-
|
416
|
+
|
426
417
|
Returns:
|
427
418
|
bool: True if chain is valid, False otherwise
|
428
419
|
"""
|
429
420
|
try:
|
430
421
|
return self.security_manager.cert_manager.validate_certificate_chain(
|
431
|
-
cert_pem,
|
432
|
-
self.config.ssl.ca_cert_file if self.config.ssl else None
|
422
|
+
cert_pem, self.config.ssl.ca_cert_file if self.config.ssl else None
|
433
423
|
)
|
434
424
|
except Exception as e:
|
435
425
|
self.logger.error(
|
436
|
-
"Certificate chain validation failed",
|
437
|
-
extra={"error": str(e)}
|
426
|
+
"Certificate chain validation failed", extra={"error": str(e)}
|
438
427
|
)
|
439
428
|
return False
|