From dd9f2a3e578c26fd0182642f7450c121391bd6f2 Mon Sep 17 00:00:00 2001 From: ReturnFI <151555003+ReturnFI@users.noreply.github.com> Date: Thu, 16 Oct 2025 19:51:58 +0000 Subject: [PATCH] feat(nodes): Add optional obfs parameter for node management --- core/cli.py | 5 +++-- core/cli_api.py | 4 +++- core/scripts/nodes/node.py | 22 +++++++++++++++------- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/core/cli.py b/core/cli.py index 407ae19..4b1f0ab 100644 --- a/core/cli.py +++ b/core/cli.py @@ -334,10 +334,11 @@ def 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.') -def add_node(name, ip, port, sni, pinsha256): +@click.option('--obfs', required=False, type=str, help='Optional: The obfuscation key/password.') +def add_node(name, ip, port, sni, pinsha256, obfs): """Add a new external node.""" try: - output = cli_api.add_node(name, ip, sni, pinSHA256=pinsha256, port=port) + output = cli_api.add_node(name, ip, sni, pinSHA256=pinsha256, port=port, obfs=obfs) 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 87b86bf..045b2ef 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): +def add_node(name: str, ip: str, sni: Optional[str] = None, pinSHA256: Optional[str] = None, port: Optional[int] = None, obfs: Optional[str] = None): """ Adds a new external node. """ @@ -475,6 +475,8 @@ def add_node(name: str, ip: str, sni: Optional[str] = None, pinSHA256: Optional[ command.extend(['--sni', sni]) if pinSHA256: command.extend(['--pinSHA256', pinSHA256]) + if obfs: + command.extend(['--obfs', obfs]) return run_cmd(command) def delete_node(name: str): diff --git a/core/scripts/nodes/node.py b/core/scripts/nodes/node.py index 050bb43..7f697c8 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): +def add_node(name: str, ip: str, sni: str | None = None, pinSHA256: str | None = None, port: int | None = None, obfs: str | None = None): 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,6 +89,10 @@ 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) @@ -98,12 +102,14 @@ 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() nodes.append(new_node) write_nodes(nodes) @@ -127,15 +133,16 @@ def list_nodes(): print("No nodes configured.") return - print(f"{'Name':<20} {'IP / Domain':<25} {'Port':<10} {'SNI':<25} {'Pin SHA256'}") - print(f"{'-'*20} {'-'*25} {'-'*10} {'-'*25} {'-'*30}") + print(f"{'Name':<15} {'IP / Domain':<25} {'Port':<8} {'OBFS':<15} {'SNI':<20} {'Pin SHA256'}") + print(f"{'-'*15} {'-'*25} {'-'*8} {'-'*15} {'-'*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') pin = node.get('pinSHA256', 'N/A') - print(f"{name:<20} {ip:<25} {str(port):<10} {sni:<25} {pin}") + print(f"{name:<15} {ip:<25} {str(port):<8} {obfs:<15} {sni:<20} {pin}") def generate_cert(): try: @@ -198,6 +205,7 @@ def main(): 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('--pinSHA256', type=str, help='Optional: The public key SHA256 pin.') + add_parser.add_argument('--obfs', type=str, help='Optional: The obfuscation key/password.') 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.') @@ -209,7 +217,7 @@ def main(): args = parser.parse_args() if args.command == 'add': - add_node(args.name, args.ip, args.sni, args.pinSHA256, args.port) + add_node(args.name, args.ip, args.sni, args.pinSHA256, args.port, args.obfs) elif args.command == 'delete': delete_node(args.name) elif args.command == 'list':