mcp-proxy-adapter 6.2.35__py3-none-any.whl → 6.3.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_proxy_adapter/config.py +1 -0
- mcp_proxy_adapter/core/app_factory.py +127 -86
- mcp_proxy_adapter/core/config_validator.py +92 -55
- mcp_proxy_adapter/core/crl_utils.py +348 -0
- mcp_proxy_adapter/core/security_integration.py +130 -66
- mcp_proxy_adapter/core/ssl_utils.py +37 -5
- mcp_proxy_adapter/examples/scripts/config_generator.py +1 -0
- mcp_proxy_adapter/main.py +19 -10
- mcp_proxy_adapter/utils/config_generator.py +1 -0
- mcp_proxy_adapter/version.py +1 -1
- {mcp_proxy_adapter-6.2.35.dist-info → mcp_proxy_adapter-6.3.0.dist-info}/METADATA +1 -1
- {mcp_proxy_adapter-6.2.35.dist-info → mcp_proxy_adapter-6.3.0.dist-info}/RECORD +16 -15
- {mcp_proxy_adapter-6.2.35.dist-info → mcp_proxy_adapter-6.3.0.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.2.35.dist-info → mcp_proxy_adapter-6.3.0.dist-info}/entry_points.txt +0 -0
- {mcp_proxy_adapter-6.2.35.dist-info → mcp_proxy_adapter-6.3.0.dist-info}/licenses/LICENSE +0 -0
- {mcp_proxy_adapter-6.2.35.dist-info → mcp_proxy_adapter-6.3.0.dist-info}/top_level.txt +0 -0
@@ -8,24 +8,33 @@ Author: Vasiliy Zdanovskiy
|
|
8
8
|
email: vasilyvz@gmail.com
|
9
9
|
"""
|
10
10
|
|
11
|
-
import
|
12
|
-
from typing import Dict, Any, Optional, List
|
13
|
-
from pathlib import Path
|
11
|
+
from typing import Dict, Any, List
|
14
12
|
|
15
13
|
# Direct imports from framework
|
16
14
|
try:
|
17
15
|
from mcp_security_framework import (
|
18
|
-
SecurityManager,
|
19
|
-
|
16
|
+
SecurityManager,
|
17
|
+
AuthManager,
|
18
|
+
CertificateManager,
|
19
|
+
PermissionManager,
|
20
|
+
RateLimiter,
|
20
21
|
)
|
21
22
|
from mcp_security_framework.schemas.config import (
|
22
|
-
SecurityConfig,
|
23
|
-
|
23
|
+
SecurityConfig,
|
24
|
+
AuthConfig,
|
25
|
+
SSLConfig,
|
26
|
+
PermissionConfig,
|
27
|
+
RateLimitConfig,
|
28
|
+
CertificateConfig,
|
29
|
+
LoggingConfig,
|
24
30
|
)
|
25
31
|
from mcp_security_framework.schemas.models import (
|
26
|
-
AuthResult,
|
32
|
+
AuthResult,
|
33
|
+
ValidationResult,
|
34
|
+
CertificatePair,
|
27
35
|
)
|
28
36
|
from mcp_security_framework.middleware.fastapi_middleware import FastAPISecurityMiddleware
|
37
|
+
|
29
38
|
SECURITY_FRAMEWORK_AVAILABLE = True
|
30
39
|
except ImportError:
|
31
40
|
SECURITY_FRAMEWORK_AVAILABLE = False
|
@@ -43,38 +52,38 @@ from mcp_proxy_adapter.core.logging import logger
|
|
43
52
|
class SecurityIntegration:
|
44
53
|
"""
|
45
54
|
Direct integration with mcp_security_framework.
|
46
|
-
|
55
|
+
|
47
56
|
This class replaces all project security methods with direct calls
|
48
57
|
to the security framework components.
|
49
58
|
"""
|
50
|
-
|
59
|
+
|
51
60
|
def __init__(self, config: Dict[str, Any]):
|
52
61
|
"""
|
53
62
|
Initialize security integration.
|
54
|
-
|
63
|
+
|
55
64
|
Args:
|
56
65
|
config: Configuration dictionary
|
57
66
|
"""
|
58
67
|
if not SECURITY_FRAMEWORK_AVAILABLE:
|
59
68
|
raise ImportError("mcp_security_framework is not available")
|
60
|
-
|
69
|
+
|
61
70
|
self.config = config
|
62
71
|
self.security_config = self._create_security_config()
|
63
|
-
|
72
|
+
|
64
73
|
# Initialize framework components
|
65
74
|
self.security_manager = SecurityManager(self.security_config)
|
66
75
|
self.permission_manager = PermissionManager(self.security_config.permissions)
|
67
76
|
self.auth_manager = AuthManager(self.security_config.auth, self.permission_manager)
|
68
77
|
self.certificate_manager = CertificateManager(self.security_config.certificates)
|
69
78
|
self.rate_limiter = RateLimiter(self.security_config.rate_limit)
|
70
|
-
|
79
|
+
|
71
80
|
logger.info("Security integration initialized with mcp_security_framework")
|
72
|
-
|
81
|
+
|
73
82
|
def _create_security_config(self) -> SecurityConfig:
|
74
83
|
"""Create SecurityConfig from project configuration."""
|
75
84
|
# self.config is already the security section passed from unified_security.py
|
76
85
|
security_section = self.config
|
77
|
-
|
86
|
+
|
78
87
|
# Create SSL config
|
79
88
|
ssl_config = SSLConfig(
|
80
89
|
enabled=security_section.get("ssl", {}).get("enabled", False),
|
@@ -87,9 +96,9 @@ class SecurityIntegration:
|
|
87
96
|
min_tls_version=security_section.get("ssl", {}).get("min_tls_version", "TLSv1.2"),
|
88
97
|
check_hostname=security_section.get("ssl", {}).get("check_hostname", True),
|
89
98
|
check_expiry=security_section.get("ssl", {}).get("check_expiry", True),
|
90
|
-
expiry_warning_days=security_section.get("ssl", {}).get("expiry_warning_days", 30)
|
99
|
+
expiry_warning_days=security_section.get("ssl", {}).get("expiry_warning_days", 30),
|
91
100
|
)
|
92
|
-
|
101
|
+
|
93
102
|
# Create auth config
|
94
103
|
auth_config = AuthConfig(
|
95
104
|
enabled=security_section.get("auth", {}).get("enabled", True),
|
@@ -100,21 +109,23 @@ class SecurityIntegration:
|
|
100
109
|
jwt_algorithm=security_section.get("auth", {}).get("jwt_algorithm", "HS256"),
|
101
110
|
jwt_expiry_hours=security_section.get("auth", {}).get("jwt_expiry_hours", 24),
|
102
111
|
certificate_auth=security_section.get("auth", {}).get("certificate_auth", False),
|
103
|
-
public_paths=security_section.get("auth", {}).get("public_paths", [])
|
112
|
+
public_paths=security_section.get("auth", {}).get("public_paths", []),
|
104
113
|
)
|
105
|
-
|
114
|
+
|
106
115
|
# Create permission config - handle null values properly
|
107
116
|
permissions_section = security_section.get("permissions", {})
|
108
117
|
permissions_enabled = permissions_section.get("enabled", True)
|
109
|
-
|
118
|
+
|
110
119
|
if permissions_enabled:
|
111
120
|
roles_file = permissions_section.get("roles_file")
|
112
121
|
|
113
122
|
# If roles_file is None or empty string, don't pass it to avoid framework errors
|
114
123
|
if roles_file is None or roles_file == "":
|
115
|
-
logger.warning(
|
124
|
+
logger.warning(
|
125
|
+
"roles_file is None or empty, permissions will use default configuration"
|
126
|
+
)
|
116
127
|
roles_file = None
|
117
|
-
|
128
|
+
|
118
129
|
permission_config = PermissionConfig(
|
119
130
|
enabled=True,
|
120
131
|
roles_file=roles_file,
|
@@ -125,7 +136,7 @@ class SecurityIntegration:
|
|
125
136
|
permission_cache_ttl=permissions_section.get("permission_cache_ttl", 300),
|
126
137
|
wildcard_permissions=permissions_section.get("wildcard_permissions", False),
|
127
138
|
strict_mode=permissions_section.get("strict_mode", True),
|
128
|
-
roles=permissions_section.get("roles")
|
139
|
+
roles=permissions_section.get("roles"),
|
129
140
|
)
|
130
141
|
else:
|
131
142
|
# Create minimal permission config when permissions are disabled
|
@@ -139,42 +150,56 @@ class SecurityIntegration:
|
|
139
150
|
permission_cache_ttl=300,
|
140
151
|
wildcard_permissions=False,
|
141
152
|
strict_mode=False,
|
142
|
-
roles={}
|
153
|
+
roles={},
|
143
154
|
)
|
144
|
-
|
155
|
+
|
145
156
|
# Create rate limit config
|
146
157
|
rate_limit_config = RateLimitConfig(
|
147
158
|
enabled=security_section.get("rate_limit", {}).get("enabled", True),
|
148
|
-
default_requests_per_minute=security_section.get("rate_limit", {}).get(
|
149
|
-
|
159
|
+
default_requests_per_minute=security_section.get("rate_limit", {}).get(
|
160
|
+
"default_requests_per_minute", 60
|
161
|
+
),
|
162
|
+
default_requests_per_hour=security_section.get("rate_limit", {}).get(
|
163
|
+
"default_requests_per_hour", 1000
|
164
|
+
),
|
150
165
|
burst_limit=security_section.get("rate_limit", {}).get("burst_limit", 2),
|
151
|
-
window_size_seconds=security_section.get("rate_limit", {}).get(
|
166
|
+
window_size_seconds=security_section.get("rate_limit", {}).get(
|
167
|
+
"window_size_seconds", 60
|
168
|
+
),
|
152
169
|
storage_backend=security_section.get("rate_limit", {}).get("storage_backend", "memory"),
|
153
170
|
exempt_paths=security_section.get("rate_limit", {}).get("exempt_paths", []),
|
154
|
-
exempt_roles=security_section.get("rate_limit", {}).get("exempt_roles", [])
|
171
|
+
exempt_roles=security_section.get("rate_limit", {}).get("exempt_roles", []),
|
155
172
|
)
|
156
|
-
|
173
|
+
|
157
174
|
# Create certificate config
|
158
175
|
certificate_config = CertificateConfig(
|
159
176
|
enabled=security_section.get("certificates", {}).get("enabled", False),
|
160
177
|
ca_cert_path=security_section.get("certificates", {}).get("ca_cert_path"),
|
161
178
|
ca_key_path=security_section.get("certificates", {}).get("ca_key_path"),
|
162
|
-
cert_storage_path=security_section.get("certificates", {}).get(
|
163
|
-
|
164
|
-
|
179
|
+
cert_storage_path=security_section.get("certificates", {}).get(
|
180
|
+
"cert_storage_path", "./certs"
|
181
|
+
),
|
182
|
+
key_storage_path=security_section.get("certificates", {}).get(
|
183
|
+
"key_storage_path", "./keys"
|
184
|
+
),
|
185
|
+
default_validity_days=security_section.get("certificates", {}).get(
|
186
|
+
"default_validity_days", 365
|
187
|
+
),
|
165
188
|
key_size=security_section.get("certificates", {}).get("key_size", 2048),
|
166
|
-
hash_algorithm=security_section.get("certificates", {}).get("hash_algorithm", "sha256")
|
189
|
+
hash_algorithm=security_section.get("certificates", {}).get("hash_algorithm", "sha256"),
|
167
190
|
)
|
168
|
-
|
191
|
+
|
169
192
|
# Create logging config
|
170
193
|
logging_config = LoggingConfig(
|
171
194
|
enabled=security_section.get("logging", {}).get("enabled", True),
|
172
195
|
level=security_section.get("logging", {}).get("level", "INFO"),
|
173
|
-
format=security_section.get("logging", {}).get(
|
196
|
+
format=security_section.get("logging", {}).get(
|
197
|
+
"format", "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
198
|
+
),
|
174
199
|
console_output=security_section.get("logging", {}).get("console_output", True),
|
175
|
-
file_path=security_section.get("logging", {}).get("file_path")
|
200
|
+
file_path=security_section.get("logging", {}).get("file_path"),
|
176
201
|
)
|
177
|
-
|
202
|
+
|
178
203
|
# Create main security config
|
179
204
|
return SecurityConfig(
|
180
205
|
ssl=ssl_config,
|
@@ -185,95 +210,134 @@ class SecurityIntegration:
|
|
185
210
|
logging=logging_config,
|
186
211
|
debug=security_section.get("debug", False),
|
187
212
|
environment=security_section.get("environment", "dev"),
|
188
|
-
version=security_section.get("version", "1.0.0")
|
213
|
+
version=security_section.get("version", "1.0.0"),
|
189
214
|
)
|
190
|
-
|
215
|
+
|
191
216
|
# Authentication methods - direct calls to AuthManager
|
192
217
|
async def authenticate_api_key(self, api_key: str) -> AuthResult:
|
193
218
|
"""Authenticate using API key."""
|
194
219
|
return await self.auth_manager.authenticate_api_key(api_key)
|
195
|
-
|
220
|
+
|
196
221
|
async def authenticate_jwt(self, token: str) -> AuthResult:
|
197
222
|
"""Authenticate using JWT token."""
|
198
223
|
return await self.auth_manager.authenticate_jwt(token)
|
199
|
-
|
224
|
+
|
200
225
|
async def authenticate_certificate(self, cert_data: bytes) -> AuthResult:
|
201
226
|
"""Authenticate using certificate."""
|
202
227
|
return await self.auth_manager.authenticate_certificate(cert_data)
|
203
|
-
|
228
|
+
|
204
229
|
async def validate_request(self, request_data: Dict[str, Any]) -> ValidationResult:
|
205
230
|
"""Validate request using security manager."""
|
206
231
|
return await self.security_manager.validate_request(request_data)
|
207
|
-
|
232
|
+
|
208
233
|
# Certificate methods - direct calls to CertificateManager
|
209
234
|
async def create_ca_certificate(self, common_name: str, **kwargs) -> CertificatePair:
|
210
235
|
"""Create CA certificate."""
|
211
236
|
return await self.certificate_manager.create_ca_certificate(common_name, **kwargs)
|
212
|
-
|
237
|
+
|
213
238
|
async def create_client_certificate(self, common_name: str, **kwargs) -> CertificatePair:
|
214
239
|
"""Create client certificate."""
|
215
240
|
return await self.certificate_manager.create_client_certificate(common_name, **kwargs)
|
216
|
-
|
241
|
+
|
217
242
|
async def create_server_certificate(self, common_name: str, **kwargs) -> CertificatePair:
|
218
243
|
"""Create server certificate."""
|
219
244
|
return await self.certificate_manager.create_server_certificate(common_name, **kwargs)
|
220
|
-
|
245
|
+
|
221
246
|
async def validate_certificate(self, cert_path: str) -> bool:
|
222
|
-
"""Validate certificate."""
|
223
|
-
|
224
|
-
|
247
|
+
"""Validate certificate with CRL check if enabled."""
|
248
|
+
try:
|
249
|
+
# Get CRL configuration from security config
|
250
|
+
crl_config = None
|
251
|
+
if hasattr(self.security_config, "certificates"):
|
252
|
+
cert_config = self.security_config.certificates
|
253
|
+
# Only analyze CRL paths if certificates are enabled
|
254
|
+
if (
|
255
|
+
hasattr(cert_config, "enabled")
|
256
|
+
and cert_config.enabled
|
257
|
+
and hasattr(cert_config, "crl_enabled")
|
258
|
+
and cert_config.crl_enabled
|
259
|
+
):
|
260
|
+
crl_config = {
|
261
|
+
"crl_enabled": cert_config.crl_enabled,
|
262
|
+
"crl_path": getattr(cert_config, "crl_path", None),
|
263
|
+
"crl_url": getattr(cert_config, "crl_url", None),
|
264
|
+
"crl_validity_days": getattr(cert_config, "crl_validity_days", 30),
|
265
|
+
}
|
266
|
+
|
267
|
+
# Use mcp_security_framework's validate_certificate_chain with CRL
|
268
|
+
if crl_config and crl_config.get("crl_enabled"):
|
269
|
+
from mcp_security_framework.utils.cert_utils import validate_certificate_chain
|
270
|
+
from .crl_utils import CRLManager
|
271
|
+
|
272
|
+
# Get CRL data
|
273
|
+
crl_manager = CRLManager(crl_config)
|
274
|
+
crl_data = crl_manager.get_crl_data()
|
275
|
+
|
276
|
+
# Validate with CRL
|
277
|
+
if crl_data:
|
278
|
+
return validate_certificate_chain(
|
279
|
+
cert_path, self.security_config.certificates.ca_cert_path, crl_data
|
280
|
+
)
|
281
|
+
|
282
|
+
# Fallback to standard validation
|
283
|
+
return await self.certificate_manager.validate_certificate(cert_path)
|
284
|
+
|
285
|
+
except Exception as e:
|
286
|
+
logger.error(f"Certificate validation failed: {e}")
|
287
|
+
return False
|
288
|
+
|
225
289
|
async def extract_roles_from_certificate(self, cert_path: str) -> List[str]:
|
226
290
|
"""Extract roles from certificate."""
|
227
291
|
return await self.certificate_manager.extract_roles_from_certificate(cert_path)
|
228
|
-
|
292
|
+
|
229
293
|
async def revoke_certificate(self, cert_path: str) -> bool:
|
230
294
|
"""Revoke certificate."""
|
231
295
|
return await self.certificate_manager.revoke_certificate(cert_path)
|
232
|
-
|
296
|
+
|
233
297
|
# Permission methods - direct calls to PermissionManager
|
234
298
|
async def check_permission(self, user_id: str, permission: str) -> bool:
|
235
299
|
"""Check user permission."""
|
236
300
|
return await self.permission_manager.check_permission(user_id, permission)
|
237
|
-
|
301
|
+
|
238
302
|
async def get_user_roles(self, user_id: str) -> List[str]:
|
239
303
|
"""Get user roles."""
|
240
304
|
return await self.permission_manager.get_user_roles(user_id)
|
241
|
-
|
305
|
+
|
242
306
|
async def add_user_role(self, user_id: str, role: str) -> bool:
|
243
307
|
"""Add role to user."""
|
244
308
|
return await self.permission_manager.add_user_role(user_id, role)
|
245
|
-
|
309
|
+
|
246
310
|
async def remove_user_role(self, user_id: str, role: str) -> bool:
|
247
311
|
"""Remove role from user."""
|
248
312
|
return await self.permission_manager.remove_user_role(user_id, role)
|
249
|
-
|
313
|
+
|
250
314
|
# Rate limiting methods - direct calls to RateLimiter
|
251
315
|
async def check_rate_limit(self, identifier: str, limit_type: str = "per_minute") -> bool:
|
252
316
|
"""Check rate limit."""
|
253
317
|
return await self.rate_limiter.check_rate_limit(identifier, limit_type)
|
254
|
-
|
318
|
+
|
255
319
|
async def increment_rate_limit(self, identifier: str) -> None:
|
256
320
|
"""Increment rate limit counter."""
|
257
321
|
await self.rate_limiter.increment_rate_limit(identifier)
|
258
|
-
|
322
|
+
|
259
323
|
async def get_rate_limit_info(self, identifier: str) -> Dict[str, Any]:
|
260
324
|
"""Get rate limit information."""
|
261
325
|
return await self.rate_limiter.get_rate_limit_info(identifier)
|
262
|
-
|
326
|
+
|
263
327
|
# Middleware creation - direct use of framework middleware
|
264
328
|
def create_fastapi_middleware(self, app) -> FastAPISecurityMiddleware:
|
265
329
|
"""Create FastAPI security middleware."""
|
266
330
|
return FastAPISecurityMiddleware(app, self.security_config)
|
267
|
-
|
331
|
+
|
268
332
|
# Utility methods
|
269
333
|
def is_security_enabled(self) -> bool:
|
270
334
|
"""Check if security is enabled."""
|
271
335
|
return self.security_config.auth.enabled or self.security_config.ssl.enabled
|
272
|
-
|
336
|
+
|
273
337
|
def get_public_paths(self) -> List[str]:
|
274
338
|
"""Get public paths that bypass authentication."""
|
275
339
|
return self.security_config.auth.public_paths
|
276
|
-
|
340
|
+
|
277
341
|
def get_security_config(self) -> SecurityConfig:
|
278
342
|
"""Get security configuration."""
|
279
343
|
return self.security_config
|
@@ -283,13 +347,13 @@ class SecurityIntegration:
|
|
283
347
|
def create_security_integration(config: Dict[str, Any]) -> SecurityIntegration:
|
284
348
|
"""
|
285
349
|
Create security integration instance.
|
286
|
-
|
350
|
+
|
287
351
|
Args:
|
288
352
|
config: Configuration dictionary
|
289
|
-
|
353
|
+
|
290
354
|
Returns:
|
291
355
|
SecurityIntegration instance
|
292
|
-
|
356
|
+
|
293
357
|
Raises:
|
294
358
|
RuntimeError: If security integration cannot be created
|
295
359
|
"""
|
@@ -3,8 +3,10 @@ SSL Utilities Module
|
|
3
3
|
|
4
4
|
This module provides utilities for SSL/TLS configuration and certificate validation.
|
5
5
|
Integrates with AuthValidator from Phase 0 for certificate validation.
|
6
|
+
Supports CRL (Certificate Revocation List) validation.
|
6
7
|
|
7
|
-
Author:
|
8
|
+
Author: Vasiliy Zdanovskiy
|
9
|
+
email: vasilyvz@gmail.com
|
8
10
|
Version: 1.0.0
|
9
11
|
"""
|
10
12
|
|
@@ -14,6 +16,7 @@ from typing import List, Optional, Dict, Any
|
|
14
16
|
from pathlib import Path
|
15
17
|
|
16
18
|
from .auth_validator import AuthValidator
|
19
|
+
from .crl_utils import CRLManager
|
17
20
|
|
18
21
|
logger = logging.getLogger(__name__)
|
19
22
|
|
@@ -47,7 +50,8 @@ class SSLUtils:
|
|
47
50
|
verify_client: bool = False,
|
48
51
|
cipher_suites: Optional[List[str]] = None,
|
49
52
|
min_tls_version: str = "1.2",
|
50
|
-
max_tls_version: str = "1.3"
|
53
|
+
max_tls_version: str = "1.3",
|
54
|
+
crl_config: Optional[Dict[str, Any]] = None) -> ssl.SSLContext:
|
51
55
|
"""
|
52
56
|
Create SSL context with specified configuration.
|
53
57
|
|
@@ -59,6 +63,7 @@ class SSLUtils:
|
|
59
63
|
cipher_suites: List of cipher suites to use
|
60
64
|
min_tls_version: Minimum TLS version
|
61
65
|
max_tls_version: Maximum TLS version
|
66
|
+
crl_config: CRL configuration dictionary (optional)
|
62
67
|
|
63
68
|
Returns:
|
64
69
|
Configured SSL context
|
@@ -73,6 +78,17 @@ class SSLUtils:
|
|
73
78
|
if not result.is_valid:
|
74
79
|
raise ValueError(f"Invalid certificate: {result.error_message}")
|
75
80
|
|
81
|
+
# Check CRL if configured
|
82
|
+
if crl_config:
|
83
|
+
try:
|
84
|
+
crl_manager = CRLManager(crl_config)
|
85
|
+
if crl_manager.is_certificate_revoked(cert_file):
|
86
|
+
raise ValueError(f"Certificate is revoked according to CRL: {cert_file}")
|
87
|
+
except Exception as e:
|
88
|
+
logger.error(f"CRL check failed: {e}")
|
89
|
+
# For security, fail if CRL check fails
|
90
|
+
raise ValueError(f"CRL validation failed: {e}")
|
91
|
+
|
76
92
|
# Check if files exist
|
77
93
|
if not Path(cert_file).exists():
|
78
94
|
raise FileNotFoundError(f"Certificate file not found: {cert_file}")
|
@@ -111,12 +127,13 @@ class SSLUtils:
|
|
111
127
|
return context
|
112
128
|
|
113
129
|
@staticmethod
|
114
|
-
def validate_certificate(cert_file: str) -> bool:
|
130
|
+
def validate_certificate(cert_file: str, crl_config: Optional[Dict[str, Any]] = None) -> bool:
|
115
131
|
"""
|
116
|
-
Validate certificate using AuthValidator.
|
132
|
+
Validate certificate using AuthValidator and optional CRL check.
|
117
133
|
|
118
134
|
Args:
|
119
135
|
cert_file: Path to certificate file
|
136
|
+
crl_config: CRL configuration dictionary (optional)
|
120
137
|
|
121
138
|
Returns:
|
122
139
|
True if certificate is valid, False otherwise
|
@@ -124,7 +141,22 @@ class SSLUtils:
|
|
124
141
|
try:
|
125
142
|
validator = AuthValidator()
|
126
143
|
result = validator.validate_certificate(cert_file)
|
127
|
-
|
144
|
+
if not result.is_valid:
|
145
|
+
return False
|
146
|
+
|
147
|
+
# Check CRL if configured
|
148
|
+
if crl_config:
|
149
|
+
try:
|
150
|
+
crl_manager = CRLManager(crl_config)
|
151
|
+
if crl_manager.is_certificate_revoked(cert_file):
|
152
|
+
logger.warning(f"Certificate is revoked according to CRL: {cert_file}")
|
153
|
+
return False
|
154
|
+
except Exception as e:
|
155
|
+
logger.error(f"CRL check failed: {e}")
|
156
|
+
# For security, consider certificate invalid if CRL check fails
|
157
|
+
return False
|
158
|
+
|
159
|
+
return True
|
128
160
|
except Exception as e:
|
129
161
|
logger.error(f"Certificate validation failed: {e}")
|
130
162
|
return False
|
mcp_proxy_adapter/main.py
CHANGED
@@ -19,6 +19,7 @@ sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
19
19
|
|
20
20
|
from mcp_proxy_adapter.api.app import create_app
|
21
21
|
from mcp_proxy_adapter.config import Config
|
22
|
+
from mcp_proxy_adapter.core.config_validator import ConfigValidator
|
22
23
|
|
23
24
|
|
24
25
|
def main():
|
@@ -27,28 +28,36 @@ def main():
|
|
27
28
|
parser = argparse.ArgumentParser(description="MCP Proxy Adapter Server")
|
28
29
|
parser.add_argument("--config", "-c", type=str, help="Path to configuration file")
|
29
30
|
args = parser.parse_args()
|
30
|
-
|
31
|
+
|
31
32
|
# Load configuration
|
32
33
|
if args.config:
|
33
34
|
config = Config(config_path=args.config)
|
34
35
|
else:
|
35
36
|
config = Config()
|
36
37
|
|
38
|
+
# Validate UUID configuration (mandatory)
|
39
|
+
validator = ConfigValidator(config.get_all())
|
40
|
+
if not validator.validate_all():
|
41
|
+
print("❌ Configuration validation failed:")
|
42
|
+
for error in validator.get_errors():
|
43
|
+
print(f" - {error}")
|
44
|
+
sys.exit(1)
|
45
|
+
print("✅ Configuration validation passed")
|
37
46
|
|
38
47
|
# Create application
|
39
48
|
app = create_app(app_config=config)
|
40
|
-
|
49
|
+
|
41
50
|
# Get server configuration
|
42
51
|
host = config.get("server.host", "0.0.0.0")
|
43
52
|
port = config.get("server.port", 8000)
|
44
|
-
|
53
|
+
|
45
54
|
# Get SSL configuration
|
46
55
|
ssl_enabled = config.get("ssl.enabled", False)
|
47
56
|
ssl_cert_file = config.get("ssl.cert_file")
|
48
57
|
ssl_key_file = config.get("ssl.key_file")
|
49
58
|
ssl_ca_cert = config.get("ssl.ca_cert")
|
50
59
|
verify_client = config.get("ssl.verify_client", False)
|
51
|
-
|
60
|
+
|
52
61
|
print(f"🚀 Starting MCP Proxy Adapter")
|
53
62
|
print(f"🌐 Server: {host}:{port}")
|
54
63
|
if ssl_enabled:
|
@@ -59,18 +68,18 @@ def main():
|
|
59
68
|
print(f" CA: {ssl_ca_cert}")
|
60
69
|
print(f" Client verification: {verify_client}")
|
61
70
|
print("=" * 50)
|
62
|
-
|
71
|
+
|
63
72
|
# Configure hypercorn
|
64
73
|
config_hypercorn = hypercorn.config.Config()
|
65
74
|
config_hypercorn.bind = [f"{host}:{port}"]
|
66
|
-
|
75
|
+
|
67
76
|
if ssl_enabled and ssl_cert_file and ssl_key_file:
|
68
77
|
config_hypercorn.certfile = ssl_cert_file
|
69
78
|
config_hypercorn.keyfile = ssl_key_file
|
70
|
-
|
79
|
+
|
71
80
|
if ssl_ca_cert:
|
72
81
|
config_hypercorn.ca_certs = ssl_ca_cert
|
73
|
-
|
82
|
+
|
74
83
|
if verify_client:
|
75
84
|
# For mTLS, require client certificates
|
76
85
|
config_hypercorn.set_cert_reqs(ssl.CERT_REQUIRED)
|
@@ -80,11 +89,11 @@ def main():
|
|
80
89
|
# For regular HTTPS without client verification
|
81
90
|
config_hypercorn.set_cert_reqs(ssl.CERT_NONE)
|
82
91
|
config_hypercorn.verify_mode = ssl.CERT_NONE
|
83
|
-
|
92
|
+
|
84
93
|
print(f"🔐 Starting HTTPS server with hypercorn...")
|
85
94
|
else:
|
86
95
|
print(f"🌐 Starting HTTP server with hypercorn...")
|
87
|
-
|
96
|
+
|
88
97
|
# Run the server
|
89
98
|
asyncio.run(hypercorn.asyncio.serve(app, config_hypercorn))
|
90
99
|
|
mcp_proxy_adapter/version.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: mcp-proxy-adapter
|
3
|
-
Version: 6.
|
3
|
+
Version: 6.3.0
|
4
4
|
Summary: Powerful JSON-RPC microservices framework with built-in security, authentication, and proxy registration
|
5
5
|
Home-page: https://github.com/maverikod/mcp-proxy-adapter
|
6
6
|
Author: Vasiliy Zdanovskiy
|