diff --git a/README.md b/README.md index 44f29a4..44aa605 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,7 @@ $ clashsecret - 可通过浏览器打开 `Web` 控制台进行可视化操作,例如切换节点、查看日志等。 - `clashctl secret` 与 `clashsecret` 都支持无参数查看、有参数直接设置。 - 默认使用 [zashboard](https://github.com/Zephyruso/zashboard) 作为控制台前端,如需更换可自行配置。 +- 运行配置会把 `external-ui` 指向本地 Dashboard 目录,并把 `external-ui-url` 指向 zashboard 的 `dist.zip` 下载地址;面板内 `/upgrade/ui` 会使用该地址更新前端。 - 若需将控制台暴露到公网,建议定期更换访问密钥,或通过 `SSH` 端口转发方式进行安全访问。 ------ @@ -445,11 +446,17 @@ clashctl relay remove 多跳-示例 ```bash clashctl tun on clashctl tun off +clashctl tun on-proxy-off +clashctl tun off-proxy-on clashctl tun doctor clashctl tun logs ``` -Tun 用于透明接管链路。`tun on` 是动作反馈,展示当前关键配置和简短状态;完整证据请看: +Tun 用于透明接管链路。`tun on` 只负责开启 Tun,不会自动关闭系统代理;如需切换到 Tun 接管并关闭系统代理,使用 `clashctl tun on-proxy-off`。如需关闭 Tun 并恢复普通系统代理模式,使用 `clashctl tun off-proxy-on`。 + +`clashctl doctor` 会检查 Tun 与系统代理是否同时开启;如果同时开启,会提示 `clashctl tun on-proxy-off`,避免流量接管路径重复或排障混淆。 + +`tun on` 是动作反馈,展示当前关键配置和简短状态;完整证据请看: ```bash clashctl tun doctor diff --git a/scripts/core/clashctl.sh b/scripts/core/clashctl.sh index da31090..4d14724 100644 --- a/scripts/core/clashctl.sh +++ b/scripts/core/clashctl.sh @@ -3241,6 +3241,32 @@ doctor_container_tun() { esac } +doctor_tun_system_proxy() { + local tun_state system_proxy_state + + doctor_print_title "Tun 与系统代理检查" + + tun_state="$(tun_enabled 2>/dev/null || echo false)" + system_proxy_state="$(system_proxy_status 2>/dev/null || echo off)" + + if [ "$tun_state" = "true" ]; then + doctor_ok "Tun 模式:已开启" + else + doctor_ok "Tun 模式:已关闭" + fi + + if [ "$system_proxy_state" = "on" ]; then + doctor_ok "系统代理持久接管:已开启" + else + doctor_ok "系统代理持久接管:已关闭" + fi + + if [ "$tun_state" = "true" ] && [ "$system_proxy_state" = "on" ]; then + doctor_warn "Tun 模式与系统代理同时开启,可能造成流量接管路径重复或排障混淆" + echo " 建议:clashctl tun on-proxy-off" + fi +} + doctor_dependencies() { local dashboard_source @@ -3843,6 +3869,7 @@ cmd_doctor() { doctor_install_context doctor_dependencies doctor_container_tun + doctor_tun_system_proxy doctor_config doctor_subscription doctor_build @@ -4571,6 +4598,11 @@ doctor_problem_lines() { if [ "$(status_build_last_status 2>/dev/null || true)" = "failed" ]; then echo "🚨 最近一次编译失败" fi + + if [ "$(tun_enabled 2>/dev/null || echo false)" = "true" ] \ + && [ "$(system_proxy_status 2>/dev/null || echo off)" = "on" ]; then + echo "🚨 Tun 模式与系统代理同时开启" + fi } doctor_primary_conclusion() { @@ -4653,6 +4685,13 @@ doctor_recommendation_lines() { return 0 fi + if [ "$(tun_enabled 2>/dev/null || echo false)" = "true" ] \ + && [ "$(system_proxy_status 2>/dev/null || echo off)" = "on" ]; then + echo "💡 clashctl tun on-proxy-off" + echo "💡 如需恢复普通系统代理模式:clashctl tun off-proxy-on" + return 0 + fi + case "$system_proxy_text" in unsupported|off) if [ -n "${mixed_port:-}" ] && [ "$mixed_port" != "null" ]; then @@ -4672,7 +4711,7 @@ doctor_recommendation_lines() { doctor_evidence_lines() { local active_sub mixed_port controller bind_failure_kind bind_failure_line dns_port - local system_proxy_text process_proxy_env + local system_proxy_text process_proxy_env tun_state system_proxy_state active_sub="$(active_subscription_name 2>/dev/null || true)" mixed_port="$(runtime_mixed_port_bind_failure_port 2>/dev/null || status_read_mixed_port 2>/dev/null || true)" @@ -4682,6 +4721,8 @@ doctor_evidence_lines() { dns_port="$(runtime_config_dns_port 2>/dev/null || true)" system_proxy_text="$(persistent_system_proxy_text 2>/dev/null || echo unknown)" process_proxy_env="$(current_process_proxy_env_text 2>/dev/null || echo unknown)" + tun_state="$(tun_enabled 2>/dev/null || echo false)" + system_proxy_state="$(system_proxy_status 2>/dev/null || echo off)" if runtime_config_exists; then echo "🔍 运行配置:存在" @@ -4744,6 +4785,7 @@ doctor_evidence_lines() { echo "🔍 系统代理持久接管:${system_proxy_text}" echo "🔍 当前命令环境代理:${process_proxy_env}" + echo "🔍 Tun 与系统代理:tun=${tun_state}, system_proxy=${system_proxy_state}" } set_controller_secret() { @@ -4978,6 +5020,53 @@ cmd_tun_off() { print_tun_off_feedback "$verify_result" } +cmd_tun_on_proxy_off() { + local system_proxy_rc + + cmd_tun_on || return $? + + if boot_proxy_keep_disable; then + ui_blank + ui_ok "系统代理已关闭,当前使用 Tun 模式接管" + ui_blank + return 0 + fi + + system_proxy_rc=$? + if [ "$system_proxy_rc" -eq 2 ]; then + ui_warn "当前环境不支持清理系统代理持久块,Tun 已按前序结果处理" + else + ui_warn "系统代理持久块清理失败,Tun 已按前序结果处理" + fi + ui_next "clashctl doctor" + ui_blank + return "$system_proxy_rc" +} + +cmd_tun_off_proxy_on() { + local system_proxy_rc + + cmd_tun_off || return $? + + if system_proxy_enable; then + ui_blank + ui_ok "系统代理已开启,当前使用本地代理接管" + ui_blank + return 0 + fi + + system_proxy_rc=$? + write_runtime_value "RUNTIME_BOOT_PROXY_KEEP" "false" 2>/dev/null || true + if [ "$system_proxy_rc" -eq 2 ]; then + ui_warn "当前环境不支持系统代理持久接管,Tun 已按前序结果处理" + else + ui_warn "系统代理持久接管写入失败,Tun 已按前序结果处理" + fi + ui_next "clashctl doctor" + ui_blank + return "$system_proxy_rc" +} + tun_runtime_status_text() { if [ "$(tun_enabled)" = "true" ]; then echo "已开启" @@ -6024,6 +6113,15 @@ cmd_tun() { off) cmd_tun_off ;; + on-proxy-off|proxy-off) + cmd_tun_on_proxy_off + ;; + off-proxy-on|proxy-on) + cmd_tun_off_proxy_on + ;; + log|logs) + cmd_logs mihomo + ;; doctor) doctor_tun_checks ;; @@ -6034,7 +6132,10 @@ cmd_tun() { echo " clashctl tun status" echo " clashctl tun on" echo " clashctl tun off" + echo " clashctl tun on-proxy-off" + echo " clashctl tun off-proxy-on" echo " clashctl tun doctor" + echo " clashctl tun logs" echo echo "🧩 说明:" echo " Tun 属于高级接管能力" diff --git a/scripts/core/common.sh b/scripts/core/common.sh index fbc78c0..3fd3ce5 100644 --- a/scripts/core/common.sh +++ b/scripts/core/common.sh @@ -13,6 +13,7 @@ DEFAULT_MIHOMO_VERSION="${MIHOMO_VERSION:-v1.19.23}" DEFAULT_CLASH_VERSION="${CLASH_VERSION:-v1.18.0}" DEFAULT_SUBCONVERTER_VERSION="${SUBCONVERTER_VERSION:-v0.9.9}" DEFAULT_YQ_VERSION="${YQ_VERSION:-v4.52.4}" +DEFAULT_DASHBOARD_UI_URL="${DASHBOARD_UI_URL:-https://github.com/Zephyruso/zashboard/releases/latest/download/dist.zip}" log() { printf '%s\n' "$*"; } info() { printf 'ℹ %s\n' "$*"; } diff --git a/scripts/core/completion.sh b/scripts/core/completion.sh index 541f8be..9615bda 100644 --- a/scripts/core/completion.sh +++ b/scripts/core/completion.sh @@ -258,7 +258,7 @@ _clash_for_linux_complete_tun() { COMPREPLY=() if [ "$rel_index" -eq 1 ]; then - _clash_for_linux_add_matches "$cur" status on off doctor + _clash_for_linux_add_matches "$cur" status on off on-proxy-off off-proxy-on proxy-off proxy-on doctor log logs fi } diff --git a/scripts/core/config.sh b/scripts/core/config.sh index 372733e..47df6a3 100644 --- a/scripts/core/config.sh +++ b/scripts/core/config.sh @@ -801,7 +801,7 @@ normalize_runtime_config() { local file="$1" local mixed_port controller tun_enable_value tun_stack_value dns_port_value controller_secret_value local tun_auto_route_value tun_auto_redirect_value tun_strict_route_value tun_dns_hijack_value - local dashboard_dir_value allow_lan_value + local dashboard_dir_value dashboard_url_value allow_lan_value local resolved_ports err_file output [ -s "$file" ] || die "待规范化的配置文件不存在:$file" @@ -820,6 +820,7 @@ normalize_runtime_config() { dns_port_value="$CLASH_DNS_PORT_RESOLVED" controller_secret_value="$(ensure_controller_secret)" dashboard_dir_value="$(runtime_dashboard_dir)" + dashboard_url_value="$DEFAULT_DASHBOARD_UI_URL" allow_lan_value="$(config_allow_lan 2>/dev/null || echo true)" err_file="$(mktemp)" @@ -834,13 +835,14 @@ normalize_runtime_config() { tun_dns_hijack_value="$tun_dns_hijack_value" \ controller_secret_value="$controller_secret_value" \ dashboard_dir_value="$dashboard_dir_value" \ + dashboard_url_value="$dashboard_url_value" \ dns_listen_value="0.0.0.0:${dns_port_value}" \ "$(yq_bin)" eval -i ' .["mixed-port"] = (env(mixed_port) | tonumber) | .["external-controller"] = env(controller) | .secret = env(controller_secret_value) | .["external-ui"] = env(dashboard_dir_value) | - .["external-ui-url"] = "/ui" | + .["external-ui-url"] = env(dashboard_url_value) | .["allow-lan"] = (env(allow_lan_value) == "true") | .mode = "rule" | .["log-level"] = (.["log-level"] // "info") |