mcp-proxy-adapter 6.7.2__py3-none-any.whl → 6.8.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/api/app.py +14 -0
- mcp_proxy_adapter/core/app_factory.py +41 -55
- mcp_proxy_adapter/core/mtls_server.py +5 -1
- mcp_proxy_adapter/core/signal_handler.py +6 -4
- mcp_proxy_adapter/core/utils.py +142 -0
- mcp_proxy_adapter/examples/basic_framework/main.py +10 -11
- mcp_proxy_adapter/examples/full_application/main.py +12 -62
- mcp_proxy_adapter/main.py +22 -0
- mcp_proxy_adapter/version.py +1 -1
- {mcp_proxy_adapter-6.7.2.dist-info → mcp_proxy_adapter-6.8.0.dist-info}/METADATA +1 -1
- {mcp_proxy_adapter-6.7.2.dist-info → mcp_proxy_adapter-6.8.0.dist-info}/RECORD +14 -16
- mcp_proxy_adapter/core/async_proxy_registration.py +0 -285
- mcp_proxy_adapter/examples/generate_certificates.py +0 -385
- {mcp_proxy_adapter-6.7.2.dist-info → mcp_proxy_adapter-6.8.0.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.7.2.dist-info → mcp_proxy_adapter-6.8.0.dist-info}/entry_points.txt +0 -0
- {mcp_proxy_adapter-6.7.2.dist-info → mcp_proxy_adapter-6.8.0.dist-info}/top_level.txt +0 -0
@@ -1,285 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Asynchronous proxy registration with heartbeat functionality.
|
3
|
-
|
4
|
-
This module provides automatic proxy registration in a separate thread
|
5
|
-
with continuous heartbeat monitoring and automatic re-registration on failures.
|
6
|
-
|
7
|
-
Author: Vasiliy Zdanovskiy
|
8
|
-
email: vasilyvz@gmail.com
|
9
|
-
"""
|
10
|
-
|
11
|
-
import asyncio
|
12
|
-
import time
|
13
|
-
import threading
|
14
|
-
from typing import Dict, Any, Optional
|
15
|
-
from dataclasses import dataclass
|
16
|
-
from enum import Enum
|
17
|
-
|
18
|
-
from mcp_proxy_adapter.core.logging import logger
|
19
|
-
from mcp_proxy_adapter.core.proxy_registration import ProxyRegistrationManager
|
20
|
-
|
21
|
-
|
22
|
-
class RegistrationState(Enum):
|
23
|
-
"""Registration state enumeration."""
|
24
|
-
DISABLED = "disabled"
|
25
|
-
NOT_REGISTERED = "not_registered"
|
26
|
-
REGISTERING = "registering"
|
27
|
-
REGISTERED = "registered"
|
28
|
-
HEARTBEAT_FAILED = "heartbeat_failed"
|
29
|
-
ERROR = "error"
|
30
|
-
|
31
|
-
|
32
|
-
@dataclass
|
33
|
-
class RegistrationStatus:
|
34
|
-
"""Registration status information."""
|
35
|
-
state: RegistrationState
|
36
|
-
server_key: Optional[str] = None
|
37
|
-
last_attempt: Optional[float] = None
|
38
|
-
last_success: Optional[float] = None
|
39
|
-
last_error: Optional[str] = None
|
40
|
-
attempt_count: int = 0
|
41
|
-
success_count: int = 0
|
42
|
-
|
43
|
-
|
44
|
-
class AsyncProxyRegistrationManager:
|
45
|
-
"""
|
46
|
-
Asynchronous proxy registration manager with heartbeat functionality.
|
47
|
-
|
48
|
-
Runs registration and heartbeat in a separate thread, automatically
|
49
|
-
handling re-registration on failures and continuous monitoring.
|
50
|
-
"""
|
51
|
-
|
52
|
-
def __init__(self, config: Dict[str, Any]):
|
53
|
-
"""
|
54
|
-
Initialize the async proxy registration manager.
|
55
|
-
|
56
|
-
Args:
|
57
|
-
config: Application configuration
|
58
|
-
"""
|
59
|
-
self.config = config
|
60
|
-
self.registration_manager = ProxyRegistrationManager(config)
|
61
|
-
self.status = RegistrationStatus(state=RegistrationState.NOT_REGISTERED)
|
62
|
-
self._thread: Optional[threading.Thread] = None
|
63
|
-
self._stop_event = threading.Event()
|
64
|
-
self._loop: Optional[asyncio.AbstractEventLoop] = None
|
65
|
-
|
66
|
-
# Get heartbeat configuration
|
67
|
-
heartbeat_config = config.get("registration", {}).get("heartbeat", {})
|
68
|
-
self.heartbeat_enabled = heartbeat_config.get("enabled", True)
|
69
|
-
self.heartbeat_interval = heartbeat_config.get("interval", 30)
|
70
|
-
self.heartbeat_timeout = heartbeat_config.get("timeout", 10)
|
71
|
-
self.retry_attempts = heartbeat_config.get("retry_attempts", 3)
|
72
|
-
self.retry_delay = heartbeat_config.get("retry_delay", 5)
|
73
|
-
|
74
|
-
logger.info(f"AsyncProxyRegistrationManager initialized: heartbeat_enabled={self.heartbeat_enabled}, interval={self.heartbeat_interval}s")
|
75
|
-
|
76
|
-
def is_enabled(self) -> bool:
|
77
|
-
"""Check if proxy registration is enabled."""
|
78
|
-
return self.registration_manager.is_enabled()
|
79
|
-
|
80
|
-
def set_server_url(self, server_url: str):
|
81
|
-
"""Set the server URL for registration."""
|
82
|
-
self.registration_manager.set_server_url(server_url)
|
83
|
-
logger.info(f"Server URL set for async registration: {server_url}")
|
84
|
-
|
85
|
-
def start(self):
|
86
|
-
"""Start the async registration and heartbeat thread."""
|
87
|
-
if not self.is_enabled():
|
88
|
-
logger.info("Proxy registration is disabled, not starting async manager")
|
89
|
-
self.status.state = RegistrationState.DISABLED
|
90
|
-
return
|
91
|
-
|
92
|
-
if self._thread and self._thread.is_alive():
|
93
|
-
logger.warning("Async registration thread is already running")
|
94
|
-
return
|
95
|
-
|
96
|
-
logger.info("Starting async proxy registration and heartbeat thread")
|
97
|
-
self._stop_event.clear()
|
98
|
-
self._thread = threading.Thread(target=self._run_async_loop, daemon=True)
|
99
|
-
self._thread.start()
|
100
|
-
|
101
|
-
def stop(self):
|
102
|
-
"""Stop the async registration and heartbeat thread."""
|
103
|
-
if not self._thread or not self._thread.is_alive():
|
104
|
-
return
|
105
|
-
|
106
|
-
logger.info("Stopping async proxy registration and heartbeat thread")
|
107
|
-
self._stop_event.set()
|
108
|
-
|
109
|
-
# Unregister if registered
|
110
|
-
if self.status.state == RegistrationState.REGISTERED:
|
111
|
-
try:
|
112
|
-
# Run unregistration in the thread's event loop
|
113
|
-
if self._loop and not self._loop.is_closed():
|
114
|
-
future = asyncio.run_coroutine_threadsafe(
|
115
|
-
self.registration_manager.unregister_server(),
|
116
|
-
self._loop
|
117
|
-
)
|
118
|
-
future.result(timeout=10)
|
119
|
-
logger.info("Successfully unregistered from proxy during shutdown")
|
120
|
-
except Exception as e:
|
121
|
-
logger.error(f"Failed to unregister during shutdown: {e}")
|
122
|
-
|
123
|
-
self._thread.join(timeout=5)
|
124
|
-
if self._thread.is_alive():
|
125
|
-
logger.warning("Async registration thread did not stop gracefully")
|
126
|
-
|
127
|
-
def _run_async_loop(self):
|
128
|
-
"""Run the async event loop in the thread."""
|
129
|
-
try:
|
130
|
-
self._loop = asyncio.new_event_loop()
|
131
|
-
asyncio.set_event_loop(self._loop)
|
132
|
-
self._loop.run_until_complete(self._async_main())
|
133
|
-
except Exception as e:
|
134
|
-
logger.error(f"Async registration loop error: {e}")
|
135
|
-
finally:
|
136
|
-
if self._loop and not self._loop.is_closed():
|
137
|
-
self._loop.close()
|
138
|
-
|
139
|
-
async def _async_main(self):
|
140
|
-
"""Main async loop for registration and heartbeat."""
|
141
|
-
logger.info("Async registration loop started")
|
142
|
-
|
143
|
-
# Initial registration attempt
|
144
|
-
await self._attempt_registration()
|
145
|
-
|
146
|
-
# Main loop
|
147
|
-
while not self._stop_event.is_set():
|
148
|
-
try:
|
149
|
-
if self.status.state == RegistrationState.REGISTERED:
|
150
|
-
if self.heartbeat_enabled:
|
151
|
-
# Perform heartbeat
|
152
|
-
await self._perform_heartbeat()
|
153
|
-
else:
|
154
|
-
# Just wait if heartbeat is disabled
|
155
|
-
await asyncio.sleep(self.heartbeat_interval)
|
156
|
-
else:
|
157
|
-
# Try to register
|
158
|
-
await self._attempt_registration()
|
159
|
-
# Wait before retry
|
160
|
-
await asyncio.sleep(self.retry_delay)
|
161
|
-
|
162
|
-
except Exception as e:
|
163
|
-
logger.error(f"Error in async registration loop: {e}")
|
164
|
-
self.status.state = RegistrationState.ERROR
|
165
|
-
self.status.last_error = str(e)
|
166
|
-
await asyncio.sleep(self.retry_delay)
|
167
|
-
|
168
|
-
logger.info("Async registration loop stopped")
|
169
|
-
|
170
|
-
async def _attempt_registration(self):
|
171
|
-
"""Attempt to register with the proxy."""
|
172
|
-
if self.status.state == RegistrationState.REGISTERING:
|
173
|
-
return # Already registering
|
174
|
-
|
175
|
-
self.status.state = RegistrationState.REGISTERING
|
176
|
-
self.status.attempt_count += 1
|
177
|
-
self.status.last_attempt = time.time()
|
178
|
-
|
179
|
-
logger.info(f"Attempting proxy registration (attempt #{self.status.attempt_count})")
|
180
|
-
|
181
|
-
try:
|
182
|
-
success = await self.registration_manager.register_server()
|
183
|
-
|
184
|
-
if success:
|
185
|
-
self.status.state = RegistrationState.REGISTERED
|
186
|
-
self.status.last_success = time.time()
|
187
|
-
self.status.success_count += 1
|
188
|
-
self.status.last_error = None
|
189
|
-
logger.info(f"✅ Proxy registration successful (attempt #{self.status.attempt_count})")
|
190
|
-
else:
|
191
|
-
self.status.state = RegistrationState.NOT_REGISTERED
|
192
|
-
self.status.last_error = "Registration returned False"
|
193
|
-
logger.warning(f"❌ Proxy registration failed (attempt #{self.status.attempt_count}): Registration returned False")
|
194
|
-
|
195
|
-
except Exception as e:
|
196
|
-
self.status.state = RegistrationState.NOT_REGISTERED
|
197
|
-
self.status.last_error = str(e)
|
198
|
-
logger.error(f"❌ Proxy registration failed (attempt #{self.status.attempt_count}): {e}")
|
199
|
-
|
200
|
-
async def _perform_heartbeat(self):
|
201
|
-
"""Perform heartbeat to the proxy."""
|
202
|
-
try:
|
203
|
-
# Use the registration manager's heartbeat functionality
|
204
|
-
success = await self.registration_manager.heartbeat()
|
205
|
-
|
206
|
-
if success:
|
207
|
-
logger.debug("💓 Heartbeat successful")
|
208
|
-
self.status.last_success = time.time()
|
209
|
-
else:
|
210
|
-
logger.warning("💓 Heartbeat failed")
|
211
|
-
self.status.state = RegistrationState.HEARTBEAT_FAILED
|
212
|
-
self.status.last_error = "Heartbeat failed"
|
213
|
-
|
214
|
-
# Try to re-register after heartbeat failure
|
215
|
-
logger.info("Attempting re-registration after heartbeat failure")
|
216
|
-
await self._attempt_registration()
|
217
|
-
|
218
|
-
except Exception as e:
|
219
|
-
logger.error(f"💓 Heartbeat error: {e}")
|
220
|
-
self.status.state = RegistrationState.HEARTBEAT_FAILED
|
221
|
-
self.status.last_error = str(e)
|
222
|
-
|
223
|
-
# Try to re-register after heartbeat error
|
224
|
-
logger.info("Attempting re-registration after heartbeat error")
|
225
|
-
await self._attempt_registration()
|
226
|
-
|
227
|
-
# Wait for next heartbeat
|
228
|
-
await asyncio.sleep(self.heartbeat_interval)
|
229
|
-
|
230
|
-
def get_status(self) -> Dict[str, Any]:
|
231
|
-
"""Get current registration status."""
|
232
|
-
return {
|
233
|
-
"state": self.status.state.value,
|
234
|
-
"server_key": self.status.server_key,
|
235
|
-
"last_attempt": self.status.last_attempt,
|
236
|
-
"last_success": self.status.last_success,
|
237
|
-
"last_error": self.status.last_error,
|
238
|
-
"attempt_count": self.status.attempt_count,
|
239
|
-
"success_count": self.status.success_count,
|
240
|
-
"heartbeat_enabled": self.heartbeat_enabled,
|
241
|
-
"heartbeat_interval": self.heartbeat_interval,
|
242
|
-
"thread_alive": self._thread.is_alive() if self._thread else False
|
243
|
-
}
|
244
|
-
|
245
|
-
|
246
|
-
# Global instance
|
247
|
-
_async_registration_manager: Optional[AsyncProxyRegistrationManager] = None
|
248
|
-
|
249
|
-
|
250
|
-
def get_async_registration_manager() -> Optional[AsyncProxyRegistrationManager]:
|
251
|
-
"""Get the global async registration manager instance."""
|
252
|
-
return _async_registration_manager
|
253
|
-
|
254
|
-
|
255
|
-
def initialize_async_registration(config: Dict[str, Any]) -> AsyncProxyRegistrationManager:
|
256
|
-
"""Initialize the global async registration manager."""
|
257
|
-
global _async_registration_manager
|
258
|
-
_async_registration_manager = AsyncProxyRegistrationManager(config)
|
259
|
-
return _async_registration_manager
|
260
|
-
|
261
|
-
|
262
|
-
def start_async_registration(server_url: str):
|
263
|
-
"""Start async registration with the given server URL."""
|
264
|
-
global _async_registration_manager
|
265
|
-
if _async_registration_manager:
|
266
|
-
_async_registration_manager.set_server_url(server_url)
|
267
|
-
_async_registration_manager.start()
|
268
|
-
else:
|
269
|
-
logger.error("Async registration manager not initialized")
|
270
|
-
|
271
|
-
|
272
|
-
def stop_async_registration():
|
273
|
-
"""Stop async registration."""
|
274
|
-
global _async_registration_manager
|
275
|
-
if _async_registration_manager:
|
276
|
-
_async_registration_manager.stop()
|
277
|
-
|
278
|
-
|
279
|
-
def get_registration_status() -> Dict[str, Any]:
|
280
|
-
"""Get current registration status."""
|
281
|
-
global _async_registration_manager
|
282
|
-
if _async_registration_manager:
|
283
|
-
return _async_registration_manager.get_status()
|
284
|
-
else:
|
285
|
-
return {"state": "not_initialized"}
|
@@ -1,385 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
"""
|
3
|
-
Generate Certificates Using mcp_security_framework with Bugfix
|
4
|
-
This script generates all necessary SSL certificates using the mcp_security_framework
|
5
|
-
with the CertificateConfig bugfix applied.
|
6
|
-
|
7
|
-
Author: Vasiliy Zdanovskiy
|
8
|
-
email: vasilyvz@gmail.com
|
9
|
-
"""
|
10
|
-
import json
|
11
|
-
import os
|
12
|
-
import sys
|
13
|
-
from pathlib import Path
|
14
|
-
from typing import Dict, List, Optional
|
15
|
-
|
16
|
-
# Import mcp_security_framework components
|
17
|
-
from mcp_security_framework.core.cert_manager import CertificateManager
|
18
|
-
from mcp_security_framework.schemas.config import CAConfig, ServerCertConfig, ClientCertConfig, CertificateConfig
|
19
|
-
from mcp_security_framework.utils.cert_utils import validate_certificate_format
|
20
|
-
|
21
|
-
# Import required certificates configuration
|
22
|
-
from required_certificates import REQUIRED_CERTIFICATES, get_all_required_certificates
|
23
|
-
|
24
|
-
|
25
|
-
class BugfixCertificateGenerator:
|
26
|
-
"""Certificate generator using mcp_security_framework with bugfix."""
|
27
|
-
|
28
|
-
def __init__(self):
|
29
|
-
"""Initialize the certificate generator."""
|
30
|
-
self.working_dir = Path.cwd()
|
31
|
-
self.certs_dir = self.working_dir / "certs"
|
32
|
-
self.keys_dir = self.working_dir / "keys"
|
33
|
-
|
34
|
-
# Ensure directories exist
|
35
|
-
self.certs_dir.mkdir(exist_ok=True)
|
36
|
-
self.keys_dir.mkdir(exist_ok=True)
|
37
|
-
|
38
|
-
# Certificate manager will be initialized after CA is created
|
39
|
-
self.cert_manager = None
|
40
|
-
|
41
|
-
def print_step(self, step: str, description: str):
|
42
|
-
"""Print a formatted step header."""
|
43
|
-
print(f"\n{'=' * 60}")
|
44
|
-
print(f"🔧 STEP {step}: {description}")
|
45
|
-
print(f"{'=' * 60}")
|
46
|
-
|
47
|
-
def print_success(self, message: str):
|
48
|
-
"""Print a success message."""
|
49
|
-
print(f"✅ {message}")
|
50
|
-
|
51
|
-
def print_error(self, message: str):
|
52
|
-
"""Print an error message."""
|
53
|
-
print(f"❌ {message}")
|
54
|
-
|
55
|
-
def print_info(self, message: str):
|
56
|
-
"""Print an info message."""
|
57
|
-
print(f"ℹ️ {message}")
|
58
|
-
|
59
|
-
def check_framework(self) -> bool:
|
60
|
-
"""Check if mcp_security_framework is available."""
|
61
|
-
try:
|
62
|
-
from mcp_security_framework import __version__
|
63
|
-
self.print_success(f"mcp_security_framework v{__version__} is available")
|
64
|
-
return True
|
65
|
-
except ImportError as e:
|
66
|
-
self.print_error(f"mcp_security_framework is not available: {e}")
|
67
|
-
return False
|
68
|
-
|
69
|
-
def _initialize_cert_manager(self, ca_creation_mode: bool = False):
|
70
|
-
"""Initialize certificate manager with configuration."""
|
71
|
-
cert_config = CertificateConfig(
|
72
|
-
enabled=True,
|
73
|
-
ca_creation_mode=ca_creation_mode,
|
74
|
-
ca_cert_path=str(self.certs_dir / "ca_cert.pem") if not ca_creation_mode else None,
|
75
|
-
ca_key_path=str(self.keys_dir / "ca_key.pem") if not ca_creation_mode else None,
|
76
|
-
cert_storage_path=str(self.certs_dir),
|
77
|
-
key_storage_path=str(self.keys_dir),
|
78
|
-
default_validity_days=365,
|
79
|
-
key_size=2048,
|
80
|
-
hash_algorithm="sha256"
|
81
|
-
)
|
82
|
-
self.cert_manager = CertificateManager(cert_config)
|
83
|
-
|
84
|
-
def generate_ca_certificate(self) -> bool:
|
85
|
-
"""Generate CA certificate using mcp_security_framework with bugfix."""
|
86
|
-
self.print_step("1", "Generating CA Certificate with Bugfix")
|
87
|
-
|
88
|
-
ca_info = REQUIRED_CERTIFICATES["ca_cert"]
|
89
|
-
|
90
|
-
try:
|
91
|
-
# Check if CA certificate already exists
|
92
|
-
if ca_info["output_cert"].exists() and ca_info["output_key"].exists():
|
93
|
-
self.print_info(f"CA certificate already exists: {ca_info['output_cert']}")
|
94
|
-
return True
|
95
|
-
|
96
|
-
# Create CA configuration
|
97
|
-
ca_config = CAConfig(
|
98
|
-
common_name=ca_info["common_name"],
|
99
|
-
organization=ca_info["organization"],
|
100
|
-
country=ca_info["country"],
|
101
|
-
state=ca_info["state"],
|
102
|
-
locality=ca_info["city"],
|
103
|
-
validity_years=ca_info["validity_days"] // 365,
|
104
|
-
key_size=2048,
|
105
|
-
hash_algorithm="sha256"
|
106
|
-
)
|
107
|
-
|
108
|
-
self.print_info(f"Generating CA certificate: {ca_info['common_name']}")
|
109
|
-
|
110
|
-
# Initialize certificate manager in CA creation mode
|
111
|
-
self._initialize_cert_manager(ca_creation_mode=True)
|
112
|
-
|
113
|
-
# Generate CA certificate using framework
|
114
|
-
result = self.cert_manager.create_root_ca(ca_config=ca_config)
|
115
|
-
|
116
|
-
if result:
|
117
|
-
# Save the generated certificate and key to expected locations
|
118
|
-
with open(ca_info["output_cert"], 'w') as f:
|
119
|
-
f.write(result.certificate_pem)
|
120
|
-
with open(ca_info["output_key"], 'w') as f:
|
121
|
-
f.write(result.private_key_pem)
|
122
|
-
|
123
|
-
self.print_success(f"CA certificate generated: {ca_info['output_cert']}")
|
124
|
-
# Reinitialize certificate manager in normal mode
|
125
|
-
self._initialize_cert_manager(ca_creation_mode=False)
|
126
|
-
return True
|
127
|
-
else:
|
128
|
-
self.print_error("Failed to generate CA certificate")
|
129
|
-
return False
|
130
|
-
|
131
|
-
except Exception as e:
|
132
|
-
self.print_error(f"Exception during CA certificate generation: {e}")
|
133
|
-
return False
|
134
|
-
|
135
|
-
def generate_server_certificate(self) -> bool:
|
136
|
-
"""Generate server certificate using mcp_security_framework."""
|
137
|
-
self.print_step("2", "Generating Server Certificate")
|
138
|
-
|
139
|
-
server_info = REQUIRED_CERTIFICATES["server_cert"]
|
140
|
-
|
141
|
-
try:
|
142
|
-
# Check if server certificate already exists
|
143
|
-
if server_info["output_cert"].exists() and server_info["output_key"].exists():
|
144
|
-
self.print_info(f"Server certificate already exists: {server_info['output_cert']}")
|
145
|
-
return True
|
146
|
-
|
147
|
-
# Ensure certificate manager is initialized in normal mode
|
148
|
-
if self.cert_manager is None:
|
149
|
-
self._initialize_cert_manager(ca_creation_mode=False)
|
150
|
-
|
151
|
-
# Create server certificate configuration
|
152
|
-
server_config = ServerCertConfig(
|
153
|
-
common_name=server_info["common_name"],
|
154
|
-
organization=server_info["organization"],
|
155
|
-
country=server_info["country"],
|
156
|
-
state=server_info["state"],
|
157
|
-
locality=server_info["city"],
|
158
|
-
validity_days=server_info["validity_days"],
|
159
|
-
key_size=2048,
|
160
|
-
hash_algorithm="sha256",
|
161
|
-
ca_cert_path=str(server_info["ca_cert_path"]),
|
162
|
-
ca_key_path=str(server_info["ca_key_path"]),
|
163
|
-
san=server_info.get("san", [])
|
164
|
-
)
|
165
|
-
|
166
|
-
self.print_info(f"Generating server certificate: {server_info['common_name']}")
|
167
|
-
|
168
|
-
# Generate server certificate using framework
|
169
|
-
result = self.cert_manager.create_server_certificate(server_config=server_config)
|
170
|
-
|
171
|
-
if result:
|
172
|
-
# Save the generated certificate and key to expected locations
|
173
|
-
with open(server_info["output_cert"], 'w') as f:
|
174
|
-
f.write(result.certificate_pem)
|
175
|
-
with open(server_info["output_key"], 'w') as f:
|
176
|
-
f.write(result.private_key_pem)
|
177
|
-
|
178
|
-
self.print_success(f"Server certificate generated: {server_info['output_cert']}")
|
179
|
-
return True
|
180
|
-
else:
|
181
|
-
self.print_error("Failed to generate server certificate")
|
182
|
-
return False
|
183
|
-
|
184
|
-
except Exception as e:
|
185
|
-
self.print_error(f"Exception during server certificate generation: {e}")
|
186
|
-
return False
|
187
|
-
|
188
|
-
def generate_client_certificate(self, cert_name: str) -> bool:
|
189
|
-
"""Generate client certificate using mcp_security_framework."""
|
190
|
-
self.print_step(f"3.{cert_name}", f"Generating {cert_name.title()} Client Certificate")
|
191
|
-
|
192
|
-
client_info = REQUIRED_CERTIFICATES[cert_name]
|
193
|
-
|
194
|
-
try:
|
195
|
-
# Check if client certificate already exists
|
196
|
-
if client_info["output_cert"].exists() and client_info["output_key"].exists():
|
197
|
-
self.print_info(f"{cert_name} certificate already exists: {client_info['output_cert']}")
|
198
|
-
return True
|
199
|
-
|
200
|
-
# Ensure certificate manager is initialized in normal mode
|
201
|
-
if self.cert_manager is None:
|
202
|
-
self._initialize_cert_manager(ca_creation_mode=False)
|
203
|
-
|
204
|
-
# Create client certificate configuration
|
205
|
-
client_config = ClientCertConfig(
|
206
|
-
common_name=client_info["common_name"],
|
207
|
-
organization=client_info["organization"],
|
208
|
-
country=client_info["country"],
|
209
|
-
state=client_info["state"],
|
210
|
-
locality=client_info["city"],
|
211
|
-
validity_days=client_info["validity_days"],
|
212
|
-
key_size=2048,
|
213
|
-
hash_algorithm="sha256",
|
214
|
-
ca_cert_path=str(client_info["ca_cert_path"]),
|
215
|
-
ca_key_path=str(client_info["ca_key_path"]),
|
216
|
-
roles=client_info.get("roles", []),
|
217
|
-
permissions=client_info.get("permissions", [])
|
218
|
-
)
|
219
|
-
|
220
|
-
self.print_info(f"Generating {cert_name} certificate: {client_info['common_name']}")
|
221
|
-
|
222
|
-
# Generate client certificate using framework
|
223
|
-
result = self.cert_manager.create_client_certificate(client_config=client_config)
|
224
|
-
|
225
|
-
if result:
|
226
|
-
# Save the generated certificate and key to expected locations
|
227
|
-
with open(client_info["output_cert"], 'w') as f:
|
228
|
-
f.write(result.certificate_pem)
|
229
|
-
with open(client_info["output_key"], 'w') as f:
|
230
|
-
f.write(result.private_key_pem)
|
231
|
-
|
232
|
-
# Also create a copy in certs/ directory for easier access
|
233
|
-
cert_name_base = client_info["common_name"].replace("-", "_")
|
234
|
-
certs_cert = self.certs_dir / f"{cert_name_base}_client.crt"
|
235
|
-
certs_key = self.certs_dir / f"{cert_name_base}_client.key"
|
236
|
-
|
237
|
-
with open(certs_cert, 'w') as f:
|
238
|
-
f.write(result.certificate_pem)
|
239
|
-
with open(certs_key, 'w') as f:
|
240
|
-
f.write(result.private_key_pem)
|
241
|
-
|
242
|
-
self.print_success(f"{cert_name} certificate generated: {client_info['output_cert']}")
|
243
|
-
self.print_success(f"Also created: {certs_cert} and {certs_key}")
|
244
|
-
return True
|
245
|
-
else:
|
246
|
-
self.print_error(f"Failed to generate {cert_name} certificate")
|
247
|
-
return False
|
248
|
-
|
249
|
-
except Exception as e:
|
250
|
-
self.print_error(f"Exception during {cert_name} certificate generation: {e}")
|
251
|
-
return False
|
252
|
-
|
253
|
-
def create_certificate_aliases(self) -> bool:
|
254
|
-
"""Create certificate aliases for different configurations."""
|
255
|
-
self.print_step("4", "Creating Certificate Aliases")
|
256
|
-
|
257
|
-
try:
|
258
|
-
# Create aliases for HTTPS configurations
|
259
|
-
if (self.certs_dir / "server_cert.pem").exists():
|
260
|
-
# HTTPS aliases
|
261
|
-
(self.certs_dir / "mcp_proxy_adapter_server.crt").unlink(missing_ok=True)
|
262
|
-
(self.certs_dir / "mcp_proxy_adapter_server.crt").symlink_to("server_cert.pem")
|
263
|
-
|
264
|
-
(self.certs_dir / "mcp_proxy_adapter_server.key").unlink(missing_ok=True)
|
265
|
-
(self.certs_dir / "mcp_proxy_adapter_server.key").symlink_to(self.keys_dir / "server_key.pem")
|
266
|
-
|
267
|
-
# mTLS aliases
|
268
|
-
(self.certs_dir / "localhost_server.crt").unlink(missing_ok=True)
|
269
|
-
(self.certs_dir / "localhost_server.crt").symlink_to("server_cert.pem")
|
270
|
-
|
271
|
-
self.print_success("Certificate aliases created")
|
272
|
-
|
273
|
-
# Create CA alias
|
274
|
-
if (self.certs_dir / "ca_cert.pem").exists():
|
275
|
-
(self.certs_dir / "mcp_proxy_adapter_ca_ca.crt").unlink(missing_ok=True)
|
276
|
-
(self.certs_dir / "mcp_proxy_adapter_ca_ca.crt").symlink_to("ca_cert.pem")
|
277
|
-
|
278
|
-
self.print_success("CA certificate alias created")
|
279
|
-
|
280
|
-
return True
|
281
|
-
|
282
|
-
except Exception as e:
|
283
|
-
self.print_error(f"Failed to create certificate aliases: {e}")
|
284
|
-
return False
|
285
|
-
|
286
|
-
def validate_certificates(self) -> bool:
|
287
|
-
"""Validate generated certificates using framework."""
|
288
|
-
self.print_step("5", "Validating Certificates")
|
289
|
-
|
290
|
-
all_required = get_all_required_certificates()
|
291
|
-
validation_results = []
|
292
|
-
|
293
|
-
for cert_name in all_required:
|
294
|
-
cert_info = REQUIRED_CERTIFICATES[cert_name]
|
295
|
-
cert_file = cert_info["output_cert"]
|
296
|
-
key_file = cert_info["output_key"]
|
297
|
-
|
298
|
-
if cert_file.exists() and key_file.exists():
|
299
|
-
try:
|
300
|
-
# Validate certificate format using framework
|
301
|
-
format_valid = validate_certificate_format(str(cert_file))
|
302
|
-
|
303
|
-
if format_valid:
|
304
|
-
self.print_success(f"{cert_name}: Valid format")
|
305
|
-
validation_results.append(True)
|
306
|
-
else:
|
307
|
-
self.print_error(f"{cert_name}: Invalid format")
|
308
|
-
validation_results.append(False)
|
309
|
-
|
310
|
-
except Exception as e:
|
311
|
-
self.print_error(f"{cert_name}: Validation failed - {e}")
|
312
|
-
validation_results.append(False)
|
313
|
-
else:
|
314
|
-
self.print_error(f"{cert_name}: Missing files")
|
315
|
-
validation_results.append(False)
|
316
|
-
|
317
|
-
success_count = sum(validation_results)
|
318
|
-
total_count = len(validation_results)
|
319
|
-
|
320
|
-
self.print_info(f"Validation results: {success_count}/{total_count} certificates valid")
|
321
|
-
|
322
|
-
return success_count == total_count
|
323
|
-
|
324
|
-
def generate_all_certificates(self) -> bool:
|
325
|
-
"""Generate all required certificates."""
|
326
|
-
print("🔐 Generating All Certificates Using mcp_security_framework with Bugfix")
|
327
|
-
print("=" * 60)
|
328
|
-
|
329
|
-
try:
|
330
|
-
# Check framework availability
|
331
|
-
if not self.check_framework():
|
332
|
-
return False
|
333
|
-
|
334
|
-
# Generate CA certificate first
|
335
|
-
if not self.generate_ca_certificate():
|
336
|
-
return False
|
337
|
-
|
338
|
-
# Generate server certificate
|
339
|
-
if not self.generate_server_certificate():
|
340
|
-
return False
|
341
|
-
|
342
|
-
# Generate client certificates
|
343
|
-
client_certs = ["admin_cert", "user_cert", "proxy_cert"]
|
344
|
-
for cert_name in client_certs:
|
345
|
-
if cert_name in REQUIRED_CERTIFICATES:
|
346
|
-
if not self.generate_client_certificate(cert_name):
|
347
|
-
return False
|
348
|
-
|
349
|
-
# Create aliases
|
350
|
-
if not self.create_certificate_aliases():
|
351
|
-
return False
|
352
|
-
|
353
|
-
# Validate certificates
|
354
|
-
if not self.validate_certificates():
|
355
|
-
return False
|
356
|
-
|
357
|
-
# Print summary
|
358
|
-
print(f"\n{'=' * 60}")
|
359
|
-
print("📊 CERTIFICATE GENERATION SUMMARY")
|
360
|
-
print(f"{'=' * 60}")
|
361
|
-
print("✅ All certificates generated successfully!")
|
362
|
-
print(f"📁 Certificates directory: {self.certs_dir}")
|
363
|
-
print(f"📁 Keys directory: {self.keys_dir}")
|
364
|
-
|
365
|
-
return True
|
366
|
-
|
367
|
-
except Exception as e:
|
368
|
-
self.print_error(f"Certificate generation failed: {e}")
|
369
|
-
return False
|
370
|
-
|
371
|
-
|
372
|
-
def main():
|
373
|
-
"""Main entry point."""
|
374
|
-
generator = BugfixCertificateGenerator()
|
375
|
-
|
376
|
-
try:
|
377
|
-
success = generator.generate_all_certificates()
|
378
|
-
sys.exit(0 if success else 1)
|
379
|
-
except Exception as e:
|
380
|
-
print(f"❌ Fatal error: {e}")
|
381
|
-
sys.exit(1)
|
382
|
-
|
383
|
-
|
384
|
-
if __name__ == "__main__":
|
385
|
-
main()
|
File without changes
|
File without changes
|
File without changes
|