From 1f1a74d319b6ca584d02f919836143962047f809 Mon Sep 17 00:00:00 2001 From: Anya Lin Date: Sun, 19 Nov 2023 13:06:29 +0800 Subject: [PATCH] feat(client): Safety first --- .../resources/view/homeproxy/client.js | 26 ++++++++++++++++--- root/etc/homeproxy/scripts/generate_client.uc | 3 ++- root/etc/init.d/homeproxy | 11 ++++++++ root/etc/nginx/conf.d/homeproxy.locations | 11 ++++++++ root/usr/share/rpcd/ucode/luci.homeproxy | 1 + 5 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 root/etc/nginx/conf.d/homeproxy.locations diff --git a/htdocs/luci-static/resources/view/homeproxy/client.js b/htdocs/luci-static/resources/view/homeproxy/client.js index 7377a394..30776b53 100644 --- a/htdocs/luci-static/resources/view/homeproxy/client.js +++ b/htdocs/luci-static/resources/view/homeproxy/client.js @@ -56,11 +56,13 @@ function getServiceStatus() { } function renderStatus(isRunning, args) { + let nginx = args.features.hp_has_nginx && args.nginx_support === '1'; var spanTemp = '%s %s'; var renderHTML; if (isRunning) { var button = String.format(' %s', - 'http://' + window.location.hostname + ':' + args.api_port + '/ui/' + '?hostname=' + window.location.hostname + '&port=' + args.api_port + '&secret=' + args.api_secret, _('Open Clash Dashboard')); + (nginx ? 'https:' : 'http:') + '//' + window.location.hostname + + (nginx ? '/homeproxy' : ':' + args.api_port) + '/ui/', _('Open Clash Dashboard')); renderHTML = spanTemp.format('green', _('HomeProxy'), _('RUNNING')) + button; } else renderHTML = spanTemp.format('red', _('HomeProxy'), _('NOT RUNNING')); @@ -116,7 +118,8 @@ return view.extend({ var features = data[1], hosts = data[2]?.hosts, api_port = uci.get(data[0], 'experimental', 'clash_api_port'), - api_secret = data[3]?.secret || ''; + api_secret = data[3]?.secret || '', + nginx_support = uci.get(data[0], 'experimental', 'nginx_support') || '0'; m = new form.Map('homeproxy', _('HomeProxy'), _('The modern ImmortalWrt proxy platform for ARM64/AMD64.')); @@ -126,7 +129,7 @@ return view.extend({ poll.add(function () { return L.resolveDefault(getServiceStatus()).then((res) => { var view = document.getElementById('service_status'); - view.innerHTML = renderStatus(res, {api_port, api_secret}); + view.innerHTML = renderStatus(res, {features, nginx_support, api_port, api_secret}); }); }); @@ -874,11 +877,28 @@ return view.extend({ so = ss.option(form.Flag, 'clash_api_enabled', _('Enable Clash API')); so.default = so.disabled; + so = ss.option(form.Flag, 'nginx_support', _('Nginx Support')); + so.rmempty = true; + if (! features.hp_has_nginx) { + so.description = _('To enable this feature you need install luci-nginx and luci-ssl-nginx
first'); + so.readonly = true; + } + so.write = function(section_id, value) { + return uci.set(data[0], section_id, 'nginx_support', features.hp_has_nginx ? value : null); + } + so = ss.option(form.ListValue, 'dashboard_repo', _('Select Clash Dashboard')); so.value('', _('Use Online Dashboard')); so.value('metacubex/yacd-meta', _('yacd-meta')); so.value('metacubex/metacubexd', _('metacubexd')); so.default = ''; + if (features.hp_has_nginx && nginx_support === '1') { + so.description = _('The current API URL is %s') + .format('https://' + window.location.hostname + '/homeproxy/'); + } else { + so.description = _('The current API URL is %s') + .format('http://' + window.location.hostname + ':' + api_port); + } so = ss.option(form.Value, 'clash_api_port', _('Port')); so.datatype = "and(port, min(1))"; diff --git a/root/etc/homeproxy/scripts/generate_client.uc b/root/etc/homeproxy/scripts/generate_client.uc index eac438a8..aaa02894 100755 --- a/root/etc/homeproxy/scripts/generate_client.uc +++ b/root/etc/homeproxy/scripts/generate_client.uc @@ -82,6 +82,7 @@ const proxy_mode = uci.get(uciconfig, ucimain, 'proxy_mode') || 'redirect_tproxy default_interface = uci.get(uciconfig, ucicontrol, 'bind_interface'); const clash_api_enabled = uci.get(uciconfig, uciexp, 'clash_api_enabled'), + nginx_support = uci.get(uciconfig, uciexp, 'nginx_support'), dashboard_repo = uci.get(uciconfig, uciexp, 'dashboard_repo'), clash_api_port = uci.get(uciconfig, uciexp, 'clash_api_port') || '9090', clash_api_secret = uci.get(uciconfig, uciexp, 'clash_api_secret') || trim(readfile('/proc/sys/kernel/random/uuid')); @@ -624,7 +625,7 @@ if (dashboard_repo) { } config.experimental = { clash_api: { - external_controller: (clash_api_enabled === '1') ? '[::]:'+ clash_api_port : null, + external_controller: (clash_api_enabled === '1') ? (nginx_support ? '[::1]:' : '[::]:') + clash_api_port : null, external_ui: dashboard_repo ? RUN_DIR + '/ui' : null, secret: clash_api_secret, store_mode: true, diff --git a/root/etc/init.d/homeproxy b/root/etc/init.d/homeproxy index 7b13b826..ae904e46 100755 --- a/root/etc/init.d/homeproxy +++ b/root/etc/init.d/homeproxy @@ -15,6 +15,7 @@ HP_DIR="/etc/homeproxy" RUN_DIR="/var/run/homeproxy" LOG_PATH="$RUN_DIR/homeproxy.log" DNSMASQ_DIR="/tmp/dnsmasq.d/dnsmasq-homeproxy.d" +APILOCATION_PATH="/etc/nginx/conf.d/homeproxy.locations" log() { echo -e "$(date "+%Y-%m-%d %H:%M:%S") [DAEMON] $*" >> "$LOG_PATH" @@ -64,6 +65,16 @@ start_service() { /etc/init.d/cron restart fi + # Clash API uses Nginx reverse proxy + local clash_api_enabled clash_api_port nginx_support + config_get_bool clash_api_enabled "experimental" "clash_api_enabled" "0" + config_get_bool nginx_support "experimental" "nginx_support" "0" + if [ "$clash_api_enabled" = "1" -a "$nginx_support" = "1" ]; then + config_get clash_api_port "experimental" "clash_api_port" "9090" + [ "$(sed -En "s|^\s*proxy_pass\s+https?://[^:]+:(\d+).*|\1|p" "$APILOCATION_PATH")" = "$clash_api_port" ] || sed -Ei "/proxy_pass/{s|(\bproxy_pass\b)(\s+.+)?|\1 http://localhost:$clash_api_port/;|}" "$APILOCATION_PATH" + /etc/init.d/nginx reload + fi + # DNSMasq rules local ipv6_support config_get_bool ipv6_support "config" "ipv6_support" "0" diff --git a/root/etc/nginx/conf.d/homeproxy.locations b/root/etc/nginx/conf.d/homeproxy.locations new file mode 100644 index 00000000..7cfe9730 --- /dev/null +++ b/root/etc/nginx/conf.d/homeproxy.locations @@ -0,0 +1,11 @@ +location /homeproxy/ { + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_pass http://localhost:9090/; + proxy_redirect default; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection upgrade; + proxy_set_header Connection "keep-alive"; +} diff --git a/root/usr/share/rpcd/ucode/luci.homeproxy b/root/usr/share/rpcd/ucode/luci.homeproxy index fdff9c95..7d2d86de 100755 --- a/root/usr/share/rpcd/ucode/luci.homeproxy +++ b/root/usr/share/rpcd/ucode/luci.homeproxy @@ -180,6 +180,7 @@ const methods = { features.hp_has_tcp_brutal = hasKernelModule('brutal.ko'); features.hp_has_tproxy = hasKernelModule('nft_tproxy.ko') || access('/etc/modules.d/nft-tproxy'); features.hp_has_tun = hasKernelModule('tun.ko') || access('/etc/modules.d/30-tun'); + features.hp_has_nginx = access('/usr/sbin/nginx'); return features; }