mcp-proxy-adapter 6.6.0__py3-none-any.whl → 6.6.3__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 +28 -26
- mcp_proxy_adapter/config.py +2 -9
- mcp_proxy_adapter/core/server_adapter.py +1 -1
- mcp_proxy_adapter/examples/check_config.py +415 -0
- mcp_proxy_adapter/examples/config_builder.py +11 -17
- mcp_proxy_adapter/examples/{generate_certificates_bugfix.py → generate_certificates.py} +11 -0
- mcp_proxy_adapter/examples/generate_config.py +343 -0
- mcp_proxy_adapter/examples/run_full_test_suite.py +3 -3
- mcp_proxy_adapter/examples/security_test_client.py +6 -5
- mcp_proxy_adapter/examples/test_chk_hostname_automated.py +211 -0
- mcp_proxy_adapter/examples/test_framework_complete.py +269 -0
- mcp_proxy_adapter/examples/test_mcp_server.py +188 -0
- mcp_proxy_adapter/main.py +11 -18
- mcp_proxy_adapter/version.py +1 -1
- {mcp_proxy_adapter-6.6.0.dist-info → mcp_proxy_adapter-6.6.3.dist-info}/METADATA +1 -1
- {mcp_proxy_adapter-6.6.0.dist-info → mcp_proxy_adapter-6.6.3.dist-info}/RECORD +19 -20
- mcp_proxy_adapter/examples/config_builder_simple.py +0 -271
- mcp_proxy_adapter/examples/generate_all_certificates.py +0 -487
- mcp_proxy_adapter/examples/generate_certificates_cli.py +0 -406
- mcp_proxy_adapter/examples/generate_certificates_fixed.py +0 -313
- mcp_proxy_adapter/examples/generate_certificates_framework.py +0 -366
- mcp_proxy_adapter/examples/generate_certificates_openssl.py +0 -391
- {mcp_proxy_adapter-6.6.0.dist-info → mcp_proxy_adapter-6.6.3.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.6.0.dist-info → mcp_proxy_adapter-6.6.3.dist-info}/entry_points.txt +0 -0
- {mcp_proxy_adapter-6.6.0.dist-info → mcp_proxy_adapter-6.6.3.dist-info}/top_level.txt +0 -0
mcp_proxy_adapter/api/app.py
CHANGED
@@ -88,11 +88,11 @@ def create_lifespan(config_path: Optional[str] = None):
|
|
88
88
|
public_host = reg_cfg.get("public_host")
|
89
89
|
public_port = reg_cfg.get("public_port")
|
90
90
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
protocol = "https" if
|
91
|
+
# Check SSL configuration from new structure
|
92
|
+
protocol = config.get("server.protocol", "http")
|
93
|
+
verify_client = config.get("transport.verify_client", False)
|
94
|
+
ssl_enabled = protocol in ["https", "mtls"] or verify_client
|
95
|
+
protocol = "https" if ssl_enabled else "http"
|
96
96
|
|
97
97
|
import os
|
98
98
|
docker_host_addr = os.getenv("DOCKER_HOST_ADDR", "172.17.0.1")
|
@@ -227,20 +227,26 @@ def create_ssl_context(
|
|
227
227
|
"""
|
228
228
|
current_config = app_config if app_config is not None else config.get_all()
|
229
229
|
|
230
|
-
#
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
# Fallback to legacy SSL config
|
235
|
-
if not ssl_config.get("enabled", False):
|
236
|
-
ssl_config = current_config.get("ssl", {})
|
230
|
+
# Check SSL configuration from new structure
|
231
|
+
protocol = current_config.get("server", {}).get("protocol", "http")
|
232
|
+
verify_client = current_config.get("transport", {}).get("verify_client", False)
|
233
|
+
ssl_enabled = protocol in ["https", "mtls"] or verify_client
|
237
234
|
|
238
|
-
if not
|
235
|
+
if not ssl_enabled:
|
239
236
|
logger.info("SSL is disabled in configuration")
|
240
237
|
return None
|
241
238
|
|
242
|
-
|
243
|
-
|
239
|
+
# Get certificate paths from configuration
|
240
|
+
cert_file = current_config.get("ssl", {}).get("cert_file")
|
241
|
+
key_file = current_config.get("ssl", {}).get("key_file")
|
242
|
+
|
243
|
+
# If not found in ssl section, use default paths
|
244
|
+
if not cert_file:
|
245
|
+
project_root = Path(__file__).parent.parent.parent
|
246
|
+
cert_file = str(project_root / "certs" / "server.crt")
|
247
|
+
if not key_file:
|
248
|
+
project_root = Path(__file__).parent.parent.parent
|
249
|
+
key_file = str(project_root / "certs" / "server.key")
|
244
250
|
|
245
251
|
if not cert_file or not key_file:
|
246
252
|
logger.warning("SSL enabled but certificate or key file not specified")
|
@@ -308,17 +314,13 @@ def create_app(
|
|
308
314
|
print(
|
309
315
|
f"🔍 Debug: create_app received app_config keys: {list(app_config.keys())}"
|
310
316
|
)
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
)
|
319
|
-
print(
|
320
|
-
f"🔍 Debug: create_app SSL config: key_file={ssl_config.get('key_file')}"
|
321
|
-
)
|
317
|
+
# Debug SSL configuration
|
318
|
+
protocol = app_config.get("server", {}).get("protocol", "http")
|
319
|
+
verify_client = app_config.get("transport", {}).get("verify_client", False)
|
320
|
+
ssl_enabled = protocol in ["https", "mtls"] or verify_client
|
321
|
+
print(f"🔍 Debug: create_app SSL config: enabled={ssl_enabled}")
|
322
|
+
print(f"🔍 Debug: create_app protocol: {protocol}")
|
323
|
+
print(f"🔍 Debug: create_app verify_client: {verify_client}")
|
322
324
|
else:
|
323
325
|
print(f"🔍 Debug: create_app received app_config type: {type(app_config)}")
|
324
326
|
else:
|
mcp_proxy_adapter/config.py
CHANGED
@@ -84,15 +84,8 @@ class Config:
|
|
84
84
|
"transport": {
|
85
85
|
"type": "http",
|
86
86
|
"port": None,
|
87
|
-
"
|
88
|
-
|
89
|
-
"cert_file": None,
|
90
|
-
"key_file": None,
|
91
|
-
"ca_cert": None,
|
92
|
-
"verify_client": False,
|
93
|
-
"client_cert_required": False,
|
94
|
-
"chk_hostname": False, # Default to False when SSL is disabled
|
95
|
-
},
|
87
|
+
"verify_client": False,
|
88
|
+
"chk_hostname": False, # Default to False for HTTP
|
96
89
|
},
|
97
90
|
"proxy_registration": {
|
98
91
|
"enabled": False,
|
@@ -65,7 +65,7 @@ class ServerConfigAdapter:
|
|
65
65
|
|
66
66
|
# Map verification mode
|
67
67
|
if ssl_config.get("verify_client", False):
|
68
|
-
hypercorn_ssl["verify_mode"] =
|
68
|
+
hypercorn_ssl["verify_mode"] = 2 # ssl.CERT_REQUIRED
|
69
69
|
|
70
70
|
# Map hostname checking
|
71
71
|
if "chk_hostname" in ssl_config:
|
@@ -0,0 +1,415 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Configuration Checker CLI for MCP Proxy Adapter
|
4
|
+
Validates configuration files and checks for common issues.
|
5
|
+
|
6
|
+
Author: Vasiliy Zdanovskiy
|
7
|
+
email: vasilyvz@gmail.com
|
8
|
+
"""
|
9
|
+
|
10
|
+
import argparse
|
11
|
+
import json
|
12
|
+
import sys
|
13
|
+
import os
|
14
|
+
from pathlib import Path
|
15
|
+
from typing import Dict, Any, List, Optional
|
16
|
+
|
17
|
+
# Add the project root to the path
|
18
|
+
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
|
19
|
+
|
20
|
+
from mcp_proxy_adapter.config import Config
|
21
|
+
from mcp_proxy_adapter.examples.config_builder import ConfigBuilder, Protocol, AuthMethod
|
22
|
+
|
23
|
+
|
24
|
+
class ConfigChecker:
|
25
|
+
"""Configuration checker with validation and analysis."""
|
26
|
+
|
27
|
+
def __init__(self):
|
28
|
+
self.errors: List[str] = []
|
29
|
+
self.warnings: List[str] = []
|
30
|
+
self.info: List[str] = []
|
31
|
+
|
32
|
+
def check_config_file(self, config_path: str) -> bool:
|
33
|
+
"""
|
34
|
+
Check a configuration file for issues.
|
35
|
+
|
36
|
+
Args:
|
37
|
+
config_path: Path to configuration file
|
38
|
+
|
39
|
+
Returns:
|
40
|
+
True if config is valid, False otherwise
|
41
|
+
"""
|
42
|
+
self.errors.clear()
|
43
|
+
self.warnings.clear()
|
44
|
+
self.info.clear()
|
45
|
+
|
46
|
+
print(f"🔍 Checking configuration: {config_path}")
|
47
|
+
print("=" * 60)
|
48
|
+
|
49
|
+
# Check if file exists
|
50
|
+
if not os.path.exists(config_path):
|
51
|
+
self.errors.append(f"Configuration file not found: {config_path}")
|
52
|
+
return False
|
53
|
+
|
54
|
+
# Check if file is readable
|
55
|
+
if not os.access(config_path, os.R_OK):
|
56
|
+
self.errors.append(f"Configuration file is not readable: {config_path}")
|
57
|
+
return False
|
58
|
+
|
59
|
+
try:
|
60
|
+
# Load and parse JSON
|
61
|
+
with open(config_path, 'r', encoding='utf-8') as f:
|
62
|
+
config_data = json.load(f)
|
63
|
+
except json.JSONDecodeError as e:
|
64
|
+
self.errors.append(f"Invalid JSON format: {e}")
|
65
|
+
return False
|
66
|
+
except Exception as e:
|
67
|
+
self.errors.append(f"Error reading configuration file: {e}")
|
68
|
+
return False
|
69
|
+
|
70
|
+
# Load with Config class
|
71
|
+
try:
|
72
|
+
config = Config(config_path)
|
73
|
+
except Exception as e:
|
74
|
+
self.errors.append(f"Error loading configuration with Config class: {e}")
|
75
|
+
return False
|
76
|
+
|
77
|
+
# Perform checks
|
78
|
+
self._check_basic_structure(config_data)
|
79
|
+
self._check_server_config(config)
|
80
|
+
self._check_security_config(config)
|
81
|
+
self._check_ssl_config(config)
|
82
|
+
self._check_logging_config(config)
|
83
|
+
self._check_file_paths(config_path, config)
|
84
|
+
self._check_port_availability(config)
|
85
|
+
|
86
|
+
# Print results
|
87
|
+
self._print_results()
|
88
|
+
|
89
|
+
return len(self.errors) == 0
|
90
|
+
|
91
|
+
def _check_basic_structure(self, config_data: Dict[str, Any]) -> None:
|
92
|
+
"""Check basic configuration structure."""
|
93
|
+
required_sections = ["server", "security", "logging", "transport"]
|
94
|
+
|
95
|
+
for section in required_sections:
|
96
|
+
if section not in config_data:
|
97
|
+
self.errors.append(f"Missing required section: {section}")
|
98
|
+
else:
|
99
|
+
self.info.append(f"✅ Section '{section}' present")
|
100
|
+
|
101
|
+
def _check_server_config(self, config: Config) -> None:
|
102
|
+
"""Check server configuration."""
|
103
|
+
protocol = config.get("server.protocol")
|
104
|
+
host = config.get("server.host")
|
105
|
+
port = config.get("server.port")
|
106
|
+
|
107
|
+
if not protocol:
|
108
|
+
self.errors.append("Server protocol not specified")
|
109
|
+
elif protocol not in ["http", "https", "mtls"]:
|
110
|
+
self.errors.append(f"Invalid server protocol: {protocol}")
|
111
|
+
else:
|
112
|
+
self.info.append(f"✅ Server protocol: {protocol}")
|
113
|
+
|
114
|
+
if not host:
|
115
|
+
self.warnings.append("Server host not specified, using default")
|
116
|
+
else:
|
117
|
+
self.info.append(f"✅ Server host: {host}")
|
118
|
+
|
119
|
+
if not port:
|
120
|
+
self.errors.append("Server port not specified")
|
121
|
+
elif not isinstance(port, int) or port < 1 or port > 65535:
|
122
|
+
self.errors.append(f"Invalid server port: {port}")
|
123
|
+
else:
|
124
|
+
self.info.append(f"✅ Server port: {port}")
|
125
|
+
|
126
|
+
def _check_security_config(self, config: Config) -> None:
|
127
|
+
"""Check security configuration."""
|
128
|
+
security_enabled = config.get("security.enabled", False)
|
129
|
+
tokens = config.get("security.tokens", {})
|
130
|
+
roles = config.get("security.roles", {})
|
131
|
+
roles_file = config.get("security.roles_file")
|
132
|
+
|
133
|
+
if security_enabled:
|
134
|
+
self.info.append("✅ Security enabled")
|
135
|
+
|
136
|
+
if not tokens and not roles and not roles_file:
|
137
|
+
self.warnings.append("Security enabled but no authentication methods configured")
|
138
|
+
else:
|
139
|
+
if tokens:
|
140
|
+
self.info.append(f"✅ Tokens configured: {len(tokens)} tokens")
|
141
|
+
if roles:
|
142
|
+
self.info.append(f"✅ Roles configured: {len(roles)} roles")
|
143
|
+
if roles_file:
|
144
|
+
self.info.append(f"✅ Roles file: {roles_file}")
|
145
|
+
else:
|
146
|
+
self.info.append("ℹ️ Security disabled")
|
147
|
+
|
148
|
+
def _check_ssl_config(self, config: Config) -> None:
|
149
|
+
"""Check SSL configuration."""
|
150
|
+
protocol = config.get("server.protocol")
|
151
|
+
chk_hostname = config.get("transport.ssl.chk_hostname")
|
152
|
+
|
153
|
+
if protocol in ["https", "mtls"]:
|
154
|
+
self.info.append(f"✅ SSL protocol: {protocol}")
|
155
|
+
|
156
|
+
if chk_hostname is None:
|
157
|
+
self.warnings.append("chk_hostname not specified for SSL protocol")
|
158
|
+
elif chk_hostname:
|
159
|
+
self.info.append("✅ Hostname checking enabled")
|
160
|
+
else:
|
161
|
+
self.warnings.append("Hostname checking disabled for SSL protocol")
|
162
|
+
else:
|
163
|
+
if chk_hostname:
|
164
|
+
self.warnings.append("Hostname checking enabled for non-SSL protocol")
|
165
|
+
else:
|
166
|
+
self.info.append("✅ Hostname checking disabled for HTTP")
|
167
|
+
|
168
|
+
def _check_logging_config(self, config: Config) -> None:
|
169
|
+
"""Check logging configuration."""
|
170
|
+
log_level = config.get("logging.level")
|
171
|
+
log_file = config.get("logging.file")
|
172
|
+
log_dir = config.get("logging.log_dir")
|
173
|
+
|
174
|
+
if log_level and log_level.upper() in ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]:
|
175
|
+
self.info.append(f"✅ Log level: {log_level}")
|
176
|
+
else:
|
177
|
+
self.warnings.append(f"Invalid or missing log level: {log_level}")
|
178
|
+
|
179
|
+
if log_file:
|
180
|
+
self.info.append(f"✅ Log file: {log_file}")
|
181
|
+
|
182
|
+
if log_dir:
|
183
|
+
self.info.append(f"✅ Log directory: {log_dir}")
|
184
|
+
|
185
|
+
def _check_file_paths(self, config_path: str, config: Config) -> None:
|
186
|
+
"""Check file paths in configuration."""
|
187
|
+
config_dir = Path(config_path).parent
|
188
|
+
|
189
|
+
# Check roles file
|
190
|
+
roles_file = config.get("security.roles_file")
|
191
|
+
if roles_file:
|
192
|
+
roles_path = config_dir / roles_file
|
193
|
+
if not roles_path.exists():
|
194
|
+
self.warnings.append(f"Roles file not found: {roles_path}")
|
195
|
+
else:
|
196
|
+
self.info.append(f"✅ Roles file exists: {roles_path}")
|
197
|
+
|
198
|
+
# Check log directory
|
199
|
+
log_dir = config.get("logging.log_dir")
|
200
|
+
if log_dir:
|
201
|
+
log_path = config_dir / log_dir
|
202
|
+
if not log_path.exists():
|
203
|
+
self.warnings.append(f"Log directory not found: {log_path}")
|
204
|
+
else:
|
205
|
+
self.info.append(f"✅ Log directory exists: {log_path}")
|
206
|
+
|
207
|
+
def _check_port_availability(self, config: Config) -> None:
|
208
|
+
"""Check if the configured port is available."""
|
209
|
+
import socket
|
210
|
+
|
211
|
+
port = config.get("server.port")
|
212
|
+
if not port:
|
213
|
+
return
|
214
|
+
|
215
|
+
try:
|
216
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
217
|
+
s.bind(('localhost', port))
|
218
|
+
self.info.append(f"✅ Port {port} is available")
|
219
|
+
except OSError:
|
220
|
+
self.warnings.append(f"Port {port} is already in use")
|
221
|
+
|
222
|
+
def _print_results(self) -> None:
|
223
|
+
"""Print check results."""
|
224
|
+
if self.info:
|
225
|
+
print("\n📋 Information:")
|
226
|
+
for info in self.info:
|
227
|
+
print(f" {info}")
|
228
|
+
|
229
|
+
if self.warnings:
|
230
|
+
print("\n⚠️ Warnings:")
|
231
|
+
for warning in self.warnings:
|
232
|
+
print(f" {warning}")
|
233
|
+
|
234
|
+
if self.errors:
|
235
|
+
print("\n❌ Errors:")
|
236
|
+
for error in self.errors:
|
237
|
+
print(f" {error}")
|
238
|
+
|
239
|
+
print(f"\n📊 Summary:")
|
240
|
+
print(f" Information: {len(self.info)}")
|
241
|
+
print(f" Warnings: {len(self.warnings)}")
|
242
|
+
print(f" Errors: {len(self.errors)}")
|
243
|
+
|
244
|
+
if self.errors:
|
245
|
+
print(f" Status: ❌ FAILED")
|
246
|
+
elif self.warnings:
|
247
|
+
print(f" Status: ⚠️ WARNINGS")
|
248
|
+
else:
|
249
|
+
print(f" Status: ✅ PASSED")
|
250
|
+
|
251
|
+
|
252
|
+
def check_all_configs(config_dir: str = "./configs") -> None:
|
253
|
+
"""Check all configuration files in a directory."""
|
254
|
+
config_dir_path = Path(config_dir)
|
255
|
+
|
256
|
+
if not config_dir_path.exists():
|
257
|
+
print(f"❌ Configuration directory not found: {config_dir}")
|
258
|
+
return
|
259
|
+
|
260
|
+
config_files = list(config_dir_path.glob("*.json"))
|
261
|
+
|
262
|
+
if not config_files:
|
263
|
+
print(f"❌ No JSON configuration files found in: {config_dir}")
|
264
|
+
return
|
265
|
+
|
266
|
+
print(f"🔍 Checking {len(config_files)} configuration files in {config_dir}")
|
267
|
+
print("=" * 80)
|
268
|
+
|
269
|
+
checker = ConfigChecker()
|
270
|
+
total_checked = 0
|
271
|
+
total_passed = 0
|
272
|
+
|
273
|
+
for config_file in sorted(config_files):
|
274
|
+
if checker.check_config_file(str(config_file)):
|
275
|
+
total_passed += 1
|
276
|
+
total_checked += 1
|
277
|
+
print("\n" + "-" * 80 + "\n")
|
278
|
+
|
279
|
+
print(f"📊 Final Summary:")
|
280
|
+
print(f" Total files checked: {total_checked}")
|
281
|
+
print(f" Passed: {total_passed}")
|
282
|
+
print(f" Failed: {total_checked - total_passed}")
|
283
|
+
print(f" Success rate: {(total_passed / total_checked * 100):.1f}%")
|
284
|
+
|
285
|
+
|
286
|
+
def validate_config_syntax(config_path: str) -> bool:
|
287
|
+
"""Validate configuration syntax only."""
|
288
|
+
try:
|
289
|
+
with open(config_path, 'r', encoding='utf-8') as f:
|
290
|
+
json.load(f)
|
291
|
+
print(f"✅ Configuration syntax is valid: {config_path}")
|
292
|
+
return True
|
293
|
+
except json.JSONDecodeError as e:
|
294
|
+
print(f"❌ Invalid JSON syntax: {e}")
|
295
|
+
return False
|
296
|
+
except Exception as e:
|
297
|
+
print(f"❌ Error reading file: {e}")
|
298
|
+
return False
|
299
|
+
|
300
|
+
|
301
|
+
def compare_configs(config1_path: str, config2_path: str) -> None:
|
302
|
+
"""Compare two configuration files."""
|
303
|
+
print(f"🔍 Comparing configurations:")
|
304
|
+
print(f" Config 1: {config1_path}")
|
305
|
+
print(f" Config 2: {config2_path}")
|
306
|
+
print("=" * 60)
|
307
|
+
|
308
|
+
try:
|
309
|
+
with open(config1_path, 'r') as f:
|
310
|
+
config1 = json.load(f)
|
311
|
+
with open(config2_path, 'r') as f:
|
312
|
+
config2 = json.load(f)
|
313
|
+
except Exception as e:
|
314
|
+
print(f"❌ Error loading configurations: {e}")
|
315
|
+
return
|
316
|
+
|
317
|
+
# Compare key sections
|
318
|
+
sections_to_compare = ["server", "security", "logging", "transport"]
|
319
|
+
|
320
|
+
for section in sections_to_compare:
|
321
|
+
print(f"\n📋 Section: {section}")
|
322
|
+
|
323
|
+
if section not in config1 and section not in config2:
|
324
|
+
print(" Both configs missing this section")
|
325
|
+
continue
|
326
|
+
elif section not in config1:
|
327
|
+
print(f" ❌ Missing in {config1_path}")
|
328
|
+
continue
|
329
|
+
elif section not in config2:
|
330
|
+
print(f" ❌ Missing in {config2_path}")
|
331
|
+
continue
|
332
|
+
|
333
|
+
if config1[section] == config2[section]:
|
334
|
+
print(" ✅ Identical")
|
335
|
+
else:
|
336
|
+
print(" ⚠️ Different")
|
337
|
+
# Show differences for key fields
|
338
|
+
if section == "server":
|
339
|
+
for key in ["protocol", "host", "port"]:
|
340
|
+
val1 = config1[section].get(key)
|
341
|
+
val2 = config2[section].get(key)
|
342
|
+
if val1 != val2:
|
343
|
+
print(f" {key}: {val1} vs {val2}")
|
344
|
+
|
345
|
+
|
346
|
+
def main():
|
347
|
+
"""Main CLI function."""
|
348
|
+
parser = argparse.ArgumentParser(
|
349
|
+
description="MCP Proxy Adapter Configuration Checker",
|
350
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
351
|
+
epilog="""
|
352
|
+
Examples:
|
353
|
+
# Check a single configuration file
|
354
|
+
python check_config.py configs/http.json
|
355
|
+
|
356
|
+
# Check all configurations in a directory
|
357
|
+
python check_config.py --all
|
358
|
+
|
359
|
+
# Validate JSON syntax only
|
360
|
+
python check_config.py --syntax configs/http.json
|
361
|
+
|
362
|
+
# Compare two configurations
|
363
|
+
python check_config.py --compare configs/http.json configs/https.json
|
364
|
+
"""
|
365
|
+
)
|
366
|
+
|
367
|
+
parser.add_argument(
|
368
|
+
"config_file",
|
369
|
+
nargs="?",
|
370
|
+
help="Configuration file to check"
|
371
|
+
)
|
372
|
+
|
373
|
+
parser.add_argument(
|
374
|
+
"--all",
|
375
|
+
action="store_true",
|
376
|
+
help="Check all configuration files in ./configs directory"
|
377
|
+
)
|
378
|
+
|
379
|
+
parser.add_argument(
|
380
|
+
"--syntax",
|
381
|
+
action="store_true",
|
382
|
+
help="Validate JSON syntax only"
|
383
|
+
)
|
384
|
+
|
385
|
+
parser.add_argument(
|
386
|
+
"--compare",
|
387
|
+
nargs=2,
|
388
|
+
metavar=("CONFIG1", "CONFIG2"),
|
389
|
+
help="Compare two configuration files"
|
390
|
+
)
|
391
|
+
|
392
|
+
parser.add_argument(
|
393
|
+
"--config-dir",
|
394
|
+
default="./configs",
|
395
|
+
help="Configuration directory (default: ./configs)"
|
396
|
+
)
|
397
|
+
|
398
|
+
args = parser.parse_args()
|
399
|
+
|
400
|
+
if args.compare:
|
401
|
+
compare_configs(args.compare[0], args.compare[1])
|
402
|
+
elif args.all:
|
403
|
+
check_all_configs(args.config_dir)
|
404
|
+
elif args.config_file:
|
405
|
+
if args.syntax:
|
406
|
+
validate_config_syntax(args.config_file)
|
407
|
+
else:
|
408
|
+
checker = ConfigChecker()
|
409
|
+
checker.check_config_file(args.config_file)
|
410
|
+
else:
|
411
|
+
parser.print_help()
|
412
|
+
|
413
|
+
|
414
|
+
if __name__ == "__main__":
|
415
|
+
main()
|
@@ -77,15 +77,8 @@ class ConfigBuilder:
|
|
77
77
|
"transport": {
|
78
78
|
"type": "http",
|
79
79
|
"port": None,
|
80
|
-
"
|
81
|
-
|
82
|
-
"cert_file": None,
|
83
|
-
"key_file": None,
|
84
|
-
"ca_cert": None,
|
85
|
-
"verify_client": False,
|
86
|
-
"client_cert_required": False,
|
87
|
-
"chk_hostname": False,
|
88
|
-
},
|
80
|
+
"verify_client": False,
|
81
|
+
"chk_hostname": False
|
89
82
|
}
|
90
83
|
}
|
91
84
|
|
@@ -94,18 +87,19 @@ class ConfigBuilder:
|
|
94
87
|
self.config["server"]["protocol"] = protocol.value
|
95
88
|
|
96
89
|
if protocol == Protocol.HTTP:
|
97
|
-
# HTTP - no SSL
|
98
|
-
|
90
|
+
# HTTP - no SSL, no client verification
|
91
|
+
self.config["transport"]["verify_client"] = False
|
92
|
+
self.config["transport"]["chk_hostname"] = False
|
99
93
|
|
100
94
|
elif protocol == Protocol.HTTPS:
|
101
|
-
# HTTPS -
|
102
|
-
|
103
|
-
|
95
|
+
# HTTPS - SSL enabled, no client verification
|
96
|
+
self.config["transport"]["verify_client"] = False
|
97
|
+
self.config["transport"]["chk_hostname"] = True
|
104
98
|
|
105
99
|
elif protocol == Protocol.MTLS:
|
106
|
-
# mTLS -
|
107
|
-
|
108
|
-
|
100
|
+
# mTLS - SSL enabled, client verification required
|
101
|
+
self.config["transport"]["verify_client"] = True
|
102
|
+
self.config["transport"]["chk_hostname"] = True
|
109
103
|
|
110
104
|
return self
|
111
105
|
|
@@ -229,7 +229,18 @@ class BugfixCertificateGenerator:
|
|
229
229
|
with open(client_info["output_key"], 'w') as f:
|
230
230
|
f.write(result.private_key_pem)
|
231
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
|
+
|
232
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}")
|
233
244
|
return True
|
234
245
|
else:
|
235
246
|
self.print_error(f"Failed to generate {cert_name} certificate")
|