From a2898dfd7284b651da290f9480fceb3fc98538c1 Mon Sep 17 00:00:00 2001 From: kenzok8 Date: Mon, 19 Jan 2026 14:27:35 +0800 Subject: [PATCH] update 2026-01-19 14:27:35 --- easytier/Makefile | 3 +- luci-app-easytier/easytier/Makefile | 3 +- luci-app-easytier/luci-app-easytier/Makefile | 4 +- .../luasrc/controller/easytier.lua | 107 ++++++---- .../luasrc/model/cbi/easytier.lua | 32 ++- .../root/etc/config/easytier | 4 +- .../root/etc/init.d/easytier | 16 +- .../root/etc/uci-defaults/luci-easytier | 2 + .../root/usr/share/easytier/download.sh | 151 ++++++++++++++ .../root/usr/share/easytier/firewall.sh | 194 ++++++++++++++++++ .../root/usr/share/easytier/utils.sh | 89 ++++++++ luci-app-easytier/version.mk | 5 + 12 files changed, 559 insertions(+), 51 deletions(-) create mode 100644 luci-app-easytier/luci-app-easytier/root/usr/share/easytier/download.sh create mode 100644 luci-app-easytier/luci-app-easytier/root/usr/share/easytier/firewall.sh create mode 100644 luci-app-easytier/luci-app-easytier/root/usr/share/easytier/utils.sh create mode 100644 luci-app-easytier/version.mk diff --git a/easytier/Makefile b/easytier/Makefile index 089b6f7b4..2171be146 100644 --- a/easytier/Makefile +++ b/easytier/Makefile @@ -1,7 +1,8 @@ include $(TOPDIR)/rules.mk +-include $(dir $(lastword $(MAKEFILE_LIST)))../version.mk PKG_NAME:=easytier -PKG_VERSION:=2.5.0 +PKG_VERSION:=$(or $(EASYTIER_VERSION),2.5.0) ifeq ($(ARCH),mipsel) APP_ARCH:=mipsel diff --git a/luci-app-easytier/easytier/Makefile b/luci-app-easytier/easytier/Makefile index 089b6f7b4..2171be146 100644 --- a/luci-app-easytier/easytier/Makefile +++ b/luci-app-easytier/easytier/Makefile @@ -1,7 +1,8 @@ include $(TOPDIR)/rules.mk +-include $(dir $(lastword $(MAKEFILE_LIST)))../version.mk PKG_NAME:=easytier -PKG_VERSION:=2.5.0 +PKG_VERSION:=$(or $(EASYTIER_VERSION),2.5.0) ifeq ($(ARCH),mipsel) APP_ARCH:=mipsel diff --git a/luci-app-easytier/luci-app-easytier/Makefile b/luci-app-easytier/luci-app-easytier/Makefile index 4518d710b..09189380f 100644 --- a/luci-app-easytier/luci-app-easytier/Makefile +++ b/luci-app-easytier/luci-app-easytier/Makefile @@ -5,8 +5,9 @@ # include $(TOPDIR)/rules.mk +-include $(dir $(lastword $(MAKEFILE_LIST)))../version.mk -PKG_VERSION:=2.5.0 +PKG_VERSION:=$(or $(EASYTIER_VERSION),2.5.0) PKG_RELEASE:=2 LUCI_TITLE:=LuCI support for EasyTier @@ -16,6 +17,7 @@ LUCI_PKGARCH:=all PKG_NAME:=luci-app-easytier define Package/$(PKG_NAME)/conffiles +/etc/config/easytier /etc/easytier/ endef diff --git a/luci-app-easytier/luci-app-easytier/luasrc/controller/easytier.lua b/luci-app-easytier/luci-app-easytier/luasrc/controller/easytier.lua index e25549882..c3fc398ef 100644 --- a/luci-app-easytier/luci-app-easytier/luasrc/controller/easytier.lua +++ b/luci-app-easytier/luci-app-easytier/luasrc/controller/easytier.lua @@ -1,6 +1,46 @@ module("luci.controller.easytier", package.seeall) +-- 安全执行命令并返回结果 +local function safe_exec(cmd) + local handle = io.popen(cmd) + if not handle then return "" end + local result = handle:read("*all") or "" + handle:close() + return result:gsub("[\r\n]+$", "") +end + +-- 安全读取文件内容 +local function safe_read_file(path) + local file = io.open(path, "r") + if not file then return nil end + local content = file:read("*all") + file:close() + return content +end + +-- 计算运行时长 +local function calc_uptime(start_time_file) + local content = safe_read_file(start_time_file) + if not content or content == "" then return "" end + + local start_time = tonumber(content:match("%d+")) + if not start_time then return "" end + + local now = os.time() + local elapsed = now - start_time + + local days = math.floor(elapsed / 86400) + local hours = math.floor((elapsed % 86400) / 3600) + local mins = math.floor((elapsed % 3600) / 60) + local secs = elapsed % 60 + + local result = "" + if days > 0 then result = days .. "天 " end + result = result .. string.format("%02d小时%02d分%02d秒", hours, mins, secs) + return result +end + function index() if not nixio.fs.access("/etc/config/easytier") then return @@ -27,62 +67,55 @@ function act_status() e.wrunning = luci.sys.call("pgrep easytier-web >/dev/null") == 0 e.port = (port or 0) - local tagfile = io.open("/tmp/easytier_time", "r") - if tagfile then - local tagcontent = tagfile:read("*all") - tagfile:close() - if tagcontent and tagcontent ~= "" then - os.execute("start_time=$(cat /tmp/easytier_time) && time=$(($(date +%s)-start_time)) && day=$((time/86400)) && [ $day -eq 0 ] && day='' || day=${day}天 && time=$(date -u -d @${time} +'%H小时%M分%S秒') && echo $day $time > /tmp/command_easytier 2>&1") - local command_output_file = io.open("/tmp/command_easytier", "r") - if command_output_file then - e.etsta = command_output_file:read("*all") - command_output_file:close() - end - end - end + -- 使用 Lua 原生计算运行时长 + e.etsta = calc_uptime("/tmp/easytier_time") + e.etwebsta = calc_uptime("/tmp/easytierweb_time") - local command2 = io.popen('test ! -z "`pidof easytier-core`" && (top -b -n1 | grep -E "$(pidof easytier-core)" 2>/dev/null | grep -v grep | awk \'{for (i=1;i<=NF;i++) {if ($i ~ /easytier-core/) break; else cpu=i}} END {print $cpu}\')') + -- 获取 CPU 和内存使用率(使用原始命令) + local command2 = io.popen('test ! -z "`pidof easytier-core`" && (top -b -n1 | grep -E "$(pidof easytier-core)" 2>/dev/null | grep -v grep | awk \'{for (i=1;i<=NF;i++) {if ($i ~ /easytier-core/) break; else cpu=i}} END {print $cpu}\')') e.etcpu = command2:read("*all") command2:close() - local command3 = io.popen("test ! -z `pidof easytier-core` && (cat /proc/$(pidof easytier-core | awk '{print $NF}')/status | grep -w VmRSS | awk '{printf \"%.2f MB\", $2/1024}')") + local command3 = io.popen("test ! -z `pidof easytier-core` && (cat /proc/$(pidof easytier-core | awk '{print $NF}')/status | grep -w VmRSS | awk '{printf \"%.2f MB\", $2/1024}')") e.etram = command3:read("*all") command3:close() - - local wtagfile = io.open("/tmp/easytierweb_time", "r") - if wtagfile then - local wtagcontent = wtagfile:read("*all") - wtagfile:close() - if wtagcontent and wtagcontent ~= "" then - os.execute("start_time=$(cat /tmp/easytierweb_time) && time=$(($(date +%s)-start_time)) && day=$((time/86400)) && [ $day -eq 0 ] && day='' || day=${day}天 && time=$(date -u -d @${time} +'%H小时%M分%S秒') && echo $day $time > /tmp/command_easytierweb 2>&1") - local wcommand_output_file = io.open("/tmp/command_easytierweb", "r") - if wcommand_output_file then - e.etwebsta = wcommand_output_file:read("*all") - wcommand_output_file:close() - end - end - end - + local command4 = io.popen('test ! -z "`pidof easytier-web`" && (top -b -n1 | grep -E "$(pidof easytier-web)" 2>/dev/null | grep -v grep | awk \'{for (i=1;i<=NF;i++) {if ($i ~ /easytier-web/) break; else cpu=i}} END {print $cpu}\')') e.etwebcpu = command4:read("*all") command4:close() - local command5 = io.popen("test ! -z `pidof easytier-web` && (cat /proc/$(pidof easytier-web | awk '{print $NF}')/status | grep -w VmRSS | awk '{printf \"%.2f MB\", $2/1024}')") + local command5 = io.popen("test ! -z `pidof easytier-web` && (cat /proc/$(pidof easytier-web | awk '{print $NF}')/status | grep -w VmRSS | awk '{printf \"%.2f MB\", $2/1024}')") e.etwebram = command5:read("*all") command5:close() - local command8 = io.popen("([ -s /tmp/easytiernew.tag ] && cat /tmp/easytiernew.tag ) || ( curl -L -k -s --connect-timeout 3 --user-agent 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36' https://api.github.com/repos/EasyTier/EasyTier/releases/latest | grep tag_name | sed 's/[^0-9.]*//g' >/tmp/easytiernew.tag && cat /tmp/easytiernew.tag )") - e.etnewtag = command8:read("*all") - command8:close() + -- 获取版本信息 + local cached_newtag = safe_read_file("/tmp/easytiernew.tag") + if cached_newtag and cached_newtag ~= "" then + e.etnewtag = cached_newtag:gsub("[\r\n]+", "") + else + e.etnewtag = safe_exec("curl -L -k -s --connect-timeout 3 --user-agent 'Mozilla/5.0' https://api.github.com/repos/EasyTier/EasyTier/releases/latest | grep tag_name | sed 's/[^0-9.]*//g'") + if e.etnewtag ~= "" then + local f = io.open("/tmp/easytiernew.tag", "w") + if f then f:write(e.etnewtag); f:close() end + end + end - local command9 = io.popen("([ -s /tmp/easytier.tag ] && cat /tmp/easytier.tag ) || ( echo `$(uci -q get easytier.@easytier[0].easytierbin) -V | sed 's/^[^0-9]*//'` > /tmp/easytier.tag && cat /tmp/easytier.tag && [ ! -s /tmp/easytier.tag ] && echo '?' >> /tmp/easytier.tag && cat /tmp/easytier.tag )") - e.ettag = command9:read("*all") - command9:close() + local cached_tag = safe_read_file("/tmp/easytier.tag") + if cached_tag and cached_tag ~= "" then + e.ettag = cached_tag:gsub("[\r\n]+", "") + else + local easytierbin = uci:get_first("easytier", "easytier", "easytierbin") or "/usr/bin/easytier-core" + e.ettag = safe_exec(easytierbin .. " -V | sed 's/^[^0-9]*//'") + if e.ettag == "" then e.ettag = "?" end + local f = io.open("/tmp/easytier.tag", "w") + if f then f:write(e.ettag); f:close() end + end luci.http.prepare_content("application/json") luci.http.write_json(e) end + function get_log() local log = "" local files = {"/tmp/easytier.log"} diff --git a/luci-app-easytier/luci-app-easytier/luasrc/model/cbi/easytier.lua b/luci-app-easytier/luci-app-easytier/luasrc/model/cbi/easytier.lua index b6223233b..970ee2c89 100644 --- a/luci-app-easytier/luci-app-easytier/luasrc/model/cbi/easytier.lua +++ b/luci-app-easytier/luci-app-easytier/luasrc/model/cbi/easytier.lua @@ -74,12 +74,20 @@ network_name = s:taboption("general", Value, "network_name", translate("Network translate("The network name used to identify this VPN network (--network-name parameter)")) network_name.password = true network_name.placeholder = "easytier-name" +network_name.maxlength = 64 +network_name.validate = function(self, value) + if value and value ~= "" and value:match("[^%w%-_]") then + return nil, translate("Only alphanumeric characters, hyphens and underscores allowed") + end + return value +end network_name:depends("etcmd", "etcmd") network_secret = s:taboption("general", Value, "network_secret", translate("Network Secret"), translate("Network secret used to verify whether this node belongs to the VPN network (--network-secret parameter)")) network_secret.password = true network_secret.placeholder = "easytier-password" +network_secret.maxlength = 128 network_secret:depends("etcmd", "etcmd") ip_dhcp = s:taboption("general", Flag, "ip_dhcp", translate("Enable DHCP"), @@ -192,14 +200,14 @@ local model = nixio.fs.readfile("/proc/device-tree/model") or "" local hostname = nixio.fs.readfile("/proc/sys/kernel/hostname") or "" model = model:gsub("\n", "") hostname = hostname:gsub("\n", "") -local device_name = (model ~= "" and model) or (hostname ~= "" and hostname) or "OpenWrt" -device_name = device_name:gsub(" ", "_") -desvice_name = s:taboption("general", Value, "desvice_name", translate("Hostname"), +local device_name_default = (model ~= "" and model) or (hostname ~= "" and hostname) or "OpenWrt" +device_name_default = device_name_default:gsub(" ", "_") +hostname_opt = s:taboption("general", Value, "desvice_name", translate("Hostname"), translate("The hostname used to identify this device (--hostname parameter)")) -desvice_name.placeholder = device_name -desvice_name.default = device_name -desvice_name:depends("etcmd", "etcmd") -desvice_name:depends("etcmd", "web") +hostname_opt.placeholder = device_name_default +hostname_opt.default = device_name_default +hostname_opt:depends("etcmd", "etcmd") +hostname_opt:depends("etcmd", "web") uuid = s:taboption("general", Value, "uuid", translate("UUID"), translate("Unique identifier used to recognize this device when connecting to the web console, for issuing configuration files")) @@ -496,6 +504,16 @@ webbin = s:taboption("upload", Value, "webbin", translate("easytier-web Binary P webbin.placeholder = "/usr/bin/easytier-web" webbin.default = "/usr/bin/easytier-web" +github_proxys = s:taboption("upload", Value, "github_proxys", translate("GitHub Proxy URLs"), + translate("Space-separated list of GitHub proxy URLs for downloading binaries. " + .. "Leave empty to use default proxies.")) +github_proxys.placeholder = "https://ghproxy.net/ https://gh-proxy.com/" + +fallback_version = s:taboption("upload", Value, "fallback_version", translate("Fallback Version"), + translate("Fallback version to use when unable to fetch the latest version from GitHub.")) +fallback_version.placeholder = "v2.5.0" +fallback_version.default = "v2.5.0" + local upload = s:taboption("upload", FileUpload, "upload_file") upload.optional = true upload.default = "" diff --git a/luci-app-easytier/luci-app-easytier/root/etc/config/easytier b/luci-app-easytier/luci-app-easytier/root/etc/config/easytier index c0dc3d995..3444bf788 100644 --- a/luci-app-easytier/luci-app-easytier/root/etc/config/easytier +++ b/luci-app-easytier/luci-app-easytier/root/etc/config/easytier @@ -1,6 +1,4 @@ config easytier option enabled '0' - option easytierbin '/usr/bin/easytier-core' - - + option easytierbin '/usr/bin/easytier-core' diff --git a/luci-app-easytier/luci-app-easytier/root/etc/init.d/easytier b/luci-app-easytier/luci-app-easytier/root/etc/init.d/easytier index 23da90d0c..201adc0e6 100644 --- a/luci-app-easytier/luci-app-easytier/root/etc/init.d/easytier +++ b/luci-app-easytier/luci-app-easytier/root/etc/init.d/easytier @@ -4,6 +4,12 @@ START=99 USE_PROCD=1 +# 引入模块化脚本 +EASYTIER_LIB_DIR="/usr/share/easytier" +[ -f "${EASYTIER_LIB_DIR}/utils.sh" ] && . "${EASYTIER_LIB_DIR}/utils.sh" +[ -f "${EASYTIER_LIB_DIR}/firewall.sh" ] && . "${EASYTIER_LIB_DIR}/firewall.sh" +[ -f "${EASYTIER_LIB_DIR}/download.sh" ] && . "${EASYTIER_LIB_DIR}/download.sh" + check() { if [ ! -z "$checkip" ]; then echo ' ' >/tmp/easytier_check @@ -49,6 +55,9 @@ EOF fi } +# get_tz 函数已移至 utils.sh,保留兼容性包装 +# 如果 utils.sh 未加载,使用内联定义 +if ! type get_tz >/dev/null 2>&1; then get_tz() { SET_TZ="" [ -e "/etc/localtime" ] && return @@ -60,6 +69,7 @@ get_tz() { [ -z "$tz" ] && return SET_TZ=$tz } +fi check_bin() { curltest=`which curl` @@ -78,7 +88,11 @@ check_bin() { tag="$( curl -k --connect-timeout 3 --user-agent "$user_agent" https://api.github.com/repos/EasyTier/EasyTier/releases/latest 2>&1 | grep 'tag_name' | cut -d\" -f4 )" [ -z "$tag" ] && tag="$( curl -Lk --connect-timeout 3 --user-agent "$user_agent" -s https://api.github.com/repos/EasyTier/EasyTier/releases/latest 2>&1 | grep 'tag_name' | cut -d\" -f4 )" fi - [ -z "$tag"] && tag=v2.5.0 + # 如果获取失败,从 UCI 配置或使用默认版本 + if [ -z "$tag" ]; then + tag=$(uci -q get easytier.@easytier[0].fallback_version) + [ -z "$tag" ] && tag="v2.5.0" + fi echo "$(date '+%Y-%m-%d %H:%M:%S') easytier : 开始在线下载${tag}版本,${proxy}https://github.com/EasyTier/EasyTier/releases/download/${tag}/easytier-linux-${cpucore}-${tag}.zip下载较慢耐心等候" >>/tmp/easytier.log echo "$(date '+%Y-%m-%d %H:%M:%S') easytier : 开始在线下载${tag}版本,${proxy}https://github.com/EasyTier/EasyTier/releases/download/${tag}/easytier-linux-${cpucore}-${tag}.zip下载较慢耐心等候" >>/tmp/easytierweb.log mkdir -p "$path" diff --git a/luci-app-easytier/luci-app-easytier/root/etc/uci-defaults/luci-easytier b/luci-app-easytier/luci-app-easytier/root/etc/uci-defaults/luci-easytier index 3bd0f38ea..0d4867387 100644 --- a/luci-app-easytier/luci-app-easytier/root/etc/uci-defaults/luci-easytier +++ b/luci-app-easytier/luci-app-easytier/root/etc/uci-defaults/luci-easytier @@ -6,6 +6,8 @@ uci -q batch <<-EOF >/dev/null set ucitrack.@easytier[-1].init=easytier commit ucitrack EOF + chmod +x /etc/init.d/easytier +/etc/init.d/easytier enable rm -rf /tmp/luci-indexcache /tmp/luci-modulecache exit 0 diff --git a/luci-app-easytier/luci-app-easytier/root/usr/share/easytier/download.sh b/luci-app-easytier/luci-app-easytier/root/usr/share/easytier/download.sh new file mode 100644 index 000000000..083253d8a --- /dev/null +++ b/luci-app-easytier/luci-app-easytier/root/usr/share/easytier/download.sh @@ -0,0 +1,151 @@ +#!/bin/sh +# EasyTier Download Management +# Handles binary downloads from GitHub with proxy support + +# 引入工具函数 +. /usr/share/easytier/utils.sh 2>/dev/null || true + +# 默认 GitHub 加速代理列表 +DEFAULT_PROXYS=" +https://ghproxy.net/ +https://gh-proxy.com/ +https://cdn.gh-proxy.com/ +https://ghfast.top/ +" + +# 获取代理列表 +# 优先从 UCI 配置读取,否则使用默认值 +get_proxy_list() { + local proxys=$(uci -q get easytier.@easytier[0].github_proxys) + [ -z "$proxys" ] && proxys="$DEFAULT_PROXYS" + echo "$proxys" +} + +# 获取最新版本号 +get_latest_version() { + local user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36' + local tag="" + local curltest=$(which curl) + + if [ -z "$curltest" ] || [ ! -s "$(which curl)" ]; then + tag=$(wget --no-check-certificate -T 5 -t 3 --user-agent "$user_agent" --max-redirect=0 --output-document=- \ + https://api.github.com/repos/EasyTier/EasyTier/releases/latest 2>&1 | grep 'tag_name' | cut -d\" -f4) + [ -z "$tag" ] && tag=$(wget --no-check-certificate -T 5 -t 3 --user-agent "$user_agent" --quiet --output-document=- \ + https://api.github.com/repos/EasyTier/EasyTier/releases/latest 2>&1 | grep 'tag_name' | cut -d\" -f4) + else + tag=$(curl -k --connect-timeout 3 --user-agent "$user_agent" \ + https://api.github.com/repos/EasyTier/EasyTier/releases/latest 2>&1 | grep 'tag_name' | cut -d\" -f4) + [ -z "$tag" ] && tag=$(curl -Lk --connect-timeout 3 --user-agent "$user_agent" -s \ + https://api.github.com/repos/EasyTier/EasyTier/releases/latest 2>&1 | grep 'tag_name' | cut -d\" -f4) + fi + + # 如果获取失败,从 UCI 配置或使用默认版本 + if [ -z "$tag" ]; then + tag=$(uci -q get easytier.@easytier[0].fallback_version) + [ -z "$tag" ] && tag="v2.5.0" + fi + + echo "$tag" +} + +# 下载二进制文件 +# 参数: $1=版本号 $2=CPU架构 $3=目标路径 +download_binary() { + local tag="$1" + local cpucore="$2" + local path="$3" + local proxys=$(get_proxy_list) + local download_url="https://github.com/EasyTier/EasyTier/releases/download/${tag}/easytier-linux-${cpucore}-${tag}.zip" + + mkdir -p "$path" + + for proxy in $proxys; do + log_message "INFO" "easytier" "尝试使用代理 ${proxy} 下载" "/tmp/easytier.log" + + if curl -L -k -o /tmp/easytier.zip --connect-timeout 10 --retry 3 "${proxy}${download_url}" || \ + wget --no-check-certificate --timeout=10 --tries=3 -O /tmp/easytier.zip "${proxy}${download_url}"; then + + unzip -j -q -o /tmp/easytier.zip -d /tmp + chmod +x /tmp/easytier-core /tmp/easytier-cli /tmp/easytier-web /tmp/easytier-web-embed 2>/dev/null || true + rm -rf /tmp/easytier.zip + + log_message "INFO" "easytier" "下载成功" "/tmp/easytier.log" + return 0 + else + log_message "WARN" "easytier" "${proxy}${download_url} 下载失败" "/tmp/easytier.log" + fi + done + + log_message "ERROR" "easytier" "所有代理下载均失败,请手动下载上传程序" "/tmp/easytier.log" + return 1 +} + +# 检查并下载程序 +# 参数: $1=程序路径 $2=目标路径 $3=CPU架构 +check_and_download() { + local easytierbin="$1" + local path="$2" + local cpucore="$3" + + # 检查程序是否存在且完整 + if [ ! -f "$easytierbin" ] || [ "$($easytierbin -h 2>&1 | wc -l)" -lt 3 ]; then + log_message "INFO" "easytier" "$easytierbin 不存在或程序不完整,开始在线下载..." "/tmp/easytier.log" + + local tag=$(get_latest_version) + log_message "INFO" "easytier" "开始在线下载${tag}版本" "/tmp/easytier.log" + + if download_binary "$tag" "$cpucore" "$path"; then + # 移动下载的文件到目标位置 + if [ "$(uci -q get easytier.@easytier[0].enabled)" = "1" ]; then + mv -f /tmp/easytier-core "${path}/" 2>/dev/null + mv -f /tmp/easytier-cli "${path}/" 2>/dev/null + chmod +x "$easytierbin" 2>/dev/null + fi + + if [ "$(uci -q get easytier.@easytier[0].web_enabled)" = "1" ]; then + local webbin=$(uci -q get easytier.@easytier[0].webbin) + [ -z "$webbin" ] && webbin="/usr/bin/easytier-web" + mv -f /tmp/easytier-web-embed "$webbin" 2>/dev/null || true + chmod +x "$webbin" 2>/dev/null + fi + + return 0 + fi + + return 1 + fi + + return 0 +} + +# 处理上传的程序 +# 参数: $1=目标路径 $2=程序路径 +handle_uploaded_binary() { + local path="$1" + local easytierbin="$2" + local size=$(get_available_space "$path") + + if [ -f /tmp/easytier-core ] || [ -f /tmp/easytier-cli ]; then + if [ "${path:0:4}" != "/tmp" ]; then + chmod +x /tmp/easytier-core 2>/dev/null + chmod +x /tmp/easytier-cli 2>/dev/null + mkdir -p "$path" + + log_message "INFO" "easytier" "找到上传的程序/tmp/easytier-core,替换为$easytierbin" "/tmp/easytier.log" + + local upsize=$(du -k /tmp/easytier-core 2>/dev/null | cut -f1) + local result=$((size - upsize)) + + if [ "$(/tmp/easytier-core -h 2>&1 | wc -l)" -gt 3 ] && [ "$result" -gt 1000 ]; then + mv -f /tmp/easytier-core "$easytierbin" 2>/dev/null + mv -f /tmp/easytier-cli "${path}/easytier-cli" 2>/dev/null + return 0 + else + log_message "WARN" "easytier" "无法替换,上传的程序不完整或自定义路径的可用空间不足,当前空间剩余${size}kb" "/tmp/easytier.log" + return 1 + fi + fi + fi + + return 1 +} diff --git a/luci-app-easytier/luci-app-easytier/root/usr/share/easytier/firewall.sh b/luci-app-easytier/luci-app-easytier/root/usr/share/easytier/firewall.sh new file mode 100644 index 000000000..4c6f5ccc9 --- /dev/null +++ b/luci-app-easytier/luci-app-easytier/root/usr/share/easytier/firewall.sh @@ -0,0 +1,194 @@ +#!/bin/sh +# EasyTier Firewall Management +# Manages firewall rules and network interfaces for EasyTier + +# 引入工具函数 +. /usr/share/easytier/utils.sh 2>/dev/null || true + +# 添加单条防火墙规则 +# 参数: $1=规则名称 $2=协议 $3=端口 $4=描述 +add_firewall_rule() { + local rule_name="$1" + local proto="$2" + local port="$3" + local desc="$4" + + [ -z "$port" ] && return 1 + + log_message "INFO" "easytier" "添加防火墙规则 ${rule_name} 放行端口 ${port}" "/tmp/easytier.log" + + uci -q delete "firewall.${rule_name}" + uci set "firewall.${rule_name}=rule" + uci set "firewall.${rule_name}.name=${rule_name}" + uci set "firewall.${rule_name}.target=ACCEPT" + uci set "firewall.${rule_name}.src=wan" + uci set "firewall.${rule_name}.proto=${proto}" + uci set "firewall.${rule_name}.dest_port=${port}" + uci set "firewall.${rule_name}.enabled=1" +} + +# 设置所有 EasyTier 防火墙规则 +# 需要设置的变量: tcp_port, udp_port, ws_port, wss_port, wg_port, quic_port, socks_port +set_firewall_rules() { + [ -n "$tcp_port" ] && add_firewall_rule "easytier_tcp_udp" "tcp udp" "$tcp_port" "EasyTier TCP/UDP" + [ -n "$udp_port" ] && add_firewall_rule "easytier_udp" "udp" "$udp_port" "EasyTier UDP" + [ -n "$ws_port" ] && add_firewall_rule "easytier_ws" "tcp" "$ws_port" "EasyTier WS" + [ -n "$wss_port" ] && add_firewall_rule "easytier_wss" "tcp" "$wss_port" "EasyTier WSS" + [ -n "$wg_port" ] && add_firewall_rule "easytier_wg" "udp" "$wg_port" "EasyTier WG" + [ -n "$quic_port" ] && add_firewall_rule "easytier_quic" "tcp udp" "$quic_port" "EasyTier QUIC" + [ -n "$socks_port" ] && add_firewall_rule "easytier_socks5" "tcp" "$socks_port" "EasyTier SOCKS5" +} + +# 设置网络接口 +# 参数: $1=接口名称 $2=IP地址(可选) +setup_network_interface() { + local tunname="${1:-tun0}" + local ipaddr="$2" + + uci -q delete network.EasyTier >/dev/null 2>&1 + + if [ -z "$(uci -q get network.EasyTier)" ]; then + uci set network.EasyTier='interface' + if [ -z "$ipaddr" ]; then + uci set network.EasyTier.proto='none' + else + uci set network.EasyTier.proto='static' + uci set network.EasyTier.ipaddr="$ipaddr" + uci set network.EasyTier.netmask='255.0.0.0' + fi + log_message "INFO" "easytier" "添加网络接口 EasyTier 绑定虚拟接口 ${tunname}" "/tmp/easytier.log" + uci set network.EasyTier.device="$tunname" + uci set network.EasyTier.ifname="$tunname" + fi +} + +# 设置防火墙区域 +setup_firewall_zone() { + if [ -z "$(uci -q get firewall.easytierzone)" ]; then + log_message "INFO" "easytier" "添加防火墙规则,放行网络接口 EasyTier 允许出入转发,开启IP动态伪装 MSS钳制" "/tmp/easytier.log" + uci set firewall.easytierzone='zone' + uci set firewall.easytierzone.input='ACCEPT' + uci set firewall.easytierzone.output='ACCEPT' + uci set firewall.easytierzone.forward='ACCEPT' + uci set firewall.easytierzone.masq='1' + uci set firewall.easytierzone.mtu_fix='1' + uci set firewall.easytierzone.name='EasyTier' + uci set firewall.easytierzone.network='EasyTier' + fi +} + +# 设置转发规则 +# 参数: $1=et_forward 配置值 +setup_forwarding_rules() { + local et_forward="$1" + + if [ "${et_forward#*etfwlan}" != "$et_forward" ]; then + log_message "INFO" "easytier" "允许从虚拟网络 EasyTier 到局域网 lan 的流量" "/tmp/easytier.log" + uci set firewall.easytierfwlan=forwarding + uci set firewall.easytierfwlan.dest='lan' + uci set firewall.easytierfwlan.src='EasyTier' + else + uci -q delete firewall.easytierfwlan + fi + + if [ "${et_forward#*etfwwan}" != "$et_forward" ]; then + log_message "INFO" "easytier" "允许从虚拟网络 EasyTier 到广域网 wan 的流量" "/tmp/easytier.log" + uci set firewall.easytierfwwan=forwarding + uci set firewall.easytierfwwan.dest='wan' + uci set firewall.easytierfwwan.src='EasyTier' + else + uci -q delete firewall.easytierfwwan + fi + + if [ "${et_forward#*lanfwet}" != "$et_forward" ]; then + log_message "INFO" "easytier" "允许从局域网 lan 到虚拟网络 EasyTier 的流量" "/tmp/easytier.log" + uci set firewall.lanfweasytier=forwarding + uci set firewall.lanfweasytier.dest='EasyTier' + uci set firewall.lanfweasytier.src='lan' + else + uci -q delete firewall.lanfweasytier + fi + + if [ "${et_forward#*wanfwet}" != "$et_forward" ]; then + log_message "INFO" "easytier" "允许从广域网 wan 到虚拟网络 EasyTier 的流量" "/tmp/easytier.log" + uci set firewall.wanfweasytier=forwarding + uci set firewall.wanfweasytier.dest='EasyTier' + uci set firewall.wanfweasytier.src='wan' + else + uci -q delete firewall.wanfweasytier + fi +} + +# 清理所有 EasyTier 防火墙规则 +clean_firewall_rules() { + uci -q delete network.EasyTier >/dev/null 2>&1 + uci -q delete firewall.easytierzone >/dev/null 2>&1 + uci -q delete firewall.easytierfwlan >/dev/null 2>&1 + uci -q delete firewall.easytierfwwan >/dev/null 2>&1 + uci -q delete firewall.lanfweasytier >/dev/null 2>&1 + uci -q delete firewall.wanfweasytier >/dev/null 2>&1 + uci -q delete firewall.easytier_tcp >/dev/null 2>&1 + uci -q delete firewall.easytier_udp >/dev/null 2>&1 + uci -q delete firewall.easytier_tcp_udp >/dev/null 2>&1 + uci -q delete firewall.easytier_wss >/dev/null 2>&1 + uci -q delete firewall.easytier_ws >/dev/null 2>&1 + uci -q delete firewall.easytier_wg >/dev/null 2>&1 + uci -q delete firewall.easytier_quic >/dev/null 2>&1 + uci -q delete firewall.easytier_wireguard >/dev/null 2>&1 + uci -q delete firewall.easytier_socks5 >/dev/null 2>&1 + uci -q delete firewall.easytier_webserver >/dev/null 2>&1 + uci -q delete firewall.easytier_webapi >/dev/null 2>&1 + uci -q delete firewall.easytier_webhtml >/dev/null 2>&1 +} + +# 设置 Web 控制台防火墙规则 +# 参数: $1=web_port $2=api_port $3=html_port $4=fw_web $5=fw_api +setup_web_firewall() { + local web_port="$1" + local api_port="$2" + local html_port="$3" + local fw_web="$4" + local fw_api="$5" + + if [ -n "$web_port" ] && [ "$fw_web" = "1" ]; then + log_message "INFO" "easytier" "添加防火墙规则 easytier_web 放行服务端口 ${web_port}" "/tmp/easytierweb.log" + uci -q delete firewall.easytier_webserver + uci set firewall.easytier_webserver=rule + uci set firewall.easytier_webserver.name="easytier_webserver" + uci set firewall.easytier_webserver.target="ACCEPT" + uci set firewall.easytier_webserver.src="wan" + uci set firewall.easytier_webserver.proto="tcp udp" + uci set firewall.easytier_webserver.dest_port="$web_port" + uci set firewall.easytier_webserver.enabled="1" + fi + + if [ -n "$api_port" ] && [ "$fw_api" = "1" ]; then + log_message "INFO" "easytier" "添加防火墙规则 easytier_web 放行API端口 ${api_port}" "/tmp/easytierweb.log" + uci -q delete firewall.easytier_webapi + uci set firewall.easytier_webapi=rule + uci set firewall.easytier_webapi.name="easytier_webapi" + uci set firewall.easytier_webapi.target="ACCEPT" + uci set firewall.easytier_webapi.src="wan" + uci set firewall.easytier_webapi.proto="tcp" + uci set firewall.easytier_webapi.dest_port="$api_port" + uci set firewall.easytier_webapi.enabled="1" + fi + + if [ -n "$html_port" ] && [ "$fw_api" = "1" ] && [ "$html_port" != "$api_port" ]; then + log_message "INFO" "easytier" "添加防火墙规则 easytier_web 放行html端口 ${html_port}" "/tmp/easytierweb.log" + uci -q delete firewall.easytier_webhtml + uci set firewall.easytier_webhtml=rule + uci set firewall.easytier_webhtml.name="easytier_webhtml" + uci set firewall.easytier_webhtml.target="ACCEPT" + uci set firewall.easytier_webhtml.src="wan" + uci set firewall.easytier_webhtml.proto="tcp" + uci set firewall.easytier_webhtml.dest_port="$html_port" + uci set firewall.easytier_webhtml.enabled="1" + fi +} + +# 应用防火墙和网络配置更改 +apply_network_changes() { + [ -n "$(uci changes network)" ] && uci commit network && /etc/init.d/network reload >/dev/null 2>&1 + [ -n "$(uci changes firewall)" ] && uci commit firewall && /etc/init.d/firewall reload >/dev/null 2>&1 +} diff --git a/luci-app-easytier/luci-app-easytier/root/usr/share/easytier/utils.sh b/luci-app-easytier/luci-app-easytier/root/usr/share/easytier/utils.sh new file mode 100644 index 000000000..ad6b72943 --- /dev/null +++ b/luci-app-easytier/luci-app-easytier/root/usr/share/easytier/utils.sh @@ -0,0 +1,89 @@ +#!/bin/sh +# EasyTier Utility Functions +# Common helper functions for EasyTier init scripts + +# 获取时区设置 +get_tz() { + SET_TZ="" + [ -e "/etc/localtime" ] && return + for tzfile in /etc/TZ /var/etc/TZ; do + [ -e "$tzfile" ] || continue + tz="$(cat $tzfile 2>/dev/null)" + done + [ -z "$tz" ] && return + SET_TZ=$tz +} + +# 获取 CPU 架构 +get_cpu_arch() { + local cputype=$(uname -ms | tr ' ' '_' | tr '[A-Z]' '[a-z]') + local cpucore="" + + [ -n "$(echo $cputype | grep -E 'linux.*armv.*')" ] && cpucore="arm" + [ -n "$(echo $cputype | grep -E 'linux.*armv7.*')" ] && [ -n "$(cat /proc/cpuinfo | grep vfp)" ] && cpucore="armv7" + [ -n "$(echo $cputype | grep -E 'linux.*aarch64.*|linux.*armv8.*')" ] && cpucore="aarch64" + [ -n "$(echo $cputype | grep -E 'linux.*86.*')" ] && cpucore="i386" + [ -n "$(echo $cputype | grep -E 'linux.*86_64.*')" ] && cpucore="x86_64" + + if [ -n "$(echo $cputype | grep -E 'linux.*mips.*')" ]; then + local mipstype=$(echo -n I | hexdump -o 2>/dev/null | awk '{ print substr($2,6,1); exit}') + [ "$mipstype" = "0" ] && cpucore="mips" || cpucore="mipsel" + fi + + echo "$cpucore" +} + +# 统一日志记录 +log_message() { + local level="$1" + local component="$2" + local message="$3" + local logfile="${4:-/tmp/easytier.log}" + + echo "$(date '+%Y-%m-%d %H:%M:%S') ${component} : ${message}" >> "$logfile" +} + +# 日志大小管理 +manage_log_size() { + local logfile="$1" + local max_size_kb="${2:-5120}" + local keep_lines="${3:-500}" + + while true; do + local log_size=$(ls -l "$logfile" 2>/dev/null | awk '{print int($5/1024)}') + if [ "${log_size:-0}" -gt "$max_size_kb" ]; then + tail -n "$keep_lines" "$logfile" > "${logfile}.tmp" + mv "${logfile}.tmp" "$logfile" + fi + sleep 300 + done +} + +# 获取可用磁盘空间 (KB) +get_available_space() { + local path="$1" + local size=$(df -k | awk '/\/overlay$/ {sub(/K$/, "", $4); print $4}') + [ -z "$size" ] && size=$(df -kP "$path" 2>/dev/null | awk 'NR==2 {print $(NF-2)}') + echo "${size:-0}" +} + +# 安全执行命令并返回结果 +safe_exec() { + local cmd="$1" + local result=$(eval "$cmd" 2>/dev/null) + echo "$result" +} + +# 检查进程是否运行 +is_process_running() { + local process_name="$1" + pgrep -f "$process_name" >/dev/null 2>&1 + return $? +} + +# 停止进程 +stop_process() { + local process_name="$1" + ps | grep "$process_name" | grep -v grep | awk '{print $1}' | xargs kill >/dev/null 2>&1 + ps | grep "$process_name" | grep -v grep | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1 +} diff --git a/luci-app-easytier/version.mk b/luci-app-easytier/version.mk new file mode 100644 index 000000000..697aa7fa8 --- /dev/null +++ b/luci-app-easytier/version.mk @@ -0,0 +1,5 @@ +# EasyTier Version Configuration +# This file is the single source of truth for the EasyTier version +# Used by: easytier/Makefile, luci-app-easytier/Makefile, init.d/easytier + +EASYTIER_VERSION=2.5.0