diff --git a/core/scripts/singbox/singbox.json b/core/scripts/singbox/singbox.json deleted file mode 100644 index 30337c7..0000000 --- a/core/scripts/singbox/singbox.json +++ /dev/null @@ -1,225 +0,0 @@ -{ - "log": { - "level": "warn", - "output": "box.log", - "timestamp": true - }, - "dns": { - "servers": [ - { - "tag": "dns-remote", - "address": "udp://1.1.1.1", - "address_resolver": "dns-direct" - }, - { - "tag": "dns-direct", - "address": "1.1.1.1", - "address_resolver": "dns-local", - "detour": "direct" - }, - { - "tag": "dns-local", - "address": "local", - "detour": "direct" - }, - { - "tag": "dns-block", - "address": "rcode://success" - } - ], - "independent_cache": true - }, - "inbounds": [ - { - "type": "tun", - "tag": "tun-in", - "address": [ - "172.18.0.1/30", - "fdfe:dcba:9876::1/126" - ], - "mtu": 9000, - "auto_route": true, - "auto_redirect": false, - "strict_route": true, - "route_address": [ - "0.0.0.0/1", - "128.0.0.0/1", - "::/1", - "8000::/1" - ], - - "route_exclude_address": [ - "192.168.0.0/16", - "fc00::/7" - ], - "sniff": true, - "sniff_override_destination": true - }, - { - "type": "direct", - "tag": "dns-in", - "listen": "127.0.0.1", - "listen_port": 6450 - } - ], - "outbounds": [ - { - "type": "selector", - "tag": "select", - "outbounds": [ - "auto", - "{username}-Hysteria2" - ], - "default": "auto" - }, - { - "type": "urltest", - "tag": "auto", - "outbounds": [ - "{username}-Hysteria2" - ], - "url": "http://connectivitycheck.gstatic.com/generate_204", - "interval": "10m0s", - "idle_timeout": "1h40m0s" - }, - { - "type": "hysteria2", - "tag": "{username}-Hysteria2", - "server": "{ip}", - "server_port": "{port}", - "obfs": { - "type": "salamander", - "password": "{obfs_password}" - }, - "password": "{username}:{password}", - "tls": { - "enabled": true, - "server_name": "{sni}", - "insecure": true - } - }, - { - "type": "direct", - "tag": "direct" - }, - { - "type": "direct", - "tag": "bypass" - } - ], - "route": { - "rules": [ - { - "inbound": "tun-in", - "action": "sniff" - }, - { - "action": "reject", - "rule_set": "geosite-category-ads-all" - }, - { - "action": "reject", - "rule_set": "geosite-malware" - }, - { - "action": "reject", - "rule_set": "geosite-phishing" - }, - { - "action": "reject", - "rule_set": "geosite-cryptominers" - }, - { - "action": "reject", - "rule_set": "geoip-malware" - }, - { - "action": "reject", - "rule_set": "geoip-phishing" - }, - { - "action": "hijack-dns", - "protocol": "dns" - }, - { - "action": "route", - "rule_set": "geosite-ir", - "outbound": "direct" - }, - { - "action": "route", - "rule_set": "geoip-ir", - "outbound": "direct" - } - ], - - "rule_set": [ - { - "type": "remote", - "tag": "geosite-ir", - "format": "binary", - "url": "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geosite-ir.srs", - "download_detour": "direct", - "update_interval": "72h0m0s" - }, - { - "type": "remote", - "tag": "geosite-category-ads-all", - "format": "binary", - "url": "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geosite-category-ads-all.srs", - "download_detour": "direct", - "update_interval": "72h0m0s" - }, - { - "type": "remote", - "tag": "geosite-malware", - "format": "binary", - "url": "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geosite-malware.srs", - "download_detour": "direct", - "update_interval": "72h0m0s" - }, - { - "type": "remote", - "tag": "geosite-phishing", - "format": "binary", - "url": "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geosite-phishing.srs", - "download_detour": "direct", - "update_interval": "72h0m0s" - }, - { - "type": "remote", - "tag": "geosite-cryptominers", - "format": "binary", - "url": "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geosite-cryptominers.srs", - "download_detour": "direct", - "update_interval": "72h0m0s" - }, - { - "type": "remote", - "tag": "geoip-ir", - "format": "binary", - "url": "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geoip-ir.srs", - "download_detour": "direct", - "update_interval": "72h0m0s" - }, - { - "type": "remote", - "tag": "geoip-malware", - "format": "binary", - "url": "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geoip-malware.srs", - "download_detour": "direct", - "update_interval": "72h0m0s" - }, - { - "type": "remote", - "tag": "geoip-phishing", - "format": "binary", - "url": "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geoip-phishing.srs", - "download_detour": "direct", - "update_interval": "72h0m0s" - } - ], - "auto_detect_interface": true, - "override_android_vpn": true - } -} diff --git a/core/scripts/singbox/singbox.py b/core/scripts/singbox/singbox.py deleted file mode 100644 index 8e7cfac..0000000 --- a/core/scripts/singbox/singbox.py +++ /dev/null @@ -1,182 +0,0 @@ -import os -import ssl -import json -import subprocess -from aiohttp import web -from aiohttp.web_middlewares import middleware -from urllib.parse import unquote, parse_qs -import re -import time -import shlex -from dotenv import load_dotenv - -load_dotenv() - -# Environment variables -DOMAIN = os.getenv('HYSTERIA_DOMAIN') -CERTFILE = os.getenv('HYSTERIA_CERTFILE') -KEYFILE = os.getenv('HYSTERIA_KEYFILE') -PORT = int(os.getenv('HYSTERIA_PORT', '3324')) - -def load_sni_from_env(): - sni = "bts.com" - try: - with open('/etc/hysteria/.configs.env', 'r') as env_file: - for line in env_file: - if line.startswith('SNI='): - sni = line.strip().split('=')[1] - break - except FileNotFoundError: - print("Warning: /etc/hysteria/.configs.env not found. Using default SNI.") - return sni - -SNI = load_sni_from_env() - -RATE_LIMIT = 100 -RATE_LIMIT_WINDOW = 60 - -rate_limit_store = {} - -@middleware -async def rate_limit_middleware(request, handler): - client_ip = request.headers.get('X-Forwarded-For', request.remote) - current_time = time.time() - - if client_ip in rate_limit_store: - requests, last_request_time = rate_limit_store[client_ip] - if current_time - last_request_time < RATE_LIMIT_WINDOW: - if requests >= RATE_LIMIT: - return web.Response(status=429, text="Rate limit exceeded.") - if current_time - last_request_time >= RATE_LIMIT_WINDOW: - rate_limit_store[client_ip] = (1, current_time) - else: - rate_limit_store[client_ip] = (requests + 1, last_request_time) - else: - rate_limit_store[client_ip] = (1, current_time) - - return await handler(request) - -def sanitize_input(value, pattern): - if not re.match(pattern, value): - raise ValueError(f"Invalid value: {value}") - return shlex.quote(value) - -async def handle(request): - try: - username = sanitize_input(request.match_info.get('username', ''), r'^[a-zA-Z0-9_-]+$') - ip_version = sanitize_input(request.match_info.get('ip_version', ''), r'^[46]$') - fragment = request.query.get('fragment', '') - - if not username: - return web.Response(status=400, text="Error: Missing 'username' parameter.") - - if not ip_version: - return web.Response(status=400, text="Error: Missing 'ip' parameter.") - - if ip_version not in ['4', '6']: - return web.Response(status=400, text="Error: Invalid 'ip' parameter. Must be '4' or '6'.") - - config = generate_singbox_config(username, ip_version, fragment) - config_json = json.dumps(config, indent=4, sort_keys=True) - - return web.Response(text=config_json, content_type='application/json') - except ValueError as e: - return web.Response(status=400, text=f"Error: {str(e)}") - except Exception as e: - print(f"Internal Server Error: {str(e)}") - return web.Response(status=500, text="Error: Internal server error.") - -def generate_singbox_config(username, ip_version, fragment): - try: - username = sanitize_input(username, r'^[a-zA-Z0-9_-]+$') - ip_version = sanitize_input(ip_version, r'^[46]$') - - command = [ - 'python3', - '/etc/hysteria/core/cli.py', - 'show-user-uri', - '-u', username, - '-ip', ip_version - ] - - uri = subprocess.check_output(command).decode().strip() - except subprocess.CalledProcessError: - raise RuntimeError("Failed to get URI.") - - if ip_version == '4': - components = extract_uri_components(uri, 'IPv4:') - else: - components = extract_uri_components(uri, 'IPv6:') - - config = load_singbox_template() - hysteria_tag = f"{username}-Hysteria2" - config['outbounds'][2]['tag'] = hysteria_tag - config['outbounds'][2]['server'] = components['ip'] - config['outbounds'][2]['server_port'] = int(components['port']) - config['outbounds'][2]['obfs']['password'] = components['obfs_password'] - config['outbounds'][2]['password'] = f"{username}:{components['password']}" - - config['outbounds'][2]['tls']['server_name'] = fragment if fragment else SNI - - config['outbounds'][0]['outbounds'] = ["auto", hysteria_tag] - config['outbounds'][1]['outbounds'] = [hysteria_tag] - - return config - -def extract_uri_components(uri, prefix): - if uri.startswith(prefix): - uri = uri[len(prefix):].strip() - - decoded_uri = unquote(uri) - pattern = re.compile( - r'^hy2://([^:]+):([^@]+)@(\[?[^\]]+?\]?):(\d+)\?([^#]+)(?:#([^/]+))?$' - ) - match = pattern.match(decoded_uri) - - if not match: - raise ValueError("Could not parse URI.") - - username = match.group(1) - password = match.group(2) - ip = match.group(3) - port = match.group(4) - query_params = match.group(5) - fragment = match.group(6) - - if ip.startswith('[') and ip.endswith(']'): - ip = ip[1:-1] - - params = parse_qs(query_params) - obfs_password = params.get('obfs-password', [''])[0] - - return { - 'username': username, - 'password': password, - 'ip': ip, - 'port': port, - 'obfs_password': obfs_password, - } - -def load_singbox_template(): - try: - with open('/etc/hysteria/core/scripts/singbox/singbox.json', 'r') as f: - return json.load(f) - except IOError: - raise RuntimeError("Failed to load template.") - -async def handle_404(request): - print(f"404 Not Found: {request.path}") - return web.Response(status=404, text="Not Found") - -if __name__ == '__main__': - app = web.Application(middlewares=[rate_limit_middleware]) - - app.add_routes([web.get('/sub/singbox/{username}/{ip_version}', handle)]) - app.router.add_route('*', '/sub/singbox/{tail:.*}', handle_404) - - ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) - ssl_context.load_cert_chain(certfile=CERTFILE, keyfile=KEYFILE) - ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2 - ssl_context.set_ciphers('AES256+EECDH:AES256+EDH') - - web.run_app(app, port=PORT, ssl_context=ssl_context) diff --git a/core/scripts/singbox/singbox_shell.sh b/core/scripts/singbox/singbox_shell.sh deleted file mode 100644 index 9f0d16c..0000000 --- a/core/scripts/singbox/singbox_shell.sh +++ /dev/null @@ -1,126 +0,0 @@ -#!/bin/bash -source /etc/hysteria/core/scripts/utils.sh -define_colors - -# install_dependencies() { -# echo "Installing necessary dependencies..." -# apt-get install certbot -y > /dev/null 2>&1 -# if [ $? -ne 0 ]; then -# echo -e "${red}Error: Failed to install certbot. ${NC}" -# exit 1 -# fi -# echo -e "${green}Certbot installed successfully. ${NC}" -# } - -update_env_file() { - local domain=$1 - local port=$2 - local cert_dir="/etc/letsencrypt/live/$domain" - - cat < /etc/hysteria/core/scripts/singbox/.env -HYSTERIA_DOMAIN=$domain -HYSTERIA_PORT=$port -HYSTERIA_CERTFILE=$cert_dir/fullchain.pem -HYSTERIA_KEYFILE=$cert_dir/privkey.pem -EOL -} - -create_service_file() { - cat < /etc/systemd/system/hysteria-singbox.service -[Unit] -Description=Singbox Python Service -After=network.target - -[Service] -ExecStart=/bin/bash -c 'source /etc/hysteria/hysteria2_venv/bin/activate && /etc/hysteria/hysteria2_venv/bin/python /etc/hysteria/core/scripts/singbox/singbox.py' -WorkingDirectory=/etc/hysteria/core/scripts/singbox -EnvironmentFile=/etc/hysteria/core/scripts/singbox/.env -Restart=always -User=root -Group=root - -[Install] -WantedBy=multi-user.target -EOL -} - -start_service() { - local domain=$1 - local port=$2 - - if systemctl is-active --quiet hysteria-singbox.service; then - echo "The hysteria-singbox.service is already running." - return - fi - - # install_dependencies - # systemctl stop caddy.service > /dev/null 2>&1 # We stopped caddy service just after its installation - - echo "Generating SSL certificates for $domain..." - certbot certonly --standalone --agree-tos --register-unsafely-without-email -d "$domain" - if [ $? -ne 0 ]; then - echo -e "${red}Error: Failed to generate SSL certificates. ${NC}" - exit 1 - fi - - update_env_file "$domain" "$port" - create_service_file - if [ $? -ne 0 ]; then - echo -e "${red}Error: Failed to create the service file. ${NC}" - exit 1 - fi - chown -R hysteria:hysteria "/etc/letsencrypt/live/$domain" - chown -R hysteria:hysteria /etc/hysteria/core/scripts/singbox - systemctl daemon-reload - systemctl enable hysteria-singbox.service > /dev/null 2>&1 - systemctl start hysteria-singbox.service > /dev/null 2>&1 - # systemctl restart caddy.service > /dev/null 2>&1 # We stopped caddy service just after its installation - systemctl daemon-reload > /dev/null 2>&1 - - if systemctl is-active --quiet hysteria-singbox.service; then - echo -e "${green}Singbox service setup completed. The service is now running on port $port. ${NC}" - else - echo -e "${red}Singbox setup completed. The service failed to start. ${NC}" - fi -} - -stop_service() { - if [ -f /etc/hysteria/core/scripts/singbox/.env ]; then - source /etc/hysteria/core/scripts/singbox/.env - fi - - if [ -n "$HYSTERIA_DOMAIN" ]; then - echo -e "${yellow}Deleting SSL certificate for domain: $HYSTERIA_DOMAIN...${NC}" - certbot delete --cert-name "$HYSTERIA_DOMAIN" --non-interactive > /dev/null 2>&1 - else - echo -e "${red}HYSTERIA_DOMAIN not found in .env. Skipping certificate deletion.${NC}" - fi - - systemctl stop hysteria-singbox.service > /dev/null 2>&1 - systemctl disable hysteria-singbox.service > /dev/null 2>&1 - systemctl daemon-reload > /dev/null 2>&1 - - rm -f /etc/hysteria/core/scripts/singbox/.env - - echo -e "${yellow}Singbox service stopped and disabled. .env file removed.${NC}" -} - - -case "$1" in - start) - if [ -z "$2" ] || [ -z "$3" ]; then - echo -e "${red}Usage: $0 start ${NC}" - exit 1 - fi - start_service "$2" "$3" - ;; - stop) - stop_service - ;; - *) - echo -e "${red}Usage: $0 {start|stop} ${NC}" - exit 1 - ;; -esac - -define_colors diff --git a/menu.sh b/menu.sh index 2c4ea0e..3e37b7d 100644 --- a/menu.sh +++ b/menu.sh @@ -601,56 +601,7 @@ telegram_bot_handler() { } singbox_handler() { - while true; do - echo -e "${cyan}Merged with Normal-Sub sublink.${NC}" - # echo -e "${cyan}1.${NC} Start Singbox service" - echo -e "${red}2.${NC} Stop Singbox service" - echo "0. Back" - read -p "Choose an option: " option - - case $option in - # 1) - # if systemctl is-active --quiet hysteria-singbox.service; then - # echo "The hysteria-singbox.service is already active." - # else - # while true; do - # read -e -p "Enter the domain name for the SSL certificate: " domain - # if [ -z "$domain" ]; then - # echo "Domain name cannot be empty. Please try again." - # else - # break - # fi - # done - - # while true; do - # read -e -p "Enter the port number for the service: " port - # if [ -z "$port" ]; then - # echo "Port number cannot be empty. Please try again." - # elif ! [[ "$port" =~ ^[0-9]+$ ]]; then - # echo "Port must be a number. Please try again." - # else - # break - # fi - # done - - # python3 $CLI_PATH singbox -a start -d "$domain" -p "$port" - # fi - # ;; - 2) - if ! systemctl is-active --quiet hysteria-singbox.service; then - echo "The hysteria-singbox.service is already inactive." - else - python3 $CLI_PATH singbox -a stop - fi - ;; - 0) - break - ;; - *) - echo "Invalid option. Please try again." - ;; - esac - done + echo -e "${red} Deprecated${NC}" } normalsub_handler() {