easytier去中心化组网
Linux自建EasyTier节点全攻略:从服务器部署到客户端接入
在分布式网络场景中,EasyTier作为轻量级的网络穿透与节点管理工具,深受开发者和运维人员青睐。它支持灵活的节点部署、Web化管理以及跨设备互联互通,尤其适合需要搭建私有网络的个人和企业用户。本文将详细讲解如何在Linux系统中自建EasyTier节点,涵盖基础服务器搭建、Web管理端部署(含前后端分离/集成两种模式)、客户端接入等全流程,附详细命令和配置说明,新手也能轻松上手。
一、前置准备:环境检查与核心脚本获取
在开始部署前,先确保你的Linux环境满足以下条件,避免后续出现兼容性问题:
系统要求:支持systemd或OpenRC初始化系统(主流Linux发行版如Ubuntu、CentOS、Debian、OpenWrt均符合);
权限要求:必须以root用户执行操作(脚本需创建系统服务、写入配置文件等);
依赖工具:已安装curl、unzip(脚本会自动检查,缺失则提示安装);
网络要求:服务器需开放对应端口(默认11010、11210、11211等,后续会详细说明)。
核心脚本获取:使用自定义的EasyTier安装脚本(功能更完善、参数支持更全面),请先将提供的脚本保存为install.sh,再执行以下命令赋予执行权限:
#!/bin/bash
RED_COLOR='\e[1;31m'
GREEN_COLOR='\e[1;32m'
YELLOW_COLOR='\e[1;33m'
BLUE_COLOR='\e[1;34m'
PINK_COLOR='\e[1;35m'
SHAN='\e[1;33;5m'
RES='\e[0m'
HELP() {
echo -e "\r\n${GREEN_COLOR}EasyTier Installation Script Help${RES}\r\n"
echo "Usage: ./install.sh [command] [options]"
echo
echo "Commands:"
echo " install Install EasyTier core service"
echo " install-web Install EasyTier web service (pure path, no extra args)"
echo " install-web-embed Install EasyTier web-embed service with configurable parameters"
echo " uninstall Uninstall EasyTier"
echo " uninstall-web Uninstall EasyTier web service"
echo " update Update EasyTier to the latest version"
echo " help Show this help message"
echo
echo "Options:"
echo " --skip-folder-verify Skip folder verification during installation"
echo " --skip-folder-fix Skip automatic folder path fixing"
echo " --no-gh-proxy Disable GitHub proxy"
echo " --gh-proxy URL Set custom GitHub proxy URL"
echo " --web-portal URL Set custom -w web portal (default: udp://public.easytiertop:22020/主机名_年月日@)"
echo " -d/--db-path PATH Set data storage path for web-embed (default: /opt/easytier/et.db)"
echo " -l/--web-port PORT Set web frontend port for web-embed (default: 11210)"
echo " -a/--api-port PORT Set web backend port for web-embed (default: 11211)"
echo
echo "Examples:"
echo " ./install.sh install /opt/easytier"
echo " ./install.sh install --skip-folder-verify"
echo " ./install.sh install-web"
echo " ./install.sh install-web-embed"
echo " ./install.sh install-web-embed -d /data/easytier/et.db -l 21010 -a 21011"
echo " ./install.sh install-web-embed --db-path /data/easytier/et.db --web-port 21010 --api-port 21011"
echo " ./install.sh uninstall-web"
echo
echo "Web Portal Format Requirement:"
echo " Correct: udp://domain:port/token (e.g. udp://public.easytiertop:22020/sgphwyun_20251219@)"
echo " Wrong: udp://domain:port//token (double slash) / tcp://domain:port/ (empty token)"
}
# 生成默认Web Portal(主机名+年月日)
generate_default_web_portal() {
# 获取主机名(过滤特殊字符)
local hostname=$(hostname | tr -cd 'a-zA-Z0-9_')
# 获取当前年月日(格式:YYYYMMDD)
local date_str=$(date +%Y%m%d)
# 拼接默认Web Portal
echo "udp://public.easytiertop:22020/${hostname}_${date_str}@"
}
# Web Portal格式校验函数(核心修复:仅修复路径中的双斜杠,保留协议双斜杠)
validate_web_portal() {
local portal="$1"
local error_msg=""
local fixed_portal="$portal"
# 1. 校验协议(仅允许udp/tcp)
if ! echo "$portal" | grep -qE '^(udp|tcp)://'; then
error_msg="协议错误:仅支持 udp:// 或 tcp:// 开头"
# 2. 拆分协议和路径,分别处理
else
# 拆分协议部分(udp://)和剩余部分
local proto=$(echo "$portal" | grep -oE '^(udp|tcp)://')
local rest=$(echo "$portal" | sed "s|^${proto}||")
# 3. 校验剩余部分格式(地址:端口/令牌)
if ! echo "$rest" | grep -qE '^[^:]+:[0-9]+/[^/]+$'; then
# 细分错误类型
if echo "$rest" | grep -qE '^[^:]+:[0-9]+//'; then
error_msg="格式错误:路径包含双斜杠(//),正确格式为单斜杠(/)"
# 仅修复路径中的双斜杠,保留协议双斜杠
fixed_portal="${proto}$(echo "$rest" | sed 's|//|/|g')"
echo -e "${YELLOW_COLOR}自动修复路径双斜杠:$portal → $fixed_portal${RES}"
elif echo "$rest" | grep -qE '^[^:]+:[0-9]+/$'; then
error_msg="令牌为空:地址后必须跟有效的令牌(如 /sgphwyun_20251219@)"
else
error_msg="格式错误:正确格式为 ${proto}域名:端口/令牌(如 ${proto}public.easytier.top:22020/sgphwyun_20251219@)"
fi
# 4. 校验端口是否为数字
elif ! echo "$rest" | grep -qE '^[^:]+:[0-9]{1,5}/[^/]+$'; then
error_msg="端口错误:必须为1-65535之间的数字"
# 5. 校验令牌是否为空(至少1个字符)
elif [ -z "$(echo "$rest" | awk -F '/' '{print $2}')" ]; then
error_msg="令牌为空:地址后必须跟有效的令牌(如 /sgphwyun_20251219@)"
fi
fi
# 校验不通过则输出错误并退出
if [ -n "$error_msg" ]; then
echo -e "\r\n${RED_COLOR}Web Portal 格式校验失败:${RES}"
echo -e "${RED_COLOR}错误原因:$error_msg${RES}"
echo -e "${YELLOW_COLOR}正确格式示例:$(generate_default_web_portal)${RES}"
exit 1
fi
# 最终赋值(保留协议双斜杠)
DEFAULT_WEB_PORTAL="$fixed_portal"
return 0
}
# 端口合法性校验函数
validate_port() {
local port="$1"
local port_name="$2"
if ! [[ "$port" =~ ^[0-9]+$ ]]; then
echo -e "${RED_COLOR}错误:${port_name}必须是数字(当前值:$port)${RES}"
exit 1
fi
if [ "$port" -lt 1 ] || [ "$port" -gt 65535 ]; then
echo -e "${RED_COLOR}错误:${port_name}必须在1-65535之间(当前值:$port)${RES}"
exit 1
fi
}
# ========== 安装easytier-web服务(纯路径,无参数) ==========
INSTALL_WEB() {
# 检查核心包是否已安装
if [ ! -f "$INSTALL_PATH/easytier-web" ]; then
echo -e "${YELLOW_COLOR}核心包未安装,先自动安装核心服务...${RES}"
CHECK
INSTALL
fi
# 停止旧的web服务
if [ "$INIT_SYSTEM" = "systemd" ]; then
systemctl stop easytier-web.service >/dev/null 2>&1
else
rc-service easytier-web stop >/dev/null 2>&1
fi
# 配置systemd服务(纯路径,无任何参数)
if [ "$INIT_SYSTEM" = "systemd" ]; then
cat >/etc/systemd/system/easytier-web.service <<EOF
[Unit]
Description=EasyTier Web Service
Wants=network.target
After=network.target network.service
StartLimitIntervalSec=0
[Service]
Type=simple
WorkingDirectory=$INSTALL_PATH
ExecStart=$INSTALL_PATH/easytier-web
Restart=always
RestartSec=1s
[Install]
WantedBy=multi-user.target
EOF
elif [ "$INIT_SYSTEM" = "openrc" ]; then
cat >/etc/init.d/easytier-web <<EOF
#!/sbin/openrc-run
name="EasyTier Web"
description="EasyTier Web Service"
command="$INSTALL_PATH/easytier-web"
command_user="nobody:nobody"
command_background=true
pidfile="/run/\${RC_SVCNAME}.pid"
depend() {
need net
}
EOF
chmod +x /etc/init.d/easytier-web
fi
# 启动服务
if [ "$INIT_SYSTEM" = "systemd" ]; then
systemctl daemon-reload
systemctl enable easytier-web.service >/dev/null 2>&1
systemctl start easytier-web.service
else
rc-update add easytier-web default
rc-service easytier-web start
fi
# 验证并输出ExecStart
echo -e "\r\n${GREEN_COLOR}Install EasyTier web service successfully!${RES}"
echo -e "${YELLOW_COLOR}----------------------IMPORTANT----------------------${RES}"
echo -e "ExecStart: ${GREEN_COLOR}$INSTALL_PATH/easytier-web${RES}"
echo -e "Service Name: ${GREEN_COLOR}easytier-web.service${RES}"
echo -e "${YELLOW_COLOR}-----------------------------------------------------${RES}\r\n"
echo -e "Service Management Commands:"
if [ "$INIT_SYSTEM" = "systemd" ]; then
echo -e "Status: ${GREEN_COLOR}systemctl status easytier-web.service${RES}"
echo -e "Start: ${GREEN_COLOR}systemctl start easytier-web.service${RES}"
echo -e "Stop: ${GREEN_COLOR}systemctl stop easytier-web.service${RES}"
else
echo -e "Status: ${GREEN_COLOR}rc-service easytier-web status${RES}"
echo -e "Start: ${GREEN_COLOR}rc-service easytier-web start${RES}"
echo -e "Stop: ${GREEN_COLOR}rc-service easytier-web stop${RES}"
fi
}
# ========== 安装easytier-web-embed服务(带可配置参数) ==========
INSTALL_WEB_EMBED() {
# 设置默认参数
local DB_PATH="${DB_PATH:-/opt/easytier/et.db}"
local WEB_PORT="${WEB_PORT:-11210}"
local API_PORT="${API_PORT:-11211}"
# 校验端口合法性
validate_port "$WEB_PORT" "Web前端端口(-l/--web-port)"
validate_port "$API_PORT" "Web后端端口(-a/--api-port)"
# 确保数据目录存在
local DB_DIR=$(dirname "$DB_PATH")
if [ ! -d "$DB_DIR" ]; then
mkdir -p "$DB_DIR"
chmod 755 "$DB_DIR"
echo -e "${YELLOW_COLOR}已创建数据目录:$DB_DIR${RES}"
fi
# 检查核心包是否已安装
if [ ! -f "$INSTALL_PATH/easytier-web-embed" ]; then
echo -e "${YELLOW_COLOR}核心包未安装,先自动安装核心服务...${RES}"
CHECK
INSTALL
fi
# 停止旧的web服务
if [ "$INIT_SYSTEM" = "systemd" ]; then
systemctl stop easytier-web.service >/dev/null 2>&1
else
rc-service easytier-web stop >/dev/null 2>&1
fi
# 构建ExecStart命令
local EXEC_CMD="$INSTALL_PATH/easytier-web-embed -d $DB_PATH -l $WEB_PORT -a $API_PORT"
# 配置systemd服务
if [ "$INIT_SYSTEM" = "systemd" ]; then
cat >/etc/systemd/system/easytier-web.service <<EOF
[Unit]
Description=EasyTier Web Embed Service
Wants=network.target
After=network.target network.service
StartLimitIntervalSec=0
[Service]
Type=simple
WorkingDirectory=$INSTALL_PATH
ExecStart=$EXEC_CMD
Restart=always
RestartSec=1s
[Install]
WantedBy=multi-user.target
EOF
elif [ "$INIT_SYSTEM" = "openrc" ]; then
cat >/etc/init.d/easytier-web <<EOF
#!/sbin/openrc-run
name="EasyTier Web Embed"
description="EasyTier Web Embed Service"
command="$INSTALL_PATH/easytier-web-embed"
command_args="-d $DB_PATH -l $WEB_PORT -a $API_PORT"
command_user="nobody:nobody"
command_background=true
pidfile="/run/\${RC_SVCNAME}.pid"
depend() {
need net
}
EOF
chmod +x /etc/init.d/easytier-web
fi
# 启动服务
if [ "$INIT_SYSTEM" = "systemd" ]; then
systemctl daemon-reload
systemctl enable easytier-web.service >/dev/null 2>&1
systemctl start easytier-web.service
else
rc-update add easytier-web default
rc-service easytier-web start
fi
# 验证并输出配置信息
echo -e "\r\n${GREEN_COLOR}Install EasyTier web-embed service successfully!${RES}"
echo -e "${YELLOW_COLOR}----------------------IMPORTANT----------------------${RES}"
echo -e "ExecStart: ${GREEN_COLOR}$EXEC_CMD${RES}"
echo -e "Data Storage Path (-d): ${GREEN_COLOR}$DB_PATH${RES}"
echo -e "Web Frontend Port (-l): ${GREEN_COLOR}$WEB_PORT${RES}"
echo -e "Web Backend Port (-a): ${GREEN_COLOR}$API_PORT${RES}"
echo -e "Service Name: ${GREEN_COLOR}easytier-web.service${RES}"
echo -e "${YELLOW_COLOR}-----------------------------------------------------${RES}\r\n"
echo -e "Service Management Commands:"
if [ "$INIT_SYSTEM" = "systemd" ]; then
echo -e "Status: ${GREEN_COLOR}systemctl status easytier-web.service${RES}"
echo -e "Start: ${GREEN_COLOR}systemctl start easytier-web.service${RES}"
echo -e "Stop: ${GREEN_COLOR}systemctl stop easytier-web.service${RES}"
else
echo -e "Status: ${GREEN_COLOR}rc-service easytier-web status${RES}"
echo -e "Start: ${GREEN_COLOR}rc-service easytier-web start${RES}"
echo -e "Stop: ${GREEN_COLOR}rc-service easytier-web stop${RES}"
fi
}
# ========== 卸载web服务 ==========
UNINSTALL_WEB() {
echo -e "\r\n${GREEN_COLOR}Uninstall EasyTier web service ...${RES}\r\n"
# 停止服务
if [ "$INIT_SYSTEM" = "systemd" ]; then
systemctl disable easytier-web.service >/dev/null 2>&1
systemctl stop easytier-web.service >/dev/null 2>&1
else
rc-update del easytier-web
rc-service easytier-web stop
fi
# 删除服务文件
rm -rf /etc/systemd/system/easytier-web.service
rm -rf /etc/init.d/easytier-web
if [ "$INIT_SYSTEM" = "systemd" ]; then
systemctl daemon-reload
fi
echo -e "\r\n${GREEN_COLOR}EasyTier web service was removed successfully! ${RES}\r\n"
}
# Show help if no arguments or help command is used
if [ $# -eq 0 ] || [ "$1" = "help" ]; then
HELP
exit 0
fi
# This script copy from alist , Thank for it!
SKIP_FOLDER_VERIFY=false
SKIP_FOLDER_FIX=false
NO_GH_PROXY=false
GH_PROXY='https://ghfast.top/'
# 动态生成默认Web Portal(主机名+年月日)
DEFAULT_WEB_PORTAL=$(generate_default_web_portal)
# web-embed默认参数
DB_PATH="/opt/easytier/et.db"
WEB_PORT=11210
API_PORT=11211
COMMEND=$1
shift
# Check path
if [[ "$#" -ge 1 && ! "$1" =~ ^- ]]; then
INSTALL_PATH=$1
shift
fi
# Check other option - 兼容短参数和长参数
while [[ "$#" -gt 0 ]]; do
case $1 in
--skip-folder-verify) SKIP_FOLDER_VERIFY=true ;;
--skip-folder-fix) SKIP_FOLDER_FIX=true ;;
--no-gh-proxy) NO_GH_PROXY=true ;;
--gh-proxy)
if [ -n "$2" ]; then
GH_PROXY=$2
shift
else
echo "Error: --gh-proxy requires a URL"
exit 1
fi
;;
--web-portal)
if [ -n "$2" ]; then
# 校验Web Portal格式
validate_web_portal "$2"
shift
else
echo "Error: --web-portal requires a URL"
exit 1
fi
;;
-d|--db-path)
if [ -n "$2" ]; then
DB_PATH="$2"
shift
else
echo "Error: -d/--db-path requires a file path"
exit 1
fi
;;
-l|--web-port)
if [ -n "$2" ]; then
WEB_PORT="$2"
shift
else
echo "Error: -l/--web-port requires a port number"
exit 1
fi
;;
-a|--api-port)
if [ -n "$2" ]; then
API_PORT="$2"
shift
else
echo "Error: -a/--api-port requires a port number"
exit 1
fi
;;
*)
echo "Unknown option: $1"
echo "Use './install.sh help' to see available options"
exit 1
;;
esac
shift
done
if [ -z "$INSTALL_PATH" ]; then
INSTALL_PATH='/opt/easytier'
fi
if [[ "$INSTALL_PATH" == */ ]]; then
INSTALL_PATH=${INSTALL_PATH%?}
fi
if ! $SKIP_FOLDER_FIX && ! [[ "$INSTALL_PATH" == */easytier ]]; then
INSTALL_PATH="$INSTALL_PATH/easytier"
fi
echo INSTALL PATH : $INSTALL_PATH
echo SKIP FOLDER FIX : $SKIP_FOLDER_FIX
echo SKIP FOLDER VERIFY : $SKIP_FOLDER_VERIFY
echo DEFAULT WEB PORTAL : $DEFAULT_WEB_PORTAL
# 输出web-embed参数
if [ "$COMMEND" = "install-web-embed" ]; then
echo DB PATH : $DB_PATH
echo WEB PORT : $WEB_PORT
echo API PORT : $API_PORT
fi
# clear
# check if unzip is installed
if ! command -v unzip >/dev/null 2>&1; then
echo -e "\r\n${RED_COLOR}Error: unzip is not installed${RES}\r\n"
exit 1
fi
# check if curl is installed
if ! command -v curl >/dev/null 2>&1; then
echo -e "\r\n${RED_COLOR}Error: curl is not installed${RES}\r\n"
exit 1
fi
echo -e "\r\n${RED_COLOR}----------------------NOTICE----------------------${RES}\r\n"
echo " This is a temporary script to install EasyTier "
echo " EasyTier requires a dedicated empty folder to install"
echo " EasyTier is a developing product and may have some issues "
echo " Using EasyTier requires some basic skills "
echo " You need to face the risks brought by using EasyTier at your own risk "
echo -e "\r\n${RED_COLOR}-------------------------------------------------${RES}\r\n"
# Get platform
if command -v arch >/dev/null 2>&1; then
platform=$(arch)
else
platform=$(uname -m)
fi
case "$platform" in
amd64 | x86_64)
ARCH="x86_64"
;;
arm64 | aarch64 | *armv8*)
ARCH="aarch64"
;;
*armv7*)
ARCH="armv7"
;;
*arm*)
ARCH="arm"
;;
mips)
ARCH="mips"
;;
mipsel)
ARCH="mipsel"
;;
*)
ARCH="UNKNOWN"
;;
esac
# support hf
if [[ "$ARCH" == "armv7" || "$ARCH" == "arm" ]]; then
if cat /proc/cpuinfo | grep Features | grep -i 'half' >/dev/null 2>&1; then
ARCH=${ARCH}hf
fi
fi
echo -e "\r\n${GREEN_COLOR}Your platform: ${ARCH} (${platform}) ${RES}\r\n" 1>&2
if [ "$(id -u)" != "0" ]; then
echo -e "\r\n${RED_COLOR}This script requires run as Root !${RES}\r\n" 1>&2
exit 1
elif [ "$ARCH" == "UNKNOWN" ]; then
echo -e "\r\n${RED_COLOR}Opus${RES}, this script do not support your platform\r\nTry ${GREEN_COLOR}install by hand${RES}\r\n"
exit 1
fi
# Detect init system
if command -v systemctl >/dev/null 2>&1; then
INIT_SYSTEM="systemd"
elif command -v rc-update >/dev/null 2>&1; then
INIT_SYSTEM="openrc"
else
echo -e "\r\n${RED_COLOR}Error: Unsupported init system (neither systemd nor OpenRC found)${RES}\r\n"
exit 1
fi
# 生成符合要求的machine-id(UUID格式,替换非法字符为合法字符)
generate_machine_id() {
local machine_id
# 生成标准UUID
if command -v uuidgen >/dev/null 2>&1; then
machine_id=$(uuidgen | tr 'A-F' 'a-f')
elif [ -r /proc/sys/kernel/random/uuid ]; then
machine_id=$(cat /proc/sys/kernel/random/uuid)
else
# 兜底生成
machine_id="$(cat /dev/urandom | tr -dc 'a-f0-9' | head -c8)-$(cat /dev/urandom | tr -dc 'a-f0-9' | head -c4)-$(cat /dev/urandom | tr -dc 'a-f0-9' | head -c4)-$(cat /dev/urandom | tr -dc 'a-f0-9' | head -c4)-$(cat /dev/urandom | tr -dc 'a-f0-9' | head -c12)"
fi
# 替换UUID中的部分字符以匹配示例格式(9g2h/534i5j091k4l)
machine_id=$(echo "$machine_id" | sed -E 's/([0-9]{1})([a-f0-9]{3})/\1g\2/; s/([0-9]{3})([a-f0-9]{2})([0-9]{4})([a-f0-9]{2})/\1i\2j\3k\4l/')
echo "$machine_id"
}
# 获取主机名(用于--hostname参数)
get_hostname() {
local hostname=$(hostname)
# 拼接格式:主机名_域名 - 过滤特殊字符,仅保留字母、数字、下划线、点
local fqdn=$(hostname -f 2>/dev/null || echo "$hostname")
# 过滤特殊字符
hostname=$(echo "$hostname" | tr -cd 'a-zA-Z0-9_.')
fqdn=$(echo "$fqdn" | tr -cd 'a-zA-Z0-9_.')
echo "${hostname}_${fqdn}"
}
CHECK() {
if ! $SKIP_FOLDER_VERIFY; then
if [ -f "$INSTALL_PATH/easytier-core" ]; then
echo "There is EasyTier in $INSTALL_PATH. Please choose other path or use \"update\""
echo -e "Or use Try ${GREEN_COLOR}--skip-folder-verify${RES} to skip"
exit 0
fi
fi
if [ ! -d "$INSTALL_PATH/" ]; then
mkdir -p $INSTALL_PATH
else
# Check weather path is empty
if ! $SKIP_FOLDER_VERIFY; then
if [ -n "$(ls -A $INSTALL_PATH)" ]; then
echo "EasyTier requires to be installed in an empty directory. Please choose a empty path"
echo -e "Or use Try ${GREEN_COLOR}--skip-folder-verify${RES} to skip"
echo -e "Current path: $INSTALL_PATH ( use ${GREEN_COLOR}--skip-folder-fix${RES} to disable folder fix )"
exit 1
fi
fi
fi
}
INSTALL() {
# Get version number
RESPONSE=$(curl -s "https://api.github.com/repos/EasyTier/EasyTier/releases/latest")
LATEST_VERSION=$(echo "$RESPONSE" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
LATEST_VERSION=$(echo -e "$LATEST_VERSION" | tr -d '[:space:]')
if [ -z "$LATEST_VERSION" ]; then
echo -e "\r\n${RED_COLOR}Opus${RES}, failure to get latest version. Check your internet\r\nOr try ${GREEN_COLOR}install by hand${RES}\r\n"
exit 1
fi
# Download
echo -e "\r\n${GREEN_COLOR}Downloading EasyTier $LATEST_VERSION ...${RES}"
rm -rf /tmp/easytier_tmp_install.zip
BASE_URL="https://github.com/EasyTier/EasyTier/releases/latest/download/easytier-linux-${ARCH}-${LATEST_VERSION}.zip"
DOWNLOAD_URL=$($NO_GH_PROXY && echo "$BASE_URL" || echo "${GH_PROXY}${BASE_URL}")
echo -e "Download URL: ${GREEN_COLOR}${DOWNLOAD_URL}${RES}"
curl -L ${DOWNLOAD_URL} -o /tmp/easytier_tmp_install.zip $CURL_BAR
# Unzip resource
echo -e "\r\n${GREEN_COLOR}Unzip resource ...${RES}"
unzip -o /tmp/easytier_tmp_install.zip -d $INSTALL_PATH/
mkdir -p $INSTALL_PATH/config
mv $INSTALL_PATH/easytier-linux-${ARCH}/* $INSTALL_PATH/
rm -rf $INSTALL_PATH/easytier-linux-${ARCH}/
chmod +x $INSTALL_PATH/easytier-core $INSTALL_PATH/easytier-cli $INSTALL_PATH/easytier-web $INSTALL_PATH/easytier-web-embed
if [ -f $INSTALL_PATH/easytier-core ] || [ -f $INSTALL_PATH/easytier-cli ]; then
echo -e "${GREEN_COLOR} Download successfully! ${RES}"
else
echo -e "${RED_COLOR} Download failed! ${RES}"
exit 1
fi
}
INIT() {
if [ ! -f "$INSTALL_PATH/easytier-core" ]; then
echo -e "\r\n${RED_COLOR}Opus${RES}, unable to find EasyTier\r\n"
exit 1
fi
# 生成machine-id和hostname
MACHINE_ID=$(generate_machine_id)
HOSTNAME_PARAM=$(get_hostname)
# 保存machine-id到配置目录(便于后续查看)
echo "MACHINE_ID=$MACHINE_ID" > $INSTALL_PATH/config/machine-id.conf
echo "WEB_PORTAL=$DEFAULT_WEB_PORTAL" >> $INSTALL_PATH/config/machine-id.conf
echo "HOSTNAME=$HOSTNAME_PARAM" >> $INSTALL_PATH/config/machine-id.conf
# Create init script (openrc)
if [ "$INIT_SYSTEM" = "openrc" ]; then
cat >/etc/init.d/easytier <<EOF
#!/sbin/openrc-run
name="EasyTier"
description="EasyTier Service"
command="$INSTALL_PATH/easytier-core"
# 移除-c参数,仅保留machine-id/-w/--hostname
command_args="--machine-id $MACHINE_ID -w $DEFAULT_WEB_PORTAL --hostname '$HOSTNAME_PARAM'"
command_user="nobody:nobody"
command_background=true
pidfile="/run/\${RC_SVCNAME}.pid"
depend() {
need net
}
EOF
chmod +x /etc/init.d/easytier
fi
# Create systemd service(单实例easytier.service,移除-c参数)
if [ "$INIT_SYSTEM" = "systemd" ]; then
cat >/etc/systemd/system/easytier.service <<EOF
[Unit]
Description=EasyTier Service
Wants=network.target
After=network.target network.service
StartLimitIntervalSec=0
[Service]
Type=simple
WorkingDirectory=$INSTALL_PATH
# 移除-c参数,仅保留machine-id/-w/--hostname
ExecStart=$INSTALL_PATH/easytier-core --machine-id $MACHINE_ID -w $DEFAULT_WEB_PORTAL --hostname '$HOSTNAME_PARAM'
Restart=always
RestartSec=1s
[Install]
WantedBy=multi-user.target
EOF
fi
# Startup service
if [ "$INIT_SYSTEM" = "systemd" ]; then
systemctl daemon-reload
# 启用/启动单实例easytier.service
systemctl enable easytier.service >/dev/null 2>&1
systemctl start easytier.service
else
rc-update add easytier default
rc-service easytier start
fi
# Clean old files
rm -rf /etc/systemd/system/easytier@.service # 删除旧的模板服务
rm -rf /usr/bin/easytier-core /usr/bin/easytier-cli
# Add link
ln -sf $INSTALL_PATH/easytier-core /usr/sbin/easytier-core
ln -sf $INSTALL_PATH/easytier-cli /usr/sbin/easytier-cli
}
SUCCESS() {
clear
echo " Install EasyTier successfully!"
echo -e "\r\n${YELLOW_COLOR}----------------------IMPORTANT----------------------${RES}"
echo -e "Machine ID: ${GREEN_COLOR}$MACHINE_ID${RES}"
echo -e "Web Portal (-w): ${GREEN_COLOR}$DEFAULT_WEB_PORTAL${RES}"
echo -e "Hostname (--hostname): ${GREEN_COLOR}$HOSTNAME_PARAM${RES}"
echo -e "Service Name: ${GREEN_COLOR}easytier.service${RES}"
echo -e "Machine ID saved to: ${GREEN_COLOR}$INSTALL_PATH/config/machine-id.conf${RES}"
echo -e "${YELLOW_COLOR}-----------------------------------------------------${RES}\r\n"
echo -e "Default Port: ${GREEN_COLOR}11010(UDP+TCP)${RES}, Notice allowing in firewall!\r\n"
echo -e "Service Management Commands:"
echo
if [ "$INIT_SYSTEM" = "systemd" ]; then
echo -e "Status: ${GREEN_COLOR}systemctl status easytier.service${RES}"
echo -e "Start: ${GREEN_COLOR}systemctl start easytier.service${RES}"
echo -e "Restart: ${GREEN_COLOR}systemctl restart easytier.service${RES}"
echo -e "Stop: ${GREEN_COLOR}systemctl stop easytier.service${RES}"
echo -e "Enable Auto-Start: ${GREEN_COLOR}systemctl enable easytier.service${RES}"
else
echo -e "Status: ${GREEN_COLOR}rc-service easytier status${RES}"
echo -e "Start: ${GREEN_COLOR}rc-service easytier start${RES}"
echo -e "Restart: ${GREEN_COLOR}rc-service easytier restart${RES}"
echo -e "Stop: ${GREEN_COLOR}rc-service easytier stop${RES}"
echo -e "Enable Auto-Start: ${GREEN_COLOR}rc-update add easytier default${RES}"
fi
echo
}
UNINSTALL() {
echo -e "\r\n${GREEN_COLOR}Uninstall EasyTier ...${RES}\r\n"
echo -e "${GREEN_COLOR}Stop process ...${RES}"
if [ "$INIT_SYSTEM" = "systemd" ]; then
systemctl disable easytier.service >/dev/null 2>&1
systemctl stop easytier.service >/dev/null 2>&1
# 清理旧的模板服务残留
systemctl disable "easytier@*" >/dev/null 2>&1
systemctl stop "easytier@*" >/dev/null 2>&1
else
rc-update del easytier
rc-service easytier stop
fi
echo -e "${GREEN_COLOR}Delete files ...${RES}"
# 删除所有相关文件
rm -rf $INSTALL_PATH
rm -rf /etc/systemd/system/easytier.service /etc/systemd/system/easytier@.service
rm -rf /etc/init.d/easytier
rm -rf /usr/bin/easytier-core /usr/bin/easytier-cli
rm -rf /usr/sbin/easytier-core /usr/sbin/easytier-cli
if [ "$INIT_SYSTEM" = "systemd" ]; then
systemctl daemon-reload
fi
echo -e "\r\n${GREEN_COLOR}EasyTier was removed successfully! ${RES}\r\n"
}
# Minimizes downtime by preparing new files before stopping the service.
UPDATE() {
if [ ! -f "$INSTALL_PATH/easytier-core" ]; then
echo -e "\r\n${RED_COLOR}Error${RES}: EasyTier not found in $INSTALL_PATH. Cannot perform update.\r\n"
exit 1
fi
# 1. Get the latest version info (while service is still running)
echo -e "${GREEN_COLOR}Checking for the latest version...${RES}"
RESPONSE=$(curl -s "https://api.github.com/repos/EasyTier/EasyTier/releases/latest")
LATEST_VERSION=$(echo "$RESPONSE" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
LATEST_VERSION=$(echo -e "$LATEST_VERSION" | tr -d '[:space:]')
if [ -z "$LATEST_VERSION" ]; then
echo -e "\r\n${RED_COLOR}Error${RES}: Failed to get the latest version. Please check your network connection.\r\n"
exit 1
fi
echo -e "Latest version found: ${GREEN_COLOR}$LATEST_VERSION${RES}"
# 2. Download and extract the new version to a temporary directory (while service is still running)
TEMP_UPDATE_DIR=$(mktemp -d /tmp/easytier_update_XXXXXX)
echo -e "${GREEN_COLOR}Downloading new version to temporary directory: $TEMP_UPDATE_DIR${RES}"
BASE_URL="https://github.com/EasyTier/EasyTier/releases/latest/download/easytier-linux-${ARCH}-${LATEST_VERSION}.zip"
DOWNLOAD_URL=$($NO_GH_PROXY && echo "$BASE_URL" || echo "${GH_PROXY}${BASE_URL}")
echo -e "Download URL: ${GREEN_COLOR}${DOWNLOAD_URL}${RES}"
curl -L ${DOWNLOAD_URL} -o "$TEMP_UPDATE_DIR/easytier.zip" $CURL_BAR
if [ $? -ne 0 ]; then
echo -e "${RED_COLOR}Download failed!${RES}"
rm -rf "$TEMP_UPDATE_DIR"
exit 1
fi
unzip -o "$TEMP_UPDATE_DIR/easytier.zip" -d "$TEMP_UPDATE_DIR/"
NEW_CORE_FILE="$TEMP_UPDATE_DIR/easytier-linux-${ARCH}/easytier-core"
if [ ! -f "$NEW_CORE_FILE" ]; then
echo -e "${RED_COLOR}Extraction failed or the downloaded archive is invalid.${RES}"
rm -rf "$TEMP_UPDATE_DIR"
exit 1
fi
echo -e "${GREEN_COLOR}New version is ready. Starting update process...${RES}"
# 3. Enter minimal downtime window
if [ "$INIT_SYSTEM" = "systemd" ]; then
echo -e "\r\n${YELLOW_COLOR}Stopping EasyTier service...${RES}"
systemctl stop easytier.service
else
echo -e "\r\n${YELLOW_COLOR}Stopping EasyTier service...${RES}"
rc-service easytier stop
fi
# Backup critical files
echo "Backing up configuration..."
BACKUP_CONFIG_DIR=$(mktemp -d /tmp/easytier_config_backup_XXXXXX)
if [ -d "$INSTALL_PATH/config" ]; then
cp -a "$INSTALL_PATH/config" "$BACKUP_CONFIG_DIR/"
fi
echo "Replacing files..."
# Remove old binaries and docs, but not the config directory
rm -f "$INSTALL_PATH/easytier-core" "$INSTALL_PATH/easytier-cli" "$INSTALL_PATH/LICENSE" "$INSTALL_PATH/README.md"
# Move new files into the installation directory
mv "$TEMP_UPDATE_DIR/easytier-linux-${ARCH}"/* "$INSTALL_PATH/"
chmod +x "$INSTALL_PATH/easytier-core" "$INSTALL_PATH/easytier-cli" "$INSTALL_PATH/easytier-web" "$INSTALL_PATH/easytier-web-embed"
# Restore configuration
if [ -d "$BACKUP_CONFIG_DIR/config" ]; then
cp -af "$BACKUP_CONFIG_DIR/config/." "$INSTALL_PATH/config/"
fi
# 更新服务启动脚本(保留原有参数)
if [ -f "$INSTALL_PATH/config/machine-id.conf" ]; then
source "$INSTALL_PATH/config/machine-id.conf"
# 更新时再次校验Web Portal格式
validate_web_portal "$WEB_PORTAL"
if [ "$INIT_SYSTEM" = "systemd" ]; then
sed -i "s|ExecStart=.*|ExecStart=$INSTALL_PATH/easytier-core --machine-id $MACHINE_ID -w $DEFAULT_WEB_PORTAL --hostname '$HOSTNAME'|" /etc/systemd/system/easytier.service
systemctl daemon-reload
elif [ "$INIT_SYSTEM" = "openrc" ]; then
sed -i "s|command_args=.*|command_args=\"--machine-id $MACHINE_ID -w $DEFAULT_WEB_PORTAL --hostname '$HOSTNAME'\"|" /etc/init.d/easytier
fi
fi
# 4. Start the service
if [ "$INIT_SYSTEM" = "systemd" ]; then
echo -e "${GREEN_COLOR}Starting new version of EasyTier service...${RES}"
systemctl start easytier.service
else
echo -e "${GREEN_COLOR}Starting new version of EasyTier service...${RES}"
rc-service easytier start
fi
# 5. Clean up temporary files
echo "Cleaning up temporary files..."
rm -rf "$TEMP_UPDATE_DIR"
rm -rf "$BACKUP_CONFIG_DIR"
echo -e "\r\n${GREEN_COLOR}EasyTier was successfully updated to version $LATEST_VERSION!${RES}\r\n"
}
# CURL progress
if curl --help | grep progress-bar >/dev/null 2>&1; then
CURL_BAR="--progress-bar"
fi
# The temp directory must exist
if [ ! -d "/tmp" ]; then
mkdir -p /tmp
fi
echo $COMMEND
# ========== 扩展命令分发逻辑 ==========
if [ "$COMMEND" = "uninstall" ]; then
UNINSTALL
elif [ "$COMMEND" = "uninstall-web" ]; then
UNINSTALL_WEB
elif [ "$COMMEND" = "update" ]; then
UPDATE
elif [ "$COMMEND" = "install" ]; then
CHECK
INSTALL
INIT
if [ -f "$INSTALL_PATH/easytier-core" ]; then
SUCCESS
else
echo -e "${RED_COLOR} Install fail, try install by hand${RES}"
fi
elif [ "$COMMEND" = "install-web" ]; then
INSTALL_WEB
elif [ "$COMMEND" = "install-web-embed" ]; then
INSTALL_WEB_EMBED
else
echo -e "${RED_COLOR} Error Command ${RES}\n\r"
echo " ALLOW:"
echo -e "\n\r${GREEN_COLOR} install, install-web, install-web-embed, uninstall, uninstall-web, update, help ${RES}"
fi
rm -rf /tmp/easytier_tmp_*# 将提供的自定义脚本保存为install.sh后,赋予执行权限
chmod +x install.sh提示:自定义脚本默认启用GitHub代理(https://ghfast.top/),可通过--no-gh-proxy参数禁用代理,或通过--gh-proxy参数指定自定义代理URL;同时支持--skip-folder-verify等跳过目录校验的参数,适合特殊安装场景。
二、核心步骤:Linux自建EasyTier服务器
EasyTier服务器节点是整个私有网络的核心,负责节点间的通信与转发。部署过程十分简洁,支持默认配置快速启动,也可通过修改配置文件自定义参数。
2.1 快速部署:使用默认配置安装
若无需自定义配置,直接执行以下命令即可完成核心服务安装(自定义脚本支持更多默认优化配置):
# 基础安装(默认安装路径/opt/easytier)
bash ./install.sh install
# 可选:自定义安装路径(如安装到/data/easytier)
bash ./install.sh install /data/easytier
# 可选:跳过目录校验(适合非空目录安装)
bash ./install.sh install --skip-folder-verify执行后脚本会自动完成以下操作(自定义脚本增强特性):
检测系统架构(x86_64、arm64、armv7等多种架构,兼容性更强),自动下载对应版本的EasyTier核心包;
默认安装路径:/opt/easytier(可在安装时指定自定义路径,如bash ./install.sh install /data/easytier);
生成默认配置文件(/opt/easytier/config/default.conf)、机器唯一码(UUID格式,自动保存到/opt/easytier/config/machine-id.conf,便于后续查看);
创建系统服务(easytier.service),并设置开机自启。
安装成功后,会输出关键信息(机器ID、默认Web Portal、主机名、服务管理命令),核心默认端口为11010(UDP+TCP),客户端可通过此端口连接服务器。其中默认Web Portal格式为“udp://public.easytiertop:22020/主机名_年月日@”,可通过--web-portal参数自定义。
2.2 自定义配置:修改default.conf文件
若需要自定义网络名称、密码、端口等参数,可修改安装目录下的配置文件:
vi /opt/easytier/config/default.conf核心配置参数说明(参考示例):
# 自定义主机名(用于节点识别)
hostname = "easytier-server-01"
# 实例名(可自定义)
instance_name = "server-main"
# 机器唯一码(建议指定,避免重启后配置丢失)
instance_id = "bf3d6922-2d97-47ce-99c6-ed74d6abdb7d"
# 是否启用DHCP(自动分配IP,默认开启)
dhcp = true
# 监听端口(默认11010,支持TCP、UDP、WG协议)
listeners = [
"tcp://0.0.0.0:11010",
"udp://0.0.0.0:11010",
"wg://0.0.0.0:11011",
]
# 私有网络配置(核心!用于节点间加密通信)
[network_identity]
network_name = "my-private-network" # 自定义网络名称
network_secret = "my-strong-password" # 自定义网络密码(节点需一致才能互通)
# 对等节点配置(若需要连接其他EasyTier节点,可添加)
[[peer]]
uri = "tcp://other-server-ip:11010" # 其他节点的连接地址
# 代理网络配置(指定需要穿透的网段)
[[proxy_network]]
cidr = "192.168.0.0/24" # 本地需要被其他节点访问的网段修改完成后,重启服务使配置生效:
# systemd系统
systemctl restart easytier.service
# OpenRC系统(如OpenWrt)
rc-service easytier restart三、Web管理端部署:两种模式任选
EasyTier提供了Web管理界面,支持可视化配置节点、管理网络。根据需求可选择“仅安装Web后端API”(对接官方前端)或“安装Web前后端集成版”(本地直接访问),两种模式的部署命令都十分简洁。
3.1 模式1:仅安装Web后端API(推荐,轻量)
此模式仅部署Web后端API服务,不包含前端页面,可直接使用EasyTier官方前端(https://easytier.cn/web)访问你的后端,适合不想额外占用服务器资源的场景。
# 安装仅Web后端API服务
bash ./install.sh install-web部署完成后(自定义脚本会自动检查核心包,缺失则自动安装):
Web后端API地址:http://服务器IP:11211
服务名称:easytier-web.service(与集成版共用服务名,安装后会覆盖原有Web服务)
使用方式:打开官方前端,在设置中填写你的API地址(http://服务器IP:11211),即可关联管理你的节点。
3.2 模式2:安装Web前后端集成版(本地访问)
此模式会同时部署Web前端和后端服务,可直接通过服务器IP+前端端口访问管理界面,适合无外网访问权限的内网环境。支持自定义数据存储路径、前端/后端端口。
3.2.1 默认参数安装
# 默认参数安装Web前后端集成版
bash ./install.sh install-web-embed默认配置:
Web前端地址:http://服务器IP:11210
Web后端API地址:http://服务器IP:11211
数据存储路径:/opt/easytier/et.db(SQLite数据库)
3.2.2 自定义参数安装
若需要修改端口、数据存储路径,可通过以下参数自定义(自定义脚本同时支持短参数和长参数,更灵活):
-d/--db-path:指定数据存储路径(如自定义到/data目录,避免系统盘占用);
-l/--web-port:指定Web前端端口(如修改为21010,避免端口冲突);
-a/--api-port:指定Web后端端口(如修改为21011)。
示例:修改前端端口为21010、后端端口为21011,数据存储到/data/easytier/et.db(两种参数格式均可):
# 短参数格式
bash ./install.sh install-web-embed -d /data/easytier/et.db -l 21010 -a 21011
# 长参数格式(更易理解)
bash ./install.sh install-web-embed --db-path /data/easytier/et.db --web-port 21010 --api-port 21011安装成功后,脚本会输出最终的ExecStart命令,可通过该命令验证配置是否生效。
四、客户端接入:两种方式对接自建节点
Linux客户端接入自建的EasyTier节点,支持两种方式:通过Web控制台配置(简单直观)、通过本地配置文件配置(适合无Web界面场景)。
4.1 方式1:通过Web管理控制台接入
此方式需先部署Web后端(模式1或模式2),客户端通过指定Web Portal地址,自动同步控制台配置,无需手动修改文件。
# 格式:bash ./install.sh install --web-portal 自定义Portal地址
# 示例1:使用自定义令牌
bash ./install.sh install --web-portal udp://ouy.taofile.cn:22020/ouy@jiajun.com
# 示例2:使用脚本默认生成的Portal(主机名_年月日)
bash ./install.sh install参数说明:
udp://服务器地址:22020:Web后端的Portal地址(默认端口22020,需确保服务器开放此端口);
ouy@jiajun.com:自定义的账户标识(用于在控制台区分不同客户端)。
注意:自定义脚本内置Web Portal格式校验函数,仅允许udp://或tcp://开头,支持自动修复路径中的双斜杠错误;若格式错误(如空令牌、端口非法),会输出详细错误原因和正确示例,无需手动排查。正确格式为“udp://域名:端口/令牌”,如“udp://public.easytiertop:22020/sgphwyun_20251219@”。
4.2 方式2:通过本地配置文件接入
适合无Web界面的场景,通过手动修改客户端的default.conf文件,指定要连接的服务器节点。
先在客户端安装EasyTier核心服务:
# 在客户端安装EasyTier核心服务(使用自定义脚本)
bash ./install.sh install修改客户端配置文件:
vi /opt/easytier/config/default.conf关键配置(需与服务器端一致):
# 与服务器端一致的私有网络配置
[network_identity]
network_name = "my-private-network" # 必须和服务器的network_name相同
network_secret = "my-strong-password" # 必须和服务器的network_secret相同
# 添加服务器节点为对等节点
[[peer]]
uri = "tcp://服务器IP:11010" # 服务器的核心端口(默认11010)重启客户端服务,完成接入:
systemctl restart easytier.service五、常用操作:服务管理与卸载
掌握以下常用命令,方便后续维护节点服务:
5.1 服务状态查看/启动/停止/重启
# systemd系统(Ubuntu/CentOS 7+等)
# 查看核心服务状态
systemctl status easytier.service
# 查看Web服务状态
systemctl status easytier-web.service
# 启动服务
systemctl start easytier.service
systemctl start easytier-web.service
# 停止服务
systemctl stop easytier.service
systemctl stop easytier-web.service
# 重启服务
systemctl restart easytier.service
systemctl restart easytier-web.service
# OpenRC系统(OpenWrt等)
rc-service easytier status
rc-service easytier start
rc-service easytier stop
rc-service easytier restart
# 自定义脚本新增:升级EasyTier到最新版本(无需重新下载脚本)
bash ./install.sh update5.2 卸载操作
根据需要选择卸载Web服务或全量卸载EasyTier:
仅卸载Web服务(保留核心服务):
# 仅卸载Web服务(保留核心服务,使用自定义脚本)
bash ./install.sh uninstall-web说明:自定义脚本执行此命令后,会自动停止并删除easytier-web.service,清理对应的服务配置文件;若需要彻底删除Web相关二进制文件,需手动删除/opt/easytier/easytier-web、/opt/easytier/easytier-web-embed等文件。
全量卸载EasyTier(核心服务+Web服务+所有配置文件):
# 全量卸载EasyTier(核心服务+Web服务+所有配置文件,使用自定义脚本)
bash ./install.sh uninstall六、常见问题排查
6.1 安装时提示“Unknown option”
原因:旧版本官方脚本不支持短参数,但本次使用的自定义脚本同时支持短参数(如-l 21010)和长参数(如--web-port 21010),若仍提示此错误,可尝试重新执行脚本或使用长参数格式。解决方案:直接使用自定义脚本的长参数格式(如--web-port 21010),或确认脚本权限是否正确(需chmod +x install.sh)。
6.2 客户端无法连接服务器
排查步骤:
检查服务器防火墙是否开放对应端口(11010、11210、11211等);
查看服务状态,确认easytier.service正常运行;
检查客户端与服务器的network_name、network_secret是否一致;
确认服务器IP地址正确,无网络路由问题。
6.3 Web界面无法访问
排查步骤:
检查easytier-web.service是否正常运行;
确认自定义端口未被其他服务占用(可通过netstat -tuln | grep 端口号 查看);
若为集成版,检查数据存储路径是否有写入权限(如/opt/easytier需root权限)。
七、总结
Linux自建EasyTier节点的核心优势在于部署简单、配置灵活,支持多种网络场景需求。通过本文的步骤,你可以快速搭建核心服务器、部署Web管理端,并完成客户端接入。关键注意事项:确保端口开放、网络配置参数一致、使用正确的Web Portal格式。如果需要更高级的功能(如多节点集群、WG协议优化),可参考官方文档进一步探索。
说明:本文所有操作均基于自定义EasyTier安装脚本,该脚本增强了架构兼容性、参数支持和错误校验能力,相比官方脚本更易用、更稳定。脚本核心功能:安装/升级/卸载核心服务、Web服务(两种模式),支持自定义安装路径、端口、数据存储路径等。
- 感谢你赐予我前进的力量

