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