mcp-proxy-adapter 6.1.1__py3-none-any.whl → 6.2.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/__main__.py +27 -7
- mcp_proxy_adapter/api/app.py +18 -7
- mcp_proxy_adapter/commands/ssl_setup_command.py +234 -351
- mcp_proxy_adapter/core/app_factory.py +87 -3
- mcp_proxy_adapter/core/app_runner.py +272 -0
- mcp_proxy_adapter/core/certificate_utils.py +291 -73
- mcp_proxy_adapter/core/client.py +574 -0
- mcp_proxy_adapter/core/client_manager.py +284 -0
- mcp_proxy_adapter/core/server_adapter.py +17 -80
- mcp_proxy_adapter/core/server_engine.py +5 -99
- mcp_proxy_adapter/core/ssl_utils.py +13 -12
- mcp_proxy_adapter/core/transport_manager.py +5 -5
- mcp_proxy_adapter/examples/__init__.py +16 -0
- mcp_proxy_adapter/examples/basic_framework/__init__.py +7 -0
- mcp_proxy_adapter/examples/basic_framework/commands/__init__.py +4 -0
- mcp_proxy_adapter/examples/basic_framework/hooks/__init__.py +4 -0
- mcp_proxy_adapter/examples/basic_framework/main.py +21 -40
- mcp_proxy_adapter/examples/commands/__init__.py +5 -1
- mcp_proxy_adapter/examples/create_certificates_simple.py +260 -75
- mcp_proxy_adapter/examples/debug_request_state.py +4 -36
- mcp_proxy_adapter/examples/debug_role_chain.py +2 -49
- mcp_proxy_adapter/examples/demo_client.py +0 -66
- mcp_proxy_adapter/examples/full_application/__init__.py +11 -0
- mcp_proxy_adapter/examples/full_application/commands/__init__.py +7 -0
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +0 -19
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +0 -16
- mcp_proxy_adapter/examples/full_application/hooks/__init__.py +7 -0
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +0 -22
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +0 -24
- mcp_proxy_adapter/examples/full_application/main.py +65 -44
- mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +154 -0
- mcp_proxy_adapter/examples/generate_all_certificates.py +0 -67
- mcp_proxy_adapter/examples/generate_certificates.py +0 -15
- mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +369 -0
- mcp_proxy_adapter/examples/generate_test_configs.py +204 -0
- mcp_proxy_adapter/examples/proxy_registration_example.py +3 -70
- mcp_proxy_adapter/examples/run_example.py +1 -23
- mcp_proxy_adapter/examples/run_security_tests.py +2 -60
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +0 -53
- mcp_proxy_adapter/examples/security_test_client.py +18 -123
- mcp_proxy_adapter/examples/setup_test_environment.py +179 -0
- mcp_proxy_adapter/examples/test_config.py +148 -0
- mcp_proxy_adapter/examples/test_config_generator.py +1 -25
- mcp_proxy_adapter/examples/test_examples.py +4 -67
- mcp_proxy_adapter/examples/universal_client.py +154 -162
- mcp_proxy_adapter/main.py +51 -161
- mcp_proxy_adapter/version.py +1 -1
- mcp_proxy_adapter-6.2.0.dist-info/METADATA +687 -0
- mcp_proxy_adapter-6.2.0.dist-info/RECORD +122 -0
- mcp_proxy_adapter/docs/EN/TROUBLESHOOTING.md +0 -285
- mcp_proxy_adapter/docs/RU/TROUBLESHOOTING.md +0 -285
- mcp_proxy_adapter/examples/README.md +0 -257
- mcp_proxy_adapter/examples/README_EN.md +0 -258
- mcp_proxy_adapter/examples/SECURITY_TESTING.md +0 -455
- mcp_proxy_adapter/examples/basic_framework/configs/http_auth.json +0 -37
- mcp_proxy_adapter/examples/basic_framework/configs/http_simple.json +0 -23
- mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +0 -43
- mcp_proxy_adapter/examples/basic_framework/configs/https_no_protocol_middleware.json +0 -36
- mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +0 -29
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_protocol_middleware.json +0 -34
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +0 -39
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_simple.json +0 -35
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_with_roles.json +0 -45
- mcp_proxy_adapter/examples/basic_framework/roles.json +0 -21
- mcp_proxy_adapter/examples/cert_config.json +0 -9
- mcp_proxy_adapter/examples/certs/admin.crt +0 -32
- mcp_proxy_adapter/examples/certs/admin.key +0 -52
- mcp_proxy_adapter/examples/certs/admin_cert.pem +0 -21
- mcp_proxy_adapter/examples/certs/admin_key.pem +0 -28
- mcp_proxy_adapter/examples/certs/ca_cert.pem +0 -23
- mcp_proxy_adapter/examples/certs/ca_cert.srl +0 -1
- mcp_proxy_adapter/examples/certs/ca_key.pem +0 -28
- mcp_proxy_adapter/examples/certs/cert_config.json +0 -9
- mcp_proxy_adapter/examples/certs/client.crt +0 -32
- mcp_proxy_adapter/examples/certs/client.key +0 -52
- mcp_proxy_adapter/examples/certs/client_admin.crt +0 -32
- mcp_proxy_adapter/examples/certs/client_admin.key +0 -52
- mcp_proxy_adapter/examples/certs/client_user.crt +0 -32
- mcp_proxy_adapter/examples/certs/client_user.key +0 -52
- mcp_proxy_adapter/examples/certs/guest_cert.pem +0 -21
- mcp_proxy_adapter/examples/certs/guest_key.pem +0 -28
- mcp_proxy_adapter/examples/certs/mcp_proxy_adapter_ca_ca.crt +0 -23
- mcp_proxy_adapter/examples/certs/proxy_cert.pem +0 -21
- mcp_proxy_adapter/examples/certs/proxy_key.pem +0 -28
- mcp_proxy_adapter/examples/certs/readonly.crt +0 -32
- mcp_proxy_adapter/examples/certs/readonly.key +0 -52
- mcp_proxy_adapter/examples/certs/readonly_cert.pem +0 -21
- mcp_proxy_adapter/examples/certs/readonly_key.pem +0 -28
- mcp_proxy_adapter/examples/certs/server.crt +0 -32
- mcp_proxy_adapter/examples/certs/server.key +0 -52
- mcp_proxy_adapter/examples/certs/server_cert.pem +0 -32
- mcp_proxy_adapter/examples/certs/server_key.pem +0 -52
- mcp_proxy_adapter/examples/certs/test_ca_ca.crt +0 -20
- mcp_proxy_adapter/examples/certs/user.crt +0 -32
- mcp_proxy_adapter/examples/certs/user.key +0 -52
- mcp_proxy_adapter/examples/certs/user_cert.pem +0 -21
- mcp_proxy_adapter/examples/certs/user_key.pem +0 -28
- mcp_proxy_adapter/examples/client_configs/api_key_client.json +0 -13
- mcp_proxy_adapter/examples/client_configs/basic_auth_client.json +0 -13
- mcp_proxy_adapter/examples/client_configs/certificate_client.json +0 -22
- mcp_proxy_adapter/examples/client_configs/jwt_client.json +0 -15
- mcp_proxy_adapter/examples/client_configs/no_auth_client.json +0 -9
- mcp_proxy_adapter/examples/full_application/configs/http_auth.json +0 -37
- mcp_proxy_adapter/examples/full_application/configs/http_simple.json +0 -23
- mcp_proxy_adapter/examples/full_application/configs/https_auth.json +0 -39
- mcp_proxy_adapter/examples/full_application/configs/https_simple.json +0 -25
- mcp_proxy_adapter/examples/full_application/configs/mtls_no_roles.json +0 -39
- mcp_proxy_adapter/examples/full_application/configs/mtls_with_roles.json +0 -45
- mcp_proxy_adapter/examples/full_application/roles.json +0 -21
- mcp_proxy_adapter/examples/keys/ca_key.pem +0 -28
- mcp_proxy_adapter/examples/keys/mcp_proxy_adapter_ca_ca.key +0 -28
- mcp_proxy_adapter/examples/keys/test_ca_ca.key +0 -28
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log +0 -220
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.1 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.2 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.3 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.4 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.5 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log +0 -220
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.1 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.2 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.3 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.4 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.5 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log +0 -2
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.1 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.2 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.3 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.4 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.5 +0 -1
- mcp_proxy_adapter/examples/roles.json +0 -38
- mcp_proxy_adapter/examples/server_configs/config_basic_http.json +0 -204
- mcp_proxy_adapter/examples/server_configs/config_http_token.json +0 -238
- mcp_proxy_adapter/examples/server_configs/config_https.json +0 -215
- mcp_proxy_adapter/examples/server_configs/config_https_token.json +0 -231
- mcp_proxy_adapter/examples/server_configs/config_mtls.json +0 -215
- mcp_proxy_adapter/examples/server_configs/config_proxy_registration.json +0 -250
- mcp_proxy_adapter/examples/server_configs/config_simple.json +0 -46
- mcp_proxy_adapter/examples/server_configs/roles.json +0 -38
- mcp_proxy_adapter-6.1.1.dist-info/METADATA +0 -205
- mcp_proxy_adapter-6.1.1.dist-info/RECORD +0 -197
- {mcp_proxy_adapter-6.1.1.dist-info → mcp_proxy_adapter-6.2.0.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.1.1.dist-info → mcp_proxy_adapter-6.2.0.dist-info}/entry_points.txt +0 -0
- {mcp_proxy_adapter-6.1.1.dist-info → mcp_proxy_adapter-6.2.0.dist-info}/licenses/LICENSE +0 -0
- {mcp_proxy_adapter-6.1.1.dist-info → mcp_proxy_adapter-6.2.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,369 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Author: Vasiliy Zdanovskiy
|
4
|
+
email: vasilyvz@gmail.com
|
5
|
+
Script for generating certificates and tokens for MCP Proxy Adapter configurations.
|
6
|
+
Generates all necessary certificates, keys, and tokens based on configuration requirements.
|
7
|
+
Uses mcp_security_framework for certificate generation.
|
8
|
+
"""
|
9
|
+
import json
|
10
|
+
import os
|
11
|
+
import sys
|
12
|
+
import argparse
|
13
|
+
import subprocess
|
14
|
+
from pathlib import Path
|
15
|
+
from typing import Dict, Any, List, Optional
|
16
|
+
# Import mcp_security_framework
|
17
|
+
try:
|
18
|
+
from mcp_security_framework.core.cert_manager import CertificateManager
|
19
|
+
from mcp_security_framework.schemas.config import CertificateConfig, CAConfig, ServerCertConfig, ClientCertConfig
|
20
|
+
from mcp_security_framework.schemas.models import CertificateType
|
21
|
+
SECURITY_FRAMEWORK_AVAILABLE = True
|
22
|
+
except ImportError:
|
23
|
+
SECURITY_FRAMEWORK_AVAILABLE = False
|
24
|
+
print("Warning: mcp_security_framework not available, falling back to OpenSSL")
|
25
|
+
def generate_ca_certificate(output_dir: str) -> str:
|
26
|
+
"""
|
27
|
+
Generate CA certificate and key using mcp_security_framework.
|
28
|
+
Args:
|
29
|
+
output_dir: Output directory for certificates
|
30
|
+
Returns:
|
31
|
+
Path to CA certificate file
|
32
|
+
"""
|
33
|
+
ca_dir = os.path.join(output_dir, "certs")
|
34
|
+
os.makedirs(ca_dir, exist_ok=True)
|
35
|
+
if SECURITY_FRAMEWORK_AVAILABLE:
|
36
|
+
try:
|
37
|
+
# Configure CA certificate
|
38
|
+
ca_config = CAConfig(
|
39
|
+
common_name="MCP Proxy Adapter CA",
|
40
|
+
organization="MCP Proxy Adapter",
|
41
|
+
organizational_unit="Certificate Authority",
|
42
|
+
country="US",
|
43
|
+
state="State",
|
44
|
+
locality="City",
|
45
|
+
validity_years=10, # Используем validity_years вместо validity_days
|
46
|
+
key_size=2048,
|
47
|
+
hash_algorithm="sha256"
|
48
|
+
)
|
49
|
+
# Create certificate manager
|
50
|
+
cert_config = CertificateConfig(
|
51
|
+
cert_storage_path=ca_dir,
|
52
|
+
key_storage_path=ca_dir,
|
53
|
+
default_validity_days=3650,
|
54
|
+
key_size=2048,
|
55
|
+
hash_algorithm="sha256"
|
56
|
+
)
|
57
|
+
cert_manager = CertificateManager(cert_config)
|
58
|
+
# Create CA certificate
|
59
|
+
cert_pair = cert_manager.create_root_ca(ca_config)
|
60
|
+
if cert_pair and cert_pair.certificate_path:
|
61
|
+
print(f"✅ Generated CA certificate using mcp_security_framework")
|
62
|
+
return cert_pair.certificate_path
|
63
|
+
else:
|
64
|
+
print(f"❌ Failed to create CA certificate: Invalid certificate pair")
|
65
|
+
return None
|
66
|
+
except Exception as e:
|
67
|
+
print(f"❌ Error creating CA certificate with framework: {e}")
|
68
|
+
return None
|
69
|
+
else:
|
70
|
+
# Fallback to OpenSSL
|
71
|
+
ca_key = os.path.join(ca_dir, "ca.key")
|
72
|
+
ca_cert = os.path.join(ca_dir, "ca.crt")
|
73
|
+
# Generate CA private key
|
74
|
+
subprocess.run([
|
75
|
+
"openssl", "genrsa", "-out", ca_key, "2048"
|
76
|
+
], check=True, capture_output=True)
|
77
|
+
# Generate CA certificate
|
78
|
+
subprocess.run([
|
79
|
+
"openssl", "req", "-new", "-x509", "-days", "365", "-key", ca_key,
|
80
|
+
"-out", ca_cert, "-subj", "/C=US/ST=State/L=City/O=Organization/CN=CA"
|
81
|
+
], check=True, capture_output=True)
|
82
|
+
print(f"✅ Generated CA certificate using OpenSSL: {ca_cert}")
|
83
|
+
return ca_cert
|
84
|
+
def generate_server_certificate(output_dir: str, ca_cert: str) -> tuple[str, str]:
|
85
|
+
"""
|
86
|
+
Generate server certificate and key using mcp_security_framework.
|
87
|
+
Args:
|
88
|
+
output_dir: Output directory for certificates
|
89
|
+
ca_cert: Path to CA certificate
|
90
|
+
Returns:
|
91
|
+
Tuple of (certificate_path, key_path)
|
92
|
+
"""
|
93
|
+
certs_dir = os.path.join(output_dir, "certs")
|
94
|
+
keys_dir = os.path.join(output_dir, "keys")
|
95
|
+
os.makedirs(certs_dir, exist_ok=True)
|
96
|
+
os.makedirs(keys_dir, exist_ok=True)
|
97
|
+
if SECURITY_FRAMEWORK_AVAILABLE:
|
98
|
+
try:
|
99
|
+
# Find CA key file
|
100
|
+
ca_key = None
|
101
|
+
if ca_cert.endswith('.crt'):
|
102
|
+
ca_key = ca_cert.replace('.crt', '.key')
|
103
|
+
elif ca_cert.endswith('.pem'):
|
104
|
+
ca_key = ca_cert.replace('.pem', '_key.pem')
|
105
|
+
if not os.path.exists(ca_key):
|
106
|
+
print(f"❌ CA key file not found: {ca_key}")
|
107
|
+
return None, None
|
108
|
+
# Configure server certificate
|
109
|
+
server_config = ServerCertConfig(
|
110
|
+
common_name="localhost",
|
111
|
+
organization="MCP Proxy Adapter",
|
112
|
+
organizational_unit="Server",
|
113
|
+
country="US",
|
114
|
+
state="State",
|
115
|
+
locality="City",
|
116
|
+
validity_days=365,
|
117
|
+
key_size=2048,
|
118
|
+
hash_algorithm="sha256",
|
119
|
+
subject_alt_names=["localhost", "127.0.0.1"], # Используем subject_alt_names вместо san_dns
|
120
|
+
ca_cert_path=ca_cert,
|
121
|
+
ca_key_path=ca_key
|
122
|
+
)
|
123
|
+
# Create certificate manager
|
124
|
+
cert_config = CertificateConfig(
|
125
|
+
cert_storage_path=certs_dir,
|
126
|
+
key_storage_path=keys_dir,
|
127
|
+
default_validity_days=365,
|
128
|
+
key_size=2048,
|
129
|
+
hash_algorithm="sha256"
|
130
|
+
)
|
131
|
+
cert_manager = CertificateManager(cert_config)
|
132
|
+
# Create server certificate
|
133
|
+
cert_pair = cert_manager.create_server_certificate(server_config)
|
134
|
+
if cert_pair and cert_pair.certificate_path and cert_pair.private_key_path:
|
135
|
+
print(f"✅ Generated server certificate using mcp_security_framework")
|
136
|
+
return (cert_pair.certificate_path, cert_pair.private_key_path)
|
137
|
+
else:
|
138
|
+
print(f"❌ Failed to create server certificate: Invalid certificate pair")
|
139
|
+
return None, None
|
140
|
+
except Exception as e:
|
141
|
+
print(f"❌ Error creating server certificate with framework: {e}")
|
142
|
+
return None, None
|
143
|
+
else:
|
144
|
+
# Fallback to OpenSSL
|
145
|
+
ca_key = ca_cert.replace(".crt", ".key")
|
146
|
+
server_key = os.path.join(keys_dir, "server.key")
|
147
|
+
server_csr = os.path.join(certs_dir, "server.csr")
|
148
|
+
server_cert = os.path.join(certs_dir, "server.crt")
|
149
|
+
# Generate server private key
|
150
|
+
subprocess.run([
|
151
|
+
"openssl", "genrsa", "-out", server_key, "2048"
|
152
|
+
], check=True, capture_output=True)
|
153
|
+
# Generate server certificate signing request
|
154
|
+
subprocess.run([
|
155
|
+
"openssl", "req", "-new", "-key", server_key, "-out", server_csr,
|
156
|
+
"-subj", "/C=US/ST=State/L=City/O=Organization/CN=localhost"
|
157
|
+
], check=True, capture_output=True)
|
158
|
+
# Sign server certificate with CA
|
159
|
+
subprocess.run([
|
160
|
+
"openssl", "x509", "-req", "-in", server_csr, "-CA", ca_cert,
|
161
|
+
"-CAkey", ca_key, "-CAcreateserial", "-out", server_cert, "-days", "365"
|
162
|
+
], check=True, capture_output=True)
|
163
|
+
# Clean up CSR
|
164
|
+
os.remove(server_csr)
|
165
|
+
print(f"✅ Generated server certificate using OpenSSL: {server_cert}")
|
166
|
+
return server_cert, server_key
|
167
|
+
def generate_client_certificate(output_dir: str, ca_cert: str, client_name: str = "client",
|
168
|
+
roles: List[str] = None, permissions: List[str] = None) -> tuple[str, str]:
|
169
|
+
"""
|
170
|
+
Generate client certificate and key using mcp_security_framework.
|
171
|
+
Args:
|
172
|
+
output_dir: Output directory for certificates
|
173
|
+
ca_cert: Path to CA certificate
|
174
|
+
client_name: Name of the client
|
175
|
+
roles: List of roles for the client
|
176
|
+
permissions: List of permissions for the client
|
177
|
+
Returns:
|
178
|
+
Tuple of (certificate_path, key_path)
|
179
|
+
"""
|
180
|
+
certs_dir = os.path.join(output_dir, "certs")
|
181
|
+
keys_dir = os.path.join(output_dir, "keys")
|
182
|
+
os.makedirs(certs_dir, exist_ok=True)
|
183
|
+
os.makedirs(keys_dir, exist_ok=True)
|
184
|
+
if SECURITY_FRAMEWORK_AVAILABLE:
|
185
|
+
try:
|
186
|
+
# Find CA key file
|
187
|
+
ca_key = None
|
188
|
+
if ca_cert.endswith('.crt'):
|
189
|
+
ca_key = ca_cert.replace('.crt', '.key')
|
190
|
+
elif ca_cert.endswith('.pem'):
|
191
|
+
ca_key = ca_cert.replace('.pem', '_key.pem')
|
192
|
+
if not os.path.exists(ca_key):
|
193
|
+
print(f"❌ CA key file not found: {ca_key}")
|
194
|
+
return None, None
|
195
|
+
# Configure client certificate
|
196
|
+
client_config = ClientCertConfig(
|
197
|
+
common_name=f"{client_name}-client",
|
198
|
+
organization="MCP Proxy Adapter",
|
199
|
+
organizational_unit="Client",
|
200
|
+
country="US",
|
201
|
+
state="State",
|
202
|
+
locality="City",
|
203
|
+
validity_days=730,
|
204
|
+
key_size=2048,
|
205
|
+
hash_algorithm="sha256",
|
206
|
+
roles=roles or [],
|
207
|
+
permissions=permissions or [],
|
208
|
+
ca_cert_path=ca_cert,
|
209
|
+
ca_key_path=ca_key
|
210
|
+
)
|
211
|
+
# Create certificate manager
|
212
|
+
cert_config = CertificateConfig(
|
213
|
+
cert_storage_path=certs_dir,
|
214
|
+
key_storage_path=keys_dir,
|
215
|
+
default_validity_days=730,
|
216
|
+
key_size=2048,
|
217
|
+
hash_algorithm="sha256"
|
218
|
+
)
|
219
|
+
cert_manager = CertificateManager(cert_config)
|
220
|
+
# Create client certificate
|
221
|
+
cert_pair = cert_manager.create_client_certificate(client_config)
|
222
|
+
if cert_pair and cert_pair.certificate_path and cert_pair.private_key_path:
|
223
|
+
print(f"✅ Generated client certificate {client_name} using mcp_security_framework")
|
224
|
+
return (cert_pair.certificate_path, cert_pair.private_key_path)
|
225
|
+
else:
|
226
|
+
print(f"❌ Failed to create client certificate {client_name}: Invalid certificate pair")
|
227
|
+
return None, None
|
228
|
+
except Exception as e:
|
229
|
+
print(f"❌ Error creating client certificate {client_name} with framework: {e}")
|
230
|
+
return None, None
|
231
|
+
else:
|
232
|
+
# Fallback to OpenSSL
|
233
|
+
ca_key = ca_cert.replace(".crt", ".key")
|
234
|
+
client_key = os.path.join(keys_dir, f"{client_name}.key")
|
235
|
+
client_csr = os.path.join(certs_dir, f"{client_name}.csr")
|
236
|
+
client_cert = os.path.join(certs_dir, f"{client_name}.crt")
|
237
|
+
# Generate client private key
|
238
|
+
subprocess.run([
|
239
|
+
"openssl", "genrsa", "-out", client_key, "2048"
|
240
|
+
], check=True, capture_output=True)
|
241
|
+
# Generate client certificate signing request
|
242
|
+
subprocess.run([
|
243
|
+
"openssl", "req", "-new", "-key", client_key, "-out", client_csr,
|
244
|
+
"-subj", f"/C=US/ST=State/L=City/O=Organization/CN={client_name}-client"
|
245
|
+
], check=True, capture_output=True)
|
246
|
+
# Sign client certificate with CA
|
247
|
+
subprocess.run([
|
248
|
+
"openssl", "x509", "-req", "-in", client_csr, "-CA", ca_cert,
|
249
|
+
"-CAkey", ca_key, "-CAcreateserial", "-out", client_cert, "-days", "730"
|
250
|
+
], check=True, capture_output=True)
|
251
|
+
# Clean up CSR
|
252
|
+
os.remove(client_csr)
|
253
|
+
print(f"✅ Generated client certificate {client_name} using OpenSSL: {client_cert}")
|
254
|
+
return client_cert, client_key
|
255
|
+
def generate_tokens(output_dir: str) -> Dict[str, str]:
|
256
|
+
"""
|
257
|
+
Generate API tokens for different roles.
|
258
|
+
Args:
|
259
|
+
output_dir: Output directory for tokens
|
260
|
+
Returns:
|
261
|
+
Dictionary of role -> token mappings
|
262
|
+
"""
|
263
|
+
tokens_dir = os.path.join(output_dir, "tokens")
|
264
|
+
os.makedirs(tokens_dir, exist_ok=True)
|
265
|
+
tokens = {
|
266
|
+
"admin": "test-token-123",
|
267
|
+
"user": "user-token-456",
|
268
|
+
"readonly": "readonly-token-123",
|
269
|
+
"guest": "guest-token-123",
|
270
|
+
"proxy": "proxy-token-123"
|
271
|
+
}
|
272
|
+
# Save tokens to file
|
273
|
+
tokens_file = os.path.join(tokens_dir, "tokens.json")
|
274
|
+
with open(tokens_file, 'w') as f:
|
275
|
+
json.dump(tokens, f, indent=2)
|
276
|
+
print(f"✅ Generated tokens: {tokens_file}")
|
277
|
+
return tokens
|
278
|
+
def generate_roles_config(output_dir: str) -> Dict[str, Any]:
|
279
|
+
"""
|
280
|
+
Generate roles configuration file.
|
281
|
+
Args:
|
282
|
+
output_dir: Output directory for configs
|
283
|
+
Returns:
|
284
|
+
Roles configuration dictionary
|
285
|
+
"""
|
286
|
+
roles_config = {
|
287
|
+
"admin": {
|
288
|
+
"permissions": ["read", "write", "execute", "delete", "admin", "register", "unregister", "heartbeat", "discover"],
|
289
|
+
"tokens": []
|
290
|
+
},
|
291
|
+
"user": {
|
292
|
+
"permissions": ["read", "execute", "register", "unregister", "heartbeat", "discover"],
|
293
|
+
"tokens": []
|
294
|
+
},
|
295
|
+
"readonly": {
|
296
|
+
"permissions": ["read", "discover"],
|
297
|
+
"tokens": []
|
298
|
+
},
|
299
|
+
"guest": {
|
300
|
+
"permissions": ["read", "discover"],
|
301
|
+
"tokens": []
|
302
|
+
},
|
303
|
+
"proxy": {
|
304
|
+
"permissions": ["register", "unregister", "heartbeat", "discover"],
|
305
|
+
"tokens": []
|
306
|
+
}
|
307
|
+
}
|
308
|
+
# Save roles config to file
|
309
|
+
roles_file = os.path.join(output_dir, "roles.json")
|
310
|
+
with open(roles_file, 'w') as f:
|
311
|
+
json.dump(roles_config, f, indent=2)
|
312
|
+
print(f"✅ Generated roles configuration: {roles_file}")
|
313
|
+
return roles_config
|
314
|
+
def main():
|
315
|
+
"""Main function for certificate and token generation."""
|
316
|
+
parser = argparse.ArgumentParser(description="Generate certificates and tokens")
|
317
|
+
parser.add_argument("--output-dir", "-o", default="./certs", help="Output directory")
|
318
|
+
parser.add_argument("--framework", action="store_true", help="Use mcp_security_framework")
|
319
|
+
args = parser.parse_args()
|
320
|
+
print("🔐 Certificate and Token Generation Script")
|
321
|
+
print("=" * 50)
|
322
|
+
if args.framework and not SECURITY_FRAMEWORK_AVAILABLE:
|
323
|
+
print("❌ mcp_security_framework not available")
|
324
|
+
return 1
|
325
|
+
# Create output directory
|
326
|
+
os.makedirs(args.output_dir, exist_ok=True)
|
327
|
+
try:
|
328
|
+
# 1. Generate CA certificate
|
329
|
+
print("\n🔧 Generating CA certificate...")
|
330
|
+
ca_cert = generate_ca_certificate(args.output_dir)
|
331
|
+
if not ca_cert:
|
332
|
+
print("❌ Failed to generate CA certificate")
|
333
|
+
return 1
|
334
|
+
# 2. Generate server certificate
|
335
|
+
print("\n🔧 Generating server certificate...")
|
336
|
+
server_cert, server_key = generate_server_certificate(args.output_dir, ca_cert)
|
337
|
+
if not server_cert or not server_key:
|
338
|
+
print("❌ Failed to generate server certificate")
|
339
|
+
return 1
|
340
|
+
# 3. Generate client certificates
|
341
|
+
print("\n🔧 Generating client certificates...")
|
342
|
+
client_configs = [
|
343
|
+
("admin", ["admin"], ["read", "write", "execute", "delete", "admin", "register", "unregister", "heartbeat", "discover"]),
|
344
|
+
("user", ["user"], ["read", "execute", "register", "unregister", "heartbeat", "discover"]),
|
345
|
+
("readonly", ["readonly"], ["read", "discover"]),
|
346
|
+
("guest", ["guest"], ["read", "discover"]),
|
347
|
+
("proxy", ["proxy"], ["register", "unregister", "heartbeat", "discover"])
|
348
|
+
]
|
349
|
+
for client_name, roles, permissions in client_configs:
|
350
|
+
client_cert, client_key = generate_client_certificate(
|
351
|
+
args.output_dir, ca_cert, client_name, roles, permissions
|
352
|
+
)
|
353
|
+
if not client_cert or not client_key:
|
354
|
+
print(f"❌ Failed to generate client certificate {client_name}")
|
355
|
+
return 1
|
356
|
+
# 4. Generate tokens
|
357
|
+
print("\n🔧 Generating tokens...")
|
358
|
+
tokens = generate_tokens(args.output_dir)
|
359
|
+
# 5. Generate roles configuration
|
360
|
+
print("\n🔧 Generating roles configuration...")
|
361
|
+
roles_config = generate_roles_config(args.output_dir)
|
362
|
+
print("\n🎉 All certificates and tokens generated successfully!")
|
363
|
+
print(f"📁 Output directory: {args.output_dir}")
|
364
|
+
return 0
|
365
|
+
except Exception as e:
|
366
|
+
print(f"❌ Error during generation: {e}")
|
367
|
+
return 1
|
368
|
+
if __name__ == "__main__":
|
369
|
+
exit(main())
|
@@ -0,0 +1,204 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Author: Vasiliy Zdanovskiy
|
4
|
+
email: vasilyvz@gmail.com
|
5
|
+
Script for generating test configurations for MCP Proxy Adapter.
|
6
|
+
Generates 6 different configuration types for testing various security scenarios.
|
7
|
+
"""
|
8
|
+
import json
|
9
|
+
import os
|
10
|
+
import argparse
|
11
|
+
from typing import Dict, Any
|
12
|
+
def generate_http_simple_config(port: int = 8000) -> Dict[str, Any]:
|
13
|
+
"""Generate HTTP configuration without authorization."""
|
14
|
+
return {
|
15
|
+
"server": {"host": "127.0.0.1", "port": port},
|
16
|
+
"ssl": {"enabled": False},
|
17
|
+
"security": {"enabled": False},
|
18
|
+
"protocols": {"enabled": True, "allowed_protocols": ["http"]}
|
19
|
+
}
|
20
|
+
def generate_http_token_config(port: int = 8001) -> Dict[str, Any]:
|
21
|
+
"""Generate HTTP configuration with token authorization."""
|
22
|
+
return {
|
23
|
+
"server": {"host": "127.0.0.1", "port": port},
|
24
|
+
"ssl": {"enabled": False},
|
25
|
+
"security": {
|
26
|
+
"enabled": True,
|
27
|
+
"auth": {"enabled": True, "methods": ["api_key"]},
|
28
|
+
"permissions": {"enabled": True, "roles_file": "./roles.json"}
|
29
|
+
},
|
30
|
+
"protocols": {"enabled": True, "allowed_protocols": ["http"]}
|
31
|
+
}
|
32
|
+
def generate_https_simple_config(port: int = 8002) -> Dict[str, Any]:
|
33
|
+
"""Generate HTTPS configuration without client certificate verification and authorization."""
|
34
|
+
return {
|
35
|
+
"server": {"host": "127.0.0.1", "port": port},
|
36
|
+
"ssl": {
|
37
|
+
"enabled": True,
|
38
|
+
"cert_file": "./certs/server_cert.pem",
|
39
|
+
"key_file": "./certs/server_key.pem"
|
40
|
+
},
|
41
|
+
"security": {"enabled": False},
|
42
|
+
"protocols": {"enabled": True, "allowed_protocols": ["http", "https"]}
|
43
|
+
}
|
44
|
+
def generate_https_token_config(port: int = 8003) -> Dict[str, Any]:
|
45
|
+
"""Generate HTTPS configuration without client certificate verification with token authorization."""
|
46
|
+
return {
|
47
|
+
"server": {"host": "127.0.0.1", "port": port},
|
48
|
+
"ssl": {
|
49
|
+
"enabled": True,
|
50
|
+
"cert_file": "./certs/server_cert.pem",
|
51
|
+
"key_file": "./certs/server_key.pem"
|
52
|
+
},
|
53
|
+
"security": {
|
54
|
+
"enabled": True,
|
55
|
+
"auth": {"enabled": True, "methods": ["api_key"]},
|
56
|
+
"permissions": {"enabled": True, "roles_file": "./roles.json"}
|
57
|
+
},
|
58
|
+
"protocols": {"enabled": True, "allowed_protocols": ["http", "https"]}
|
59
|
+
}
|
60
|
+
def generate_mtls_no_roles_config(port: int = 8004) -> Dict[str, Any]:
|
61
|
+
"""Generate mTLS configuration without roles."""
|
62
|
+
return {
|
63
|
+
"server": {"host": "127.0.0.1", "port": port},
|
64
|
+
"ssl": {
|
65
|
+
"enabled": True,
|
66
|
+
"cert_file": "./certs/server_cert.pem",
|
67
|
+
"key_file": "./certs/server_key.pem",
|
68
|
+
"ca_cert": "./certs/ca_cert.pem",
|
69
|
+
"verify_client": True
|
70
|
+
},
|
71
|
+
"security": {
|
72
|
+
"enabled": True,
|
73
|
+
"auth": {"enabled": True, "methods": ["certificate"]},
|
74
|
+
"permissions": {"enabled": False}
|
75
|
+
},
|
76
|
+
"protocols": {"enabled": True, "allowed_protocols": ["https", "mtls"]}
|
77
|
+
}
|
78
|
+
def generate_mtls_with_roles_config(port: int = 8005) -> Dict[str, Any]:
|
79
|
+
"""Generate mTLS configuration with roles."""
|
80
|
+
return {
|
81
|
+
"server": {"host": "127.0.0.1", "port": port},
|
82
|
+
"ssl": {
|
83
|
+
"enabled": True,
|
84
|
+
"cert_file": "./certs/server_cert.pem",
|
85
|
+
"key_file": "./certs/server_key.pem",
|
86
|
+
"ca_cert": "./certs/ca_cert.pem",
|
87
|
+
"verify_client": True
|
88
|
+
},
|
89
|
+
"security": {
|
90
|
+
"enabled": True,
|
91
|
+
"auth": {"enabled": True, "methods": ["certificate"]},
|
92
|
+
"permissions": {"enabled": True, "roles_file": "./roles.json"}
|
93
|
+
},
|
94
|
+
"protocols": {"enabled": True, "allowed_protocols": ["https", "mtls"]}
|
95
|
+
}
|
96
|
+
def generate_roles_config() -> Dict[str, Any]:
|
97
|
+
"""Generate roles configuration for testing."""
|
98
|
+
return {
|
99
|
+
"admin": {
|
100
|
+
"description": "Administrator role with full access",
|
101
|
+
"permissions": [
|
102
|
+
"read",
|
103
|
+
"write",
|
104
|
+
"execute",
|
105
|
+
"delete",
|
106
|
+
"admin",
|
107
|
+
"register",
|
108
|
+
"unregister",
|
109
|
+
"heartbeat",
|
110
|
+
"discover"
|
111
|
+
],
|
112
|
+
"tokens": []
|
113
|
+
},
|
114
|
+
"user": {
|
115
|
+
"description": "User role with limited access",
|
116
|
+
"permissions": [
|
117
|
+
"read",
|
118
|
+
"execute",
|
119
|
+
"register",
|
120
|
+
"unregister",
|
121
|
+
"heartbeat",
|
122
|
+
"discover"
|
123
|
+
],
|
124
|
+
"tokens": []
|
125
|
+
},
|
126
|
+
"readonly": {
|
127
|
+
"description": "Read-only role",
|
128
|
+
"permissions": [
|
129
|
+
"read",
|
130
|
+
"discover"
|
131
|
+
],
|
132
|
+
"tokens": []
|
133
|
+
},
|
134
|
+
"guest": {
|
135
|
+
"description": "Guest role with read-only access",
|
136
|
+
"permissions": [
|
137
|
+
"read",
|
138
|
+
"discover"
|
139
|
+
],
|
140
|
+
"tokens": []
|
141
|
+
},
|
142
|
+
"proxy": {
|
143
|
+
"description": "Proxy role for registration",
|
144
|
+
"permissions": [
|
145
|
+
"register",
|
146
|
+
"unregister",
|
147
|
+
"heartbeat",
|
148
|
+
"discover"
|
149
|
+
],
|
150
|
+
"tokens": []
|
151
|
+
}
|
152
|
+
}
|
153
|
+
def generate_all_configs(output_dir: str) -> None:
|
154
|
+
"""Generate all 6 configuration types and save them to files."""
|
155
|
+
configs = {
|
156
|
+
"http_simple": generate_http_simple_config(8000),
|
157
|
+
"http_token": generate_http_token_config(8001),
|
158
|
+
"https_simple": generate_https_simple_config(8002),
|
159
|
+
"https_token": generate_https_token_config(8003),
|
160
|
+
"mtls_no_roles": generate_mtls_no_roles_config(8004),
|
161
|
+
"mtls_with_roles": generate_mtls_with_roles_config(8005)
|
162
|
+
}
|
163
|
+
# Ensure output directory exists
|
164
|
+
os.makedirs(output_dir, exist_ok=True)
|
165
|
+
# Generate each configuration
|
166
|
+
for name, config in configs.items():
|
167
|
+
filename = os.path.join(output_dir, f"{name}.json")
|
168
|
+
with open(filename, 'w', encoding='utf-8') as f:
|
169
|
+
json.dump(config, f, indent=2, ensure_ascii=False)
|
170
|
+
print(f"Generated: {filename}")
|
171
|
+
# Generate roles configuration
|
172
|
+
roles_config = generate_roles_config()
|
173
|
+
roles_filename = os.path.join(output_dir, "roles.json")
|
174
|
+
with open(roles_filename, 'w', encoding='utf-8') as f:
|
175
|
+
json.dump(roles_config, f, indent=2, ensure_ascii=False)
|
176
|
+
print(f"Generated: {roles_filename}")
|
177
|
+
# Also create roles.json in certs directory for compatibility
|
178
|
+
certs_dir = os.path.join(os.path.dirname(output_dir), "certs")
|
179
|
+
if os.path.exists(certs_dir):
|
180
|
+
certs_roles_filename = os.path.join(certs_dir, "roles.json")
|
181
|
+
with open(certs_roles_filename, 'w', encoding='utf-8') as f:
|
182
|
+
json.dump(roles_config, f, indent=2, ensure_ascii=False)
|
183
|
+
print(f"Generated: {certs_roles_filename}")
|
184
|
+
print(f"\nGenerated {len(configs)} configuration files and roles.json in {output_dir}")
|
185
|
+
def main():
|
186
|
+
"""Main function for command line execution."""
|
187
|
+
parser = argparse.ArgumentParser(
|
188
|
+
description="Generate test configurations for MCP Proxy Adapter"
|
189
|
+
)
|
190
|
+
parser.add_argument(
|
191
|
+
"--output-dir",
|
192
|
+
default="./configs",
|
193
|
+
help="Output directory for configuration files (default: ./configs)"
|
194
|
+
)
|
195
|
+
args = parser.parse_args()
|
196
|
+
try:
|
197
|
+
generate_all_configs(args.output_dir)
|
198
|
+
print("Configuration generation completed successfully!")
|
199
|
+
except Exception as e:
|
200
|
+
print(f"Error generating configurations: {e}")
|
201
|
+
return 1
|
202
|
+
return 0
|
203
|
+
if __name__ == "__main__":
|
204
|
+
exit(main())
|