From 8cfed0f1b422372bf19a38b9c4868c689efd54fe Mon Sep 17 00:00:00 2001 From: ReturnFI <151555003+ReturnFI@users.noreply.github.com> Date: Thu, 23 Oct 2025 11:07:50 +0000 Subject: [PATCH] feat(nodes): Add insecure TLS option for external nodes --- core/cli.py | 17 ++++++----- core/cli_api.py | 4 ++- core/scripts/nodes/node.py | 30 +++++++++---------- core/scripts/webpanel/assets/js/settings.js | 11 +++---- .../webpanel/routers/api/v1/config/ip.py | 5 ++-- .../routers/api/v1/schema/config/ip.py | 1 + core/scripts/webpanel/templates/settings.html | 9 ++++++ 7 files changed, 44 insertions(+), 33 deletions(-) diff --git a/core/cli.py b/core/cli.py index 4b1f0ab..9522b49 100644 --- a/core/cli.py +++ b/core/cli.py @@ -329,16 +329,17 @@ def node(): pass @node.command('add') -@click.option('--name', required=True, type=str, help='A unique name for the node (e.g., "Node-DE").') -@click.option('--ip', required=True, type=str, help='The public IP address of the node.') -@click.option('--port', required=False, type=int, help='Optional: The port of the node.') -@click.option('--sni', required=False, type=str, help='Optional: The Server Name Indication.') -@click.option('--pinSHA256', required=False, type=str, help='Optional: The public key SHA256 pin.') -@click.option('--obfs', required=False, type=str, help='Optional: The obfuscation key/password.') -def add_node(name, ip, port, sni, pinsha256, obfs): +@click.option('--name', required=True, type=str, help='Unique name for the node.') +@click.option('--ip', required=True, type=str, help='Public IP address of the node.') +@click.option('--port', required=False, type=int, help='Optional: Port of the node.') +@click.option('--sni', required=False, type=str, help='Optional: Server Name Indication.') +@click.option('--pinSHA256', required=False, type=str, help='Optional: Public key SHA256 pin.') +@click.option('--obfs', required=False, type=str, help='Optional: Obfuscation key.') +@click.option('--insecure', is_flag=True, default=False, help='Optional: Skip certificate verification.') +def add_node(name, ip, port, sni, pinsha256, obfs, insecure): """Add a new external node.""" try: - output = cli_api.add_node(name, ip, sni, pinSHA256=pinsha256, port=port, obfs=obfs) + output = cli_api.add_node(name, ip, sni, pinSHA256=pinsha256, port=port, obfs=obfs, insecure=insecure) click.echo(output.strip()) except Exception as e: click.echo(f'{e}', err=True) diff --git a/core/cli_api.py b/core/cli_api.py index 045b2ef..5641d22 100644 --- a/core/cli_api.py +++ b/core/cli_api.py @@ -464,7 +464,7 @@ def edit_ip_address(ipv4: str, ipv6: str): if ipv6: run_cmd(['python3', Command.IP_ADD.value, 'edit', '-6', ipv6]) -def add_node(name: str, ip: str, sni: Optional[str] = None, pinSHA256: Optional[str] = None, port: Optional[int] = None, obfs: Optional[str] = None): +def add_node(name: str, ip: str, sni: Optional[str] = None, pinSHA256: Optional[str] = None, port: Optional[int] = None, obfs: Optional[str] = None, insecure: Optional[bool] = None): """ Adds a new external node. """ @@ -477,6 +477,8 @@ def add_node(name: str, ip: str, sni: Optional[str] = None, pinSHA256: Optional[ command.extend(['--pinSHA256', pinSHA256]) if obfs: command.extend(['--obfs', obfs]) + if insecure: + command.append('--insecure') return run_cmd(command) def delete_node(name: str): diff --git a/core/scripts/nodes/node.py b/core/scripts/nodes/node.py index 7f697c8..c735793 100644 --- a/core/scripts/nodes/node.py +++ b/core/scripts/nodes/node.py @@ -72,7 +72,7 @@ def write_nodes(nodes): except (IOError, OSError) as e: sys.exit(f"Error writing to {NODES_JSON_PATH}: {e}") -def add_node(name: str, ip: str, sni: str | None = None, pinSHA256: str | None = None, port: int | None = None, obfs: str | None = None): +def add_node(name: str, ip: str, sni: str | None = None, pinSHA256: str | None = None, port: int | None = None, obfs: str | None = None, insecure: bool = False): if not is_valid_ip_or_domain(ip): print(f"Error: '{ip}' is not a valid IP address or domain name.", file=sys.stderr) sys.exit(1) @@ -89,10 +89,6 @@ def add_node(name: str, ip: str, sni: str | None = None, pinSHA256: str | None = print(f"Error: Port '{port}' must be between 1 and 65535.", file=sys.stderr) sys.exit(1) - if obfs and not obfs.strip(): - print(f"Error: OBFS value cannot be empty or just whitespace.", file=sys.stderr) - sys.exit(1) - nodes = read_nodes() if any(node['name'] == name for node in nodes): print(f"Error: A node with the name '{name}' already exists.", file=sys.stderr) @@ -102,14 +98,16 @@ def add_node(name: str, ip: str, sni: str | None = None, pinSHA256: str | None = sys.exit(1) new_node = {"name": name, "ip": ip} - if port: - new_node["port"] = port if sni: new_node["sni"] = sni.strip() if pinSHA256: new_node["pinSHA256"] = pinSHA256.strip().upper() + if port: + new_node["port"] = port if obfs: new_node["obfs"] = obfs.strip() + if insecure: + new_node["insecure"] = insecure nodes.append(new_node) write_nodes(nodes) @@ -133,16 +131,17 @@ def list_nodes(): print("No nodes configured.") return - print(f"{'Name':<15} {'IP / Domain':<25} {'Port':<8} {'OBFS':<15} {'SNI':<20} {'Pin SHA256'}") - print(f"{'-'*15} {'-'*25} {'-'*8} {'-'*15} {'-'*20} {'-'*30}") + print(f"{'Name':<15} {'IP / Domain':<25} {'Port':<8} {'SNI':<20} {'Insecure':<10} {'OBFS':<20} {'Pin SHA256'}") + print(f"{'-'*15} {'-'*25} {'-'*8} {'-'*20} {'-'*10} {'-'*20} {'-'*30}") for node in sorted(nodes, key=lambda x: x['name']): name = node['name'] ip = node['ip'] port = node.get('port', 'N/A') - obfs = node.get('obfs', 'N/A') sni = node.get('sni', 'N/A') + insecure = str(node.get('insecure', 'False')) + obfs = node.get('obfs', 'N/A') pin = node.get('pinSHA256', 'N/A') - print(f"{name:<15} {ip:<25} {str(port):<8} {obfs:<15} {sni:<20} {pin}") + print(f"{name:<15} {ip:<25} {str(port):<8} {sni:<20} {insecure:<10} {obfs:<20} {pin}") def generate_cert(): try: @@ -203,21 +202,22 @@ def main(): add_parser.add_argument('--name', type=str, required=True, help='The unique name of the node.') add_parser.add_argument('--ip', type=str, required=True, help='The IP address or domain of the node.') add_parser.add_argument('--port', type=int, help='Optional: The port of the node.') - add_parser.add_argument('--sni', type=str, help='Optional: The Server Name Indication (e.g., yourdomain.com).') + add_parser.add_argument('--sni', type=str, help='Optional: The Server Name Indication.') add_parser.add_argument('--pinSHA256', type=str, help='Optional: The public key SHA256 pin.') - add_parser.add_argument('--obfs', type=str, help='Optional: The obfuscation key/password.') + add_parser.add_argument('--obfs', type=str, help='Optional: The obfuscation key.') + add_parser.add_argument('--insecure', action='store_true', help='Optional: Skip certificate verification.') delete_parser = subparsers.add_parser('delete', help='Delete a node by name.') delete_parser.add_argument('--name', type=str, required=True, help='The name of the node to delete.') subparsers.add_parser('list', help='List all configured nodes.') - subparsers.add_parser('generate-cert', help="Generate blitz.crt and blitz.key if they don't exist or are expiring soon.") + subparsers.add_parser('generate-cert', help="Generate blitz.crt and blitz.key.") args = parser.parse_args() if args.command == 'add': - add_node(args.name, args.ip, args.sni, args.pinSHA256, args.port, args.obfs) + add_node(args.name, args.ip, args.sni, args.pinSHA256, args.port, args.obfs, args.insecure) elif args.command == 'delete': delete_node(args.name) elif args.command == 'list': diff --git a/core/scripts/webpanel/assets/js/settings.js b/core/scripts/webpanel/assets/js/settings.js index d97c2a4..b818fa9 100644 --- a/core/scripts/webpanel/assets/js/settings.js +++ b/core/scripts/webpanel/assets/js/settings.js @@ -286,6 +286,7 @@ $(document).ready(function () { ${escapeHtml(node.port || 'N/A')} ${escapeHtml(node.sni || 'N/A')} ${escapeHtml(node.obfs || 'N/A')} + ${escapeHtml(node.insecure ? 'True' : 'False')} ${escapeHtml(node.pinSHA256 || 'N/A')}