#!/bin/bash red='\033[0;31m' green='\033[0;32m' yellow='\033[0;33m' plain='\033[0m' cur_dir=$(pwd) # check root [[ $EUID -ne 0 ]] && echo -e "${red}错误:${plain} 必须使用root用户运行此脚本!\n" && exit 1 # check os if [[ -f /etc/redhat-release ]]; then release="centos" elif cat /etc/issue | grep -Eqi "alpine"; then release="alpine" echo -e "${red}脚本暂不支持alpine系统!${plain}\n" && exit 1 elif cat /etc/issue | grep -Eqi "debian"; then release="debian" elif cat /etc/issue | grep -Eqi "ubuntu"; then release="ubuntu" elif cat /etc/issue | grep -Eqi "centos|red hat|redhat|rocky|alma|oracle linux"; then release="centos" elif cat /proc/version | grep -Eqi "debian"; then release="debian" elif cat /proc/version | grep -Eqi "ubuntu"; then release="ubuntu" elif cat /proc/version | grep -Eqi "centos|red hat|redhat|rocky|alma|oracle linux"; then release="centos" elif cat /proc/version | grep -Eqi "arch"; then release="arch" else echo -e "${red}未检测到系统版本,请联系脚本作者!${plain}\n" && exit 1 fi arch=$(uname -m) if [[ $arch == "x86_64" || $arch == "x64" || $arch == "amd64" ]]; then arch="64" elif [[ $arch == "aarch64" || $arch == "arm64" ]]; then arch="arm64-v8a" elif [[ $arch == "s390x" ]]; then arch="s390x" else arch="64" echo -e "${red}检测架构失败,使用默认架构: ${arch}${plain}" fi echo "架构: ${arch}" if [ "$(getconf WORD_BIT)" != '32' ] && [ "$(getconf LONG_BIT)" != '64' ] ; then echo "本软件不支持 32 位系统(x86),请使用 64 位系统(x86_64),如果检测有误,请联系作者" exit 2 fi # os version if [[ -f /etc/os-release ]]; then os_version=$(awk -F'[= ."]' '/VERSION_ID/{print $3}' /etc/os-release) fi if [[ -z "$os_version" && -f /etc/lsb-release ]]; then os_version=$(awk -F'[= ."]+' '/DISTRIB_RELEASE/{print $2}' /etc/lsb-release) fi if [[ x"${release}" == x"centos" ]]; then if [[ ${os_version} -le 6 ]]; then echo -e "${red}请使用 CentOS 7 或更高版本的系统!${plain}\n" && exit 1 fi if [[ ${os_version} -eq 7 ]]; then echo -e "${red}注意: CentOS 7 无法使用hysteria2协议!${plain}\n" fi elif [[ x"${release}" == x"ubuntu" ]]; then if [[ ${os_version} -lt 16 ]]; then echo -e "${red}请使用 Ubuntu 16 或更高版本的系统!${plain}\n" && exit 1 fi elif [[ x"${release}" == x"debian" ]]; then if [[ ${os_version} -lt 8 ]]; then echo -e "${red}请使用 Debian 8 或更高版本的系统!${plain}\n" && exit 1 fi fi install_base() { if [[ x"${release}" == x"centos" ]]; then yum install epel-release -y yum install wget curl unzip tar crontabs socat -y yum install ca-certificates wget -y update-ca-trust force-enable elif [[ x"${release}" == x"debian" ]]; then apt-get update -y apt-get install wget curl unzip tar cron socat ca-certificates -y update-ca-certificates elif [[ x"${release}" == x"ubuntu" ]]; then apt-get update -y apt-get install wget curl unzip tar cron socat ca-certificates -y update-ca-certificates elif [[ x"${release}" == x"arch" ]]; then pacman -Sy pacman -S --noconfirm --needed wget curl unzip tar cron socat ca-certificates fi } # 0: running, 1: not running, 2: not installed check_status() { if [[ ! -f /etc/systemd/system/PPanel-node.service ]]; then return 2 fi temp=$(systemctl status PPanel-node | grep Active | awk '{print $3}' | cut -d "(" -f2 | cut -d ")" -f1) if [[ x"${temp}" == x"running" ]]; then return 0 else return 1 fi } # 检查系统是否有 IPv6 地址 check_ipv6_support() { if ip -6 addr | grep -q "inet6"; then echo "1" # 支持 IPv6 else echo "0" # 不支持 IPv6 fi } add_node_config() { echo -e "${green}请选择节点核心类型:${plain}" echo -e "${green}1. xray${plain}" echo -e "${green}2. singbox${plain}" read -rp "请输入:" core_type if [ "$core_type" == "1" ]; then core="xray" core_xray=true elif [ "$core_type" == "2" ]; then core="sing" core_sing=true else echo "无效的选择。请选择 1 2。" continue fi while true; do read -rp "请输入节点Node ID:" NodeID # 判断NodeID是否为正整数 if [[ "$NodeID" =~ ^[0-9]+$ ]]; then break # 输入正确,退出循环 else echo "错误:请输入正确的数字作为Node ID。" fi done echo -e "${yellow}请选择节点传输协议:${plain}" echo -e "${green}1. Shadowsocks${plain}" echo -e "${green}2. Vless${plain}" echo -e "${green}3. Vmess${plain}" if [ "$core_sing" == true ]; then echo -e "${green}4. Tuic${plain}" echo -e "${green}5. Hysteria2${plain}" fi echo -e "${green}6. Trojan${plain}" if [ "$core_sing" == true ]; then echo -e "${green}7. AnyTLS${plain}" fi read -rp "请输入:" NodeType case "$NodeType" in 1 ) NodeType="shadowsocks" ;; 2 ) NodeType="vless" ;; 3 ) NodeType="vmess" ;; 4 ) NodeType="tuic" ;; 5 ) NodeType="hysteria2" ;; 6 ) NodeType="trojan" ;; 7 ) NodeType="anytls" ;; * ) NodeType="shadowsocks" ;; esac if [ "$NodeType" == "vless" ]; then read -rp "请选择是否为reality节点?(y/n)" isreality elif [ "$NodeType" == "hysteria2" ] || [ "$NodeType" == "tuic" ] || [ "$NodeType" == "anytls" ]; then fastopen=false istls="y" fi if [[ "$isreality" != "y" && "$isreality" != "Y" && "$istls" != "y" ]]; then read -rp "请选择是否进行TLS配置?(y/n)" istls fi certmode="none" certdomain="example.com" if [[ "$isreality" != "y" && "$isreality" != "Y" && ( "$istls" == "y" || "$istls" == "Y" ) ]]; then echo -e "${yellow}请选择证书申请模式:${plain}" echo -e "${green}1. http模式自动申请,节点域名已正确解析${plain}" echo -e "${green}2. dns模式自动申请,需填入正确域名服务商API参数${plain}" echo -e "${green}3. self模式,自签证书或提供已有证书文件${plain}" read -rp "请输入:" certmode case "$certmode" in 1 ) certmode="http" ;; 2 ) certmode="dns" ;; 3 ) certmode="self" ;; esac read -rp "请输入节点证书域名(example.com)]:" certdomain if [ "$certmode" != "http" ]; then echo -e "${red}请手动修改配置文件后重启PPanel-node!${plain}" fi fi ipv6_support=$(check_ipv6_support) listen_ip="0.0.0.0" if [ "$ipv6_support" -eq 1 ]; then listen_ip="::" fi node_config="" if [ "$core_type" == "1" ]; then node_config=$(cat < /etc/PPanel-node/config.json { "Log": { "Level": "error", "Output": "" }, "Cores": $cores_config, "Nodes": [$formatted_nodes_config] } EOF # 创建 custom_outbound.json 文件 cat < /etc/PPanel-node/custom_outbound.json [ { "tag": "IPv4_out", "protocol": "freedom", "settings": { "domainStrategy": "UseIPv4v6" } }, { "tag": "IPv6_out", "protocol": "freedom", "settings": { "domainStrategy": "UseIPv6" } }, { "protocol": "blackhole", "tag": "block" } ] EOF # 创建 route.json 文件 cat < /etc/PPanel-node/route.json { "domainStrategy": "AsIs", "rules": [ { "outboundTag": "block", "ip": [ "geoip:private" ] }, { "outboundTag": "block", "ip": [ "127.0.0.1/32", "10.0.0.0/8", "fc00::/7", "fe80::/10", "172.16.0.0/12" ] }, { "outboundTag": "block", "protocol": [ "bittorrent" ] }, { "outboundTag": "IPv4_out", "network": "udp,tcp" } ] } EOF # 创建 sing_origin.json 文件 cat < /etc/PPanel-node/sing_origin.json { "dns": { "servers": [ { "tag": "cf", "address": "1.1.1.1" } ], "strategy": "prefer_ipv4" }, "outbounds": [ { "tag": "direct", "type": "direct", "domain_strategy": "prefer_ipv4" }, { "type": "block", "tag": "block" } ], "route": { "rules": [ { "ip_is_private": true, "outbound": "block" }, { "outbound": "direct", "network": [ "udp","tcp" ] } ] }, "experimental": { "cache_file": { "enabled": true } } } EOF echo -e "${green}PPanel-node 配置文件生成完成,正在重新启动服务${plain}" systemctl restart PPanel-node.service } install_PPanel-node() { if [[ -e /usr/local/PPanel-node/ ]]; then rm -rf /usr/local/PPanel-node/ fi mkdir /usr/local/PPanel-node/ -p cd /usr/local/PPanel-node/ if [ $# == 0 ] ;then last_version=$(curl -Ls "https://api.github.com/repos/wyx2685/PPanel-node/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') if [[ ! -n "$last_version" ]]; then echo -e "${red}检测 PPanel-node 版本失败,可能是超出 Github API 限制,请稍后再试,或手动指定 PPanel-node 版本安装${plain}" exit 1 fi echo -e "检测到 PPanel-node 最新版本:${last_version},开始安装" wget -q -N --no-check-certificate -O /usr/local/PPanel-node/PPanel-node-linux.zip https://github.com/wyx2685/PPanel-node/releases/download/${last_version}/PPanel-node-linux-${arch}.zip if [[ $? -ne 0 ]]; then echo -e "${red}下载 PPanel-node 失败,请确保你的服务器能够下载 Github 的文件${plain}" exit 1 fi else last_version=$1 url="https://github.com/wyx2685/PPanel-node/releases/download/${last_version}/PPanel-node-linux-${arch}.zip" echo -e "开始安装 PPanel-node $1" wget -q -N --no-check-certificate -O /usr/local/PPanel-node/PPanel-node-linux.zip ${url} if [[ $? -ne 0 ]]; then echo -e "${red}下载 PPanel-node $1 失败,请确保此版本存在${plain}" exit 1 fi fi unzip PPanel-node-linux.zip rm PPanel-node-linux.zip -f chmod +x ppnode mkdir /etc/PPanel-node/ -p rm /etc/systemd/system/PPanel-node.service -f file="https://raw.githubusercontent.com/wyx2685/ppanel-node/master/Scripts/PPanel-node.service" wget -q -N --no-check-certificate -O /etc/systemd/system/PPanel-node.service ${file} systemctl daemon-reload systemctl stop PPanel-node systemctl enable PPanel-node echo -e "${green}PPanel-node ${last_version}${plain} 安装完成,已设置开机自启" cp geoip.dat /etc/PPanel-node/ cp geosite.dat /etc/PPanel-node/ if [[ ! -f /etc/PPanel-node/config.json ]]; then cp config.json /etc/PPanel-node/ echo -e "" echo -e "全新安装,请先参看文档,配置必要的内容" first_install=true else systemctl start PPanel-node sleep 2 check_status echo -e "" if [[ $? == 0 ]]; then echo -e "${green}PPanel-node 重启成功${plain}" else echo -e "${red}PPanel-node 启动失败" fi first_install=false fi curl -o /usr/bin/ppnode -Ls https://raw.githubusercontent.com/wyx2685/ppanel-node/master/Scripts/ppnode.sh chmod +x /usr/bin/ppnode cd $cur_dir rm -f install.sh echo -e "" echo "PPanel-node 管理脚本使用方法: " echo "------------------------------------------" echo "ppnode - 显示管理菜单 (功能更多)" echo "ppnode start - 启动 PPanel-node" echo "ppnode stop - 停止 PPanel-node" echo "ppnode restart - 重启 PPanel-node" echo "ppnode status - 查看 PPanel-node 状态" echo "ppnode enable - 设置 PPanel-node 开机自启" echo "ppnode disable - 取消 PPanel-node 开机自启" echo "ppnode log - 查看 PPanel-node 日志" echo "ppnode x25519 - 生成 x25519 密钥" echo "ppnode generate - 生成 PPanel-node 配置文件" echo "ppnode update - 更新 PPanel-node" echo "ppnode update x.x.x - 安装 PPanel-node 指定版本" echo "ppnode install - 安装 PPanel-node" echo "ppnode uninstall - 卸载 PPanel-node" echo "ppnode version - 查看 PPanel-node 版本" echo "------------------------------------------" # 首次安装询问是否生成配置文件 if [[ $first_install == true ]]; then read -rp "检测到你为第一次安装PPanel-node,是否自动直接生成配置文件?(y/n): " if_generate if [[ $if_generate == [Yy] ]]; then generate_config_file fi fi } echo -e "${green}开始安装${plain}" install_base install_PPanel-node $1