commit 1f7b4314c3cce9e4def5ea0438804583d5ac8bee Author: starry <115192496+sky22333@users.noreply.github.com> Date: Wed Jul 16 12:35:21 2025 +0000 update diff --git a/.github/workflows/docker-ghcr.yml b/.github/workflows/docker-ghcr.yml new file mode 100644 index 0000000..eac4a86 --- /dev/null +++ b/.github/workflows/docker-ghcr.yml @@ -0,0 +1,41 @@ +name: ghcr镜像构建 +on: + workflow_dispatch: + inputs: + version: + description: 'Version number' + required: true + default: 'ansible' + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: write + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Docker Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set version from input + run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV + + - name: Build and push Docker image + run: | + docker buildx build --push \ + --platform linux/amd64,linux/arm64 \ + --tag ghcr.io/${{ github.repository }}:${{ env.VERSION }} \ + --build-arg VERSION=${{ env.VERSION }} \ + ./ansible + env: + GHCR_PUBLIC: true diff --git a/README.md b/README.md new file mode 100644 index 0000000..ab0cb26 --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +- [Github文件加速](https://yishijie.gitlab.io/ziyuan/) + +--- + +- Linux一键更换系统源脚本 +``` +bash <(curl -sSL https://github.com/sky22333/shell/raw/main/dev/mirrors.sh) +``` +- Linux一键切换官方源 +``` +bash <(curl -sSL https://github.com/sky22333/shell/raw/main/dev/mirrors.sh) --use-official-source true +``` + +- 一键安装Docker和配置镜像地址 +``` +bash <(curl -sSL https://github.com/sky22333/shell/raw/main/dev/docker.sh) +``` + + +- 一键自动部署异次元发卡 +> 适用于`Debian 11+` `Ubuntu 18.04+`系统 基于`Caddy` `php` `mariadb`环境 +``` +bash <(curl -sSL https://github.com/sky22333/shell/raw/main/web/acgfaka.sh) +``` + +- 一键自动安装WordPress +> 适用于`Debian 11+` `Ubuntu 18.04+`系统 基于`Caddy` `php` `mariadb`环境 +``` +bash <(curl -sSL https://github.com/sky22333/shell/raw/main/web/wp.sh) +``` + + +- acme.sh 证书一键申请脚本 + +``` +bash <(curl -sSL https://github.com/sky22333/shell/raw/main/dev/acme.sh) +``` + + +- sing-box一键脚本(多协议) +``` +bash <(curl -sSL https://cdn.jsdelivr.net/gh/admin8800/sing-box@main/install.sh) +``` +> **使用`sb`命令查看管理菜单** diff --git a/ansible/Dockerfile b/ansible/Dockerfile new file mode 100644 index 0000000..6f52e2e --- /dev/null +++ b/ansible/Dockerfile @@ -0,0 +1,8 @@ +FROM alpine + +RUN apk add --update --no-cache ansible bash openssh sshpass rsync py3-passlib + +COPY ansible /etc/ansible + +ENTRYPOINT [] +CMD ["ansible", "--help"] \ No newline at end of file diff --git a/ansible/README.md b/ansible/README.md new file mode 100644 index 0000000..841aa5d --- /dev/null +++ b/ansible/README.md @@ -0,0 +1,235 @@ +### 1:Ansibler容器环境搭建 + +#### 运行示例 +``` +docker run --rm -it \ + -v ./ansible:/etc/ansible \ + -w /etc/ansible \ + ghcr.io/sky22333/shell:ansible \ + ansible-playbook renwu.yml +``` + + +| 参数 | 作用 | +|------|------| +| `docker run` | 启动一个容器运行指定镜像 | +| `--rm` | 容器退出后自动删除(保持系统干净) | +| `-it` | 启用交互终端模式,允许输出彩色日志和提示信息 | +| `-v ./ansible:/etc/ansible` | 把你本地的 `./ansible` 目录挂载到容器的 `/etc/ansible` 路径下 | +| `-w /etc/ansible` | 设置容器的工作目录为 `/etc/ansible`,也就是你挂载的目录 | +| `ghcr.io/sky22333/shell:ansible` | 使用此镜像,预装了 ansible | +| `ansible-playbook renwu.yml` | 容器中执行的命令,也就是运行挂载进去的 `renwu.yml` 剧本文件 | + + + + +#### ansible 目录结构 +``` +. +├── ansible/ +│ ├── renwu.yml +│ ├── hosts +│ └── ansible.cfg +``` + + +`ansible`有如下示例配置 + +> `ansible.cfg` 配置Ansible的全局设置。 + +> `hosts` 定义要管理的主机和主机组。 + +> `renwu.yml(或playbook)` 描述要在主机上执行的任务和操作步骤。 + + + +--- +--- + + +### 2:禁用被控主机密钥检查 + +`ansible.cfg`中添加以下配置 +``` +[defaults] +host_key_checking = False +ansible_ssh_common_args = '-o StrictHostKeyChecking=no' +``` + + +### 3:配置被控主机清单 + + +`hosts`中添加被控主机示例 +``` +[myservers] +1 ansible_host=192.168.1.1 ansible_user=root ansible_port=22 ansible_ssh_pass=password1 +2 ansible_host=192.168.1.2 ansible_user=root ansible_port=22 ansible_ssh_pass=password2 +3 ansible_host=192.168.1.3 ansible_user=root ansible_port=22 ansible_ssh_pass=password3 +4 ansible_host=192.168.1.4 ansible_user=root ansible_port=22 ansible_ssh_pass=password4 +5 ansible_host=192.168.1.5 ansible_user=root ansible_port=22 ansible_ssh_pass=password5 +``` + +### 4:使用ping模块测试所有被控主机连通性 + + +> (可选)查看所有被控机的信息 `ansible-inventory --list -i /etc/ansible/hosts` + + +``` +ansible -m ping all +``` + +### 5:创建被控主机任务配置文件 + +`renwu.yml`中添加任务示例 + +``` +--- +# 定义要执行任务的主机组 +- hosts: myservers + become: yes + tasks: + - name: 更新包列表 + apt: + update_cache: yes + + - name: 安装所需的软件包 + apt: + name: + - curl + - wget + - zip + - tar + state: present + + - name: 将脚本复制到远程主机 + copy: + # 本地脚本路径 + src: /etc/ansible/ss.sh + # 远程主机上的目标路径 + dest: /tmp/ss.sh + # 设置脚本权限为可执行 + mode: '0755' + + - name: 在远程主机上执行脚本 + shell: /tmp/ss.sh # 在远程主机上执行脚本 +``` + + +或者直接执行远程脚本示例 +``` +--- +# 定义要执行任务的主机组 +- hosts: myservers + become: yes # 以管理员权限运行命令 + tasks: + - name: 更新包列表 + apt: + update_cache: yes + + - name: 安装所需的软件包 + apt: + name: + - curl + - wget + - zip + - tar + state: present + + - name: 在被控主机上执行远程脚本 + shell: bash <(wget -qO- https://github.com/user/shell/raw/main/dev.sh) + args: + executable: /bin/bash # 确保使用bash执行命令 +``` + +### 6:用法示例 + +- 对所有被控机器运行`renwu.yml`中的任务 +``` +ansible-playbook renwu.yml +``` + +- 临时对所有主机执行普通命令 +``` +ansible all -a "pwd" +``` +- 临时对所有主机运行远程脚本 +``` +ansible all -m shell -a "bash <(wget -qO- https://github.com/user/shell/raw/main/dev.sh)" +``` +- 临时将本地脚本复制给所有被控主机并执行 +``` +ansible all -m copy -a "src=/etc/ansible/script.sh dest=/tmp/script.sh mode=0755" +ansible all -m shell -a "/tmp/script.sh" +``` +- 临时对1,3号主机执行shell命令 +``` +ansible 1,3 -m shell -a "你的命令" +``` +- 临时对1,3号主机执行普通命令 +``` +ansible 1,3 -a "pwd" +``` +> 命令结尾后面追加`-v`选项会显示被控机器详细的执行信息 + +--- + +#### 命令解释 +> `-m` 用于指定 Ansible 模块 + +> `-a` 用于指定传递给模块的参数或命令 + +| 模块 | 指令 | 中文解释 | 用法示例 | +|-------------------|---------|----------------------------------------------|---------------------------------------------------| +| `shell` | `-a` | 执行 shell 命令。支持管道、重定向等 shell 特性。 | `ansible all -m shell -a "pwd"` | +| `command` | `-a` | 执行命令,不通过 shell。默认模块 | `ansible all -m command -a "ls -l"` | +| `copy` | `-a` | 复制文件或目录到目标主机。 | `ansible all -m copy -a "src=/local/file dest=/remote/file mode=0644"` | +| `file` | `-a` | 管理文件和目录的属性(如权限、所有权等)。 | `ansible all -m file -a "path=/remote/file state=absent"` | +| `yum` | `-a` | 使用 Yum 包管理器安装、更新或删除软件包(适用于 RHEL/CentOS)。 | `ansible all -m yum -a "name=nginx state=present"` | +| `apt` | `-a` | 使用 APT 包管理器安装、更新或删除软件包(适用于 Debian/Ubuntu)。 | `ansible all -m apt -a "name=nginx state=latest"` | +| `service` | `-a` | 管理服务(如启动、停止、重启服务)。 | `ansible all -m service -a "name=nginx state=started"` | +| `systemd` | `-a` | 管理 systemd 服务(如启动、停止、重启服务)。| `ansible all -m systemd -a "name=nginx state=started"` | +| `user` | `-a` | 管理用户账户(如创建、删除用户)。 | `ansible all -m user -a "name=alice state=present"` | +| `group` | `-a` | 管理用户组(如创建、删除组)。 | `ansible all -m group -a "name=admin state=present"` | +| `git` | `-a` | 管理 Git 仓库(如克隆、拉取、提交等)。 | `ansible all -m git -a "repo=https://github.com/user/repo.git dest=/path/to/repo"` | +| `template` | `-a` | 使用 Jinja2 模板引擎渲染模板文件。 | `ansible all -m template -a "src=template.j2 dest=/etc/config"` | +| `cron` | `-a` | 管理 cron 任务。 | `ansible all -m cron -a "name='Backup' minute='0' hour='2' job='/usr/bin/backup.sh'"` | +| `wait_for` | `-a` | 等待某个条件满足(如端口开放、文件存在等)。 | `ansible all -m wait_for -a "port=80 delay=10 timeout=300"` | +| `docker_container`| `-a` | 管理 Docker 容器(如启动、停止、删除容器)。 | `ansible all -m docker_container -a "name=my_container state=started"` | +| `docker_image` | `-a` | 管理 Docker 镜像(如拉取、删除镜像)。 | `ansible all -m docker_image -a "name=nginx tag=latest state=present"` | +| `lineinfile` | `-a` | 在文件中插入、删除或修改行。 | `ansible all -m lineinfile -a "path=/etc/hosts line='127.0.0.1 localhost' state=present"` | +| `ini_file` | `-a` | 修改 INI 配置文件。 | `ansible all -m ini_file -a "path=/etc/myconfig.ini section=database option=host value=localhost"` | +| `debug` | `-a` | 打印调试信息。 | `ansible all -m debug -a "msg='This is a debug message'"` | + + + +--- +--- + +#### 执行结果解释 +- **ok**: 表示在该主机上成功完成的任务数。 +- **changed**: 表示在该主机上有多少任务进行了更改(如文件被复制、脚本被执行)。 +- **unreachable**: 表示无法连接的主机数量。 +- **failed**: 表示任务失败的数量。 +- **skipped**: 表示被跳过的任务数量。 +- **rescued**: 表示在任务失败后被恢复的数量。 +- **ignored**: 表示被忽略的任务数量。 +- 绿色:任务顺利完成 +- 橙色:任务执行后有变化,比如文件被修改或某些服务被重启。 +- 红色:任务执行失败,一般会终止剩余的所有任务。 + + +#### 如果所有被控机端口和密码都一样 +`/etc/ansible/hosts`配置可以这样写 +``` +[all:vars] +ansible_user=root +ansible_ssh_pass=your_password +ansible_port=22 + +[myservers] +1 ansible_host=192.168.1.101 +2 ansible_host=192.168.1.102 +3 ansible_host=192.168.1.103 +``` diff --git a/ansible/ansible/ansible.cfg b/ansible/ansible/ansible.cfg new file mode 100644 index 0000000..e6de5f2 --- /dev/null +++ b/ansible/ansible/ansible.cfg @@ -0,0 +1,3 @@ +[defaults] +host_key_checking = False +ansible_ssh_common_args = '-o StrictHostKeyChecking=no' \ No newline at end of file diff --git a/ansible/ansible/hosts b/ansible/ansible/hosts new file mode 100644 index 0000000..29d0a03 --- /dev/null +++ b/ansible/ansible/hosts @@ -0,0 +1,4 @@ +[myservers] +1 ansible_host=192.168.1.1 ansible_user=root ansible_port=22 ansible_ssh_pass=password1 +2 ansible_host=192.168.1.2 ansible_user=root ansible_port=22 ansible_ssh_pass=password2 +3 ansible_host=192.168.1.3 ansible_user=root ansible_port=22 ansible_ssh_pass=password3 \ No newline at end of file diff --git a/ansible/ansible/renwu.yml b/ansible/ansible/renwu.yml new file mode 100644 index 0000000..cd2a516 --- /dev/null +++ b/ansible/ansible/renwu.yml @@ -0,0 +1,17 @@ +--- +# 定义要执行任务的主机组 +- hosts: myservers + become: yes + gather_facts: no # 禁用事实收集以避免依赖 Python + tasks: + - name: 将脚本复制到远程主机 + copy: + # 本地脚本路径 + src: ./shell.sh + # 远程主机上的目标路径 + dest: /tmp/shell.sh + # 设置脚本权限为可执行 + mode: '0755' + + - name: 在远程主机上执行脚本 + raw: /tmp/shell.sh # 在远程主机上执行脚本 \ No newline at end of file diff --git a/ansible/ansible/shell.sh b/ansible/ansible/shell.sh new file mode 100644 index 0000000..f7eeedd --- /dev/null +++ b/ansible/ansible/shell.sh @@ -0,0 +1,2 @@ +#!/bin/bash +echo "Hello, world!" \ No newline at end of file diff --git a/dev/README.md b/dev/README.md new file mode 100644 index 0000000..42e5820 --- /dev/null +++ b/dev/README.md @@ -0,0 +1,55 @@ +### 一键安装ansible + +见 [ansible.md](./ansible.md) + +--- + +### 常用运维脚本 + +- 一键切换系统源脚本 +``` +bash <(curl -sSL https://github.com/sky22333/shell/raw/main/dev/mirrors.sh) +``` +- 切换官方系统源 +``` +bash <(curl -sSL https://github.com/sky22333/shell/raw/main/dev/mirrors.sh) --use-official-source true +``` + +- 一键安装Docker和配置镜像地址 +``` +bash <(curl -sSL https://github.com/sky22333/shell/raw/main/dev/docker.sh) +``` + + +- acme.sh 证书一键申请脚本 + +``` +bash <(curl -sSL https://github.com/sky22333/shell/raw/main/dev/acme.sh) +``` + + +- Linux切换到标准内核: +``` +bash <(curl -sSL https://github.com/sky22333/shell/raw/main/dev/image.sh) +``` + +- 一键安装go环境: +``` +bash <(curl -sSL https://github.com/sky22333/shell/raw/main/dev/go.sh) +``` + + +- 一键启用BBR: +``` +bash <(curl -sSL https://github.com/sky22333/shell/raw/main/dev/bbr.sh) +``` + +- 一键内网穿透(无需域名和服务器): +``` +bash <(curl -sSL https://github.com/sky22333/shell/raw/main/dev/cf-tunnel.sh) +``` + +- `win`系统`PowerShell`在线脚本,需要以管理员模式打开`PowerShell` +``` +iwr -useb https://ghproxy.net/https://raw.githubusercontent.com/sky22333/shell/main/dev/cf-setup.ps1 | iex +``` diff --git a/dev/acme.sh b/dev/acme.sh new file mode 100644 index 0000000..11c6560 --- /dev/null +++ b/dev/acme.sh @@ -0,0 +1,157 @@ +#!/bin/bash + +if [ "$(id -u)" != "0" ]; then + echo -e "\033[0;31m请以 root 用户运行此脚本\033[0m" + exit 1 +fi + +install_requirements() { + local install_cmd="" + local pkg_manager="" + local os_type=$(grep '^ID=' /etc/os-release | cut -d'=' -f2) + + if [ "$os_type" == "ubuntu" ] || [ "$os_type" == "debian" ]; then + pkg_manager="apt" + install_cmd="apt install -y" + elif [ "$os_type" == "centos" ]; then + pkg_manager="yum" + install_cmd="yum install -y" + else + echo -e "\033[0;31m不支持的操作系统: $os_type\033[0m" + exit 1 + fi + + if ! command -v lsof &> /dev/null; then + $install_cmd lsof + fi + + if ! command -v curl &> /dev/null; then + $install_cmd curl + fi + + # 检查并安装 socat + if ! command -v socat &> /dev/null; then + echo -e "\033[0;32msocat 未安装,正在安装...\033[0m" + $install_cmd socat + else + echo -e "\033[0;32msocat 已安装\033[0m" + fi +} + +# 生成12位纯英文的随机邮箱 +generate_random_email() { + local random_email=$(tr -dc 'a-z' < /dev/urandom | fold -w 12 | head -n 1) + echo "${random_email}@gmail.com" +} + +check_acme_installation() { + if ! command -v acme.sh &> /dev/null; then + echo -e "\033[0;32macme.sh 未安装,正在安装...\033[0m" + curl https://get.acme.sh | sh + source ~/.bashrc + else + echo -e "\033[0;32macme.sh 已安装\033[0m" + fi +} + +# 检查端口 80 是否被占用,并提供释放端口的选项 +check_port_80() { + local pid + pid=$(lsof -ti:80) + + if [ -n "$pid" ]; then + echo -e "\033[0;31m端口 80 已被占用,PID为: $pid\033[0m" + read -p "是否强制释放端口 80? (Y/n): " response + + case "$response" in + [yY][eE][sS]|[yY]) + echo "正在释放端口 80..." + kill -9 $pid + ;; + *) + echo "未释放端口,脚本将退出。" + exit 1 + ;; + esac + fi +} + +register_ca() { + local ca="$1" + local email="$2" + echo -e "\033[0;32m正在注册 CA 机构 $ca 使用电子邮件 $email...\033[0m" + ~/.acme.sh/acme.sh --register-account -m "$email" --server "$ca" +} + +generate_ssl_certificate() { + local domain_name="$1" + local ca="$2" + echo -e "\033[0;32m正在为 $domain_name 生成 SSL 证书...\033[0m" + + ~/.acme.sh/acme.sh --issue --force --standalone -d "$domain_name" --server "$ca" + + if [ $? -ne 0 ]; then + echo -e "\033[0;31mSSL 证书生成失败\033[0m" + exit 1 + fi + + local cert_path="/root/.acme.sh/${domain_name}_ecc/fullchain.cer" + local key_path="/root/.acme.sh/${domain_name}_ecc/${domain_name}.key" + + ~/.acme.sh/acme.sh --install-cert -d "$domain_name" \ + --key-file "$key_path" \ + --fullchain-file "$cert_path" + + # 打印 fullchain.cer 和 .key 文件的绝对路径 + echo -e "\033[0;32m证书路径: $cert_path\033[0m" + echo -e "\033[0;32m密钥路径: $key_path\033[0m" +} +# 主流程 +install_requirements +echo -e "\033[0;32m请输入您的域名(确保已经解析到本机IP):\033[0m" +read -p "" domain_name + +# 检查端口 80 +check_port_80 + +# 检查证书和密钥是否已经存在 +cert_path="/root/.acme.sh/${domain_name}_ecc/fullchain.cer" +key_path="/root/.acme.sh/${domain_name}_ecc/${domain_name}.key" + +if [ -f "$cert_path" ] && [ -f "$key_path" ]; then + echo -e "\033[0;32m证书已存在:\033[0m" + echo -e "\033[0;32m证书全链路径: $cert_path\033[0m" + echo -e "\033[0;32m密钥文件路径: $key_path\033[0m" + exit 0 +fi + + +# 生成随机邮箱 +user_email=$(generate_random_email) +echo -e "\033[0;32m生成的邮箱: $user_email\033[0m" + +# 检查 acme.sh 安装 +check_acme_installation + +# CA 机构选择 +echo -e "\033[0;32m请选择 CA 机构:\033[0m" +echo -e "\033[0;32m1) Let's Encrypt\033[0m" +echo -e "\033[0;32m2) Buypass\033[0m" +echo -e "\033[0;32m3) ZeroSSL\033[0m" +echo -e "\033[0;32m选择 CA 机构 (回车默认选1):\033[0m" +read -p "" ca_choice + +case $ca_choice in + 2) + CA="buypass" + ;; + 3) + CA="zerossl" + ;; + *) + CA="letsencrypt" + ;; +esac + +register_ca "$CA" "$user_email" +generate_ssl_certificate "$domain_name" "$CA" diff --git a/dev/ansible.md b/dev/ansible.md new file mode 100644 index 0000000..1177899 --- /dev/null +++ b/dev/ansible.md @@ -0,0 +1,242 @@ + + +## 🔵脚本加密-编译为可执行文件 + +- 下载环境 +``` +sudo apt update +sudo apt install shc gcc -yq +``` + +- 用法 + +| 命令 | 描述 | 示例 | +|-------------------------------|-------------------------------------------------------------------|---------------------------------------------------------------| +| `shc -f + + diff --git a/docs/md/index.html b/docs/md/index.html new file mode 100644 index 0000000..dcbf2f7 --- /dev/null +++ b/docs/md/index.html @@ -0,0 +1,691 @@ + + + + + + + Markdown 实时编辑器 + + + + +
+
+

Markdown 实时编辑器

+
+ + + + + + + + + +
+ +
+ + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + diff --git a/docs/nezha/index.html b/docs/nezha/index.html new file mode 100644 index 0000000..8c6bef5 --- /dev/null +++ b/docs/nezha/index.html @@ -0,0 +1,1090 @@ + + + + + + + + + NEZHA配置生成器 + + + + +
+
+
公开备注代码
+
流量监控代码
+
+ +
+
+ + +
+
+
+

公开备注代码配置

+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + + +
+ +
+ +
+ + +
+
+ + + +
+
+ +
+ +
+ + +
+
+ + + +
+
+ +
+ +
+ + +
+
+ + + + + +
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + + +
+
+ +
+
+
+ 163 + CN2 + CN2GIA + CMI + CMIN2 + 4837 + 10099 + IEPL + IPLC +
+
+ + +
+ +
+ + +
+ + +
+
+ +
+
+
+
+ + +
+
+
+

流量监控代码配置

+ +
+ + +
+ +
+ +
+ + +
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + + +
+ + +
+ + +
+
+ +
+
+
+
+
+ + + + + + + diff --git a/gost/README.md b/gost/README.md new file mode 100644 index 0000000..dc1e446 --- /dev/null +++ b/gost/README.md @@ -0,0 +1,15 @@ + +### gost中转脚本 +``` +bash <(curl -sSL https://raw.githubusercontent.com/sky22333/shell/main/gost/gost.sh) +``` +--- + +### 国内加速 +``` +bash <(curl -sSL https://gh-proxy.com/https://raw.githubusercontent.com/sky22333/shell/main/gost/gost.sh) +``` + +--- + +基于 [Multi-EasyGost](https://github.com/KANIKIG/Multi-EasyGost) 修改 diff --git a/gost/config.json b/gost/config.json new file mode 100644 index 0000000..9b979be --- /dev/null +++ b/gost/config.json @@ -0,0 +1,7 @@ +{ + "Debug": true, + "Retries": 0, + "ServeNodes": [ + "udp://127.0.0.1:65532" + ] +} \ No newline at end of file diff --git a/gost/gost.service b/gost/gost.service new file mode 100644 index 0000000..4a38a70 --- /dev/null +++ b/gost/gost.service @@ -0,0 +1,15 @@ +[Unit] +Description=gost +After=network-online.target +Wants=network-online.target systemd-networkd-wait-online.service + +[Service] +Type=simple +User=root +Restart=always +RestartSec=5 +DynamicUser=true +ExecStart=/usr/bin/gost -C /etc/gost/config.json + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/gost/gost.sh b/gost/gost.sh new file mode 100644 index 0000000..78bdfb8 --- /dev/null +++ b/gost/gost.sh @@ -0,0 +1,942 @@ +#! /bin/bash +# 鸣谢:https://github.com/KANIKIG/Multi-EasyGost + +Green_font_prefix="\033[32m" && Red_font_prefix="\033[31m" && Green_background_prefix="\033[42;37m" && Font_color_suffix="\033[0m" +Info="${Green_font_prefix}[信息]${Font_color_suffix}" +Error="${Red_font_prefix}[错误]${Font_color_suffix}" +ct_new_ver="2.11.2" # 使用固定的稳定版本 +gost_conf_path="/etc/gost/config.json" +raw_conf_path="/etc/gost/rawconf" + +function check_sys() { + if [[ -f /etc/redhat-release ]]; then + release="centos" + elif cat /etc/issue | grep -q -E -i "debian"; then + release="debian" + elif cat /etc/issue | grep -q -E -i "ubuntu"; then + release="ubuntu" + elif cat /etc/issue | grep -q -E -i "centos|red hat|redhat"; then + release="centos" + elif cat /proc/version | grep -q -E -i "debian"; then + release="debian" + elif cat /proc/version | grep -q -E -i "ubuntu"; then + release="ubuntu" + elif cat /proc/version | grep -q -E -i "centos|red hat|redhat"; then + release="centos" + fi + bit=$(uname -m) + if test "$bit" != "x86_64"; then + echo "请输入你的芯片架构,/386/armv5/armv6/armv7/armv8" + read bit + else + bit="amd64" + fi +} +function Installation_dependency() { + gzip_ver=$(gzip -V) + if [[ -z ${gzip_ver} ]]; then + if [[ ${release} == "centos" ]]; then + yum update + yum install -y gzip wget + else + apt update + apt install -y gzip wget + fi + fi +} +function check_root() { + [[ $EUID != 0 ]] && echo -e "${Error} 当前非root用户,无法继续操作,请使用root权限运行此脚本。" && exit 1 +} + +function check_file() { + if test ! -d "/usr/lib/systemd/system/"; then + mkdir /usr/lib/systemd/system + chmod -R 755 /usr/lib/systemd/system + fi +} +function check_nor_file() { + rm -rf "$(pwd)"/gost + rm -rf "$(pwd)"/gost.service + rm -rf "$(pwd)"/config.json + rm -rf /etc/gost + rm -rf /usr/lib/systemd/system/gost.service + rm -rf /usr/bin/gost +} +function Install_ct() { + check_root + check_nor_file + Installation_dependency + check_file + check_sys + + loc=$(curl -s https://www.cloudflare.com/cdn-cgi/trace | grep 'loc=' | cut -d= -f2) + + gh_proxy_prefix="" + if [[ "$loc" == "CN" ]]; then + echo -e "检测到服务器位于中国大陆,GitHub 下载可能较慢。" + read -e -p "是否使用 GitHub 加速代理? [y/n]:" use_proxy + [[ -z $use_proxy ]] && use_proxy="y" + + if [[ $use_proxy == [Yy] ]]; then + read -e -p "请输入加速域名(回车使用内置加速域名:https://gh-proxy.com):" input_proxy + [[ -z $input_proxy ]] && input_proxy="https://gh-proxy.com" + gh_proxy_prefix="${input_proxy}/" + fi + fi + + # 下载和安装 gost + rm -rf gost-linux-"$bit"-"$ct_new_ver".gz + curl --progress-bar -L -o gost-linux-"${bit}"-"${ct_new_ver}".gz "${gh_proxy_prefix}https://github.com/ginuerzh/gost/releases/download/v${ct_new_ver}/gost-linux-${bit}-${ct_new_ver}.gz" + gunzip gost-linux-"$bit"-"$ct_new_ver".gz + mv gost-linux-"$bit"-"$ct_new_ver" gost + mv gost /usr/bin/gost + chmod -R 755 /usr/bin/gost + + # 下载 systemd 启动服务文件 + wget -q --no-check-certificate "${gh_proxy_prefix}https://raw.githubusercontent.com/sky22333/shell/main/gost/gost.service" + chmod -R 755 gost.service + mv gost.service /usr/lib/systemd/system + + # 下载默认配置文件 + mkdir -p /etc/gost + wget -q --no-check-certificate "${gh_proxy_prefix}https://raw.githubusercontent.com/sky22333/shell/main/gost/config.json" + mv config.json /etc/gost + chmod -R 755 /etc/gost + + # 启动服务 + systemctl enable gost && systemctl restart gost + + echo "------------------------------" + if [[ -f /usr/bin/gost && -f /usr/lib/systemd/system/gost.service && -f /etc/gost/config.json ]]; then + echo "gost安装成功" + rm -rf "$(pwd)/gost" "$(pwd)/gost.service" "$(pwd)/config.json" + else + echo "gost安装失败" + rm -rf "$(pwd)/gost" "$(pwd)/gost.service" "$(pwd)/config.json" "$(pwd)/gost.sh" + fi +} +function Uninstall_ct() { + systemctl stop gost.service 2>/dev/null + systemctl disable gost.service 2>/dev/null + rm -rf /usr/bin/gost + rm -rf /usr/lib/systemd/system/gost.service + rm -rf /etc/gost + systemctl daemon-reload + echo "gost已经成功删除" +} +function Start_ct() { + systemctl start gost + echo "已启动" +} +function Stop_ct() { + systemctl stop gost + echo "已停止" +} +function Restart_ct() { + rm -rf /etc/gost/config.json + confstart + writeconf + conflast + systemctl restart gost + echo "已重读配置并重启" +} +function read_protocol() { + echo -e "请问您要设置哪种功能: " + echo -e "-----------------------------------" + echo -e "[1] tcp+udp流量转发, 不加密" + echo -e "说明: 一般设置在国内中转机上" + echo -e "-----------------------------------" + echo -e "[2] 加密隧道流量转发" + echo -e "说明: 用于转发原本加密等级较低的流量, 一般设置在国内中转机上" + echo -e " 选择此协议意味着你还有一台机器用于接收此加密流量, 之后须在那台机器上配置协议[3]进行对接" + echo -e "-----------------------------------" + echo -e "[3] 解密由gost传输而来的流量并转发" + echo -e "说明: 对于经由gost加密中转的流量, 通过此选项进行解密并转发给本机的代理服务端口或转发给其他远程机器" + echo -e " 一般设置在用于接收中转流量的国外机器上" + echo -e "-----------------------------------" + echo -e "[4] 一键安装ss/socks5/http代理" + echo -e "说明: 使用gost内置的代理协议,轻量且易于管理" + echo -e "-----------------------------------" + echo -e "[5] 进阶:多落地均衡负载" + echo -e "说明: 支持各种加密方式的简单均衡负载" + echo -e "-----------------------------------" + echo -e "[6] 进阶:转发CDN自选节点" + echo -e "说明: 只需在中转机设置" + echo -e "-----------------------------------" + read -p "请选择: " numprotocol + + if [ "$numprotocol" == "1" ]; then + flag_a="nonencrypt" + elif [ "$numprotocol" == "2" ]; then + encrypt + elif [ "$numprotocol" == "3" ]; then + decrypt + elif [ "$numprotocol" == "4" ]; then + proxy + elif [ "$numprotocol" == "5" ]; then + enpeer + elif [ "$numprotocol" == "6" ]; then + cdn + else + echo "type error, please try again" + exit + fi +} +function read_s_port() { + if [ "$flag_a" == "ss" ]; then + echo -e "-----------------------------------" + read -p "请输入ss密码: " flag_b + elif [ "$flag_a" == "socks" ]; then + echo -e "-----------------------------------" + read -p "请输入socks密码: " flag_b + elif [ "$flag_a" == "http" ]; then + echo -e "-----------------------------------" + read -p "请输入http密码: " flag_b + else + echo -e "------------------------------------------------------------------" + echo -e "请问你要将本机哪个端口接收到的流量进行转发?" + read -p "请输入: " flag_b + fi +} +function read_d_ip() { + if [ "$flag_a" == "ss" ]; then + echo -e "------------------------------------------------------------------" + echo -e "请问您要设置的ss加密(仅提供常用的几种): " + echo -e "-----------------------------------" + echo -e "[1] aes-256-gcm" + echo -e "[2] aes-256-cfb" + echo -e "[3] chacha20-ietf-poly1305" + echo -e "[4] chacha20" + echo -e "[5] rc4-md5" + echo -e "[6] AEAD_CHACHA20_POLY1305" + echo -e "-----------------------------------" + read -p "请选择ss加密方式: " ssencrypt + + if [ "$ssencrypt" == "1" ]; then + flag_c="aes-256-gcm" + elif [ "$ssencrypt" == "2" ]; then + flag_c="aes-256-cfb" + elif [ "$ssencrypt" == "3" ]; then + flag_c="chacha20-ietf-poly1305" + elif [ "$ssencrypt" == "4" ]; then + flag_c="chacha20" + elif [ "$ssencrypt" == "5" ]; then + flag_c="rc4-md5" + elif [ "$ssencrypt" == "6" ]; then + flag_c="AEAD_CHACHA20_POLY1305" + else + echo "type error, please try again" + exit + fi + elif [ "$flag_a" == "socks" ]; then + echo -e "-----------------------------------" + read -p "请输入socks用户名: " flag_c + elif [ "$flag_a" == "http" ]; then + echo -e "-----------------------------------" + read -p "请输入http用户名: " flag_c + elif [[ "$flag_a" == "peer"* ]]; then + echo -e "------------------------------------------------------------------" + echo -e "请输入落地列表文件名" + read -e -p "自定义但不同配置应不重复,不用输入后缀,例如ips1、iplist2: " flag_c + touch $flag_c.txt + echo -e "------------------------------------------------------------------" + echo -e "请依次输入你要均衡负载的落地ip与端口" + while true; do + echo -e "请问你要将本机从${flag_b}接收到的流量转发向的IP或域名?" + read -p "请输入: " peer_ip + echo -e "请问你要将本机从${flag_b}接收到的流量转发向${peer_ip}的哪个端口?" + read -p "请输入: " peer_port + echo -e "$peer_ip:$peer_port" >>$flag_c.txt + read -e -p "是否继续添加落地?[Y/n]:" addyn + [[ -z ${addyn} ]] && addyn="y" + if [[ ${addyn} == [Nn] ]]; then + echo -e "------------------------------------------------------------------" + echo -e "已在root目录创建$flag_c.txt,您可以随时编辑该文件修改落地信息,重启gost即可生效" + echo -e "------------------------------------------------------------------" + break + else + echo -e "------------------------------------------------------------------" + echo -e "继续添加均衡负载落地配置" + fi + done + elif [[ "$flag_a" == "cdn"* ]]; then + echo -e "------------------------------------------------------------------" + echo -e "将本机从${flag_b}接收到的流量转发向的自选ip:" + read -p "请输入: " flag_c + echo -e "请问你要将本机从${flag_b}接收到的流量转发向${flag_c}的哪个端口?" + echo -e "[1] 80" + echo -e "[2] 443" + echo -e "[3] 自定义端口(如8080等)" + read -p "请选择端口: " cdnport + if [ "$cdnport" == "1" ]; then + flag_c="$flag_c:80" + elif [ "$cdnport" == "2" ]; then + flag_c="$flag_c:443" + elif [ "$cdnport" == "3" ]; then + read -p "请输入自定义端口: " customport + flag_c="$flag_c:$customport" + else + echo "type error, please try again" + exit + fi + else + echo -e "------------------------------------------------------------------" + echo -e "请问你要将本机从${flag_b}接收到的流量转发向哪个IP或域名?" + echo -e "注: IP既可以是[远程机器/当前机器]的公网IP, 也可是以本机本地回环IP(即127.0.0.1)" + if [[ ${is_cert} == [Yy] ]]; then + echo -e "注意: 落地机开启自定义tls证书,务必填写${Red_font_prefix}域名${Font_color_suffix}" + fi + read -p "请输入: " flag_c + fi +} +function read_d_port() { + if [ "$flag_a" == "ss" ]; then + echo -e "------------------------------------------------------------------" + echo -e "请问你要设置ss代理服务的端口?" + read -p "请输入: " flag_d + elif [ "$flag_a" == "socks" ]; then + echo -e "------------------------------------------------------------------" + echo -e "请问你要设置socks代理服务的端口?" + read -p "请输入: " flag_d + elif [ "$flag_a" == "http" ]; then + echo -e "------------------------------------------------------------------" + echo -e "请问你要设置http代理服务的端口?" + read -p "请输入: " flag_d + elif [[ "$flag_a" == "peer"* ]]; then + echo -e "------------------------------------------------------------------" + echo -e "您要设置的均衡负载策略: " + echo -e "-----------------------------------" + echo -e "[1] round - 轮询" + echo -e "[2] random - 随机" + echo -e "[3] fifo - 自上而下" + echo -e "-----------------------------------" + read -p "请选择均衡负载类型: " numstra + + if [ "$numstra" == "1" ]; then + flag_d="round" + elif [ "$numstra" == "2" ]; then + flag_d="random" + elif [ "$numstra" == "3" ]; then + flag_d="fifo" + else + echo "type error, please try again" + exit + fi + elif [[ "$flag_a" == "cdn"* ]]; then + echo -e "------------------------------------------------------------------" + read -p "请输入host:" flag_d + else + echo -e "------------------------------------------------------------------" + echo -e "请问你要将本机从${flag_b}接收到的流量转发向${flag_c}的哪个端口?" + read -p "请输入: " flag_d + if [[ ${is_cert} == [Yy] ]]; then + flag_d="$flag_d?secure=true" + fi + fi +} +function writerawconf() { + echo $flag_a"/""$flag_b""#""$flag_c""#""$flag_d" >>$raw_conf_path +} +function rawconf() { + read_protocol + read_s_port + read_d_ip + read_d_port + writerawconf +} +function eachconf_retrieve() { + d_server=${trans_conf#*#} + d_port=${d_server#*#} + d_ip=${d_server%#*} + flag_s_port=${trans_conf%%#*} + s_port=${flag_s_port#*/} + is_encrypt=${flag_s_port%/*} +} +function confstart() { + echo "{ + \"Debug\": true, + \"Retries\": 0, + \"ServeNodes\": [" >>$gost_conf_path +} +function multiconfstart() { + echo " { + \"Retries\": 0, + \"ServeNodes\": [" >>$gost_conf_path +} +function conflast() { + echo " ] +}" >>$gost_conf_path +} +function multiconflast() { + if [ $i -eq $count_line ]; then + echo " ] + }" >>$gost_conf_path + else + echo " ] + }," >>$gost_conf_path + fi +} +function encrypt() { + echo -e "请问您要设置的转发传输类型: " + echo -e "-----------------------------------" + echo -e "[1] tls隧道" + echo -e "[2] ws隧道" + echo -e "[3] wss隧道" + echo -e "注意: 同一则转发,中转与落地传输类型必须对应!本脚本默认开启tcp+udp" + echo -e "-----------------------------------" + read -p "请选择转发传输类型: " numencrypt + + if [ "$numencrypt" == "1" ]; then + flag_a="encrypttls" + echo -e "注意: 选择 是 将针对落地的自定义证书开启证书校验保证安全性,稍后落地机务必填写${Red_font_prefix}域名${Font_color_suffix}" + read -e -p "落地机是否开启了自定义tls证书?[y/n]:" is_cert + elif [ "$numencrypt" == "2" ]; then + flag_a="encryptws" + elif [ "$numencrypt" == "3" ]; then + flag_a="encryptwss" + echo -e "注意: 选择 是 将针对落地的自定义证书开启证书校验保证安全性,稍后落地机务必填写${Red_font_prefix}域名${Font_color_suffix}" + read -e -p "落地机是否开启了自定义tls证书?[y/n]:" is_cert + else + echo "type error, please try again" + exit + fi +} +function enpeer() { + echo -e "请问您要设置的均衡负载传输类型: " + echo -e "-----------------------------------" + echo -e "[1] 不加密转发" + echo -e "[2] tls隧道" + echo -e "[3] ws隧道" + echo -e "[4] wss隧道" + echo -e "注意: 同一则转发,中转与落地传输类型必须对应!本脚本默认同一配置的传输类型相同" + echo -e "此脚本仅支持简单型均衡负载,具体可参考官方文档" + echo -e "-----------------------------------" + read -p "请选择转发传输类型: " numpeer + + if [ "$numpeer" == "1" ]; then + flag_a="peerno" + elif [ "$numpeer" == "2" ]; then + flag_a="peertls" + elif [ "$numpeer" == "3" ]; then + flag_a="peerws" + elif [ "$numpeer" == "4" ]; then + flag_a="peerwss" + + else + echo "type error, please try again" + exit + fi +} +function cdn() { + echo -e "请问您要设置的CDN传输类型: " + echo -e "-----------------------------------" + echo -e "[1] 不加密转发" + echo -e "[2] ws隧道" + echo -e "[3] wss隧道" + echo -e "注意: 同一则转发,中转与落地传输类型必须对应!" + echo -e "此功能只需在中转机设置" + echo -e "-----------------------------------" + read -p "请选择CDN转发传输类型: " numcdn + + if [ "$numcdn" == "1" ]; then + flag_a="cdnno" + elif [ "$numcdn" == "2" ]; then + flag_a="cdnws" + elif [ "$numcdn" == "3" ]; then + flag_a="cdnwss" + else + echo "type error, please try again" + exit + fi +} +function status() { + systemctl status gost +} +function cert() { + echo -e "-----------------------------------" + echo -e "[1] ACME一键申请证书" + echo -e "[2] 手动上传证书" + echo -e "-----------------------------------" + echo -e "说明: 仅用于落地机配置,默认使用的gost内置的证书可能带来安全问题,使用自定义证书提高安全性" + echo -e " 配置后对本机所有tls/wss解密生效,无需再次设置" + read -p "请选择证书生成方式: " numcert + + if [ "$numcert" == "1" ]; then + check_sys + if [[ ${release} == "centos" ]]; then + yum install -y socat + else + apt-get install -y socat + fi + read -p "请随意输入一个邮箱用于申请证书:" zeromail + read -p "请输入解析到本机的域名:" domain + curl https://get.acme.sh | sh + "$HOME"/.acme.sh/acme.sh --set-default-ca --server zerossl + "$HOME"/.acme.sh/acme.sh --register-account -m "${zeromail}" --server zerossl + echo -e "ACME证书申请程序安装成功" + echo -e "-----------------------------------" + echo -e "[1] HTTP申请(需要80端口未占用)" + echo -e "[2] Cloudflare DNS API 申请(需要输入APIKEY)" + echo -e "-----------------------------------" + read -p "请选择证书申请方式: " certmethod + if [ "$certmethod" == "1" ]; then + echo -e "请确认本机${Red_font_prefix}80${Font_color_suffix}端口未被占用, 否则会申请失败" + if "$HOME"/.acme.sh/acme.sh --issue -d "${domain}" --standalone -k ec-256 --force; then + echo -e "SSL 证书生成成功,默认申请高安全性的ECC证书" + if [ ! -d "$HOME/gost_cert" ]; then + mkdir $HOME/gost_cert + fi + if "$HOME"/.acme.sh/acme.sh --installcert -d "${domain}" --fullchainpath $HOME/gost_cert/cert.pem --keypath $HOME/gost_cert/key.pem --ecc --force; then + echo -e "SSL 证书配置成功,且会自动续签,证书及秘钥位于用户目录下的 ${Red_font_prefix}gost_cert${Font_color_suffix} 目录" + echo -e "证书目录名与证书文件名请勿更改; 删除 gost_cert 目录后用脚本重启,会自动启用gost内置证书" + echo -e "-----------------------------------" + fi + else + echo -e "SSL 证书生成失败" + exit 1 + fi + else + read -p "请输入Cloudflare账户邮箱:" cfmail + read -p "请输入Cloudflare Global API Key:" cfkey + export CF_Key="${cfkey}" + export CF_Email="${cfmail}" + if "$HOME"/.acme.sh/acme.sh --issue --dns dns_cf -d "${domain}" --standalone -k ec-256 --force; then + echo -e "SSL 证书生成成功,默认申请高安全性的ECC证书" + if [ ! -d "$HOME/gost_cert" ]; then + mkdir $HOME/gost_cert + fi + if "$HOME"/.acme.sh/acme.sh --installcert -d "${domain}" --fullchainpath $HOME/gost_cert/cert.pem --keypath $HOME/gost_cert/key.pem --ecc --force; then + echo -e "SSL 证书配置成功,且会自动续签,证书及秘钥位于用户目录下的 ${Red_font_prefix}gost_cert${Font_color_suffix} 目录" + echo -e "证书目录名与证书文件名请勿更改; 删除 gost_cert 目录后使用脚本重启, 会重新启用gost内置证书" + echo -e "-----------------------------------" + fi + else + echo -e "SSL 证书生成失败" + exit 1 + fi + fi + + elif [ "$numcert" == "2" ]; then + if [ ! -d "$HOME/gost_cert" ]; then + mkdir $HOME/gost_cert + fi + echo -e "-----------------------------------" + echo -e "已在用户目录建立 ${Red_font_prefix}gost_cert${Font_color_suffix} 目录,请将证书文件 cert.pem 与秘钥文件 key.pem 上传到该目录" + echo -e "证书与秘钥文件名必须与上述一致,目录名也请勿更改" + echo -e "上传成功后,用脚本重启gost会自动启用,无需再设置; 删除 gost_cert 目录后用脚本重启,会重新启用gost内置证书" + echo -e "-----------------------------------" + else + echo "type error, please try again" + exit + fi +} +function decrypt() { + echo -e "请问您要设置的解密传输类型: " + echo -e "-----------------------------------" + echo -e "[1] tls" + echo -e "[2] ws" + echo -e "[3] wss" + echo -e "注意: 同一则转发,中转与落地传输类型必须对应!本脚本默认开启tcp+udp" + echo -e "-----------------------------------" + read -p "请选择解密传输类型: " numdecrypt + + if [ "$numdecrypt" == "1" ]; then + flag_a="decrypttls" + elif [ "$numdecrypt" == "2" ]; then + flag_a="decryptws" + elif [ "$numdecrypt" == "3" ]; then + flag_a="decryptwss" + else + echo "type error, please try again" + exit + fi +} +function proxy() { + echo -e "------------------------------------------------------------------" + echo -e "请问您要设置的代理类型: " + echo -e "-----------------------------------" + echo -e "[1] shadowsocks" + echo -e "[2] socks5(强烈建议加隧道用于Telegram代理)" + echo -e "[3] http" + echo -e "-----------------------------------" + read -p "请选择代理类型: " numproxy + if [ "$numproxy" == "1" ]; then + flag_a="ss" + elif [ "$numproxy" == "2" ]; then + flag_a="socks" + elif [ "$numproxy" == "3" ]; then + flag_a="http" + else + echo "type error, please try again" + exit + fi +} +function method() { + if [ $i -eq 1 ]; then + if [ "$is_encrypt" == "nonencrypt" ]; then + echo " \"tcp://:$s_port/$d_ip:$d_port\", + \"udp://:$s_port/$d_ip:$d_port\"" >>$gost_conf_path + elif [ "$is_encrypt" == "cdnno" ]; then + echo " \"tcp://:$s_port/$d_ip?host=$d_port\", + \"udp://:$s_port/$d_ip?host=$d_port\"" >>$gost_conf_path + elif [ "$is_encrypt" == "peerno" ]; then + echo " \"tcp://:$s_port?ip=/root/$d_ip.txt&strategy=$d_port\", + \"udp://:$s_port?ip=/root/$d_ip.txt&strategy=$d_port\"" >>$gost_conf_path + elif [ "$is_encrypt" == "encrypttls" ]; then + echo " \"tcp://:$s_port\", + \"udp://:$s_port\" + ], + \"ChainNodes\": [ + \"relay+tls://$d_ip:$d_port\"" >>$gost_conf_path + elif [ "$is_encrypt" == "encryptws" ]; then + echo " \"tcp://:$s_port\", + \"udp://:$s_port\" + ], + \"ChainNodes\": [ + \"relay+ws://$d_ip:$d_port\"" >>$gost_conf_path + elif [ "$is_encrypt" == "encryptwss" ]; then + echo " \"tcp://:$s_port\", + \"udp://:$s_port\" + ], + \"ChainNodes\": [ + \"relay+wss://$d_ip:$d_port\"" >>$gost_conf_path + elif [ "$is_encrypt" == "peertls" ]; then + echo " \"tcp://:$s_port\", + \"udp://:$s_port\" + ], + \"ChainNodes\": [ + \"relay+tls://:?ip=/root/$d_ip.txt&strategy=$d_port\"" >>$gost_conf_path + elif [ "$is_encrypt" == "peerws" ]; then + echo " \"tcp://:$s_port\", + \"udp://:$s_port\" + ], + \"ChainNodes\": [ + \"relay+ws://:?ip=/root/$d_ip.txt&strategy=$d_port\"" >>$gost_conf_path + elif [ "$is_encrypt" == "peerwss" ]; then + echo " \"tcp://:$s_port\", + \"udp://:$s_port\" + ], + \"ChainNodes\": [ + \"relay+wss://:?ip=/root/$d_ip.txt&strategy=$d_port\"" >>$gost_conf_path + elif [ "$is_encrypt" == "cdnws" ]; then + echo " \"tcp://:$s_port\", + \"udp://:$s_port\" + ], + \"ChainNodes\": [ + \"relay+ws://$d_ip?host=$d_port\"" >>$gost_conf_path + elif [ "$is_encrypt" == "cdnwss" ]; then + echo " \"tcp://:$s_port\", + \"udp://:$s_port\" + ], + \"ChainNodes\": [ + \"relay+wss://$d_ip?host=$d_port\"" >>$gost_conf_path + elif [ "$is_encrypt" == "decrypttls" ]; then + if [ -d "$HOME/gost_cert" ]; then + echo " \"relay+tls://:$s_port/$d_ip:$d_port?cert=/root/gost_cert/cert.pem&key=/root/gost_cert/key.pem\"" >>$gost_conf_path + else + echo " \"relay+tls://:$s_port/$d_ip:$d_port\"" >>$gost_conf_path + fi + elif [ "$is_encrypt" == "decryptws" ]; then + echo " \"relay+ws://:$s_port/$d_ip:$d_port\"" >>$gost_conf_path + elif [ "$is_encrypt" == "decryptwss" ]; then + if [ -d "$HOME/gost_cert" ]; then + echo " \"relay+wss://:$s_port/$d_ip:$d_port?cert=/root/gost_cert/cert.pem&key=/root/gost_cert/key.pem\"" >>$gost_conf_path + else + echo " \"relay+wss://:$s_port/$d_ip:$d_port\"" >>$gost_conf_path + fi + elif [ "$is_encrypt" == "ss" ]; then + echo " \"ss://$d_ip:$s_port@:$d_port\"" >>$gost_conf_path + elif [ "$is_encrypt" == "socks" ]; then + echo " \"socks5://$d_ip:$s_port@:$d_port\"" >>$gost_conf_path + elif [ "$is_encrypt" == "http" ]; then + echo " \"http://$d_ip:$s_port@:$d_port\"" >>$gost_conf_path + else + echo "config error" + fi + elif [ $i -gt 1 ]; then + if [ "$is_encrypt" == "nonencrypt" ]; then + echo " \"tcp://:$s_port/$d_ip:$d_port\", + \"udp://:$s_port/$d_ip:$d_port\"" >>$gost_conf_path + elif [ "$is_encrypt" == "peerno" ]; then + echo " \"tcp://:$s_port?ip=/root/$d_ip.txt&strategy=$d_port\", + \"udp://:$s_port?ip=/root/$d_ip.txt&strategy=$d_port\"" >>$gost_conf_path + elif [ "$is_encrypt" == "cdnno" ]; then + echo " \"tcp://:$s_port/$d_ip?host=$d_port\", + \"udp://:$s_port/$d_ip?host=$d_port\"" >>$gost_conf_path + elif [ "$is_encrypt" == "encrypttls" ]; then + echo " \"tcp://:$s_port\", + \"udp://:$s_port\" + ], + \"ChainNodes\": [ + \"relay+tls://$d_ip:$d_port\"" >>$gost_conf_path + elif [ "$is_encrypt" == "encryptws" ]; then + echo " \"tcp://:$s_port\", + \"udp://:$s_port\" + ], + \"ChainNodes\": [ + \"relay+ws://$d_ip:$d_port\"" >>$gost_conf_path + elif [ "$is_encrypt" == "encryptwss" ]; then + echo " \"tcp://:$s_port\", + \"udp://:$s_port\" + ], + \"ChainNodes\": [ + \"relay+wss://$d_ip:$d_port\"" >>$gost_conf_path + elif [ "$is_encrypt" == "peertls" ]; then + echo " \"tcp://:$s_port\", + \"udp://:$s_port\" + ], + \"ChainNodes\": [ + \"relay+tls://:?ip=/root/$d_ip.txt&strategy=$d_port\"" >>$gost_conf_path + elif [ "$is_encrypt" == "peerws" ]; then + echo " \"tcp://:$s_port\", + \"udp://:$s_port\" + ], + \"ChainNodes\": [ + \"relay+ws://:?ip=/root/$d_ip.txt&strategy=$d_port\"" >>$gost_conf_path + elif [ "$is_encrypt" == "peerwss" ]; then + echo " \"tcp://:$s_port\", + \"udp://:$s_port\" + ], + \"ChainNodes\": [ + \"relay+wss://:?ip=/root/$d_ip.txt&strategy=$d_port\"" >>$gost_conf_path + elif [ "$is_encrypt" == "cdnws" ]; then + echo " \"tcp://:$s_port\", + \"udp://:$s_port\" + ], + \"ChainNodes\": [ + \"relay+ws://$d_ip?host=$d_port\"" >>$gost_conf_path + elif [ "$is_encrypt" == "cdnwss" ]; then + echo " \"tcp://:$s_port\", + \"udp://:$s_port\" + ], + \"ChainNodes\": [ + \"relay+wss://$d_ip?host=$d_port\"" >>$gost_conf_path + elif [ "$is_encrypt" == "decrypttls" ]; then + if [ -d "$HOME/gost_cert" ]; then + echo " \"relay+tls://:$s_port/$d_ip:$d_port?cert=/root/gost_cert/cert.pem&key=/root/gost_cert/key.pem\"" >>$gost_conf_path + else + echo " \"relay+tls://:$s_port/$d_ip:$d_port\"" >>$gost_conf_path + fi + elif [ "$is_encrypt" == "decryptws" ]; then + echo " \"relay+ws://:$s_port/$d_ip:$d_port\"" >>$gost_conf_path + elif [ "$is_encrypt" == "decryptwss" ]; then + if [ -d "$HOME/gost_cert" ]; then + echo " \"relay+wss://:$s_port/$d_ip:$d_port?cert=/root/gost_cert/cert.pem&key=/root/gost_cert/key.pem\"" >>$gost_conf_path + else + echo " \"relay+wss://:$s_port/$d_ip:$d_port\"" >>$gost_conf_path + fi + elif [ "$is_encrypt" == "ss" ]; then + echo " \"ss://$d_ip:$s_port@:$d_port\"" >>$gost_conf_path + elif [ "$is_encrypt" == "socks" ]; then + echo " \"socks5://$d_ip:$s_port@:$d_port\"" >>$gost_conf_path + elif [ "$is_encrypt" == "http" ]; then + echo " \"http://$d_ip:$s_port@:$d_port\"" >>$gost_conf_path + else + echo "config error" + fi + else + echo "config error" + exit + fi +} + +function writeconf() { + count_line=$(awk 'END{print NR}' $raw_conf_path) + for ((i = 1; i <= $count_line; i++)); do + if [ $i -eq 1 ]; then + trans_conf=$(sed -n "${i}p" $raw_conf_path) + eachconf_retrieve + method + elif [ $i -gt 1 ]; then + if [ $i -eq 2 ]; then + echo " ], + \"Routes\": [" >>$gost_conf_path + trans_conf=$(sed -n "${i}p" $raw_conf_path) + eachconf_retrieve + multiconfstart + method + multiconflast + else + trans_conf=$(sed -n "${i}p" $raw_conf_path) + eachconf_retrieve + multiconfstart + method + multiconflast + fi + fi + done +} +function show_all_conf() { + echo -e " GOST 配置 " + echo -e "--------------------------------------------------------" + echo -e "序号|方法\t |本地端口\t|目的地地址:目的地端口" + echo -e "--------------------------------------------------------" + + count_line=$(awk 'END{print NR}' $raw_conf_path) + for ((i = 1; i <= $count_line; i++)); do + trans_conf=$(sed -n "${i}p" $raw_conf_path) + eachconf_retrieve + + if [ "$is_encrypt" == "nonencrypt" ]; then + str="不加密中转" + elif [ "$is_encrypt" == "encrypttls" ]; then + str=" tls隧道 " + elif [ "$is_encrypt" == "encryptws" ]; then + str=" ws隧道 " + elif [ "$is_encrypt" == "encryptwss" ]; then + str=" wss隧道 " + elif [ "$is_encrypt" == "peerno" ]; then + str=" 不加密均衡负载 " + elif [ "$is_encrypt" == "peertls" ]; then + str=" tls隧道均衡负载 " + elif [ "$is_encrypt" == "peerws" ]; then + str=" ws隧道均衡负载 " + elif [ "$is_encrypt" == "peerwss" ]; then + str=" wss隧道均衡负载 " + elif [ "$is_encrypt" == "decrypttls" ]; then + str=" tls解密 " + elif [ "$is_encrypt" == "decryptws" ]; then + str=" ws解密 " + elif [ "$is_encrypt" == "decryptwss" ]; then + str=" wss解密 " + elif [ "$is_encrypt" == "ss" ]; then + str=" ss " + elif [ "$is_encrypt" == "socks" ]; then + str=" socks5 " + elif [ "$is_encrypt" == "http" ]; then + str=" http " + elif [ "$is_encrypt" == "cdnno" ]; then + str="不加密转发CDN" + elif [ "$is_encrypt" == "cdnws" ]; then + str="ws隧道转发CDN" + elif [ "$is_encrypt" == "cdnwss" ]; then + str="wss隧道转发CDN" + else + str="" + fi + + echo -e " $i |$str |$s_port\t|$d_ip:$d_port" + echo -e "--------------------------------------------------------" + done +} + +cron_restart() { + echo -e "------------------------------------------------------" + echo -e "gost定时重启任务: " + echo -e "-----------------------------------" + echo -e "[1] 配置gost定时重启任务" + echo -e "[2] 删除gost定时重启任务" + echo -e "-----------------------------------" + read -p "请选择: " numcron + if [ "$numcron" == "1" ]; then + echo -e "-----------------------------------------------------" + echo -e "gost定时重启任务类型: " + echo -e "-----------------------------------" + echo -e "[1] 每?小时重启" + echo -e "[2] 每日?点重启" + echo -e "-----------------------------------" + read -p "请选择: " numcrontype + if [ "$numcrontype" == "1" ]; then + echo -e "-----------------------------------" + read -p "每?小时重启: " cronhr + echo "0 0 */$cronhr * * ? * systemctl restart gost" >>/etc/crontab + echo -e "定时重启设置成功!" + elif [ "$numcrontype" == "2" ]; then + echo -e "-----------------------------------" + read -p "每日?点重启: " cronhr + echo "0 0 $cronhr * * ? systemctl restart gost" >>/etc/crontab + echo -e "定时重启设置成功!" + else + echo "type error, please try again" + exit + fi + elif [ "$numcron" == "2" ]; then + sed -i "/gost/d" /etc/crontab + echo -e "定时重启任务删除完成!" + else + echo "type error, please try again" + exit + fi +} + + +echo && echo -e " gost 一键安装配置脚本" " + ------------------------------------------------------------- + 特性: (1)本脚本采用systemd及gost配置文件对gost进行管理 + (2)机器重启后转发也不会失效 + 功能: (1)tcp+udp不加密转发, (2)中转机加密转发, (3)落地机解密对接转发 + + ${Green_font_prefix}1.${Font_color_suffix} 安装 gost + ${Green_font_prefix}2.${Font_color_suffix} 卸载 gost +———————————— + ${Green_font_prefix}3.${Font_color_suffix} 启动 gost + ${Green_font_prefix}4.${Font_color_suffix} 停止 gost + ${Green_font_prefix}5.${Font_color_suffix} 重启 gost +———————————— + ${Green_font_prefix}6.${Font_color_suffix} 新增gost转发配置 + ${Green_font_prefix}7.${Font_color_suffix} 查看现有gost配置 + ${Green_font_prefix}8.${Font_color_suffix} 删除一则gost配置 +———————————— + ${Green_font_prefix}9.${Font_color_suffix} gost定时重启配置 + ${Green_font_prefix}10.${Font_color_suffix} 自定义TLS证书配置 + ${Green_font_prefix}11.${Font_color_suffix} 查看gost运行状态 +————————————" && echo +read -e -p " 请输入数字 [1-11]:" num +case "$num" in +1) + Install_ct + ;; +2) + Uninstall_ct + ;; +3) + Start_ct + ;; +4) + Stop_ct + ;; +5) + Restart_ct + ;; +6) + rawconf + rm -rf /etc/gost/config.json + confstart + writeconf + conflast + systemctl restart gost + echo -e "配置已生效,当前配置如下" + echo -e "--------------------------------------------------------" + show_all_conf + ;; +7) + show_all_conf + ;; +8) + show_all_conf + read -p "请输入你要删除的配置编号:" numdelete + if echo $numdelete | grep -q '[0-9]'; then + sed -i "${numdelete}d" $raw_conf_path + rm -rf /etc/gost/config.json + confstart + writeconf + conflast + systemctl restart gost + echo -e "配置已删除,服务已重启" + else + echo "请输入正确数字" + fi + ;; +9) + cron_restart + ;; +10) + cert + ;; +11) + status + ;; +*) + echo "请输入正确数字 [1-9]" + ;; +esac diff --git a/nezha/README.md b/nezha/README.md new file mode 100644 index 0000000..341f2e5 --- /dev/null +++ b/nezha/README.md @@ -0,0 +1,10 @@ +### nezha固定版本`v0.20.5`的安装`agent`脚本 + +将面板中复制所得的指令中的sh的raw文件url替换成 +``` +https://cdn.jsdelivr.net/gh/sky22333/shell@main/nezha/install.sh +``` +然后接着去vps安装即可 + + +> 旧版备份`https://cdn.jsdelivr.net/gh/sky22333/shell@main/nezha/agent.sh` diff --git a/nezha/agent.sh b/nezha/agent.sh new file mode 100644 index 0000000..6c4691a --- /dev/null +++ b/nezha/agent.sh @@ -0,0 +1,411 @@ +#!/bin/sh +# Generated by nezhahq/scriptgen. DO NOT EDIT + +NZ_BASE_PATH="/opt/nezha" +NZ_DASHBOARD_PATH="${NZ_BASE_PATH}/dashboard" +NZ_AGENT_PATH="${NZ_BASE_PATH}/agent" +NZ_DASHBOARD_SERVICE="/etc/systemd/system/nezha-dashboard.service" +NZ_DASHBOARD_SERVICERC="/etc/init.d/nezha-dashboard" +GITHUB_URL="https://github.com" + +red='\033[0;31m' +green='\033[0;32m' +yellow='\033[0;33m' +plain='\033[0m' +export PATH="$PATH:/usr/local/bin" + +os_arch="" +[ -e /etc/os-release ] && grep -i "PRETTY_NAME" /etc/os-release | grep -qi "alpine" && os_alpine='1' + +sudo() { + myEUID=$(id -ru) + if [ "$myEUID" -ne 0 ]; then + if command -v sudo > /dev/null 2>&1; then + command sudo "$@" + else + err "错误: 您的系统未安装 sudo,因此无法进行该项操作。" + exit 1 + fi + else + "$@" + fi +} + +check_systemd() { + if [ "$os_alpine" != 1 ] && ! command -v systemctl >/dev/null 2>&1; then + echo "不支持此系统:未找到 systemctl 命令" + exit 1 + fi +} + +err() { + printf "${red}%s${plain}\n" "$*" >&2 +} + +success() { + printf "${green}%s${plain}\n" "$*" +} + +info() { + printf "${yellow}%s${plain}\n" "$*" +} + +geo_check() { + api_list="https://blog.cloudflare.com/cdn-cgi/trace https://dash.cloudflare.com/cdn-cgi/trace https://developers.cloudflare.com/cdn-cgi/trace" + ua="Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/81.0" + set -- "$api_list" + for url in $api_list; do + text="$(curl -A "$ua" -m 10 -s "$url")" + endpoint="$(echo "$text" | sed -n 's/.*h=\([^ ]*\).*/\1/p')" + if echo "$text" | grep -qw 'CN'; then + isCN=true + break + elif echo "$url" | grep -q "$endpoint"; then + break + fi + done +} + +pre_check() { + umask 077 + + ## os_arch + if uname -m | grep -q 'x86_64'; then + os_arch="amd64" + elif uname -m | grep -q 'i386\|i686'; then + os_arch="386" + elif uname -m | grep -q 'aarch64\|armv8b\|armv8l'; then + os_arch="arm64" + elif uname -m | grep -q 'arm'; then + os_arch="arm" + elif uname -m | grep -q 's390x'; then + os_arch="s390x" + elif uname -m | grep -q 'riscv64'; then + os_arch="riscv64" + fi + + ## China_IP + if [ -z "$CN" ]; then + geo_check + if [ -n "$isCN" ]; then + echo "根据geoip api提供的信息,当前IP可能在中国" + printf "是否选用中国镜像完成安装? [Y/n] (自定义镜像输入 3):" + read -r input + case $input in + [yY][eE][sS] | [yY]) + echo "使用中国镜像" + CN=true + ;; + + [nN][oO] | [nN]) + echo "不使用中国镜像" + ;; + + [3]) + echo "使用自定义镜像" + printf "请输入自定义镜像 (例如:dn-dao-github-mirror.daocloud.io),留空为不使用:" + read -r input + case $input in + *) + CUSTOM_MIRROR=$input + ;; + esac + ;; + *) + echo "不使用中国镜像" + ;; + esac + fi + fi +} + + +select_version() { + if [ -z "$IS_DOCKER_NEZHA" ]; then + info "请自行选择您的Agent安装方式(输入哪个都是一样的):" + info "1. 安装Agent" + info "2. 安装Agent" + while true; do + printf "请输入选择 [1-2]:" + read -r option + case "${option}" in + 1) + IS_DOCKER_NEZHA=1 + break + ;; + 2) + IS_DOCKER_NEZHA=0 + break + ;; + *) + err "请输入正确的选择 [1-2]" + ;; + esac + done + fi +} + +before_show_menu() { + echo && info "* 按回车返回主菜单 *" && read temp + show_menu +} + +install_base() { + (command -v curl >/dev/null 2>&1 && command -v wget >/dev/null 2>&1 && command -v unzip >/dev/null 2>&1 && command -v getenforce >/dev/null 2>&1) || + (install_soft curl wget unzip) +} + +install_arch() { + info "提示:Arch安装libselinux需添加nezha-agent用户,安装完会自动删除,建议手动检查一次" + read -r -p "是否安装libselinux? [Y/n] " input + case $input in + [yY][eE][sS] | [yY]) + useradd -m nezha-agent + sed -i "$ a\nezha-agent ALL=(ALL ) NOPASSWD:ALL" /etc/sudoers + sudo -iu nezha-agent bash -c 'gpg --keyserver keys.gnupg.net --recv-keys 4695881C254508D1; + cd /tmp; git clone https://aur.archlinux.org/libsepol.git; cd libsepol; makepkg -si --noconfirm --asdeps; cd ..; + git clone https://aur.archlinux.org/libselinux.git; cd libselinux; makepkg -si --noconfirm; cd ..; + rm -rf libsepol libselinux' + sed -i '/nezha-agent/d' /etc/sudoers && sleep 30s && killall -u nezha-agent && userdel -r nezha-agent + info "提示: 已删除用户nezha-agent,请务必手动核查一遍!" + ;; + [nN][oO] | [nN]) + echo "不安装libselinux" + ;; + *) + echo "不安装libselinux" + exit 0 + ;; + esac +} + +install_soft() { + (command -v yum >/dev/null 2>&1 && sudo yum makecache && sudo yum install "$@" selinux-policy -y) || + (command -v apt >/dev/null 2>&1 && sudo apt update && sudo apt install "$@" selinux-utils -y) || + (command -v pacman >/dev/null 2>&1 && sudo pacman -Syu "$@" base-devel --noconfirm && install_arch) || + (command -v apt-get >/dev/null 2>&1 && sudo apt-get update && sudo apt-get install "$@" selinux-utils -y) || + (command -v apk >/dev/null 2>&1 && sudo apk update && sudo apk add "$@" -f) +} + + +selinux() { + #Check SELinux + if command -v getenforce >/dev/null 2>&1; then + if getenforce | grep '[Ee]nfor'; then + echo "SELinux是开启状态,正在关闭!" + sudo setenforce 0 >/dev/null 2>&1 + find_key="SELINUX=" + sudo sed -ri "/^$find_key/c${find_key}disabled" /etc/selinux/config + fi + fi +} + +install_agent() { + install_base + selinux + + echo "> 安装监控Agent" + + _version=v0.20.5 + + # Nezha Monitoring Folder + sudo mkdir -p $NZ_AGENT_PATH + + echo "正在下载监控端" + if [ -z "$CN" ]; then + NZ_AGENT_URL="${GITHUB_URL}/sky22333/shell/releases/download/${_version}/nezha-agent_linux_${os_arch}.zip" + else + NZ_AGENT_URL="${GITHUB_URL}/sky22333/shell/releases/download/${_version}/nezha-agent_linux_${os_arch}.zip" + fi + + _cmd="wget -t 2 -T 60 -O nezha-agent_linux_${os_arch}.zip $NZ_AGENT_URL >/dev/null 2>&1" + if ! eval "$_cmd"; then + err "Release 下载失败,请检查本机能否连接 ${GITHUB_URL}" + return 1 + fi + + sudo unzip -qo nezha-agent_linux_${os_arch}.zip && + sudo mv nezha-agent $NZ_AGENT_PATH && + sudo rm -rf nezha-agent_linux_${os_arch}.zip README.md + + if [ $# -ge 3 ]; then + modify_agent_config "$@" + else + modify_agent_config 0 + fi + + if [ $# = 0 ]; then + before_show_menu + fi +} + +modify_agent_config() { + echo "> 修改 Agent 配置" + + if [ $# -lt 3 ]; then + echo "请先在管理面板上添加Agent,记录下密钥" + printf "请输入一个解析到面板所在IP的域名(不可套CDN): " + read -r nz_grpc_host + printf "请输入面板RPC端口 (默认值 5555): " + read -r nz_grpc_port + printf "请输入Agent 密钥: " + read -r nz_client_secret + printf "是否启用针对 gRPC 端口的 SSL/TLS加密 (--tls),需要请按 [y],默认是不需要,不理解用户可回车跳过: " + read -r nz_grpc_proxy + echo "${nz_grpc_proxy}" | grep -qiw 'Y' && args='--tls' + if [ -z "$nz_grpc_host" ] || [ -z "$nz_client_secret" ]; then + err "所有选项都不能为空" + before_show_menu + return 1 + fi + if [ -z "$nz_grpc_port" ]; then + nz_grpc_port=5555 + fi + else + nz_grpc_host=$1 + nz_grpc_port=$2 + nz_client_secret=$3 + shift 3 + if [ $# -gt 0 ]; then + args="$*" + fi + fi + + _cmd="sudo ${NZ_AGENT_PATH}/nezha-agent service install -s $nz_grpc_host:$nz_grpc_port -p $nz_client_secret $args >/dev/null 2>&1" + + if ! eval "$_cmd"; then + sudo "${NZ_AGENT_PATH}"/nezha-agent service uninstall >/dev/null 2>&1 + sudo "${NZ_AGENT_PATH}"/nezha-agent service install -s "$nz_grpc_host:$nz_grpc_port" -p "$nz_client_secret" "$args" >/dev/null 2>&1 + fi + + success "Agent 配置 修改成功,请稍等 Agent 重启生效" + + #if [[ $# == 0 ]]; then + # before_show_menu + #fi +} + +show_agent_log() { + echo "> 获取 Agent 日志" + + if [ "$os_alpine" != 1 ]; then + sudo journalctl -xf -u nezha-agent.service + else + sudo tail -n 10 /var/log/nezha-agent.err + fi + + if [ $# = 0 ]; then + before_show_menu + fi +} + +uninstall_agent() { + echo "> 卸载 Agent" + + sudo ${NZ_AGENT_PATH}/nezha-agent service uninstall + + sudo rm -rf $NZ_AGENT_PATH + clean_all + + if [ $# = 0 ]; then + before_show_menu + fi +} + +restart_agent() { + echo "> 重启 Agent" + + sudo ${NZ_AGENT_PATH}/nezha-agent service restart + + if [ $# = 0 ]; then + before_show_menu + fi +} + +clean_all() { + if [ -z "$(ls -A ${NZ_BASE_PATH})" ]; then + sudo rm -rf ${NZ_BASE_PATH} + fi +} + +show_usage() { + echo "哪吒监控v0.20.5管理脚本使用方法: " + echo "--------------------------------------------------------" + echo "./nezha.sh - 显示管理菜单" + echo "--------------------------------------------------------" + echo "./nezha.sh install_agent - 安装监控Agent" + echo "./nezha.sh modify_agent_config - 修改Agent配置" + echo "./nezha.sh show_agent_log - 查看Agent日志" + echo "./nezha.sh uninstall_agent - 卸载Agent" + echo "./nezha.sh restart_agent - 重启Agent" + echo "--------------------------------------------------------" +} + +show_menu() { + printf " + ${green}哪吒v0.20.5监控Agent管理脚本${plain} + ————————————————- + ${green}8.${plain} 安装监控Agent + ${green}9.${plain} 修改Agent配置 + ${green}10.${plain} 查看Agent日志 + ${green}11.${plain} 卸载Agent + ${green}12.${plain} 重启Agent + ————————————————- + ${green}0.${plain} 退出脚本 + " + echo && printf "请输入选择 [0-13]: " && read -r num + case "${num}" in + 0) + exit 0 + ;; + 8) + install_agent + ;; + 9) + modify_agent_config + ;; + 10) + show_agent_log + ;; + 11) + uninstall_agent + ;; + 12) + restart_agent + ;; + *) + err "请输入正确的数字 [0-13]" + ;; + esac +} + +pre_check + +if [ $# -gt 0 ]; then + case $1 in + "install_agent") + shift + if [ $# -ge 3 ]; then + install_agent "$@" + else + install_agent 0 + fi + ;; + "modify_agent_config") + modify_agent_config 0 + ;; + "show_agent_log") + show_agent_log 0 + ;; + "uninstall_agent") + uninstall_agent 0 + ;; + "restart_agent") + restart_agent 0 + ;; + *) show_usage ;; + esac +else + select_version + show_menu +fi diff --git a/nezha/install.sh b/nezha/install.sh new file mode 100644 index 0000000..7835a0e --- /dev/null +++ b/nezha/install.sh @@ -0,0 +1,901 @@ +#!/bin/sh +# Generated by nezhahq/scriptgen. DO NOT EDIT + +NZ_BASE_PATH="/opt/nezha" +NZ_DASHBOARD_PATH="${NZ_BASE_PATH}/dashboard" +NZ_AGENT_PATH="${NZ_BASE_PATH}/agent" +NZ_DASHBOARD_SERVICE="/etc/systemd/system/nezha-dashboard.service" +NZ_DASHBOARD_SERVICERC="/etc/init.d/nezha-dashboard" + +red='\033[0;31m' +green='\033[0;32m' +yellow='\033[0;33m' +plain='\033[0m' +export PATH="$PATH:/usr/local/bin" + +os_arch="" +[ -e /etc/os-release ] && grep -i "PRETTY_NAME" /etc/os-release | grep -qi "alpine" && os_alpine='1' + +sudo() { + myEUID=$(id -ru) + if [ "$myEUID" -ne 0 ]; then + if command -v sudo > /dev/null 2>&1; then + command sudo "$@" + else + err "错误: 您的系统未安装 sudo,因此无法进行该项操作。" + exit 1 + fi + else + "$@" + fi +} + +check_systemd() { + if [ "$os_alpine" != 1 ] && ! command -v systemctl >/dev/null 2>&1; then + echo "不支持此系统:未找到 systemctl 命令" + exit 1 + fi +} + +err() { + printf "${red}%s${plain}\n" "$*" >&2 +} + +success() { + printf "${green}%s${plain}\n" "$*" +} + +info() { + printf "${yellow}%s${plain}\n" "$*" +} + +geo_check() { + api_list="https://blog.cloudflare.com/cdn-cgi/trace https://dash.cloudflare.com/cdn-cgi/trace https://developers.cloudflare.com/cdn-cgi/trace" + ua="Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/81.0" + set -- "$api_list" + for url in $api_list; do + text="$(curl -A "$ua" -m 10 -s "$url")" + endpoint="$(echo "$text" | sed -n 's/.*h=\([^ ]*\).*/\1/p')" + if echo "$text" | grep -qw 'CN'; then + isCN=true + break + elif echo "$url" | grep -q "$endpoint"; then + break + fi + done +} + +pre_check() { + umask 077 + + ## os_arch + if uname -m | grep -q 'x86_64'; then + os_arch="amd64" + elif uname -m | grep -q 'i386\|i686'; then + os_arch="386" + elif uname -m | grep -q 'aarch64\|armv8b\|armv8l'; then + os_arch="arm64" + elif uname -m | grep -q 'arm'; then + os_arch="arm" + elif uname -m | grep -q 's390x'; then + os_arch="s390x" + elif uname -m | grep -q 'riscv64'; then + os_arch="riscv64" + fi + + ## China_IP + if [ -z "$CN" ]; then + geo_check + if [ -n "$isCN" ]; then + echo "根据geoip api提供的信息,当前IP可能在中国" + printf "是否选用中国镜像完成安装? [Y/n] (自定义镜像输入 3):" + read -r input + case $input in + [yY][eE][sS] | [yY]) + echo "使用中国镜像" + CN=true + ;; + + [nN][oO] | [nN]) + echo "不使用中国镜像" + ;; + + [3]) + echo "使用自定义镜像" + printf "请输入自定义镜像 (例如:dn-dao-github-mirror.daocloud.io),留空为不使用:" + read -r input + case $input in + *) + CUSTOM_MIRROR=$input + ;; + esac + ;; + *) + echo "不使用中国镜像" + ;; + esac + fi + fi + + if [ -n "$CUSTOM_MIRROR" ]; then + GITHUB_RAW_URL="gitee.com/naibahq/scripts/raw/main" + GITHUB_URL=$CUSTOM_MIRROR + Get_Docker_URL="get.docker.com" + Get_Docker_Argu=" -s docker --mirror Aliyun" + Docker_IMG="registry.cn-shanghai.aliyuncs.com\/naibahq\/nezha-dashboard" + else + if [ -z "$CN" ]; then + GITHUB_RAW_URL="raw.githubusercontent.com/nezhahq/scripts/main" + GITHUB_URL="github.com" + Get_Docker_URL="get.docker.com" + Get_Docker_Argu=" " + Docker_IMG="ghcr.io\/naiba\/nezha-dashboard" + else + GITHUB_RAW_URL="gitee.com/naibahq/scripts/raw/main" + GITHUB_URL="gitee.com" + Get_Docker_URL="get.docker.com" + Get_Docker_Argu=" -s docker --mirror Aliyun" + Docker_IMG="registry.cn-shanghai.aliyuncs.com\/naibahq\/nezha-dashboard" + fi + fi +} + +installation_check() { + if docker compose version >/dev/null 2>&1; then + DOCKER_COMPOSE_COMMAND="docker compose" + if sudo $DOCKER_COMPOSE_COMMAND ls | grep -qw "$NZ_DASHBOARD_PATH/docker-compose.yaml" >/dev/null 2>&1; then + NEZHA_IMAGES=$(sudo docker images --format "{{.Repository}}:{{.Tag}}" | grep -w "nezha-dashboard") + if [ -n "$NEZHA_IMAGES" ]; then + echo "存在带有 nezha-dashboard 仓库的 Docker 镜像:" + echo "$NEZHA_IMAGES" + IS_DOCKER_NEZHA=1 + FRESH_INSTALL=0 + return + else + echo "未找到带有 nezha-dashboard 仓库的 Docker 镜像。" + fi + fi + elif command -v docker-compose >/dev/null 2>&1; then + DOCKER_COMPOSE_COMMAND="docker-compose" + if sudo $DOCKER_COMPOSE_COMMAND -f "$NZ_DASHBOARD_PATH/docker-compose.yaml" config >/dev/null 2>&1; then + NEZHA_IMAGES=$(sudo docker images --format "{{.Repository}}:{{.Tag}}" | grep -w "nezha-dashboard") + if [ -n "$NEZHA_IMAGES" ]; then + echo "存在带有 nezha-dashboard 仓库的 Docker 镜像:" + echo "$NEZHA_IMAGES" + IS_DOCKER_NEZHA=1 + FRESH_INSTALL=0 + return + else + echo "未找到带有 nezha-dashboard 仓库的 Docker 镜像。" + fi + fi + fi + + if [ -f "$NZ_DASHBOARD_PATH/app" ]; then + IS_DOCKER_NEZHA=0 + FRESH_INSTALL=0 + fi +} + +select_version() { + if [ -z "$IS_DOCKER_NEZHA" ]; then + info "请安装方式(此脚本是安装旧版,所以输入哪个都是一样的):" + info "1. 独立安装" + info "2. 独立安装" + while true; do + printf "请输入选择 [1-2]:" + read -r option + case "${option}" in + 1) + IS_DOCKER_NEZHA=0 + break + ;; + 2) + IS_DOCKER_NEZHA=0 + break + ;; + *) + err "请输入正确的选择 [1-2]" + ;; + esac + done + fi +} + +update_script() { + echo "> 更新脚本" + + curl -sL https://${GITHUB_RAW_URL}/install.sh -o /tmp/nezha.sh + mv -f /tmp/nezha.sh ./nezha.sh && chmod a+x ./nezha.sh + + echo "3s后执行新脚本" + sleep 3s + clear + exec ./nezha.sh + exit 0 +} + +before_show_menu() { + echo && info "* 按回车返回主菜单 *" && read temp + show_menu +} + +install_base() { + (command -v curl >/dev/null 2>&1 && command -v wget >/dev/null 2>&1 && command -v unzip >/dev/null 2>&1 && command -v getenforce >/dev/null 2>&1) || + (install_soft curl wget unzip) +} + +install_arch() { + info "提示:Arch安装libselinux需添加nezha-agent用户,安装完会自动删除,建议手动检查一次" + read -r -p "是否安装libselinux? [Y/n] " input + case $input in + [yY][eE][sS] | [yY]) + useradd -m nezha-agent + sed -i "$ a\nezha-agent ALL=(ALL ) NOPASSWD:ALL" /etc/sudoers + sudo -iu nezha-agent bash -c 'gpg --keyserver keys.gnupg.net --recv-keys 4695881C254508D1; + cd /tmp; git clone https://aur.archlinux.org/libsepol.git; cd libsepol; makepkg -si --noconfirm --asdeps; cd ..; + git clone https://aur.archlinux.org/libselinux.git; cd libselinux; makepkg -si --noconfirm; cd ..; + rm -rf libsepol libselinux' + sed -i '/nezha-agent/d' /etc/sudoers && sleep 30s && killall -u nezha-agent && userdel -r nezha-agent + info "提示: 已删除用户nezha-agent,请务必手动核查一遍!" + ;; + [nN][oO] | [nN]) + echo "不安装libselinux" + ;; + *) + echo "不安装libselinux" + exit 0 + ;; + esac +} + +install_soft() { + (command -v yum >/dev/null 2>&1 && sudo yum makecache && sudo yum install "$@" selinux-policy -y) || + (command -v apt >/dev/null 2>&1 && sudo apt update && sudo apt install "$@" selinux-utils -y) || + (command -v pacman >/dev/null 2>&1 && sudo pacman -Syu "$@" base-devel --noconfirm && install_arch) || + (command -v apt-get >/dev/null 2>&1 && sudo apt-get update && sudo apt-get install "$@" selinux-utils -y) || + (command -v apk >/dev/null 2>&1 && sudo apk update && sudo apk add "$@" -f) +} + +install_dashboard() { + check_systemd + install_base + + echo "> 安装面板" + + # Nezha Monitoring Folder + if [ ! "$FRESH_INSTALL" = 0 ]; then + sudo mkdir -p $NZ_DASHBOARD_PATH + else + echo "您可能已经安装过面板端,重复安装会覆盖数据,请注意备份。" + printf "是否退出安装? [Y/n] " + read -r input + case $input in + [yY][eE][sS] | [yY]) + echo "退出安装" + exit 0 + ;; + [nN][oO] | [nN]) + echo "继续安装" + ;; + *) + echo "退出安装" + exit 0 + ;; + esac + fi + + if [ "$IS_DOCKER_NEZHA" = 1 ]; then + install_dashboard_docker + elif [ "$IS_DOCKER_NEZHA" = 0 ]; then + install_dashboard_standalone + fi + + modify_dashboard_config 0 + + if [ $# = 0 ]; then + before_show_menu + fi +} + +install_dashboard_docker() { + if [ ! "$FRESH_INSTALL" = 0 ]; then + if ! command -v docker >/dev/null 2>&1; then + echo "正在安装 Docker" + if [ "$os_alpine" != 1 ]; then + if ! curl -sL https://${Get_Docker_URL} | sudo bash -s "${Get_Docker_Argu}"; then + err "脚本获取失败,请检查本机能否链接 ${Get_Docker_URL}" + return 0 + fi + sudo systemctl enable docker.service + sudo systemctl start docker.service + else + sudo apk add docker docker-compose + sudo rc-update add docker + sudo rc-service docker start + fi + success "Docker 安装成功" + installation_check + fi + fi +} + +install_dashboard_standalone() { + if [ ! -d "${NZ_DASHBOARD_PATH}/resource/template/theme-custom" ] || [ ! -d "${NZ_DASHBOARD_PATH}/resource/static/custom" ]; then + sudo mkdir -p "${NZ_DASHBOARD_PATH}/resource/template/theme-custom" "${NZ_DASHBOARD_PATH}/resource/static/custom" >/dev/null 2>&1 + fi +} + +selinux() { + #Check SELinux + if command -v getenforce >/dev/null 2>&1; then + if getenforce | grep '[Ee]nfor'; then + echo "SELinux是开启状态,正在关闭!" + sudo setenforce 0 >/dev/null 2>&1 + find_key="SELINUX=" + sudo sed -ri "/^$find_key/c${find_key}disabled" /etc/selinux/config + fi + fi +} + +install_agent() { + install_base + selinux + + echo "> 安装监控Agent" + + _version=v0.20.5 + + # Nezha Monitoring Folder + sudo mkdir -p $NZ_AGENT_PATH + + echo "正在下载监控端" + if [ -z "$CN" ]; then + NZ_AGENT_URL="https://${GITHUB_URL}/nezhahq/agent/releases/download/${_version}/nezha-agent_linux_${os_arch}.zip" + else + NZ_AGENT_URL="https://${GITHUB_URL}/naibahq/agent/releases/download/${_version}/nezha-agent_linux_${os_arch}.zip" + fi + + _cmd="wget -t 2 -T 60 -O nezha-agent_linux_${os_arch}.zip $NZ_AGENT_URL >/dev/null 2>&1" + if ! eval "$_cmd"; then + err "Release 下载失败,请检查本机能否连接 ${GITHUB_URL}" + return 1 + fi + + sudo unzip -qo nezha-agent_linux_${os_arch}.zip && + sudo mv nezha-agent $NZ_AGENT_PATH && + sudo rm -rf nezha-agent_linux_${os_arch}.zip README.md + + if [ $# -ge 3 ]; then + modify_agent_config "$@" + else + modify_agent_config 0 + fi + + if [ $# = 0 ]; then + before_show_menu + fi +} + +modify_agent_config() { + echo "> 修改 Agent 配置" + + if [ $# -lt 3 ]; then + echo "请先在管理面板上添加Agent,记录下密钥" + printf "请输入一个解析到面板所在IP的域名(不可套CDN): " + read -r nz_grpc_host + printf "请输入面板RPC端口 (默认值 5555): " + read -r nz_grpc_port + printf "请输入Agent 密钥: " + read -r nz_client_secret + printf "是否启用针对 gRPC 端口的 SSL/TLS加密 (--tls),需要请按 [y],默认是不需要,不理解用户可回车跳过: " + read -r nz_grpc_proxy + echo "${nz_grpc_proxy}" | grep -qiw 'Y' && args='--tls' + if [ -z "$nz_grpc_host" ] || [ -z "$nz_client_secret" ]; then + err "所有选项都不能为空" + before_show_menu + return 1 + fi + if [ -z "$nz_grpc_port" ]; then + nz_grpc_port=5555 + fi + else + nz_grpc_host=$1 + nz_grpc_port=$2 + nz_client_secret=$3 + shift 3 + if [ $# -gt 0 ]; then + args="$*" + fi + fi + + _cmd="sudo ${NZ_AGENT_PATH}/nezha-agent service install -s $nz_grpc_host:$nz_grpc_port -p $nz_client_secret $args >/dev/null 2>&1" + + if ! eval "$_cmd"; then + sudo "${NZ_AGENT_PATH}"/nezha-agent service uninstall >/dev/null 2>&1 + sudo "${NZ_AGENT_PATH}"/nezha-agent service install -s "$nz_grpc_host:$nz_grpc_port" -p "$nz_client_secret" "$args" >/dev/null 2>&1 + fi + + success "Agent 配置 修改成功,请稍等 Agent 重启生效" + + #if [[ $# == 0 ]]; then + # before_show_menu + #fi +} + +modify_dashboard_config() { + echo "> 修改 Dashboard 配置" + + if [ "$IS_DOCKER_NEZHA" = 1 ]; then + if [ -n "$DOCKER_COMPOSE_COMMAND" ]; then + echo "正在下载 Docker 脚本" + _cmd="wget -t 2 -T 60 -O /tmp/nezha-docker-compose.yaml https://${GITHUB_RAW_URL}/extras/docker-compose.yaml >/dev/null 2>&1" + if ! eval "$_cmd"; then + err "脚本获取失败,请检查本机能否链接 ${GITHUB_RAW_URL}" + return 0 + fi + else + err "请手动安装 docker-compose。 https://docs.docker.com/compose/install/linux/" + before_show_menu + fi + fi + + _cmd="wget -t 2 -T 60 -O /tmp/nezha-config.yaml https://${GITHUB_RAW_URL}/extras/config.yaml >/dev/null 2>&1" + if ! eval "$_cmd"; then + err "脚本获取失败,请检查本机能否链接 ${GITHUB_RAW_URL}" + return 0 + fi + + echo "关于 GitHub Oauth2 应用:在 https://github.com/settings/developers 创建,无需审核,Callback 填 http(s)://域名或IP/oauth2/callback" + echo "关于 Gitee Oauth2 应用:在 https://gitee.com/oauth/applications 创建,无需审核,Callback 填 http(s)://域名或IP/oauth2/callback" + printf "请输入 OAuth2 提供商(github/gitlab/jihulab/gitee,默认 github): " + read -r nz_oauth2_type + printf "请输入 Oauth2 应用的 Client ID: " + read -r nz_github_oauth_client_id + printf "请输入 Oauth2 应用的 Client Secret: " + read -r nz_github_oauth_client_secret + printf "请输入 GitHub/Gitee 登录名作为管理员,多个以逗号隔开: " + read -r nz_admin_logins + printf "请输入站点标题: " + read -r nz_site_title + printf "请输入站点访问端口: (默认 8008)" + read -r nz_site_port + printf "请输入用于 Agent 接入的 RPC 端口: (默认 5555)" + read -r nz_grpc_port + + if [ -z "$nz_admin_logins" ] || [ -z "$nz_github_oauth_client_id" ] || [ -z "$nz_github_oauth_client_secret" ] || [ -z "$nz_site_title" ]; then + err "所有选项都不能为空" + before_show_menu + return 1 + fi + + if [ -z "$nz_site_port" ]; then + nz_site_port=8008 + fi + if [ -z "$nz_grpc_port" ]; then + nz_grpc_port=5555 + fi + if [ -z "$nz_oauth2_type" ]; then + nz_oauth2_type=github + fi + + sed -i "s/nz_oauth2_type/${nz_oauth2_type}/" /tmp/nezha-config.yaml + sed -i "s/nz_admin_logins/${nz_admin_logins}/" /tmp/nezha-config.yaml + sed -i "s/nz_grpc_port/${nz_grpc_port}/" /tmp/nezha-config.yaml + sed -i "s/nz_github_oauth_client_id/${nz_github_oauth_client_id}/" /tmp/nezha-config.yaml + sed -i "s/nz_github_oauth_client_secret/${nz_github_oauth_client_secret}/" /tmp/nezha-config.yaml + sed -i "s/nz_language/zh-CN/" /tmp/nezha-config.yaml + sed -i "s/nz_site_title/${nz_site_title}/" /tmp/nezha-config.yaml + if [ "$IS_DOCKER_NEZHA" = 1 ]; then + sed -i "s/nz_site_port/${nz_site_port}/" /tmp/nezha-docker-compose.yaml + sed -i "s/nz_grpc_port/${nz_grpc_port}/g" /tmp/nezha-docker-compose.yaml + sed -i "s/nz_image_url/${Docker_IMG}/" /tmp/nezha-docker-compose.yaml + elif [ "$IS_DOCKER_NEZHA" = 0 ]; then + sed -i "s/80/${nz_site_port}/" /tmp/nezha-config.yaml + fi + + sudo mkdir -p $NZ_DASHBOARD_PATH/data + sudo mv -f /tmp/nezha-config.yaml ${NZ_DASHBOARD_PATH}/data/config.yaml + if [ "$IS_DOCKER_NEZHA" = 1 ]; then + sudo mv -f /tmp/nezha-docker-compose.yaml ${NZ_DASHBOARD_PATH}/docker-compose.yaml + fi + + if [ "$IS_DOCKER_NEZHA" = 0 ]; then + echo "正在下载服务文件" + if [ "$os_alpine" != 1 ]; then + _download="sudo wget -t 2 -T 60 -O $NZ_DASHBOARD_SERVICE https://${GITHUB_RAW_URL}/services/nezha-dashboard.service >/dev/null 2>&1" + if ! eval "$_download"; then + err "文件下载失败,请检查本机能否连接 ${GITHUB_RAW_URL}" + return 0 + fi + else + _download="sudo wget -t 2 -T 60 -O $NZ_DASHBOARD_SERVICERC https://${GITHUB_RAW_URL}/services/nezha-dashboard >/dev/null 2>&1" + if ! eval "$_download"; then + err "文件下载失败,请检查本机能否连接 ${GITHUB_RAW_URL}" + return 0 + fi + sudo chmod +x $NZ_DASHBOARD_SERVICERC + fi + fi + + success "Dashboard 配置 修改成功,请稍等 Dashboard 重启生效" + + restart_and_update + + if [ $# = 0 ]; then + before_show_menu + fi +} + +restart_and_update() { + echo "> 重启并更新面板" + + if [ "$IS_DOCKER_NEZHA" = 1 ]; then + _cmd="restart_and_update_docker" + elif [ "$IS_DOCKER_NEZHA" = 0 ]; then + _cmd="restart_and_update_standalone" + fi + + if eval "$_cmd"; then + success "哪吒监控 重启成功" + info "默认管理面板地址:域名:站点访问端口" + else + err "重启失败,可能是因为启动时间超过了两秒,请稍后查看日志信息" + fi + + if [ $# = 0 ]; then + before_show_menu + fi +} + +restart_and_update_docker() { + sudo $DOCKER_COMPOSE_COMMAND -f ${NZ_DASHBOARD_PATH}/docker-compose.yaml pull + sudo $DOCKER_COMPOSE_COMMAND -f ${NZ_DASHBOARD_PATH}/docker-compose.yaml down + sudo $DOCKER_COMPOSE_COMMAND -f ${NZ_DASHBOARD_PATH}/docker-compose.yaml up -d +} + +restart_and_update_standalone() { + _version=v0.20.13 + + if [ "$os_alpine" != 1 ]; then + sudo systemctl daemon-reload + sudo systemctl stop nezha-dashboard + else + sudo rc-service nezha-dashboard stop + fi + + if [ -z "$CN" ]; then + NZ_DASHBOARD_URL="https://${GITHUB_URL}/naiba/nezha/releases/download/${_version}/dashboard-linux-${os_arch}.zip" + else + NZ_DASHBOARD_URL="https://${GITHUB_URL}/naibahq/nezha/releases/download/${_version}/dashboard-linux-${os_arch}.zip" + fi + + sudo wget -qO $NZ_DASHBOARD_PATH/app.zip "$NZ_DASHBOARD_URL" >/dev/null 2>&1 && sudo unzip -qq -o $NZ_DASHBOARD_PATH/app.zip -d $NZ_DASHBOARD_PATH && sudo mv $NZ_DASHBOARD_PATH/dashboard-linux-$os_arch $NZ_DASHBOARD_PATH/app && sudo rm $NZ_DASHBOARD_PATH/app.zip + sudo chmod +x $NZ_DASHBOARD_PATH/app + + if [ "$os_alpine" != 1 ]; then + sudo systemctl enable nezha-dashboard + sudo systemctl restart nezha-dashboard + else + sudo rc-update add nezha-dashboard + sudo rc-service nezha-dashboard restart + fi +} + +start_dashboard() { + echo "> 启动面板" + + if [ "$IS_DOCKER_NEZHA" = 1 ]; then + _cmd="start_dashboard_docker" + elif [ "$IS_DOCKER_NEZHA" = 0 ]; then + _cmd="start_dashboard_standalone" + fi + + if eval "$_cmd"; then + success "哪吒监控 启动成功" + else + err "启动失败,请稍后查看日志信息" + fi + + if [ $# = 0 ]; then + before_show_menu + fi +} + +start_dashboard_docker() { + sudo $DOCKER_COMPOSE_COMMAND -f ${NZ_DASHBOARD_PATH}/docker-compose.yaml up -d +} + +start_dashboard_standalone() { + if [ "$os_alpine" != 1 ]; then + sudo systemctl start nezha-dashboard + else + sudo rc-service nezha-dashboard start + fi +} + +stop_dashboard() { + echo "> 停止面板" + + if [ "$IS_DOCKER_NEZHA" = 1 ]; then + _cmd="stop_dashboard_docker" + elif [ "$IS_DOCKER_NEZHA" = 0 ]; then + _cmd="stop_dashboard_standalone" + fi + + if eval "$_cmd"; then + success "哪吒监控 停止成功" + else + err "停止失败,请稍后查看日志信息" + fi + + if [ $# = 0 ]; then + before_show_menu + fi +} + +stop_dashboard_docker() { + sudo $DOCKER_COMPOSE_COMMAND -f ${NZ_DASHBOARD_PATH}/docker-compose.yaml down +} + +stop_dashboard_standalone() { + if [ "$os_alpine" != 1 ]; then + sudo systemctl stop nezha-dashboard + else + sudo rc-service nezha-dashboard stop + fi +} + +show_dashboard_log() { + echo "> 获取 Dashboard 日志" + + if [ "$IS_DOCKER_NEZHA" = 1 ]; then + show_dashboard_log_docker + elif [ "$IS_DOCKER_NEZHA" = 0 ]; then + show_dashboard_log_standalone + fi + + if [ $# = 0 ]; then + before_show_menu + fi +} + +show_dashboard_log_docker() { + sudo $DOCKER_COMPOSE_COMMAND -f ${NZ_DASHBOARD_PATH}/docker-compose.yaml logs -f +} + +show_dashboard_log_standalone() { + if [ "$os_alpine" != 1 ]; then + sudo journalctl -xf -u nezha-dashboard.service + else + sudo tail -n 10 /var/log/nezha-dashboard.err + fi +} + +uninstall_dashboard() { + echo "> 卸载 Dashboard" + + if [ "$IS_DOCKER_NEZHA" = 1 ]; then + uninstall_dashboard_docker + elif [ "$IS_DOCKER_NEZHA" = 0 ]; then + uninstall_dashboard_standalone + fi + + clean_all + + if [ $# = 0 ]; then + before_show_menu + fi +} + +uninstall_dashboard_docker() { + sudo $DOCKER_COMPOSE_COMMAND -f ${NZ_DASHBOARD_PATH}/docker-compose.yaml down + sudo rm -rf $NZ_DASHBOARD_PATH + sudo docker rmi -f ghcr.io/naiba/nezha-dashboard >/dev/null 2>&1 + sudo docker rmi -f registry.cn-shanghai.aliyuncs.com/naibahq/nezha-dashboard >/dev/null 2>&1 +} + +uninstall_dashboard_standalone() { + sudo rm -rf $NZ_DASHBOARD_PATH + + if [ "$os_alpine" != 1 ]; then + sudo systemctl disable nezha-dashboard + sudo systemctl stop nezha-dashboard + else + sudo rc-update del nezha-dashboard + sudo rc-service nezha-dashboard stop + fi + + if [ "$os_alpine" != 1 ]; then + sudo rm $NZ_DASHBOARD_SERVICE + else + sudo rm $NZ_DASHBOARD_SERVICERC + fi +} + +show_agent_log() { + echo "> 获取 Agent 日志" + + if [ "$os_alpine" != 1 ]; then + sudo journalctl -xf -u nezha-agent.service + else + sudo tail -n 10 /var/log/nezha-agent.err + fi + + if [ $# = 0 ]; then + before_show_menu + fi +} + +uninstall_agent() { + echo "> 卸载 Agent" + + sudo ${NZ_AGENT_PATH}/nezha-agent service uninstall + + sudo rm -rf $NZ_AGENT_PATH + clean_all + + if [ $# = 0 ]; then + before_show_menu + fi +} + +restart_agent() { + echo "> 重启 Agent" + + sudo ${NZ_AGENT_PATH}/nezha-agent service restart + + if [ $# = 0 ]; then + before_show_menu + fi +} + +clean_all() { + if [ -z "$(ls -A ${NZ_BASE_PATH})" ]; then + sudo rm -rf ${NZ_BASE_PATH} + fi +} + +show_usage() { + echo "哪吒监控 管理脚本使用方法: " + echo "--------------------------------------------------------" + echo "./nezha.sh - 显示管理菜单" + echo "./nezha.sh install_dashboard - 安装面板端" + echo "./nezha.sh modify_dashboard_config - 修改面板配置" + echo "./nezha.sh start_dashboard - 启动面板" + echo "./nezha.sh stop_dashboard - 停止面板" + echo "./nezha.sh restart_and_update - 重启并更新面板" + echo "./nezha.sh show_dashboard_log - 查看面板日志" + echo "./nezha.sh uninstall_dashboard - 卸载管理面板" + echo "--------------------------------------------------------" + echo "./nezha.sh install_agent - 安装监控Agent" + echo "./nezha.sh modify_agent_config - 修改Agent配置" + echo "./nezha.sh show_agent_log - 查看Agent日志" + echo "./nezha.sh uninstall_agent - 卸载Agent" + echo "./nezha.sh restart_agent - 重启Agent" + echo "./nezha.sh update_script - 更新脚本" + echo "--------------------------------------------------------" +} + +show_menu() { + printf " + ${green}哪吒监控管理脚本${plain} + --- https://github.com/naiba/nezha --- + ${green}1.${plain} 安装面板端 + ${green}2.${plain} 修改面板配置 + ${green}3.${plain} 启动面板 + ${green}4.${plain} 停止面板 + ${green}5.${plain} 重启并更新面板 + ${green}6.${plain} 查看面板日志 + ${green}7.${plain} 卸载管理面板 + ————————————————- + ${green}8.${plain} 安装监控Agent + ${green}9.${plain} 修改Agent配置 + ${green}10.${plain} 查看Agent日志 + ${green}11.${plain} 卸载Agent + ${green}12.${plain} 重启Agent + ————————————————- + ${green}0.${plain} 退出脚本 + " + echo && printf "请输入选择 [0-13]: " && read -r num + case "${num}" in + 0) + exit 0 + ;; + 1) + install_dashboard + ;; + 2) + modify_dashboard_config + ;; + 3) + start_dashboard + ;; + 4) + stop_dashboard + ;; + 5) + restart_and_update + ;; + 6) + show_dashboard_log + ;; + 7) + uninstall_dashboard + ;; + 8) + install_agent + ;; + 9) + modify_agent_config + ;; + 10) + show_agent_log + ;; + 11) + uninstall_agent + ;; + 12) + restart_agent + ;; + *) + err "请输入正确的数字 [0-13]" + ;; + esac +} + +pre_check +installation_check + +if [ $# -gt 0 ]; then + case $1 in + "install_dashboard") + install_dashboard 0 + ;; + "modify_dashboard_config") + modify_dashboard_config 0 + ;; + "start_dashboard") + start_dashboard 0 + ;; + "stop_dashboard") + stop_dashboard 0 + ;; + "restart_and_update") + restart_and_update 0 + ;; + "show_dashboard_log") + show_dashboard_log 0 + ;; + "uninstall_dashboard") + uninstall_dashboard 0 + ;; + "install_agent") + shift + if [ $# -ge 3 ]; then + install_agent "$@" + else + install_agent 0 + fi + ;; + "modify_agent_config") + modify_agent_config 0 + ;; + "show_agent_log") + show_agent_log 0 + ;; + "uninstall_agent") + uninstall_agent 0 + ;; + "restart_agent") + restart_agent 0 + ;; + "update_script") + update_script 0 + ;; + *) show_usage ;; + esac +else + select_version + show_menu +fi diff --git a/proxy/README.md b/proxy/README.md new file mode 100644 index 0000000..7b9ead6 --- /dev/null +++ b/proxy/README.md @@ -0,0 +1,31 @@ +#### 卸载xray +``` +bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ remove --purge +``` + +- 一键安装L2TP +``` +bash <(curl -sSL https://github.com/sky22333/shell/raw/main/proxy/l2tp.sh) +``` + +- 站群多IP源进源出节点脚本,支持sk5和vless+tcp协议 +``` +bash <(curl -sSL https://github.com/sky22333/shell/raw/main/proxy/zhanqun.sh) +``` + +- 快速批量搭建二级代理脚本,vmess入站sk5出站 +``` +bash <(curl -sSL https://github.com/sky22333/shell/raw/main/proxy/vmess-sk5.sh) +``` + + +- 站群多IP源进源出节点脚本sk5协议 +``` +bash <(curl -sSL https://github.com/sky22333/shell/raw/main/proxy/duosocks.sh) +``` + + +- 站群多IP源进源出节点脚本,vmess+ws协议 +``` +bash <(curl -sSL https://github.com/sky22333/shell/raw/main/proxy/duovmess.sh) +``` \ No newline at end of file diff --git a/proxy/duosk5-ss2022.sh b/proxy/duosk5-ss2022.sh new file mode 100644 index 0000000..f87a9ed --- /dev/null +++ b/proxy/duosk5-ss2022.sh @@ -0,0 +1,556 @@ +#!/bin/bash +# 批量搭建ss2022入站到sk5出站代理 +# 读取sk5文件实现批量导入出站 +# 作者sky22333 + +red='\e[31m' +yellow='\e[33m' +green='\e[32m' +none='\e[0m' +config_file="/usr/local/etc/xray/config.json" +default_config=' +{ + "inbounds": [ + { + "port": 9999, + "protocol": "shadowsocks", + "settings": { + "method": "2022-blake3-aes-256-gcm", + "password": "75ENbpfSCyzUdZnLRjVGexaQxVPdCLw5T4RXbTGRQ/Q=", + "network": "tcp,udp" + }, + "tag": "inbound0" + } + ], + "outbounds": [ + { + "protocol": "socks", + "settings": { + "servers": [ + { + "address": "127.0.0.2", + "port": 2222, + "users": [ + { + "user": "admin123", + "pass": "admin333" + } + ] + } + ] + }, + "tag": "outbound0" + } + ], + "routing": { + "rules": [ + { + "type": "field", + "inboundTag": ["inbound0"], + "outboundTag": "outbound0" + } + ] + } +} +' + +check_and_install_curl() { + if ! type curl &>/dev/null; then + echo -e "${yellow}正在安装curl...${none}" + apt update && apt install -yq curl + fi +} + +check_and_install_jq() { + if ! type jq &>/dev/null; then + echo -e "${yellow}正在安装jq...${none}" + apt update && apt install -yq jq + fi +} + +check_and_install_openssl() { + if ! type openssl &>/dev/null; then + echo -e "${yellow}正在安装 openssl...${none}" + apt update && apt install -yq openssl + fi +} + +check_and_install_xray() { + if ! type xray &>/dev/null; then + echo -e "${yellow}正在安装 xray...${none}" + sudo ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime + bash <(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh) install --version v1.8.13 + fi +} + +check_existing_inbound_config() { + if grep -q '"tag":' "$config_file"; then + return 0 + else + return 1 + fi +} + +create_default_config() { + if ! check_existing_inbound_config; then + echo "$default_config" > "$config_file" + echo -e "${green}已创建默认配置文件。${none}" + else + echo -e "${yellow}入站配置已存在,跳过创建默认配置文件。${none}" + fi +} + +get_local_ip() { + local ip=$(curl -s http://ipinfo.io/ip) + if [[ $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "$ip" + else + echo "无法自动获取公网IP地址,请手动输入。" + read -p "请输入您的公网IP地址: " manual_ip + if [[ $manual_ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "$manual_ip" + else + echo "输入的IP地址格式不正确,请重新运行脚本并输入有效的公网IP地址。" + exit 1 + fi + fi +} + +get_ss_filename() { + local timestamp=$(date +"%Y%m%d-%H点%M分%S秒") + echo "/home/${timestamp}-ss.txt" +} + +generate_ss_link() { + local server=$1 + local port=$2 + local method=$3 + local password=$4 + local ps=$5 + + local password_urlencoded=$(echo -n "$password" | xxd -p | tr -d '\n' | sed 's/\(..\)/%\1/g') + + local base64_part=$(echo -n "${method}:${password}" | base64 -w 0) + echo "ss://${base64_part}@${server}:${port}#${ps}" +} + +save_multiple_ss_links() { + local local_ip=$1 + shift + local ss_file=$(get_ss_filename) + + > "$ss_file" + + while [[ $# -gt 0 ]]; do + local port=$1 + local password=$2 + local method=$3 + local index=$4 + shift 4 + local sk5_ip=$(jq -r ".outbounds | map(select(.tag == \"outbound${port}\")) | .[0].settings.servers[0].address" "$config_file") + if [[ -z "$sk5_ip" ]]; then + sk5_ip="未知IP" + fi + + local ss_link=$(generate_ss_link "$local_ip" "$port" "$method" "$password" "$sk5_ip") + + echo "$ss_link" >> "$ss_file" + done + + echo -e "${green}已将操作的所有节点保存至 $ss_file${none}" +} + +save_multiple_ss_links_with_ps() { + local local_ip=$1 + shift + local ss_file=$(get_ss_filename) + + > "$ss_file" + + while [[ $# -gt 0 ]]; do + local port=$1 + local password=$2 + local method=$3 + local index=$4 + local sk5_ip=$5 + shift 5 + + local ss_link=$(generate_ss_link "$local_ip" "$port" "$method" "$password" "$sk5_ip") + + echo "$ss_link" >> "$ss_file" + done + + echo -e "${green}已将操作的所有节点保存至 $ss_file${none}" +} + +save_all_ss_links() { + local local_ip=$(get_local_ip) + local ss_file=$(get_ss_filename) + + > "$ss_file" + + local config=$(jq '.inbounds | map(select(.port != 9999))' "$config_file") + local length=$(jq '. | length' <<< "$config") + + for ((i = 0; i < length; i++)); do + local port=$(jq -r ".[$i].port" <<< "$config") + local method=$(jq -r ".[$i].settings.method" <<< "$config") + local password=$(jq -r ".[$i].settings.password" <<< "$config") + + local sk5_ip=$(jq -r ".outbounds | map(select(.tag == \"outbound${port}\")) | .[0].settings.servers[0].address" "$config_file") + if [[ -z "$sk5_ip" ]]; then + sk5_ip="未知IP" + fi + + # 生成SS链接 + local ss_link=$(generate_ss_link "$local_ip" "$port" "$method" "$password" "$sk5_ip") + + # 写入文件 + echo "$ss_link" >> "$ss_file" + done + + echo -e "${green}已将全部Shadowsocks节点保存至 $ss_file${none}" +} + +show_inbound_configs() { + local local_ip=$(get_local_ip) + + local config=$(jq '.inbounds | map(select(.port != 9999))' "$config_file") + local outbounds=$(jq '.outbounds' "$config_file") + echo -e "${green}入站节点配置:${none}" + + local length=$(jq '. | length' <<< "$config") + for ((i = 0; i < length; i++)); do + local port=$(jq -r ".[$i].port" <<< "$config") + local method=$(jq -r ".[$i].settings.method" <<< "$config") + local password=$(jq -r ".[$i].settings.password" <<< "$config") + + local node_address="$local_ip" + + local sk5_ip=$(jq -r ".outbounds | map(select(.tag == \"outbound${port}\")) | .[0].settings.servers[0].address" "$config_file") + if [[ -z "$sk5_ip" ]]; then + sk5_ip="未知IP" + fi + + local ss_link=$(generate_ss_link "$node_address" "$port" "$method" "$password" "$sk5_ip") + + echo -e "${yellow}节点: $(($i + 1))${none} - 端口: ${port}, Shadowsocks 链接: ${ss_link}" + + # 构造出站配置的标签 + local outbound_tag="outbound$port" + + # 根据构造的标签查找对应的出站配置 + local outbound_config=$(jq --arg tag "$outbound_tag" '.[] | select(.tag == $tag) | .settings.servers[] | {address, port, user: .users[0].user, pass: .users[0].pass}' <<< "$outbounds") + + if [[ ! -z $outbound_config ]]; then + echo -e "${green}出站配置:${none} 地址: $(jq -r '.address' <<< "$outbound_config"), 端口: $(jq -r '.port' <<< "$outbound_config"), 用户名: $(jq -r '.user' <<< "$outbound_config"), 密码: $(jq -r '.pass' <<< "$outbound_config")" + else + echo -e "${red}未找到对应的出站配置。${none}" + fi + done + + save_all_ss_links +} + +add_new_nodes() { + local sk5_file="/home/sk5.txt" + + # 检查sk5.txt文件是否存在 + if [ ! -f "$sk5_file" ]; then + echo -e "${red}错误!${none} $sk5_file 文件不存在。" + return + fi + + # 读取sk5.txt文件中的代理信息 + local sk5_proxies=() + while IFS= read -r line || [[ -n "$line" ]]; do + # 忽略空行 + if [[ -z "$line" ]]; then + continue + fi + sk5_proxies+=("$line") + done < "$sk5_file" + + local proxy_count=${#sk5_proxies[@]} + if [ $proxy_count -eq 0 ]; then + echo -e "${red}错误!${none} 未在 $sk5_file 中找到有效的代理配置。" + return + fi + + echo -e "${green}从 $sk5_file 读取到 $proxy_count 个代理配置。${none}" + read -p "是否要导入全部配置?(y/n): " confirm + + if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then + read -p "请输入要导入的代理数量 (最大 $proxy_count): " num_to_import + if ! [[ $num_to_import =~ ^[0-9]+$ ]] || [ $num_to_import -le 0 ] || [ $num_to_import -gt $proxy_count ]; then + echo -e "${red}错误!${none} 输入数量无效。" + return + fi + else + num_to_import=$proxy_count + fi + + local max_port=$(jq '[.inbounds[].port] | max // 10000' "$config_file") + local start_port=$((max_port+1)) + local local_ip=$(get_local_ip) + local nodes_to_save=() + + for ((i=0; i "$config_file.tmp" && mv "$config_file.tmp" "$config_file" + + # 添加出站配置 + jq --arg tag "$new_outbound_tag" --arg addr "$outbound_addr" --argjson port "$outbound_port" --arg user "$outbound_user" --arg pass "$outbound_pass" ' + .outbounds += [{ + protocol: "socks", + settings: { servers: [{ address: $addr, port: $port | tonumber, users: [{ user: $user, pass: $pass }] }] }, + tag: $tag + }]' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file" + + # 添加路由规则 + jq --arg inTag "$new_tag" --arg outTag "$new_outbound_tag" ' + .routing.rules += [{ type: "field", inboundTag: [$inTag], outboundTag: $outTag }] + ' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file" + + # 保存节点信息以便后续生成SS链接 + nodes_to_save+=("$new_port" "$new_password" "$method" "$((i+1))") + done + + # 保存所有新添加的节点到一个文件 + save_multiple_ss_links "$local_ip" "${nodes_to_save[@]}" + + echo -e "${green}已成功添加 $num_to_import 个节点。${none}" + sudo systemctl restart xray + echo -e "${green}Xray 服务已重新启动。${none}" +} + +# 根据xiugai.txt文件修改SOCKS5出站代理 +modify_socks5_outbound() { + local modify_file="/home/xiugai.txt" + + # 检查xiugai.txt文件是否存在 + if [ ! -f "$modify_file" ]; then + echo -e "${red}错误!${none} $modify_file 文件不存在。" + return + fi + + # 读取xiugai.txt文件中的代理信息 + local modify_proxies=() + while IFS= read -r line || [[ -n "$line" ]]; do + # 忽略空行 + if [[ -z "$line" ]]; then + continue + fi + modify_proxies+=("$line") + done < "$modify_file" + + # 检查是否读取到代理 + local proxy_count=${#modify_proxies[@]} + if [ $proxy_count -eq 0 ]; then + echo -e "${red}错误!${none} 未在 $modify_file 中找到有效的代理配置。" + return + fi + + echo -e "${green}从 $modify_file 读取到 $proxy_count 个代理配置。${none}" + local local_ip=$(get_local_ip) + local nodes_to_save=() + + # 处理每个要修改的代理 + for proxy in "${modify_proxies[@]}"; do + IFS=':' read -r old_ip new_port new_user new_pass <<< "$proxy" + + if [[ -z "$old_ip" || -z "$new_port" || -z "$new_user" || -z "$new_pass" ]]; then + echo -e "${red}警告:${none} 代理格式无效,终止脚本运行: $proxy" + exit 1 + fi + + # 查找匹配的出站节点 + local outbound_config=$(jq --arg ip "$old_ip" '.outbounds[] | select(.protocol == "socks" and .settings.servers[0].address == $ip) | {tag: .tag, address: .settings.servers[0].address, port: .settings.servers[0].port, user: .settings.servers[0].users[0].user, pass: .settings.servers[0].users[0].pass}' "$config_file") + + if [[ -z "$outbound_config" ]]; then + echo -e "${red}警告:${none} 未找到IP地址为 $old_ip 的SOCKS5出站节点,终止脚本运行" + exit 1 + fi + + local tag=$(echo "$outbound_config" | jq -r '.tag') + local old_port=$(echo "$outbound_config" | jq -r '.port') + local old_user=$(echo "$outbound_config" | jq -r '.user') + local old_pass=$(echo "$outbound_config" | jq -r '.pass') + + echo -e "${yellow}找到匹配的出站节点:${none} 标签=$tag, 旧IP=$old_ip, 旧端口=$old_port, 旧用户名=$old_user, 旧密码=$old_pass" + echo -e "${green}将更新为:${none} 新IP=$old_ip, 新端口=$new_port, 新用户名=$new_user, 新密码=$new_pass" + + # 更新SOCKS5出站配置 + local temp_file=$(mktemp) + jq --arg tag "$tag" \ + --arg ip "$old_ip" \ + --arg port "$new_port" \ + --arg user "$new_user" \ + --arg pass "$new_pass" \ + '(.outbounds[] | select(.tag == $tag) | .settings.servers[0].address) = $ip | + (.outbounds[] | select(.tag == $tag) | .settings.servers[0].port) = ($port | tonumber) | + (.outbounds[] | select(.tag == $tag) | .settings.servers[0].users[0].user) = $user | + (.outbounds[] | select(.tag == $tag) | .settings.servers[0].users[0].pass) = $pass' \ + "$config_file" > "$temp_file" + + if [ $? -eq 0 ] && [ -s "$temp_file" ]; then + mv "$temp_file" "$config_file" + echo -e "${green}成功修改SOCKS5出站节点配置!${none}" + + # 查找对应的入站配置并保存节点信息 + local inbound_port=${tag//outbound/} + local inbound_config=$(jq --arg port "$inbound_port" '.inbounds[] | select(.port == ($port | tonumber))' "$config_file") + if [[ -n "$inbound_config" ]]; then + local method=$(echo "$inbound_config" | jq -r '.settings.method') + local password=$(echo "$inbound_config" | jq -r '.settings.password') + local index=$(jq --arg port "$inbound_port" '.inbounds | map(select(.port != 9999)) | map(.port == ($port | tonumber)) | index(true)' "$config_file") + + # 包含实际的SOCKS5 IP地址作为PS字段 + nodes_to_save+=("$inbound_port" "$password" "$method" "$((index+1))" "$old_ip") + fi + else + echo -e "${red}更新配置失败!${none}" + rm -f "$temp_file" + continue + fi + done + + if [[ ${#nodes_to_save[@]} -gt 0 ]]; then + save_multiple_ss_links_with_ps "$local_ip" "${nodes_to_save[@]}" + fi + + sudo chmod 755 /usr/local/etc/xray/config.json + sudo systemctl restart xray + echo -e "${green}Xray 服务已重新启动。${none}" +} + +# 根据xiugai.txt文件删除节点 +delete_nodes_by_ip() { + local modify_file="/home/xiugai.txt" + + # 检查xiugai.txt文件是否存在 + if [ ! -f "$modify_file" ]; then + echo -e "${red}错误!${none} $modify_file 文件不存在。" + return + fi + + # 读取xiugai.txt文件中的代理信息 + local modify_proxies=() + while IFS= read -r line || [[ -n "$line" ]]; do + # 忽略空行 + if [[ -z "$line" ]]; then + continue + fi + # 只提取IP部分 + IFS=':' read -r ip _ <<< "$line" + modify_proxies+=("$ip") + done < "$modify_file" + + # 检查是否读取到IP + local ip_count=${#modify_proxies[@]} + if [ $ip_count -eq 0 ]; then + echo -e "${red}错误!${none} 未在 $modify_file 中找到有效的IP地址。" + return + fi + + echo -e "${green}从 $modify_file 读取到 $ip_count 个IP地址。${none}" + + # 处理每个要删除的IP + for ip in "${modify_proxies[@]}"; do + # 查找匹配的出站节点 + local outbound_config=$(jq --arg ip "$ip" '.outbounds[] | select(.protocol == "socks" and .settings.servers[0].address == $ip) | {tag: .tag, port: .settings.servers[0].port}' "$config_file") + + if [[ -z "$outbound_config" ]]; then + echo -e "${red}警告:${none} 未找到IP地址为 $ip 的SOCKS5出站节点,终止脚本运行" + exit 1 + fi + + local outbound_tag=$(echo "$outbound_config" | jq -r '.tag') + + # 从outbound_tag中提取端口号(假设格式为"outbound端口号") + local port=${outbound_tag#outbound} + + echo -e "${yellow}找到匹配的节点:${none} 出站标签=$outbound_tag, IP=$ip, 端口=$port" + + # 查找对应的入站配置 + local inbound_config=$(jq --arg port "$port" '.inbounds[] | select(.port == ($port | tonumber))' "$config_file") + + if [[ -z "$inbound_config" ]]; then + echo -e "${red}警告:${none} 未找到对应端口 $port 的入站配置,继续删除出站配置" + else + local inbound_tag=$(echo "$inbound_config" | jq -r '.tag') + echo -e "${yellow}找到对应的入站配置:${none} 标签=$inbound_tag" + + # 删除入站配置 + jq --arg port "$port" 'del(.inbounds[] | select(.port == ($port | tonumber)))' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file" + + # 删除路由规则(使用实际的inbound_tag而不是构造的标签) + jq --arg inTag "$inbound_tag" 'del(.routing.rules[] | select(.inboundTag[] == $inTag))' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file" + fi + + # 删除出站配置 + jq --arg tag "$outbound_tag" 'del(.outbounds[] | select(.tag == $tag))' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file" + + echo -e "${green}已成功删除IP地址为 $ip 的节点。${none}" + done + + sudo systemctl restart xray + echo -e "${green}Xray 服务已重新启动。${none}" +} + +main_menu() { + while true; do + echo -e "\n${green}快速批量搭建二级代理脚本-管理菜单:${none}" + echo "1. 查看所有节点" + echo "2. 新增Shadowsocks入站sk5出站(从/home/sk5.txt文件导入)" + echo "3. 删除节点(根据/home/xiugai.txt文件匹配)" + echo "4. 修改SOCKS5出站节点(根据/home/xiugai.txt文件匹配)" + echo "5. 退出" + read -p "请输入选项: " choice + + case $choice in + 1) show_inbound_configs ;; + 2) add_new_nodes ;; + 3) delete_nodes_by_ip ;; + 4) modify_socks5_outbound ;; + 5) break ;; + *) echo -e "${red}无效的选项,请重新选择。${none}" ;; + esac + done +} + +check_and_install_curl +check_and_install_jq +check_and_install_openssl +check_and_install_xray +create_default_config +get_local_ip +main_menu diff --git a/proxy/duosk5-vmess.sh b/proxy/duosk5-vmess.sh new file mode 100644 index 0000000..58344d6 --- /dev/null +++ b/proxy/duosk5-vmess.sh @@ -0,0 +1,578 @@ +#!/bin/bash +# 批量搭建vmess+ws入站到sk5出站代理 +# 读取sk5文件实现批量导入出站 +# 作者sky22333 + +red='\e[31m' +yellow='\e[33m' +green='\e[32m' +none='\e[0m' +config_file="/usr/local/etc/xray/config.json" +default_config=' +{ + "inbounds": [ + { + "listen": "127.0.0.1", + "port": 9999, + "protocol": "vmess", + "settings": { + "clients": [ + { + "id": "wss" + } + ] + }, + "streamSettings": { + "network": "ws", + "security": "none", + "wsSettings": { + "path": "/wss" + } + }, + "tag": "inbound0" + } + ], + "outbounds": [ + { + "protocol": "socks", + "settings": { + "servers": [ + { + "address": "127.0.0.2", + "port": 2222, + "users": [ + { + "user": "admin123", + "pass": "admin333" + } + ] + } + ] + }, + "tag": "outbound0" + } + ], + "routing": { + "rules": [ + { + "type": "field", + "inboundTag": ["inbound0"], + "outboundTag": "outbound0" + } + ] + } +} +' + +# 检查并安装curl +check_and_install_curl() { + if ! type curl &>/dev/null; then + echo -e "${yellow}正在安装curl...${none}" + apt update && apt install -yq curl + fi +} + +# 检查并安装jq +check_and_install_jq() { + if ! type jq &>/dev/null; then + echo -e "${yellow}正在安装jq...${none}" + apt update && apt install -yq jq + fi +} + +# 检查并安装uuid-runtime +check_and_install_uuid_runtime() { + if ! type uuidgen &>/dev/null; then + echo -e "${yellow}正在安装 uuid-runtime...${none}" + apt update && apt install -yq uuid-runtime + fi +} + +# 检查并安装xray +check_and_install_xray() { + if ! type xray &>/dev/null; then + echo -e "${yellow}正在安装 xray...正在启用BBR...${none}" + sudo ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime + bash <(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh) install --version v1.8.4 + fi +} + +# 检查是否已存在入站配置 +check_existing_inbound_config() { + if grep -q '"tag":' "$config_file"; then + return 0 # 已存在入站配置 + else + return 1 # 不存在入站配置 + fi +} + +# 创建默认配置文件 +create_default_config() { + if ! check_existing_inbound_config; then + echo "$default_config" > "$config_file" + echo -e "${green}已创建默认配置文件。${none}" + else + echo -e "${yellow}入站配置已存在,跳过创建默认配置文件。${none}" + fi +} + +# 获取本机公网 IP +get_local_ip() { + local ip=$(curl -s http://ipinfo.io/ip) + if [[ $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "$ip" + else + echo "无法自动获取公网IP地址,请手动输入。" + read -p "请输入您的公网IP地址: " manual_ip + if [[ $manual_ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "$manual_ip" + else + echo "输入的IP地址格式不正确,请重新运行脚本并输入有效的公网IP地址。" + exit 1 + fi + fi +} + +# 生成带时间戳的vmess文件名(精确到秒) +get_vmess_filename() { + local timestamp=$(date +"%Y%m%d-%H点%M分%S秒") + echo "/home/${timestamp}-vmess.txt" +} + +# 保存多个VMess节点到文件 +save_multiple_vmess_links() { + local local_ip=$1 + shift + local vmess_file=$(get_vmess_filename) + + # 清空或创建文件 + > "$vmess_file" + + # 处理所有传入的节点信息 + while [[ $# -gt 0 ]]; do + local port=$1 + local id=$2 + local path=$3 + local index=$4 + shift 4 + # 获取对应的出站 SOCKS5 的 IP 地址 + local sk5_ip=$(jq -r ".outbounds | map(select(.tag == \"outbound${port}\")) | .[0].settings.servers[0].address" "$config_file") + if [[ -z "$sk5_ip" ]]; then + sk5_ip="未知IP" # 如果未找到对应的IP,设置为默认值 + fi + + # 生成VMess链接 + local vmess_link="vmess://$(echo -n "{\"v\":\"2\",\"ps\":\"$sk5_ip\",\"add\":\"$local_ip\",\"port\":$port,\"id\":\"$id\",\"aid\":0,\"net\":\"ws\",\"path\":\"$path\",\"type\":\"none\"}" | base64 -w 0)" + + # 写入文件 + echo "$vmess_link" >> "$vmess_file" + done + + echo -e "${green}已将操作的所有节点保存至 $vmess_file${none}" +} + +# 保存多个VMess节点到文件(带自定义PS字段) +save_multiple_vmess_links_with_ps() { + local local_ip=$1 + shift + local vmess_file=$(get_vmess_filename) + + # 清空或创建文件 + > "$vmess_file" + + # 处理所有传入的节点信息 + while [[ $# -gt 0 ]]; do + local port=$1 + local id=$2 + local path=$3 + local index=$4 + local sk5_ip=$5 # 额外参数用于PS字段 + shift 5 + + # 生成VMess链接,使用自定义PS字段 + local vmess_link="vmess://$(echo -n "{\"v\":\"2\",\"ps\":\"$sk5_ip\",\"add\":\"$local_ip\",\"port\":$port,\"id\":\"$id\",\"aid\":0,\"net\":\"ws\",\"path\":\"$path\",\"type\":\"none\"}" | base64 -w 0)" + + # 写入文件 + echo "$vmess_link" >> "$vmess_file" + done + + echo -e "${green}已将操作的所有节点保存至 $vmess_file${none}" +} + +# 保存所有VMess节点到文件 +save_all_vmess_links() { + local local_ip=$(get_local_ip) # 获取本机IP + local vmess_file=$(get_vmess_filename) + + # 清空或创建文件 + > "$vmess_file" + + local config=$(jq '.inbounds | map(select(.port != 9999))' "$config_file") + local length=$(jq '. | length' <<< "$config") + + for ((i = 0; i < length; i++)); do + local port=$(jq -r ".[$i].port" <<< "$config") + local id=$(jq -r ".[$i].settings.clients[0].id" <<< "$config") + local path=$(jq -r ".[$i].streamSettings.wsSettings.path" <<< "$config") + + # 获取对应的出站 SOCKS5 的 IP 地址 + local sk5_ip=$(jq -r ".outbounds | map(select(.tag == \"outbound${port}\")) | .[0].settings.servers[0].address" "$config_file") + if [[ -z "$sk5_ip" ]]; then + sk5_ip="未知IP" # 如果未找到对应的IP,设置为默认值 + fi + # 生成VMess链接 + local vmess_link="vmess://$(echo -n "{\"v\":\"2\",\"ps\":\"$sk5_ip\",\"add\":\"$local_ip\",\"port\":$port,\"id\":\"$id\",\"aid\":0,\"net\":\"ws\",\"path\":\"$path\",\"type\":\"none\"}" | base64 -w 0)" + + # 写入文件 + echo "$vmess_link" >> "$vmess_file" + done + + echo -e "${green}已将全部VMess节点保存至 $vmess_file${none}" +} + +# 显示所有入站配置和 Vmess 链接以及对应的出站配置 +show_inbound_configs() { + local local_ip=$(get_local_ip) # 获取本机IP + + local config=$(jq '.inbounds | map(select(.port != 9999))' "$config_file") + local outbounds=$(jq '.outbounds' "$config_file") + echo -e "${green}入站节点配置:${none}" + + local length=$(jq '. | length' <<< "$config") + for ((i = 0; i < length; i++)); do + local port=$(jq -r ".[$i].port" <<< "$config") + local id=$(jq -r ".[$i].settings.clients[0].id" <<< "$config") + local path=$(jq -r ".[$i].streamSettings.wsSettings.path" <<< "$config") + + # 将节点地址设置为本机IP + local node_address="$local_ip" + + # 获取sk5代理IP + local sk5_ip=$(jq -r ".outbounds | map(select(.tag == \"outbound${port}\")) | .[0].settings.servers[0].address" "$config_file") + if [[ -z "$sk5_ip" ]]; then + sk5_ip="未知IP" # 如果未找到对应的IP,设置为默认值 + fi + local vmess_link="vmess://$(echo -n "{\"v\":\"2\",\"ps\":\"$sk5_ip\",\"add\":\"$node_address\",\"port\":$port,\"id\":\"$id\",\"aid\":0,\"net\":\"ws\",\"path\":\"$path\",\"type\":\"none\"}" | base64 -w 0)" + + echo -e "${yellow}节点: $(($i + 1))${none} - 端口: ${port}, Vmess 链接: ${vmess_link}" + + # 构造出站配置的标签 + local outbound_tag="outbound$port" + + # 根据构造的标签查找对应的出站配置 + local outbound_config=$(jq --arg tag "$outbound_tag" '.[] | select(.tag == $tag) | .settings.servers[] | {address, port, user: .users[0].user, pass: .users[0].pass}' <<< "$outbounds") + + if [[ ! -z $outbound_config ]]; then + echo -e "${green}出站配置:${none} 地址: $(jq -r '.address' <<< "$outbound_config"), 端口: $(jq -r '.port' <<< "$outbound_config"), 用户名: $(jq -r '.user' <<< "$outbound_config"), 密码: $(jq -r '.pass' <<< "$outbound_config")" + else + echo -e "${red}未找到对应的出站配置。${none}" + fi + done + + # 保存所有VMess链接到文件 + save_all_vmess_links +} + +# 添加新节点 +add_new_nodes() { + local sk5_file="/home/sk5.txt" + + # 检查sk5.txt文件是否存在 + if [ ! -f "$sk5_file" ]; then + echo -e "${red}错误!${none} $sk5_file 文件不存在。" + return + fi + + # 读取sk5.txt文件中的代理信息 + local sk5_proxies=() + while IFS= read -r line || [[ -n "$line" ]]; do + # 忽略空行 + if [[ -z "$line" ]]; then + continue + fi + sk5_proxies+=("$line") + done < "$sk5_file" + + # 检查是否读取到代理 + local proxy_count=${#sk5_proxies[@]} + if [ $proxy_count -eq 0 ]; then + echo -e "${red}错误!${none} 未在 $sk5_file 中找到有效的代理配置。" + return + fi + + echo -e "${green}从 $sk5_file 读取到 $proxy_count 个代理配置。${none}" + read -p "是否要导入全部配置?(y/n): " confirm + + if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then + read -p "请输入要导入的代理数量 (最大 $proxy_count): " num_to_import + if ! [[ $num_to_import =~ ^[0-9]+$ ]] || [ $num_to_import -le 0 ] || [ $num_to_import -gt $proxy_count ]; then + echo -e "${red}错误!${none} 输入数量无效。" + return + fi + else + num_to_import=$proxy_count + fi + + local max_port=$(jq '[.inbounds[].port] | max // 10000' "$config_file") + local start_port=$((max_port+1)) + local local_ip=$(get_local_ip) + local nodes_to_save=() + + for ((i=0; i "$config_file.tmp" && mv "$config_file.tmp" "$config_file" + + # 添加出站配置 + jq --arg tag "$new_outbound_tag" --arg addr "$outbound_addr" --argjson port "$outbound_port" --arg user "$outbound_user" --arg pass "$outbound_pass" ' + .outbounds += [{ + protocol: "socks", + settings: { servers: [{ address: $addr, port: $port | tonumber, users: [{ user: $user, pass: $pass }] }] }, + tag: $tag + }]' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file" + + # 添加路由规则 + jq --arg inTag "$new_tag" --arg outTag "$new_outbound_tag" ' + .routing.rules += [{ type: "field", inboundTag: [$inTag], outboundTag: $outTag }] + ' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file" + + # 保存节点信息以便后续生成VMess链接 + nodes_to_save+=("$new_port" "$new_id" "/websocket" "$((i+1))") + done + + # 保存所有新添加的节点到一个文件 + save_multiple_vmess_links "$local_ip" "${nodes_to_save[@]}" + + echo -e "${green}已成功添加 $num_to_import 个节点。${none}" + sudo systemctl restart xray + echo -e "${green}Xray 服务已重新启动。${none}" +} + +# 根据xiugai.txt文件修改SOCKS5出站代理 +modify_socks5_outbound() { + local modify_file="/home/xiugai.txt" + + # 检查xiugai.txt文件是否存在 + if [ ! -f "$modify_file" ]; then + echo -e "${red}错误!${none} $modify_file 文件不存在。" + return + fi + + # 读取xiugai.txt文件中的代理信息 + local modify_proxies=() + while IFS= read -r line || [[ -n "$line" ]]; do + # 忽略空行 + if [[ -z "$line" ]]; then + continue + fi + modify_proxies+=("$line") + done < "$modify_file" + + # 检查是否读取到代理 + local proxy_count=${#modify_proxies[@]} + if [ $proxy_count -eq 0 ]; then + echo -e "${red}错误!${none} 未在 $modify_file 中找到有效的代理配置。" + return + fi + + echo -e "${green}从 $modify_file 读取到 $proxy_count 个代理配置。${none}" + local local_ip=$(get_local_ip) + local nodes_to_save=() + + # 处理每个要修改的代理 + for proxy in "${modify_proxies[@]}"; do + IFS=':' read -r old_ip new_port new_user new_pass <<< "$proxy" + + if [[ -z "$old_ip" || -z "$new_port" || -z "$new_user" || -z "$new_pass" ]]; then + echo -e "${red}警告:${none} 代理格式无效,终止脚本运行: $proxy" + exit 1 + fi + + # 查找匹配的出站节点 + local outbound_config=$(jq --arg ip "$old_ip" '.outbounds[] | select(.protocol == "socks" and .settings.servers[0].address == $ip) | {tag: .tag, address: .settings.servers[0].address, port: .settings.servers[0].port, user: .settings.servers[0].users[0].user, pass: .settings.servers[0].users[0].pass}' "$config_file") + + if [[ -z "$outbound_config" ]]; then + echo -e "${red}警告:${none} 未找到IP地址为 $old_ip 的SOCKS5出站节点,终止脚本运行" + exit 1 + fi + + local tag=$(echo "$outbound_config" | jq -r '.tag') + local old_port=$(echo "$outbound_config" | jq -r '.port') + local old_user=$(echo "$outbound_config" | jq -r '.user') + local old_pass=$(echo "$outbound_config" | jq -r '.pass') + + echo -e "${yellow}找到匹配的出站节点:${none} 标签=$tag, 旧IP=$old_ip, 旧端口=$old_port, 旧用户名=$old_user, 旧密码=$old_pass" + echo -e "${green}将更新为:${none} 新IP=$old_ip, 新端口=$new_port, 新用户名=$new_user, 新密码=$new_pass" + + # 更新SOCKS5出站配置 + local temp_file=$(mktemp) + jq --arg tag "$tag" \ + --arg ip "$old_ip" \ + --argjson port "$new_port" \ + --arg user "$new_user" \ + --arg pass "$new_pass" \ + '(.outbounds[] | select(.tag == $tag) | .settings.servers[0].address) = $ip | + (.outbounds[] | select(.tag == $tag) | .settings.servers[0].port) = $port | + (.outbounds[] | select(.tag == $tag) | .settings.servers[0].users[0].user) = $user | + (.outbounds[] | select(.tag == $tag) | .settings.servers[0].users[0].pass) = $pass' \ + "$config_file" > "$temp_file" + + if [ $? -eq 0 ] && [ -s "$temp_file" ]; then + mv "$temp_file" "$config_file" + echo -e "${green}成功修改SOCKS5出站节点配置!${none}" + + # 查找对应的入站配置并保存节点信息 + local inbound_port=${tag//outbound/} + local inbound_config=$(jq --argjson port "$inbound_port" '.inbounds[] | select(.port == $port)' "$config_file") + if [[ -n "$inbound_config" ]]; then + local id=$(echo "$inbound_config" | jq -r '.settings.clients[0].id') + local path=$(echo "$inbound_config" | jq -r '.streamSettings.wsSettings.path') + local index=$(jq '.inbounds | map(select(.port != 9999)) | map(.port == '$inbound_port') | index(true)' "$config_file") + + # 修复: 包含实际的SOCKS5 IP地址作为PS字段 + nodes_to_save+=("$inbound_port" "$id" "$path" "$((index+1))" "$old_ip") + fi + else + echo -e "${red}更新配置失败!${none}" + rm -f "$temp_file" + continue + fi + done + + # 保存所有修改的节点到一个文件,使用新函数传递PS字段 + if [[ ${#nodes_to_save[@]} -gt 0 ]]; then + save_multiple_vmess_links_with_ps "$local_ip" "${nodes_to_save[@]}" + fi + + # 重启Xray服务使配置生效 + sudo chmod 755 /usr/local/etc/xray/config.json + sudo systemctl restart xray + echo -e "${green}Xray 服务已重新启动。${none}" +} + +# 根据xiugai.txt文件删除节点 +delete_nodes_by_ip() { + local modify_file="/home/xiugai.txt" + + # 检查xiugai.txt文件是否存在 + if [ ! -f "$modify_file" ]; then + echo -e "${red}错误!${none} $modify_file 文件不存在。" + return + fi + + # 读取xiugai.txt文件中的代理信息 + local modify_proxies=() + while IFS= read -r line || [[ -n "$line" ]]; do + # 忽略空行 + if [[ -z "$line" ]]; then + continue + fi + # 只提取IP部分 + IFS=':' read -r ip _ <<< "$line" + modify_proxies+=("$ip") + done < "$modify_file" + + # 检查是否读取到IP + local ip_count=${#modify_proxies[@]} + if [ $ip_count -eq 0 ]; then + echo -e "${red}错误!${none} 未在 $modify_file 中找到有效的IP地址。" + return + fi + + echo -e "${green}从 $modify_file 读取到 $ip_count 个IP地址。${none}" + + # 处理每个要删除的IP + for ip in "${modify_proxies[@]}"; do + # 查找匹配的出站节点 + local outbound_config=$(jq --arg ip "$ip" '.outbounds[] | select(.protocol == "socks" and .settings.servers[0].address == $ip) | {tag: .tag, port: .settings.servers[0].port}' "$config_file") + + if [[ -z "$outbound_config" ]]; then + echo -e "${red}警告:${none} 未找到IP地址为 $ip 的SOCKS5出站节点,终止脚本运行" + exit 1 + fi + + local outbound_tag=$(echo "$outbound_config" | jq -r '.tag') + + # 从outbound_tag中提取端口号(假设格式为"outbound端口号") + local port=${outbound_tag#outbound} + + echo -e "${yellow}找到匹配的节点:${none} 出站标签=$outbound_tag, IP=$ip, 端口=$port" + + # 查找对应的入站配置 + local inbound_config=$(jq --argjson port "$port" '.inbounds[] | select(.port == $port)' "$config_file") + + if [[ -z "$inbound_config" ]]; then + echo -e "${red}警告:${none} 未找到对应端口 $port 的入站配置,继续删除出站配置" + else + local inbound_tag=$(echo "$inbound_config" | jq -r '.tag') + echo -e "${yellow}找到对应的入站配置:${none} 标签=$inbound_tag" + + # 删除入站配置 + jq --argjson port "$port" 'del(.inbounds[] | select(.port == $port))' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file" + + # 删除路由规则(使用实际的inbound_tag而不是构造的标签) + jq --arg inTag "$inbound_tag" 'del(.routing.rules[] | select(.inboundTag[] == $inTag))' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file" + fi + + # 删除出站配置 + jq --arg tag "$outbound_tag" 'del(.outbounds[] | select(.tag == $tag))' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file" + + echo -e "${green}已成功删除IP地址为 $ip 的节点。${none}" + done + + sudo systemctl restart xray + echo -e "${green}Xray 服务已重新启动。${none}" +} + +# 主菜单 +main_menu() { + while true; do + echo -e "\n${green}快速批量搭建二级代理脚本-管理菜单:${none}" + echo "1. 查看所有节点" + echo "2. 新增vmess入站sk5出站(从/home/sk5.txt文件导入)" + echo "3. 删除节点(根据/home/xiugai.txt文件匹配)" + echo "4. 修改SOCKS5出站节点(根据/home/xiugai.txt文件匹配)" + echo "5. 退出" + read -p "请输入选项: " choice + + case $choice in + 1) show_inbound_configs ;; + 2) add_new_nodes ;; + 3) delete_nodes_by_ip ;; + 4) modify_socks5_outbound ;; + 5) break ;; + *) echo -e "${red}无效的选项,请重新选择。${none}" ;; + esac + done +} + +# 调用主菜单函数 +check_and_install_curl +check_and_install_jq +check_and_install_uuid_runtime +check_and_install_xray +create_default_config +get_local_ip +main_menu diff --git a/proxy/duosk5.go b/proxy/duosk5.go new file mode 100644 index 0000000..d761e55 --- /dev/null +++ b/proxy/duosk5.go @@ -0,0 +1,469 @@ +package main + +import ( + "bufio" + "crypto/rand" + "encoding/json" + "fmt" + "io" + "net" + "net/http" + "os" + "os/exec" + "regexp" + "strconv" + "strings" + "time" +) + +const ( + // 颜色常量(ANSI转义码) + ColorReset = "\033[0m" + ColorRed = "\033[31m" + ColorGreen = "\033[32m" + ColorYellow = "\033[33m" + ColorCyan = "\033[36m" + + // 构建:CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o sk5 main.go + // 脚本过期时间以及其他变量 + EXPIRE_DATE = "2025-06-08 02:01:01" + CONFIG_FILE = "/usr/local/etc/xray/config.json" + SOCKS_FILE = "/home/socks.txt" + XRAY_INSTALL_URL = "https://github.com/XTLS/Xray-install/raw/main/install-release.sh" + XRAY_VERSION = "v1.8.4" + START_PORT = 10001 +) + +// 彩色打印函数 +func colorPrint(colorCode, format string, a ...interface{}) { + fmt.Printf(colorCode+format+ColorReset+"\n", a...) +} + +// XrayConfig represents the Xray configuration structure +type XrayConfig struct { + Inbounds []Inbound `json:"inbounds"` + Outbounds []Outbound `json:"outbounds"` + Routing Routing `json:"routing"` +} + +type Inbound struct { + Port int `json:"port"` + Protocol string `json:"protocol"` + Settings InboundSettings `json:"settings"` + StreamSettings StreamSettings `json:"streamSettings"` + Tag string `json:"tag"` +} + +type InboundSettings struct { + Auth string `json:"auth"` + Accounts []Account `json:"accounts"` + UDP bool `json:"udp"` + IP string `json:"ip"` +} + +type Account struct { + User string `json:"user"` + Pass string `json:"pass"` +} + +type StreamSettings struct { + Network string `json:"network"` +} + +type Outbound struct { + Protocol string `json:"protocol"` + Settings interface{} `json:"settings"` + SendThrough string `json:"sendThrough"` + Tag string `json:"tag"` +} + +type Routing struct { + Rules []Rule `json:"rules"` +} + +type Rule struct { + Type string `json:"type"` + InboundTag []string `json:"inboundTag"` + OutboundTag string `json:"outboundTag"` +} + +type NodeInfo struct { + IP string + Port int + Username string + Password string +} + +// generateRandomString generates a random string of specified length +func generateRandomString(length int) string { + const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + b := make([]byte, length) + if _, err := rand.Read(b); err != nil { + panic(err) + } + for i := range b { + b[i] = charset[b[i]%byte(len(charset))] + } + return string(b) +} + +// checkExpiration checks if the script has expired +func checkExpiration() error { + colorPrint(ColorCyan, "开始运行...") + + // Get timestamp from cloudflare + resp, err := http.Get("https://www.cloudflare.com/cdn-cgi/trace") + if err != nil { + return fmt.Errorf("网络错误") + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("读取响应失败") + } + + // Extract timestamp + re := regexp.MustCompile(`ts=(\d+)`) + matches := re.FindStringSubmatch(string(body)) + if len(matches) < 2 { + return fmt.Errorf("无法解析时间") + } + + timestamp, err := strconv.ParseInt(matches[1], 10, 64) + if err != nil { + return fmt.Errorf("时间转换失败") + } + + // Convert to Beijing time + currentTime := time.Unix(timestamp, 0).In(time.FixedZone("CST", 8*3600)) + expireTime, _ := time.ParseInLocation("2006-01-02 15:04:05", EXPIRE_DATE, time.FixedZone("CST", 8*3600)) + + if currentTime.After(expireTime) { + return fmt.Errorf("当前脚本已过期,请联系作者") + } + + return nil +} + +// commandExists checks if a command exists in PATH +func commandExists(cmd string) bool { + _, err := exec.LookPath(cmd) + return err == nil +} + +// installJQ installs jq if not present +func installJQ() error { + if commandExists("jq") { + colorPrint(ColorGreen, "jq 已安装") + return nil + } + + colorPrint(ColorYellow, "jq 未安装,正在安装 jq...") + + // Detect OS + if _, err := os.Stat("/etc/debian_version"); err == nil { + // Debian/Ubuntu + cmd := exec.Command("bash", "-c", "apt update && apt install -yq jq") + return cmd.Run() + } else if _, err := os.Stat("/etc/redhat-release"); err == nil { + // RHEL/CentOS + cmd := exec.Command("yum", "install", "-y", "epel-release", "jq") + return cmd.Run() + } + + return fmt.Errorf("无法确定系统发行版,请手动安装 jq") +} + +// installXray installs Xray if not present +func installXray() error { + if commandExists("xray") { + colorPrint(ColorGreen, "Xray 已安装") + return nil + } + + colorPrint(ColorYellow, "Xray 未安装,正在安装 Xray...") + + cmd := exec.Command("bash", "-c", fmt.Sprintf("curl -L %s | bash -s install --version %s", XRAY_INSTALL_URL, XRAY_VERSION)) + if err := cmd.Run(); err != nil { + return fmt.Errorf("Xray 安装失败: %v", err) + } + + colorPrint(ColorGreen, "Xray 安装完成") + return nil +} + +// getPublicIPv4 gets all public IPv4 addresses +func getPublicIPv4() ([]string, error) { + var publicIPs []string + + // Get all network interfaces + interfaces, err := net.Interfaces() + if err != nil { + return nil, err + } + + for _, iface := range interfaces { + if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 { + continue + } + + addrs, err := iface.Addrs() + if err != nil { + continue + } + + for _, addr := range addrs { + if ipNet, ok := addr.(*net.IPNet); ok && !ipNet.IP.IsLoopback() { + if ipNet.IP.To4() != nil { + ip := ipNet.IP.String() + // Check if it's a public IP + if isPublicIP(ip) { + publicIPs = append(publicIPs, ip) + } + } + } + } + } + + return publicIPs, nil +} + +// isPublicIP checks if an IP is public +func isPublicIP(ip string) bool { + parsedIP := net.ParseIP(ip) + if parsedIP == nil { + return false + } + + // Check for private IP ranges + privateRanges := []string{ + "127.0.0.0/8", // loopback + "10.0.0.0/8", // private + "172.16.0.0/12", // private + "192.168.0.0/16", // private + "169.254.0.0/16", // link-local + } + + for _, cidr := range privateRanges { + _, network, _ := net.ParseCIDR(cidr) + if network.Contains(parsedIP) { + return false + } + } + + return true +} + +// ensureSocksFileExists creates socks.txt if it doesn't exist +func ensureSocksFileExists() error { + if _, err := os.Stat(SOCKS_FILE); os.IsNotExist(err) { + colorPrint(ColorYellow, "socks.txt 文件不存在,正在创建...") + file, err := os.Create(SOCKS_FILE) + if err != nil { + return err + } + file.Close() + } + return nil +} + +// saveNodeInfo saves node information to file and prints it +func saveNodeInfo(node NodeInfo) error { + // Print node info with colors + fmt.Printf(" IP: %s%s%s 端口: %s%d%s 用户名: %s%s%s 密码: %s%s%s\n", + ColorGreen, node.IP, ColorReset, + ColorGreen, node.Port, ColorReset, + ColorGreen, node.Username, ColorReset, + ColorGreen, node.Password, ColorReset) + + // Save to file + file, err := os.OpenFile(SOCKS_FILE, os.O_APPEND|os.O_WRONLY, 0644) + if err != nil { + return err + } + defer file.Close() + + _, err = fmt.Fprintf(file, "%s %d %s %s\n", node.IP, node.Port, node.Username, node.Password) + return err +} + +// configureXray configures Xray with multiple IPs +func configureXray() error { + publicIPs, err := getPublicIPv4() + if err != nil { + return fmt.Errorf("获取公网IP失败: %v", err) + } + + if len(publicIPs) == 0 { + return fmt.Errorf("未找到额外IP地址") + } + + colorPrint(ColorCyan, "找到的公网 IPv4 地址: %v", publicIPs) + + // Create initial config + config := XrayConfig{ + Inbounds: []Inbound{}, + Outbounds: []Outbound{}, + Routing: Routing{ + Rules: []Rule{}, + }, + } + + // Configure each IP + port := START_PORT + for _, ip := range publicIPs { + colorPrint(ColorCyan, "正在配置 IP: %s 端口: %d", ip, port) + + username := generateRandomString(8) + password := generateRandomString(8) + + // Create inbound + inbound := Inbound{ + Port: port, + Protocol: "socks", + Settings: InboundSettings{ + Auth: "password", + Accounts: []Account{ + {User: username, Pass: password}, + }, + UDP: true, + IP: "0.0.0.0", + }, + StreamSettings: StreamSettings{ + Network: "tcp", + }, + Tag: fmt.Sprintf("in-%d", port), + } + + // Create outbound + outbound := Outbound{ + Protocol: "freedom", + Settings: map[string]interface{}{}, + SendThrough: ip, + Tag: fmt.Sprintf("out-%d", port), + } + + // Create routing rule + rule := Rule{ + Type: "field", + InboundTag: []string{fmt.Sprintf("in-%d", port)}, + OutboundTag: fmt.Sprintf("out-%d", port), + } + + config.Inbounds = append(config.Inbounds, inbound) + config.Outbounds = append(config.Outbounds, outbound) + config.Routing.Rules = append(config.Routing.Rules, rule) + + // Save node info + node := NodeInfo{ + IP: ip, + Port: port, + Username: username, + Password: password, + } + if err := saveNodeInfo(node); err != nil { + return fmt.Errorf("保存节点信息失败: %v", err) + } + + port++ + } + + // Write config file + configData, err := json.MarshalIndent(config, "", " ") + if err != nil { + return fmt.Errorf("序列化配置失败: %v", err) + } + + if err := os.WriteFile(CONFIG_FILE, configData, 0644); err != nil { + return fmt.Errorf("写入配置文件失败: %v", err) + } + + colorPrint(ColorGreen, "Xray 配置完成") + return nil +} + +// restartXray restarts the Xray service +func restartXray() error { + colorPrint(ColorCyan, "正在重启 Xray 服务...") + + // Restart service + cmd := exec.Command("systemctl", "restart", "xray") + if err := cmd.Run(); err != nil { + return fmt.Errorf("Xray 服务重启失败: %v", err) + } + + // Enable service + cmd = exec.Command("systemctl", "enable", "xray") + if err := cmd.Run(); err != nil { + return fmt.Errorf("启用 Xray 服务失败: %v", err) + } + + colorPrint(ColorGreen, "Xray 服务已重启") + return nil +} + +// readUserInput reads user input for confirmation +func readUserInput(prompt string) string { + fmt.Print(prompt) + reader := bufio.NewReader(os.Stdin) + input, _ := reader.ReadString('\n') + return strings.TrimSpace(input) +} + +func main() { + colorPrint(ColorCyan, "站群多IP源进源出sk5协议一键脚本") + colorPrint(ColorCyan, "当前为测试版,可以联系作者获取源码") + expireTime, err := time.ParseInLocation("2006-01-02 15:04:05", EXPIRE_DATE, time.FixedZone("CST", 8*3600)) + if err == nil { + expireStr := fmt.Sprintf("%d年%d月%d日%d点%d分%d秒", + expireTime.Year(), + expireTime.Month(), + expireTime.Day(), + expireTime.Hour(), + expireTime.Minute(), + expireTime.Second()) + colorPrint(ColorCyan, "脚本过期时间: %s", expireStr) + } else { + colorPrint(ColorYellow, "脚本过期时间解析失败") + } + fmt.Println() + + // Check expiration + if err := checkExpiration(); err != nil { + colorPrint(ColorRed, "错误: %v", err) + os.Exit(1) + } + + // Ensure socks file exists + if err := ensureSocksFileExists(); err != nil { + colorPrint(ColorRed, "创建socks文件失败: %v", err) + os.Exit(1) + } + + // Install jq + if err := installJQ(); err != nil { + colorPrint(ColorRed, "安装jq失败: %v", err) + os.Exit(1) + } + + // Install Xray + if err := installXray(); err != nil { + colorPrint(ColorRed, "安装Xray失败: %v", err) + os.Exit(1) + } + + // Configure Xray + if err := configureXray(); err != nil { + colorPrint(ColorRed, "配置Xray失败: %v", err) + os.Exit(1) + } + + // Restart Xray + if err := restartXray(); err != nil { + colorPrint(ColorRed, "重启Xray失败: %v", err) + os.Exit(1) + } + + colorPrint(ColorGreen, "部署完成,所有节点信息已保存到 %s", SOCKS_FILE) +} diff --git a/proxy/duosocks.sh b/proxy/duosocks.sh new file mode 100644 index 0000000..8cb150a --- /dev/null +++ b/proxy/duosocks.sh @@ -0,0 +1,237 @@ +#!/bin/bash +# 站群多IP源进源出节点脚本sk5协议 + +# 生成随机8位数的用户名和密码 +generate_random_string() { + local length=8 + tr -dc A-Za-z0-9 /dev/null; then + echo "jq 未安装,正在安装 jq..." + if [[ -f /etc/debian_version ]]; then + apt update && apt install -yq jq + elif [[ -f /etc/redhat-release ]]; then + yum install -y epel-release jq + else + echo "无法确定系统发行版,请手动安装 jq。" + exit 1 + fi + else + echo "jq 已安装。" + fi +} + +install_xray() { + if ! command -v xray &> /dev/null; then + echo "Xray 未安装,正在安装 Xray..." + if ! bash <(curl -sSL https://gh-proxy.com/https://github.com/sky22333/shell/raw/main/proxy/xray.sh); then + echo "Xray 安装失败,请检查网络连接或安装脚本。" + exit 1 + fi + echo "Xray 安装完成。" + else + echo "Xray 已安装。" + fi +} + +get_public_ipv4() { + ip -4 addr show | awk '/inet / {ip = $2; sub(/\/.*/, "", ip); if (ip !~ /^127\./ && ip !~ /^10\./ && ip !~ /^192\.168\./ && ip !~ /^169\.254\./ && ip !~ /^172\.(1[6-9]|2[0-9]|3[0-1])\./) print ip}' +} + +# 确保 socks.txt 文件存在,如果不存在则创建 +ensure_socks_file_exists() { + if [ ! -f /home/socks.txt ]; then + echo "socks.txt 文件不存在,正在创建..." + touch /home/socks.txt + fi +} + +print_node_info() { + local ip=$1 + local port=$2 + local username=$3 + local password=$4 + local outfile=${5:-/home/socks.txt} + echo -e " IP: \033[32m$ip\033[0m 端口: \033[32m$port\033[0m 用户名: \033[32m$username\033[0m 密码: \033[32m$password\033[0m" + echo "$ip $port $username $password" >> "$outfile" +} + +configure_xray() { + public_ips=($(get_public_ipv4)) + + if [[ ${#public_ips[@]} -eq 0 ]]; then + echo "未找到额外IP地址,退出..." + exit 1 + fi + + echo "找到的公网 IPv4 地址: ${public_ips[@]}" + + config_file="/usr/local/etc/xray/config.json" + + cat > $config_file < temp.json && mv temp.json "$config_file" + + print_node_info "$ip" "$port" "$username" "$password" + + port=$((port + 1)) + done + + echo "Xray 配置完成。" +} + +restart_xray() { + echo "正在重启 Xray 服务..." + if ! systemctl restart xray; then + echo "Xray 服务重启失败,请检查配置文件。" + exit 1 + fi + systemctl enable xray + echo "Xray 服务已重启。" +} + +add_mode=false +if [[ "$1" == "-add" ]]; then + add_mode=true +fi + +main() { + ensure_socks_file_exists + install_jq + install_xray + if $add_mode; then + add_xray_nodes + else + config_file="/usr/local/etc/xray/config.json" + if [[ -f $config_file ]]; then + if jq -e '.inbounds[]? | select(.port==10001)' "$config_file" >/dev/null; then + echo "检测到已有节点配置,无需重复生成,如需添加节点请添加 -add命令" + exit 0 + fi + fi + configure_xray + restart_xray + echo "部署完成,所有节点信息已保存到 /home/socks.txt" + fi +} + +add_xray_nodes() { + public_ips=($(get_public_ipv4)) + config_file="/usr/local/etc/xray/config.json" + if [[ ! -f $config_file ]]; then + echo "Xray 配置文件不存在,无法追加。" + exit 1 + fi + # 获取已存在的IP + existing_ips=($(jq -r '.outbounds[].sendThrough' "$config_file" | grep -v null)) + # 过滤出未添加的新IP + new_ips=() + for ip in "${public_ips[@]}"; do + found=false + for eip in "${existing_ips[@]}"; do + if [[ "$ip" == "$eip" ]]; then + found=true + break + fi + done + if ! $found; then + new_ips+=("$ip") + fi + done + if [[ ${#new_ips[@]} -eq 0 ]]; then + echo "没有新IP需要追加。" + return + fi + # 生成北京时间文件名 + beijing_time=$(TZ=Asia/Shanghai date +"%Y%m%d_%H%M%S") + newfile="/home/socks_add_${beijing_time}.txt" + touch "$newfile" + # 找到当前最大端口 + last_port=$(jq -r '.inbounds[].port' "$config_file" | sort -n | tail -1) + if [[ -z "$last_port" || "$last_port" == "null" ]]; then + port=10001 + else + port=$((last_port + 1)) + fi + for ip in "${new_ips[@]}"; do + echo "追加 IP: $ip 端口: $port" + username=$(generate_random_string) + password=$(generate_random_string) + jq --argjson port "$port" --arg ip "$ip" --arg username "$username" --arg password "$password" '.inbounds += [{ + "port": $port, + "protocol": "socks", + "settings": { + "auth": "password", + "accounts": [{ + "user": $username, + "pass": $password + }], + "udp": true, + "ip": "0.0.0.0" + }, + "streamSettings": { + "network": "tcp" + }, + "tag": ("in-\($port)") + }] | .outbounds += [{ + "protocol": "freedom", + "settings": {}, + "sendThrough": $ip, + "tag": ("out-\($port)") + }] | .routing.rules += [{ + "type": "field", + "inboundTag": ["in-\($port)"], + "outboundTag": "out-\($port)" + }]' "$config_file" > temp.json && mv temp.json "$config_file" + print_node_info "$ip" "$port" "$username" "$password" "$newfile" + port=$((port + 1)) + done + echo "Xray 追加完成,新增节点信息已保存到 $newfile" + restart_xray +} + +main diff --git a/proxy/duovmess.sh b/proxy/duovmess.sh new file mode 100644 index 0000000..9a70368 --- /dev/null +++ b/proxy/duovmess.sh @@ -0,0 +1,141 @@ +#!/bin/bash +# 站群多IP源进源出节点脚本vmess+ws协议 +# 作者sky22333 + +install_jq() { + # 检查 jq 和 uuidgen 是否已安装 + if ! command -v jq &> /dev/null || ! command -v uuidgen &> /dev/null; then + echo "未找到 jq 或 uuidgen,正在安装依赖..." + if [[ -f /etc/debian_version ]]; then + apt update && apt install -yq jq uuid-runtime + elif [[ -f /etc/redhat-release ]]; then + yum install -y jq util-linux + else + echo "无法确定系统发行版,请手动安装 jq 和 uuid-runtime。" + exit 1 + fi + else + echo "jq 和 uuidgen 都已安装。" + fi +} + +install_xray() { + if ! command -v xray &> /dev/null; then + echo "Xray 未安装,正在安装 Xray..." + if ! bash <(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh) install --version v1.8.4; then + echo "Xray 安装失败,请检查网络连接或安装脚本。" + exit 1 + fi + echo "Xray 安装完成。" + else + echo "Xray 已安装。" + fi +} + +get_public_ipv4() { + ip -4 addr show | awk '/inet / {ip = $2; sub(/\/.*/, "", ip); if (ip !~ /^127\./ && ip !~ /^10\./ && ip !~ /^192\.168\./ && ip !~ /^169\.254\./ && ip !~ /^172\.(1[6-9]|2[0-9]|3[0-1])\./) print ip}' +} + +# 确保 vmess.txt 文件存在,如果不存在则创建 +ensure_vmess_file() { + if [ ! -f /home/vmess.txt ]; then + echo "vmess.txt 文件不存在,正在创建..." + touch /home/vmess.txt + fi +} + +print_node_links() { + local port=$1 + local id=$2 + local outbound_ip=$3 + local link="vmess://$(echo -n "{\"v\":\"2\",\"ps\":\"$outbound_ip\",\"add\":\"$outbound_ip\",\"port\":\"$port\",\"id\":\"$id\",\"aid\":\"0\",\"net\":\"ws\",\"type\":\"none\",\"host\":\"\",\"path\":\"/ws\",\"tls\":\"none\"}" | base64 | tr -d '\n')" + echo -e "端口: $port, 节点链接: \033[32m$link\033[0m" + + # 将 vmess 链接保存到 /home/vmess.txt 文件中,每行一个链接 + echo "$link" >> /home/vmess.txt +} + +configure_xray() { + public_ips=($(get_public_ipv4)) + + if [[ ${#public_ips[@]} -eq 0 ]]; then + echo "未找到任何公网 IPv4 地址,退出..." + exit 1 + fi + + echo "找到的公网 IPv4 地址: ${public_ips[@]}" + + config_file="/usr/local/etc/xray/config.json" + + cat > $config_file < temp.json && mv temp.json "$config_file" + + print_node_links "$port" "$id" "$ip" + + port=$((port + 1)) + done + + echo "Xray 配置完成。" +} + +restart_xray() { + echo "正在重启 Xray 服务..." + if ! systemctl restart xray; then + echo "Xray 服务重启失败,请检查配置文件。" + exit 1 + fi + systemctl enable xray + echo "Xray 服务已重启。" +} + +main() { + ensure_vmess_file + install_jq + install_xray + configure_xray + restart_xray + echo "部署完成,所有节点信息已保存在 /home/vmess.txt" +} + +main diff --git a/proxy/l2tp.sh b/proxy/l2tp.sh new file mode 100644 index 0000000..7477c36 --- /dev/null +++ b/proxy/l2tp.sh @@ -0,0 +1,564 @@ +#!/usr/bin/env bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin +export PATH +#=======================================================================# +# 系统支持: Debian 10 + / Ubuntu 18.04 + # +# 描述: L2TP VPN 自动安装脚本 # +# 基于Teddysun版本修改 # +#=======================================================================# +cur_dir=`pwd` + +rootness(){ + if [[ $EUID -ne 0 ]]; then + echo "错误: 此脚本必须以root身份运行!" 1>&2 + exit 1 + fi +} + +tunavailable(){ + if [[ ! -e /dev/net/tun ]]; then + echo "错误: TUN/TAP 不可用!" 1>&2 + exit 1 + fi +} + +disable_selinux(){ +if [ -s /etc/selinux/config ] && grep 'SELINUX=enforcing' /etc/selinux/config; then + sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config + setenforce 0 +fi +} + +get_opsy(){ + [ -f /etc/os-release ] && awk -F'[= "]' '/PRETTY_NAME/{print $3,$4,$5}' /etc/os-release && return + [ -f /etc/lsb-release ] && awk -F'[="]+' '/DESCRIPTION/{print $2}' /etc/lsb-release && return +} + +get_os_info(){ + IP=$( ip addr | egrep -o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | egrep -v "^192\.168|^172\.1[6-9]\.|^172\.2[0-9]\.|^172\.3[0-2]\.|^10\.|^127\.|^255\.|^0\." | head -n 1 ) + [ -z ${IP} ] && IP=$( wget -qO- -t1 -T2 ipinfo.io/ip ) + if [ -z ${IP} ]; then + IP=$( wget -qO- -t1 -T2 ifconfig.me ) + fi + + local cname=$( awk -F: '/model name/ {name=$2} END {print name}' /proc/cpuinfo | sed 's/^[ \t]*//;s/[ \t]*$//' ) + local cores=$( awk -F: '/model name/ {core++} END {print core}' /proc/cpuinfo ) + local freq=$( awk -F: '/cpu MHz/ {freq=$2} END {print freq}' /proc/cpuinfo | sed 's/^[ \t]*//;s/[ \t]*$//' ) + local tram=$( free -m | awk '/Mem/ {print $2}' ) + local swap=$( free -m | awk '/Swap/ {print $2}' ) + local up=$( awk '{a=$1/86400;b=($1%86400)/3600;c=($1%3600)/60;d=$1%60} {printf("%d天 %d:%d:%d\n",a,b,c,d)}' /proc/uptime ) + local load=$( w | head -1 | awk -F'load average:' '{print $2}' | sed 's/^[ \t]*//;s/[ \t]*$//' ) + local opsy=$( get_opsy ) + local arch=$( uname -m ) + local lbit=$( getconf LONG_BIT ) + local host=$( hostname ) + local kern=$( uname -r ) + + echo "########## 系统信息 ##########" + echo + echo "CPU型号 : ${cname}" + echo "CPU核心数 : ${cores}" + echo "CPU频率 : ${freq} MHz" + echo "总内存大小 : ${tram} MB" + echo "总交换分区大小 : ${swap} MB" + echo "系统运行时间 : ${up}" + echo "平均负载 : ${load}" + echo "操作系统 : ${opsy}" + echo "系统架构 : ${arch} (${lbit} Bit)" + echo "内核版本 : ${kern}" + echo "主机名 : ${host}" + echo "IPv4地址 : ${IP}" + echo + echo "##################################" +} + +check_sys(){ + local checkType=$1 + local value=$2 + + local release='' + local systemPackage='' + + if cat /etc/issue | grep -Eqi "debian"; then + release="debian" + systemPackage="apt" + elif cat /etc/issue | grep -Eqi "ubuntu"; then + release="ubuntu" + systemPackage="apt" + elif cat /proc/version | grep -Eqi "debian"; then + release="debian" + systemPackage="apt" + elif cat /proc/version | grep -Eqi "ubuntu"; then + release="ubuntu" + systemPackage="apt" + else + echo "错误: 不支持的系统,请使用Debian或Ubuntu系统!" + exit 1 + fi + + if [[ ${checkType} == "sysRelease" ]]; then + if [ "$value" == "$release" ];then + return 0 + else + return 1 + fi + elif [[ ${checkType} == "packageManager" ]]; then + if [ "$value" == "$systemPackage" ];then + return 0 + else + return 1 + fi + fi +} + +rand(){ + index=0 + str="" + for i in {a..z}; do arr[index]=${i}; index=`expr ${index} + 1`; done + for i in {A..Z}; do arr[index]=${i}; index=`expr ${index} + 1`; done + for i in {0..9}; do arr[index]=${i}; index=`expr ${index} + 1`; done + for i in {1..10}; do str="$str${arr[$RANDOM%$index]}"; done + echo ${str} +} + +is_64bit(){ + if [ `getconf WORD_BIT` = '32' ] && [ `getconf LONG_BIT` = '64' ] ; then + return 0 + else + return 1 + fi +} + +versionget(){ + if [ -f /etc/os-release ];then + grep -oE "[0-9.]+" /etc/os-release | head -1 + else + grep -oE "[0-9.]+" /etc/issue + fi +} + +debianversion(){ + if check_sys sysRelease debian;then + local version=$( get_opsy ) + local code=${1} + local main_ver=$( echo ${version} | sed 's/[^0-9]//g') + if [ "${main_ver}" == "${code}" ];then + return 0 + else + return 1 + fi + else + return 1 + fi +} + +version_check(){ + if check_sys packageManager apt; then + if debianversion 5; then + echo "错误: Debian 5 不支持,请重新安装OS并重试。" + exit 1 + fi + fi +} + +get_char(){ + SAVEDSTTY=`stty -g` + stty -echo + stty cbreak + dd if=/dev/tty bs=1 count=1 2> /dev/null + stty -raw + stty echo + stty $SAVEDSTTY +} + +preinstall_l2tp(){ + + echo + if [ -d "/proc/vz" ]; then + echo -e "\033[41;37m 警告: \033[0m 您的VPS基于OpenVZ,内核可能不支持IPSec。" + echo "是否继续安装? (y/n)" + read -p "(默认: n)" agree + [ -z ${agree} ] && agree="n" + if [ "${agree}" == "n" ]; then + echo + echo "L2TP安装已取消。" + echo + exit 0 + fi + fi + echo + echo "请输入IP范围:" + read -p "(默认范围: 192.168.18):" iprange + [ -z ${iprange} ] && iprange="192.168.18" + + echo "请输入PSK密钥:" + read -p "(默认PSK: admin123@l2tp):" mypsk + [ -z ${mypsk} ] && mypsk="admin123@l2tp" + + echo "请输入用户名:" + read -p "(默认用户名: admin123):" username + [ -z ${username} ] && username="admin123" + + password=`rand` + echo "请输入 ${username} 的密码:" + read -p "(默认密码: ${password}):" tmppassword + [ ! -z ${tmppassword} ] && password=${tmppassword} + + echo + echo "服务器IP: ${IP}" + echo "服务器本地IP: ${iprange}.1" + echo "客户端远程IP范围: ${iprange}.2-${iprange}.254" + echo "PSK密钥: ${mypsk}" + echo + echo "按任意键开始安装...或按Ctrl+C取消。" + char=`get_char` + +} + +# 安装依赖 +install_l2tp(){ + mknod /dev/random c 1 9 + apt -y update + apt -yq install curl wget ppp xl2tpd libreswan + config_install +} + +config_install(){ + + cat > /etc/ipsec.conf< /etc/ipsec.secrets< /etc/xl2tpd/xl2tpd.conf< /etc/ppp/options.xl2tpd< /etc/ppp/chap-secrets<> /etc/sysctl.conf + echo "net.ipv4.conf.${each}.accept_redirects=0" >> /etc/sysctl.conf + echo "net.ipv4.conf.${each}.send_redirects=0" >> /etc/sysctl.conf + echo "net.ipv4.conf.${each}.rp_filter=0" >> /etc/sysctl.conf + done + sysctl -p + + [ -f /etc/iptables.rules ] && cp -pf /etc/iptables.rules /etc/iptables.rules.old.`date +%Y%m%d` + + # 确保IP变量已正确获取 + if [ -z "${IP}" ]; then + IP=$(wget -qO- ipinfo.io/ip) + fi + + cat > /etc/iptables.rules < /etc/rc.local < /proc/sys/net/ipv4/ip_forward +/usr/sbin/service ipsec start +/usr/sbin/service xl2tpd start +/sbin/iptables-restore < /etc/iptables.rules + +exit 0 +EOF + chmod +x /etc/rc.local + else + # 如果已存在rc.local,则追加内容 + sed -i '/^exit 0/d' /etc/rc.local + cat >> /etc/rc.local < /proc/sys/net/ipv4/ip_forward +/usr/sbin/service ipsec start +/usr/sbin/service xl2tpd start +/sbin/iptables-restore < /etc/iptables.rules + +exit 0 +EOF + fi + + cat > /etc/network/if-up.d/iptables < /var/tmp/libreswan-nss-pwd + certutil -N -f /var/tmp/libreswan-nss-pwd -d /etc/ipsec.d + rm -f /var/tmp/libreswan-nss-pwd + fi + + update-rc.d -f xl2tpd defaults + + # 启用并启动服务 + systemctl enable ipsec + systemctl enable xl2tpd + + echo 1 > /proc/sys/net/ipv4/ip_forward + /sbin/iptables-restore < /etc/iptables.rules + systemctl restart ipsec + systemctl restart xl2tpd +} + +finally(){ + cp -f ${cur_dir}/l2tp.sh /usr/bin/l2tp 2>/dev/null || true + echo "请稍候..." + sleep 3 + ipsec verify + echo + echo "###############################################################" + echo "# L2TP 安装脚本 #" + echo "###############################################################" + echo + echo "默认用户名和密码如下:" + echo + echo "服务器IP: ${IP}" + echo "PSK密钥 : ${mypsk}" + echo "用户名 : ${username}" + echo "密码 : ${password}" + echo + echo "如果您想修改用户设置,请使用以下命令:" + echo "-a (添加用户)" + echo "-d (删除用户)" + echo "-l (列出所有用户)" + echo "-m (修改用户密码)" + echo +} + + +l2tp(){ + clear + echo + echo "###############################################################" + echo "# L2TP 安装脚本 #" + echo "###############################################################" + echo + rootness + tunavailable + disable_selinux + version_check + get_os_info + preinstall_l2tp + install_l2tp + finally +} + +list_users(){ + if [ ! -f /etc/ppp/chap-secrets ];then + echo "错误: /etc/ppp/chap-secrets 文件未找到." + exit 1 + fi + local line="+-------------------------------------------+\n" + local string=%20s + printf "${line}|${string} |${string} |\n${line}" 用户名 密码 + grep -v "^#" /etc/ppp/chap-secrets | awk '{printf "|'${string}' |'${string}' |\n", $1,$3}' + printf ${line} +} + +add_user(){ + while : + do + read -p "请输入用户名:" user + if [ -z ${user} ]; then + echo "用户名不能为空" + else + grep -w "${user}" /etc/ppp/chap-secrets > /dev/null 2>&1 + if [ $? -eq 0 ];then + echo "用户名 (${user}) 已存在。请重新输入用户名。" + else + break + fi + fi + done + pass=`rand` + echo "请输入 ${user} 的密码:" + read -p "(默认密码: ${pass}):" tmppass + [ ! -z ${tmppass} ] && pass=${tmppass} + echo "${user} l2tpd ${pass} *" >> /etc/ppp/chap-secrets + echo "用户 (${user}) 添加完成。" +} + +del_user(){ + while : + do + read -p "请输入要删除的用户名:" user + if [ -z ${user} ]; then + echo "用户名不能为空" + else + grep -w "${user}" /etc/ppp/chap-secrets >/dev/null 2>&1 + if [ $? -eq 0 ];then + break + else + echo "用户名 (${user}) 不存在。请重新输入用户名。" + fi + fi + done + sed -i "/^\<${user}\>/d" /etc/ppp/chap-secrets + echo "用户 (${user}) 删除完成。" +} + +mod_user(){ + while : + do + read -p "请输入要修改密码的用户名:" user + if [ -z ${user} ]; then + echo "用户名不能为空" + else + grep -w "${user}" /etc/ppp/chap-secrets >/dev/null 2>&1 + if [ $? -eq 0 ];then + break + else + echo "用户名 (${user}) 不存在。请重新输入用户名。" + fi + fi + done + pass=`rand` + echo "请输入 ${user} 的新密码:" + read -p "(默认密码: ${pass}):" tmppass + [ ! -z ${tmppass} ] && pass=${tmppass} + sed -i "/^\<${user}\>/d" /etc/ppp/chap-secrets + echo "${user} l2tpd ${pass} *" >> /etc/ppp/chap-secrets + echo "用户 ${user} 的密码已更改。" +} + +# 主程序 +action=$1 +if [ -z ${action} ] && [ "`basename $0`" != "l2tp" ]; then + action=install +fi + +case ${action} in + install) + l2tp 2>&1 | tee ${cur_dir}/l2tp.log + ;; + -l|--list) + list_users + ;; + -a|--add) + add_user + ;; + -d|--del) + del_user + ;; + -m|--mod) + mod_user + ;; + -h|--help) + echo "用法: -l,--list 列出所有用户" + echo " -a,--add 添加用户" + echo " -d,--del 删除用户" + echo " -m,--mod 修改用户密码" + echo " -h,--help 打印此帮助信息" + ;; + *) + echo "用法: [-l,--查看用户|-a,--添加用户|-d,--删除用户|-m,--修改密码|-h,--帮助信息]" && exit + ;; +esac diff --git a/proxy/singbox-zhanqun.sh b/proxy/singbox-zhanqun.sh new file mode 100644 index 0000000..7ffdcee --- /dev/null +++ b/proxy/singbox-zhanqun.sh @@ -0,0 +1,569 @@ +#!/bin/bash +# sing-box站群多IP源进源出节点脚本 支持sk5和vless+tcp协议 + +# 生成随机8位数的用户名和密码 +generate_random_string() { + local length=8 + tr -dc A-Za-z0-9 "$OUTPUT_FILE" + echo "将使用输出文件: $OUTPUT_FILE" +} + +install_jq() { + if ! command -v jq &> /dev/null; then + echo "jq 未安装,正在安装 jq..." + if [[ -f /etc/debian_version ]]; then + apt update && apt install -yq jq + elif [[ -f /etc/redhat-release ]]; then + yum install -y epel-release jq + else + echo "无法确定系统发行版,请手动安装 jq。" + exit 1 + fi + else + echo "jq 已安装。" + fi +} + +install_xray() { + if ! command -v sing-box &> /dev/null; then + echo "sing-box 未安装,正在安装 sing-box..." + VERSION="1.11.5" + curl -Lo sing-box.deb "https://github.com/SagerNet/sing-box/releases/download/v${VERSION}/sing-box_${VERSION}_linux_amd64.deb" + if ! dpkg -i sing-box.deb; then + echo "sing-box 安装失败,请检查dpkg输出。" + rm -f sing-box.deb + exit 1 + fi + rm -f sing-box.deb + echo "sing-box 安装完成。" + else + echo "sing-box 已安装。" + fi +} + +# 检查是否已有节点配置 +check_existing_nodes() { + local config_file="/etc/sing-box/config.json" + + # 如果配置文件不存在,则没有节点配置 + if [ ! -f "$config_file" ]; then + return 1 + fi + + # 检查 route.rules 数组数量是否大于等于2 + local rules_count=$(jq '.route.rules | length' "$config_file" 2>/dev/null) + # 如果jq命令失败或rules为空或小于2,则认为没有足够节点配置 + if [ -z "$rules_count" ] || [ "$rules_count" -lt 2 ]; then + return 1 + fi + # 有2个及以上路由规则,视为已有节点配置 + return 0 +} + +get_public_ipv4() { + ip -4 addr show | awk '/inet / {ip = $2; sub(/\/.*/, "", ip); if (ip !~ /^127\./ && ip !~ /^10\./ && ip !~ /^192\.168\./ && ip !~ /^169\.254\./ && ip !~ /^172\.(1[6-9]|2[0-9]|3[0-1])\./) print ip}' +} + +# 获取已配置的IP列表 +get_configured_ips() { + local config_file="/etc/sing-box/config.json" + + if [ ! -f "$config_file" ]; then + echo "" + return + fi + + jq -r '.outbounds[] | .inet4_bind_address' "$config_file" | sort | uniq +} + +print_node_info() { + local ip=$1 + local socks_port=$2 + local vless_port=$3 + local username=$4 + local password=$5 + local uuid=$6 + + echo -e " IP: \033[32m$ip\033[0m" + echo -e " Socks5 端口: \033[32m$socks_port\033[0m 用户名: \033[32m$username\033[0m 密码: \033[32m$password\033[0m" + echo -e " VLESS 端口: \033[32m$vless_port\033[0m UUID: \033[32m$uuid\033[0m" + # 构建vless链接,使用IP作为备注 + local vless_link="vless://$uuid@$ip:$vless_port?security=none&type=tcp#$ip" + # 保存节点信息到文件 + echo "$ip:$socks_port:$username:$password————$vless_link" >> "$OUTPUT_FILE" + echo "节点信息已保存到 $OUTPUT_FILE" +} + +# 导出所有节点配置 +export_all_nodes() { + local config_file="/etc/sing-box/config.json" + + if [ ! -f "$config_file" ]; then + echo "Xray配置文件不存在,无法导出节点信息。" + return 1 + fi + + # 初始化输出文件 + init_output_file + + echo "正在导出所有节点配置到 $OUTPUT_FILE..." + + # 获取所有Socks5节点 + local socks_nodes=$(jq -r '.inbounds[] | select(.type == "socks") | {port: .listen_port, tag: .tag}' "$config_file") + if [ -z "$socks_nodes" ]; then + echo "未找到任何节点配置。" + return 1 + fi + # 遍历所有Socks5节点,查找对应的信息 + for row in $(jq -r '.inbounds[] | select(.type == "socks") | @base64' "$config_file"); do + inbound=$(echo $row | base64 --decode) + local port=$(echo "$inbound" | jq -r '.listen_port') + local tag=$(echo "$inbound" | jq -r '.tag') + # 查找对应的outbound以获取IP + local outbound_tag="out-$port" + local ip=$(jq -r --arg tag "$outbound_tag" '.outbounds[] | select(.tag == $tag) | .inet4_bind_address' "$config_file") + # 获取Socks5的用户名和密码 + local username=$(echo "$inbound" | jq -r '.users[0].username') + local password=$(echo "$inbound" | jq -r '.users[0].password') + # 查找相应的VLESS节点 + local vless_port=$((port + 1)) + local vless_tag="in-$vless_port" + # 获取VLESS的UUID + local uuid=$(jq -r --arg tag "$vless_tag" '.inbounds[] | select(.tag == $tag) | .users[0].uuid' "$config_file") + if [ -n "$uuid" ]; then + # 构建vless链接,使用IP作为备注 + local vless_link="vless://$uuid@$ip:$vless_port?security=none&type=tcp#$ip" + # 输出节点信息 + echo "$ip:$port:$username:$password————$vless_link" >> "$OUTPUT_FILE" + echo -e "已导出节点: \033[32m$ip\033[0m Socks5端口:\033[32m$port\033[0m VLESS端口:\033[32m$vless_port\033[0m" + fi + done + + echo "所有节点导出完成,信息已保存到 $OUTPUT_FILE" + return 0 +} + +# 查找配置中未使用的端口号 +find_next_unused_port() { + local config_file="/etc/sing-box/config.json" + + if [ ! -f "$config_file" ]; then + echo "10001" # 如果配置文件不存在,从10001开始 + return + fi + + # 获取所有已使用的端口 + local used_ports=$(jq -r '.inbounds[].port' "$config_file" | sort -n) + + if [ -z "$used_ports" ]; then + echo "10001" # 如果没有已使用的端口,从10001开始 + return + fi + + # 获取最大的端口号并加1 + local max_port=$(echo "$used_ports" | tail -1) + local next_port=$((max_port + 1)) + + # 确保端口号是奇数(用于socks5) + if [ $((next_port % 2)) -eq 0 ]; then + next_port=$((next_port + 1)) + fi + + echo "$next_port" +} + +# 添加新节点(只添加未配置的IP) +add_new_nodes() { + # 获取当前系统的所有公网IP + public_ips=($(get_public_ipv4)) + + if [[ ${#public_ips[@]} -eq 0 ]]; then + echo "未找到公网IP地址,退出..." + return 1 + fi + + # 获取已经配置的IP列表 + configured_ips=($(get_configured_ips)) + + # 初始化新IP列表 + new_ips=() + + # 比对IP,找出未配置的IP + for ip in "${public_ips[@]}"; do + is_configured=false + for configured_ip in "${configured_ips[@]}"; do + if [[ "$ip" == "$configured_ip" ]]; then + is_configured=true + break + fi + done + + if ! $is_configured; then + new_ips+=("$ip") + fi + done + + # 检查是否有新的IP需要配置 + if [[ ${#new_ips[@]} -eq 0 ]]; then + echo "所有IP都已配置,无需添加新节点。" + return 0 + fi + + echo "发现 ${#new_ips[@]} 个未配置的IP: ${new_ips[@]}" + + # 初始化输出文件 + init_output_file + + # 获取配置文件路径 + config_file="/etc/sing-box/config.json" + + # 如果配置文件不存在,创建基础配置 + if [ ! -f "$config_file" ]; then + cat > $config_file < temp.json && mv temp.json "$config_file" + # 添加VLESS配置 + jq --argjson port "$vless_port" --arg ip "$ip" --arg uuid "$uuid" '.inbounds += [{ + "type": "vless", + "tag": ("in-\($port)"), + "listen": "0.0.0.0", + "listen_port": $port, + "users": [{ + "uuid": $uuid + }] + }] | .outbounds += [{ + "type": "direct", + "tag": ("out-\($port)"), + "inet4_bind_address": $ip + }] | .route.rules += [{ + "inbound": ["in-\($port)"], + "outbound": "out-\($port)" + }]' "$config_file" > temp.json && mv temp.json "$config_file" + + # 输出节点信息 + print_node_info "$ip" "$socks_port" "$vless_port" "$username" "$password" "$uuid" + + # 增加端口号,为下一个IP准备 + socks_port=$((vless_port + 1)) + done + + echo "新节点配置完成,共添加了 ${#new_ips[@]} 个节点" + return 0 +} + +configure_xray() { + public_ips=($(get_public_ipv4)) + + if [[ ${#public_ips[@]} -eq 0 ]]; then + echo "未找到额外IP地址,退出..." + exit 1 + fi + + echo "找到的公网 IPv4 地址: ${public_ips[@]}" + + # 初始化输出文件 + init_output_file + + config_file="/etc/sing-box/config.json" + + # 创建基础配置文件 + cat > $config_file < temp.json && mv temp.json "$config_file" + # 添加VLESS配置 + jq --argjson port "$vless_port" --arg ip "$ip" --arg uuid "$uuid" '.inbounds += [{ + "type": "vless", + "tag": ("in-\($port)"), + "listen": "0.0.0.0", + "listen_port": $port, + "users": [{ + "uuid": $uuid + }] + }] | .outbounds += [{ + "type": "direct", + "tag": ("out-\($port)"), + "inet4_bind_address": $ip + }] | .route.rules += [{ + "inbound": ["in-\($port)"], + "outbound": "out-\($port)" + }]' "$config_file" > temp.json && mv temp.json "$config_file" + # 输出节点信息 + print_node_info "$ip" "$socks_port" "$vless_port" "$username" "$password" "$uuid" + # 增加端口号,为下一个IP准备 + socks_port=$((vless_port + 1)) + done + + echo "sing-box 配置完成。" +} + +modify_by_ip() { + local modify_file="/home/xiugai.txt" + + if [ ! -f "$modify_file" ]; then + echo "修改文件 $modify_file 不存在,跳过修改操作。" + return + fi + + echo "检测到修改文件,开始根据IP修改节点..." + + # 读取当前配置 + local config_file="/etc/sing-box/config.json" + if [ ! -f "$config_file" ]; then + echo "Xray配置文件不存在,请先配置Xray。" + exit 1 + fi + + # 初始化输出文件 + init_output_file + + local modify_success=false + + # 逐行读取修改文件中的IP + while IFS= read -r ip || [[ -n "$ip" ]]; do + # 跳过空行和注释行 + [[ -z "$ip" || "$ip" =~ ^# ]] && continue + + echo "正在处理IP: $ip" + + # 查找此IP对应的出站配置 + local ip_exists=$(jq --arg ip "$ip" '.outbounds[] | select(.inet4_bind_address == $ip) | .tag' "$config_file") + + if [[ -z "$ip_exists" ]]; then + echo "错误: IP $ip 在当前配置中未找到,停止脚本执行。" + exit 1 + fi + + # 找到对应的入站端口和标签 + local outbound_tags=$(jq -r --arg ip "$ip" '.outbounds[] | select(.inet4_bind_address == $ip) | .tag' "$config_file") + + for outbound_tag in $outbound_tags; do + local port=$(echo $outbound_tag | cut -d'-' -f2) + local inbound_tag="in-$port" + # 检查协议类型 + local type=$(jq -r --arg tag "$inbound_tag" '.inbounds[] | select(.tag == $tag) | .type' "$config_file") + if [[ "$type" == "socks" ]]; then + # 更新socks协议的用户名和密码 + local username=$(generate_random_string) + local password=$(generate_random_string) + jq --arg tag "$inbound_tag" --arg username "$username" --arg password "$password" ' + .inbounds[] |= if .tag == $tag then + .users[0].username = $username | + .users[0].password = $password + else . end' "$config_file" > temp.json && mv temp.json "$config_file" + # 找到对应的vless端口 + local vless_port=$((port + 1)) + local vless_tag="in-$vless_port" + # 确认vless端口存在 + local vless_exists=$(jq --arg tag "$vless_tag" '.inbounds[] | select(.tag == $tag) | .tag' "$config_file") + # 如果存在,更新vless协议的UUID + if [[ -n "$vless_exists" ]]; then + local uuid=$(generate_uuid) + jq --arg tag "$vless_tag" --arg uuid "$uuid" ' + .inbounds[] |= if .tag == $tag then + .users[0].uuid = $uuid + else . end' "$config_file" > temp.json && mv temp.json "$config_file" + # 构建vless链接,使用IP作为备注 + local vless_link="vless://$uuid@$ip:$vless_port?security=none&type=tcp#$ip" + # 保存修改后的节点信息 + echo "$ip:$port:$username:$password————$vless_link" >> "$OUTPUT_FILE" + echo "已修改 IP: $ip 的Socks5(端口:$port)和VLESS(端口:$vless_port)配置" + modify_success=true + else + echo "警告: 未找到IP $ip 对应的VLESS配置" + fi + fi + done + done < "$modify_file" + + if $modify_success; then + echo "节点修改完成,信息已保存到 $OUTPUT_FILE" + else + echo "未进行任何修改" + fi +} + +restart_xray() { + echo "正在重启 sing-box 服务..." + if ! systemctl restart sing-box; then + echo "sing-box 服务重启失败,请检查配置文件。" + exit 1 + fi + systemctl enable sing-box + echo "sing-box 服务已重启。" +} + +# 显示交互式菜单 +show_menu() { + echo -e "\n\033[36m==== 站群多IP节点管理菜单 ====\033[0m" + echo -e "\033[33m1. 部署节点(首次部署)\033[0m" + echo -e "\033[33m2. 修改节点\033[0m" + echo -e "\033[33m3. 导出所有节点\033[0m" + echo -e "\033[33m4. 新增节点(自动添加未配置的IP)\033[0m" + echo -e "\033[33m0. 退出\033[0m" + echo -e "\033[36m==========================\033[0m" + + read -p "请输入选项 [0-4]: " choice + + case $choice in + 1) + if check_existing_nodes; then + echo -e "\033[31m警告: 检测到已有节点配置!\033[0m" + echo -e "\033[31m选择此选项将会清空所有现有节点并重新部署所有IP的节点\033[0m" + echo -e "\033[31m如果您只想添加新的IP节点,请使用选项4\033[0m" + read -p "是否确认清空所有节点并重新部署? (y/n): " confirm + if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then + echo "已取消操作" + show_menu + return + fi + fi + configure_xray + restart_xray + echo "节点部署完成" + ;; + 2) + echo "请确保 /home/xiugai.txt 文件中包含需要修改的IP地址列表,每行一个IP" + read -p "是否继续修改? (y/n): " confirm + if [[ "$confirm" == "y" || "$confirm" == "Y" ]]; then + modify_by_ip + restart_xray + echo "节点修改完成" + fi + ;; + 3) + export_all_nodes + ;; + 4) + add_new_nodes + if [ $? -eq 0 ]; then + restart_xray + echo "新节点添加完成" + fi + ;; + 0) + echo "退出程序" + exit 0 + ;; + *) + echo "无效选项,请重新选择" + show_menu + ;; + esac +} + +# 主函数 +main() { + # 检查是否已有节点配置 + if check_existing_nodes; then + echo "检测到已有节点配置,跳过依赖安装..." + show_menu + else + echo "未检测到节点配置,开始安装必要依赖..." + install_jq + install_xray + show_menu + fi +} + +main diff --git a/proxy/vmess-sk5.sh b/proxy/vmess-sk5.sh new file mode 100644 index 0000000..bb425fc --- /dev/null +++ b/proxy/vmess-sk5.sh @@ -0,0 +1,270 @@ +#!/bin/bash + +red='\e[31m' +yellow='\e[33m' +green='\e[32m' +none='\e[0m' +config_file="/usr/local/etc/xray/config.json" +default_config=' +{ + "inbounds": [ + { + "listen": "127.0.0.1", + "port": 9999, + "protocol": "vmess", + "settings": { + "clients": [ + { + "id": "sky22333" + } + ] + }, + "streamSettings": { + "network": "ws", + "security": "none", + "wsSettings": { + "path": "/sky22333" + } + }, + "tag": "inbound0" + } + ], + "outbounds": [ + { + "protocol": "socks", + "settings": { + "servers": [ + { + "address": "127.0.0.2", + "port": 2222, + "users": [ + { + "user": "admin123", + "pass": "admin333" + } + ] + } + ] + }, + "tag": "outbound0" + } + ], + "routing": { + "rules": [ + { + "type": "field", + "inboundTag": ["inbound0"], + "outboundTag": "outbound0" + } + ] + } +} +' + +# 检查并安装curl +check_and_install_curl() { + if ! type curl &>/dev/null; then + echo -e "${yellow}正在安装curl...${none}" + apt update && apt install -yq curl + fi +} + +# 检查并安装jq +check_and_install_jq() { + if ! type jq &>/dev/null; then + echo -e "${yellow}正在安装jq...${none}" + apt update && apt install -yq jq + fi +} + +# 检查并安装uuid-runtime +check_and_install_uuid_runtime() { + if ! type uuidgen &>/dev/null; then + echo -e "${yellow}正在安装 uuid-runtime...${none}" + apt update && apt install -yq uuid-runtime + fi +} + +# 检查并安装xray +check_and_install_xray() { + if ! type xray &>/dev/null; then + echo -e "${yellow}正在安装 xray...${none}" + bash <(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh) install --version v1.8.4 + fi +} + +# 检查是否已存在入站配置 +check_existing_inbound_config() { + if grep -q '"tag":' "$config_file"; then + return 0 # 已存在入站配置 + else + return 1 # 不存在入站配置 + fi +} + +# 创建默认配置文件 +create_default_config() { + if ! check_existing_inbound_config; then + echo "$default_config" > "$config_file" + echo -e "${green}已创建默认配置文件。${none}" + else + echo -e "${yellow}入站配置已存在,跳过创建默认配置文件。${none}" + fi +} + +# 获取本机公网 IP +get_local_ip() { + local ip=$(curl -s http://ipinfo.io/ip) + if [[ $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "$ip" + else + echo "无法自动获取公网IP地址,请手动输入。" + read -p "请输入您的公网IP地址: " manual_ip + if [[ $manual_ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "$manual_ip" + else + echo "输入的IP地址格式不正确,请重新运行脚本并输入有效的公网IP地址。" + exit 1 + fi + fi +} + +# 显示所有入站配置和 Vmess 链接以及对应的出站配置(出战只显示地址、端口、用户名和密码) +show_inbound_configs() { + local local_ip=$(get_local_ip) # 获取本机IP + + local config=$(jq '.inbounds | map(select(.port != 9999))' "$config_file") + local outbounds=$(jq '.outbounds' "$config_file") + echo -e "${green}入站节点配置:${none}" + + local length=$(jq '. | length' <<< "$config") + for ((i = 0; i < length; i++)); do + local port=$(jq -r ".[$i].port" <<< "$config") + local id=$(jq -r ".[$i].settings.clients[0].id" <<< "$config") + local path=$(jq -r ".[$i].streamSettings.wsSettings.path" <<< "$config") + + # 将节点地址设置为本机IP + local node_address="$local_ip" + + local vmess_link="vmess://$(echo -n "{\"v\":\"2\",\"ps\":\"节点$(($i + 1))\",\"add\":\"$node_address\",\"port\":$port,\"id\":\"$id\",\"aid\":0,\"net\":\"ws\",\"path\":\"$path\",\"type\":\"none\"}" | base64 -w 0)" + + echo -e "${yellow}节点: $(($i + 1))${none} - 端口: ${port}, Vmess 链接: ${vmess_link}" + + # 构造出站配置的标签 + local outbound_tag="outbound$port" + + # 根据构造的标签查找对应的出站配置 + local outbound_config=$(jq --arg tag "$outbound_tag" '.[] | select(.tag == $tag) | .settings.servers[] | {address, port, user: .users[0].user, pass: .users[0].pass}' <<< "$outbounds") + + if [[ ! -z $outbound_config ]]; then + echo -e "${green}出站配置:${none} 地址: $(jq -r '.address' <<< "$outbound_config"), 端口: $(jq -r '.port' <<< "$outbound_config"), 用户名: $(jq -r '.user' <<< "$outbound_config"), 密码: $(jq -r '.pass' <<< "$outbound_config")" + else + echo -e "${red}未找到对应的出站配置。${none}" + fi + done +} + +# 添加新节点 +add_new_nodes() { + read -p "请输入要添加的节点数量: " num_nodes + if ! [[ $num_nodes =~ ^[0-9]+$ ]]; then + echo -e "${red}错误!${none} 请输入有效的数量。\n" + return + fi + + local max_port=$(jq '[.inbounds[].port] | max // 10000' "$config_file") + local start_port=$((max_port+1)) + + for ((i=0; i "$config_file.tmp" && mv "$config_file.tmp" "$config_file" + + # 添加出站配置 + jq --arg tag "$new_outbound_tag" --arg addr "$outbound_addr" --argjson port "$outbound_port" --arg user "$outbound_user" --arg pass "$outbound_pass" ' + .outbounds += [{ + protocol: "socks", + settings: { servers: [{ address: $addr, port: $port, users: [{ user: $user, pass: $pass }] }] }, + tag: $tag + }]' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file" + + # 添加路由规则 + jq --arg inTag "$new_tag" --arg outTag "$new_outbound_tag" ' + .routing.rules += [{ type: "field", inboundTag: [$inTag], outboundTag: $outTag }] + ' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file" + done + + echo -e "${green}已成功添加 $num_nodes 个节点。${none}" + systemctl restart xray + echo -e "${green}Xray 服务已重新启动。${none}" +} + +# 删除特定端口号的节点 +delete_node_by_port() { + read -p "请输入要删除的vmess节点端口号: " port_to_delete + if ! [[ $port_to_delete =~ ^[0-9]+$ ]]; then + echo -e "${red}错误!${none} 请输入有效的端口号。\n" + return + fi + + local inbound_tag="inbound$port_to_delete" + local outbound_tag="outbound$port_to_delete" + + # 删除入站配置 + jq --argjson port "$port_to_delete" 'del(.inbounds[] | select(.port == $port))' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file" + + # 删除出站配置 + jq --arg tag "$outbound_tag" 'del(.outbounds[] | select(.tag == $tag))' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file" + + # 删除路由规则 + jq --arg inTag "$inbound_tag" --arg outTag "$outbound_tag" 'del(.routing.rules[] | select(.inboundTag[] == $inTag and .outboundTag == $outTag))' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file" + + echo -e "${green}已成功删除端口号为 $port_to_delete 的节点。${none}" + systemctl restart xray + echo -e "${green}Xray 服务已重新启动。${none}" +} + +# 主菜单 +main_menu() { + while true; do + echo -e "\n${green}sky22333-快速批量搭建二级代理脚本-管理菜单:${none}" + echo "1. 查看所有节点" + echo "2. 新增vmess入站sk5出站" + echo "3. 删除节点" + echo "4. 退出" + read -p "请输入选项: " choice + + case $choice in + 1) show_inbound_configs ;; + 2) add_new_nodes ;; + 3) delete_node_by_port ;; + 4) break ;; + *) echo -e "${red}无效的选项,请重新选择。${none}" ;; + esac + done +} + +# 调用主菜单函数 +check_and_install_curl +check_and_install_jq +check_and_install_uuid_runtime +check_and_install_xray +create_default_config +get_local_ip +main_menu diff --git a/proxy/vmess.sh b/proxy/vmess.sh new file mode 100644 index 0000000..017df36 --- /dev/null +++ b/proxy/vmess.sh @@ -0,0 +1,86 @@ +#!/bin/bash + +# 定义变量 +SERVER_IP="192.168.12.23" # 目标服务器的IP地址 +SERVER_PASSWORD="password" # 目标服务器的登录密码 +NODE_NAME="美国独享" # 用于标识节点的名称 +TARGET_DIR="/home/xray.txt" + +green='\e[32m' +none='\e[0m' +config_file="/usr/local/etc/xray/config.json" + +# 检查并安装依赖项 +install_dependencies() { + if ! type jq &>/dev/null || ! type uuidgen &>/dev/null || ! type sshpass &>/dev/null; then + echo -e "${green}正在安装 jq, uuid-runtime 和 sshpass...${none}" + apt update && apt install -yq jq uuid-runtime sshpass + fi + + if ! type xray &>/dev/null; then + echo -e "${green}正在安装 xray...${none}" + bash <(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh) install --version v1.8.4 + fi +} + +# 生成配置和传输逻辑 +configure_and_transfer() { + PORT=$(shuf -i 10000-65535 -n 1) + UUID=$(uuidgen) + RANDOM_PATH=$(cat /dev/urandom | tr -dc 'a-z' | head -c 6) + + cat > "$config_file" << EOF +{ + "inbounds": [ + { + "port": $PORT, + "protocol": "vmess", + "settings": { + "clients": [ + { + "id": "$UUID" + } + ] + }, + "streamSettings": { + "network": "ws", + "wsSettings": { + "path": "/$RANDOM_PATH" + } + }, + "listen": "0.0.0.0" + } + ], + "outbounds": [ + { + "protocol": "freedom", + "settings": {} + } + ], + "routing": { + "rules": [ + { + "type": "field", + "inboundTag": ["inbound0"], + "outboundTag": "direct" + } + ] + } +} +EOF + + local ip=$(curl -s http://ipinfo.io/ip) + local config="vmess://$(echo -n "{\"v\":\"2\",\"ps\":\"$NODE_NAME\",\"add\":\"$ip\",\"port\":$PORT,\"id\":\"$UUID\",\"aid\":\"0\",\"net\":\"ws\",\"path\":\"/$RANDOM_PATH\",\"type\":\"none\",\"host\":\"\",\"tls\":\"\"}" | base64 -w 0)" + echo -e "${green}Vmess-ws节点链接:${none}" + echo $config + + echo $config > /tmp/xray_config.txt + sshpass -p "$SERVER_PASSWORD" ssh -o StrictHostKeyChecking=no root@$SERVER_IP "cat >> $TARGET_DIR" < /tmp/xray_config.txt +} + +# 主执行逻辑 +install_dependencies +configure_and_transfer +systemctl restart xray +systemctl enable xray +echo -e "${green}Xray 服务已启动。${none}" diff --git a/proxy/xray.sh b/proxy/xray.sh new file mode 100644 index 0000000..1ce02e7 --- /dev/null +++ b/proxy/xray.sh @@ -0,0 +1,182 @@ +#!/usr/bin/env bash + +# Xray 安装脚本(极简版) +# 固定版本 v1.8.4 +# 支持通过 -p 参数设置 GitHub 加速前缀(如 https://gh-proxy.com/) +# 仅适用于 Linux 系统,需 root 权限 + +XRAY_VERSION="v1.8.4" +XRAY_BIN_URL="github.com/XTLS/Xray-core/releases/download/${XRAY_VERSION}/Xray-linux-64.zip" +INSTALL_PATH="/usr/local/bin/xray" +SERVICE_PATH="/etc/systemd/system/xray.service" +CONFIG_PATH="/usr/local/etc/xray/config.json" + +# github文件加速前缀 +GH_PROXY="https://gh-proxy.com" + +show_help() { + echo "用法: $0 [-p ] [-u|--uninstall]" + echo " -p (可选)GitHub 文件加速前缀,如 https://gh-proxy.com" + echo " -u, --uninstall 卸载 Xray 及所有相关文件和服务" + echo "此脚本会自动下载安装 Xray ${XRAY_VERSION},并注册 systemd 服务。" + exit 0 +} + +# 检查 root 权限 +if [[ "$(id -u)" -ne 0 ]]; then + echo "请以 root 用户运行此脚本。" + exit 1 +fi + +# 解析参数 +while [[ $# -gt 0 ]]; do + case "$1" in + -p) + shift + GH_PROXY="$1" + ;; + -u|--uninstall) + echo "正在卸载 Xray ..." + systemctl stop xray 2>/dev/null + systemctl disable xray 2>/dev/null + rm -f /usr/local/bin/xray + rm -rf /usr/local/etc/xray + rm -f /etc/systemd/system/xray.service + rm -rf /var/log/xray + systemctl daemon-reload + echo "Xray 及相关文件已卸载。" + exit 0 + ;; + -h|--help) + show_help + ;; + *) + show_help + ;; + esac + shift +done + +# 自动安装依赖(curl 和 unzip) +install_pkg() { + PKG_NAME="$1" + if command -v apt >/dev/null 2>&1; then + apt update && apt install -y "$PKG_NAME" + elif command -v dnf >/dev/null 2>&1; then + dnf install -y "$PKG_NAME" + elif command -v yum >/dev/null 2>&1; then + yum install -y "$PKG_NAME" + elif command -v zypper >/dev/null 2>&1; then + zypper install -y "$PKG_NAME" + elif command -v pacman >/dev/null 2>&1; then + pacman -Sy --noconfirm "$PKG_NAME" + elif command -v emerge >/dev/null 2>&1; then + emerge -qv "$PKG_NAME" + else + echo "未检测到支持的包管理器,请手动安装 $PKG_NAME 后重试。" + exit 1 + fi +} + +for cmd in curl unzip; do + if ! command -v $cmd >/dev/null 2>&1; then + echo "缺少依赖: $cmd,正在尝试自动安装..." + install_pkg "$cmd" + if ! command -v $cmd >/dev/null 2>&1; then + echo "$cmd 安装失败,请手动安装后重试。" + exit 1 + fi + fi +done + +TMP_DIR="$(mktemp -d)" +ZIP_FILE="$TMP_DIR/xray.zip" + +# 拼接加速前缀 +if [[ -n "$GH_PROXY" ]]; then + DOWNLOAD_URL="${GH_PROXY%/}/$XRAY_BIN_URL" +else + DOWNLOAD_URL="https://$XRAY_BIN_URL" +fi + +echo "下载 Xray: $DOWNLOAD_URL" +curl -L -o "$ZIP_FILE" "$DOWNLOAD_URL" +if [[ $? -ne 0 ]]; then + echo "下载失败,请检查网络或加速前缀。" + rm -rf "$TMP_DIR" + exit 1 +fi + +unzip -q "$ZIP_FILE" -d "$TMP_DIR" +if [[ $? -ne 0 ]]; then + echo "解压失败。" + rm -rf "$TMP_DIR" + exit 1 +fi + +install -m 755 "$TMP_DIR/xray" "$INSTALL_PATH" + +# 生成 systemd 服务文件(与原脚本一致,自动适配 User 和权限) +INSTALL_USER="root" +if [[ -f '/usr/local/bin/xray' ]]; then + # 若已存在旧服务文件,尝试读取 User 字段 + OLD_USER=$(grep '^[ \t]*User[ \t]*=' /etc/systemd/system/xray.service 2>/dev/null | tail -n 1 | awk -F = '{print $2}' | awk '{print $1}') + if [[ -n "$OLD_USER" ]]; then + INSTALL_USER="$OLD_USER" + fi +fi +if ! id "$INSTALL_USER" >/dev/null 2>&1; then + INSTALL_USER="root" +fi +INSTALL_USER_UID=$(id -u "$INSTALL_USER") + +# 权限相关字段 +temp_CapabilityBoundingSet="CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE" +temp_AmbientCapabilities="AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE" +temp_NoNewPrivileges="NoNewPrivileges=true" +if [[ "$INSTALL_USER_UID" -eq 0 ]]; then + temp_CapabilityBoundingSet="#${temp_CapabilityBoundingSet}" + temp_AmbientCapabilities="#${temp_AmbientCapabilities}" + temp_NoNewPrivileges="#${temp_NoNewPrivileges}" +fi + +cat > "$SERVICE_PATH" < "$CONFIG_PATH" + +# 启动并设置开机自启 +systemctl enable xray +systemctl restart xray +sleep 1 +if systemctl is-active --quiet xray; then + echo "Xray ${XRAY_VERSION} 安装并启动成功。" +else + echo "Xray 启动失败,请检查日志。" +fi + +# 清理临时文件 +rm -rf "$TMP_DIR" diff --git a/proxy/zhanqun.sh b/proxy/zhanqun.sh new file mode 100644 index 0000000..b66c3cd --- /dev/null +++ b/proxy/zhanqun.sh @@ -0,0 +1,629 @@ +#!/bin/bash +# 站群多IP源进源出节点脚本 支持sk5和vless+tcp协议 + +# 生成随机8位数的用户名和密码 +generate_random_string() { + local length=8 + tr -dc A-Za-z0-9 "$OUTPUT_FILE" + echo "将使用输出文件: $OUTPUT_FILE" +} + +install_jq() { + if ! command -v jq &> /dev/null; then + echo "jq 未安装,正在安装 jq..." + if [[ -f /etc/debian_version ]]; then + apt update && apt install -yq jq + elif [[ -f /etc/redhat-release ]]; then + yum install -y epel-release jq + else + echo "无法确定系统发行版,请手动安装 jq。" + exit 1 + fi + else + echo "jq 已安装。" + fi +} + +install_xray() { + if ! command -v xray &> /dev/null; then + echo "Xray 未安装,正在安装 Xray..." + if ! bash <(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh) install --version v1.8.4; then + echo "Xray 安装失败,请检查网络连接或安装脚本。" + exit 1 + fi + echo "Xray 安装完成。" + else + echo "Xray 已安装。" + fi +} + +# 检查是否已有节点配置 +check_existing_nodes() { + local config_file="/usr/local/etc/xray/config.json" + + # 如果配置文件不存在,则没有节点配置 + if [ ! -f "$config_file" ]; then + return 1 + fi + + # 检查inbounds数量是否大于0 + local inbounds_count=$(jq '.inbounds | length' "$config_file" 2>/dev/null) + + # 如果jq命令失败或inbounds为空,则认为没有节点配置 + if [ -z "$inbounds_count" ] || [ "$inbounds_count" -eq 0 ]; then + return 1 + fi + + # 有节点配置 + return 0 +} + +get_public_ipv4() { + ip -4 addr show | awk '/inet / {ip = $2; sub(/\/.*/, "", ip); if (ip !~ /^127\./ && ip !~ /^10\./ && ip !~ /^192\.168\./ && ip !~ /^169\.254\./ && ip !~ /^172\.(1[6-9]|2[0-9]|3[0-1])\./) print ip}' +} + +# 获取已配置的IP列表 +get_configured_ips() { + local config_file="/usr/local/etc/xray/config.json" + + if [ ! -f "$config_file" ]; then + echo "" + return + fi + + jq -r '.outbounds[] | .sendThrough' "$config_file" | sort | uniq +} + +print_node_info() { + local ip=$1 + local socks_port=$2 + local vless_port=$3 + local username=$4 + local password=$5 + local uuid=$6 + + echo -e " IP: \033[32m$ip\033[0m" + echo -e " Socks5 端口: \033[32m$socks_port\033[0m 用户名: \033[32m$username\033[0m 密码: \033[32m$password\033[0m" + echo -e " VLESS 端口: \033[32m$vless_port\033[0m UUID: \033[32m$uuid\033[0m" + + # 构建vless链接,使用IP作为备注 + local vless_link="vless://$uuid@$ip:$vless_port?security=none&type=tcp#$ip" + + # 保存节点信息到文件 + echo "$ip:$socks_port:$username:$password————$vless_link" >> "$OUTPUT_FILE" + echo "节点信息已保存到 $OUTPUT_FILE" +} + +# 导出所有节点配置 +export_all_nodes() { + local config_file="/usr/local/etc/xray/config.json" + + if [ ! -f "$config_file" ]; then + echo "Xray配置文件不存在,无法导出节点信息。" + return 1 + fi + + # 初始化输出文件 + init_output_file + + echo "正在导出所有节点配置到 $OUTPUT_FILE..." + + # 获取所有Socks5节点 + local socks_nodes=$(jq -r '.inbounds[] | select(.protocol == "socks") | {port: .port, tag: .tag}' "$config_file") + + if [ -z "$socks_nodes" ]; then + echo "未找到任何节点配置。" + return 1 + fi + + # 遍历所有Socks5节点,查找对应的信息 + for row in $(jq -r '.inbounds[] | select(.protocol == "socks") | @base64' "$config_file"); do + inbound=$(echo $row | base64 --decode) + + local port=$(echo "$inbound" | jq -r '.port') + local tag=$(echo "$inbound" | jq -r '.tag') + + # 查找对应的outbound以获取IP + local outbound_tag="out-$port" + local ip=$(jq -r --arg tag "$outbound_tag" '.outbounds[] | select(.tag == $tag) | .sendThrough' "$config_file") + + # 获取Socks5的用户名和密码 + local username=$(echo "$inbound" | jq -r '.settings.accounts[0].user') + local password=$(echo "$inbound" | jq -r '.settings.accounts[0].pass') + + # 查找相应的VLESS节点 + local vless_port=$((port + 1)) + local vless_tag="in-$vless_port" + + # 获取VLESS的UUID + local uuid=$(jq -r --arg tag "$vless_tag" '.inbounds[] | select(.tag == $tag) | .settings.clients[0].id' "$config_file") + + if [ -n "$uuid" ]; then + # 构建vless链接,使用IP作为备注 + local vless_link="vless://$uuid@$ip:$vless_port?security=none&type=tcp#$ip" + + # 输出节点信息 + echo "$ip:$port:$username:$password————$vless_link" >> "$OUTPUT_FILE" + echo -e "已导出节点: \033[32m$ip\033[0m Socks5端口:\033[32m$port\033[0m VLESS端口:\033[32m$vless_port\033[0m" + fi + done + + echo "所有节点导出完成,信息已保存到 $OUTPUT_FILE" + return 0 +} + +# 查找配置中未使用的端口号 +find_next_unused_port() { + local config_file="/usr/local/etc/xray/config.json" + + if [ ! -f "$config_file" ]; then + echo "10001" # 如果配置文件不存在,从10001开始 + return + fi + + # 获取所有已使用的端口 + local used_ports=$(jq -r '.inbounds[].port' "$config_file" | sort -n) + + if [ -z "$used_ports" ]; then + echo "10001" # 如果没有已使用的端口,从10001开始 + return + fi + + # 获取最大的端口号并加1 + local max_port=$(echo "$used_ports" | tail -1) + local next_port=$((max_port + 1)) + + # 确保端口号是奇数(用于socks5) + if [ $((next_port % 2)) -eq 0 ]; then + next_port=$((next_port + 1)) + fi + + echo "$next_port" +} + +# 添加新节点(只添加未配置的IP) +add_new_nodes() { + # 获取当前系统的所有公网IP + public_ips=($(get_public_ipv4)) + + if [[ ${#public_ips[@]} -eq 0 ]]; then + echo "未找到公网IP地址,退出..." + return 1 + fi + + # 获取已经配置的IP列表 + configured_ips=($(get_configured_ips)) + + # 初始化新IP列表 + new_ips=() + + # 比对IP,找出未配置的IP + for ip in "${public_ips[@]}"; do + is_configured=false + for configured_ip in "${configured_ips[@]}"; do + if [[ "$ip" == "$configured_ip" ]]; then + is_configured=true + break + fi + done + + if ! $is_configured; then + new_ips+=("$ip") + fi + done + + # 检查是否有新的IP需要配置 + if [[ ${#new_ips[@]} -eq 0 ]]; then + echo "所有IP都已配置,无需添加新节点。" + return 0 + fi + + echo "发现 ${#new_ips[@]} 个未配置的IP: ${new_ips[@]}" + + # 初始化输出文件 + init_output_file + + # 获取配置文件路径 + config_file="/usr/local/etc/xray/config.json" + + # 如果配置文件不存在,创建基础配置 + if [ ! -f "$config_file" ]; then + cat > $config_file < temp.json && mv temp.json "$config_file" + + # 添加VLESS配置 + jq --argjson port "$vless_port" --arg ip "$ip" --arg uuid "$uuid" '.inbounds += [{ + "port": $port, + "protocol": "vless", + "settings": { + "clients": [{ + "id": $uuid, + "level": 0 + }], + "decryption": "none" + }, + "streamSettings": { + "network": "tcp" + }, + "tag": ("in-\($port)") + }] | .outbounds += [{ + "protocol": "freedom", + "settings": {}, + "sendThrough": $ip, + "tag": ("out-\($port)") + }] | .routing.rules += [{ + "type": "field", + "inboundTag": ["in-\($port)"], + "outboundTag": "out-\($port)" + }]' "$config_file" > temp.json && mv temp.json "$config_file" + + # 输出节点信息 + print_node_info "$ip" "$socks_port" "$vless_port" "$username" "$password" "$uuid" + + # 增加端口号,为下一个IP准备 + socks_port=$((vless_port + 1)) + done + + echo "新节点配置完成,共添加了 ${#new_ips[@]} 个节点" + return 0 +} + +configure_xray() { + public_ips=($(get_public_ipv4)) + + if [[ ${#public_ips[@]} -eq 0 ]]; then + echo "未找到额外IP地址,退出..." + exit 1 + fi + + echo "找到的公网 IPv4 地址: ${public_ips[@]}" + + # 初始化输出文件 + init_output_file + + config_file="/usr/local/etc/xray/config.json" + + # 创建基础配置文件 + cat > $config_file < temp.json && mv temp.json "$config_file" + + # 添加VLESS配置 + jq --argjson port "$vless_port" --arg ip "$ip" --arg uuid "$uuid" '.inbounds += [{ + "port": $port, + "protocol": "vless", + "settings": { + "clients": [{ + "id": $uuid, + "level": 0 + }], + "decryption": "none" + }, + "streamSettings": { + "network": "tcp" + }, + "tag": ("in-\($port)") + }] | .outbounds += [{ + "protocol": "freedom", + "settings": {}, + "sendThrough": $ip, + "tag": ("out-\($port)") + }] | .routing.rules += [{ + "type": "field", + "inboundTag": ["in-\($port)"], + "outboundTag": "out-\($port)" + }]' "$config_file" > temp.json && mv temp.json "$config_file" + + # 输出节点信息 + print_node_info "$ip" "$socks_port" "$vless_port" "$username" "$password" "$uuid" + + # 增加端口号,为下一个IP准备 + socks_port=$((vless_port + 1)) + done + + echo "Xray 配置完成。" +} + +modify_by_ip() { + local modify_file="/home/xiugai.txt" + + if [ ! -f "$modify_file" ]; then + echo "修改文件 $modify_file 不存在,跳过修改操作。" + return + fi + + echo "检测到修改文件,开始根据IP修改节点..." + + # 读取当前配置 + local config_file="/usr/local/etc/xray/config.json" + if [ ! -f "$config_file" ]; then + echo "Xray配置文件不存在,请先配置Xray。" + exit 1 + fi + + # 初始化输出文件 + init_output_file + + local modify_success=false + + # 逐行读取修改文件中的IP + while IFS= read -r ip || [[ -n "$ip" ]]; do + # 跳过空行和注释行 + [[ -z "$ip" || "$ip" =~ ^# ]] && continue + + echo "正在处理IP: $ip" + + # 查找此IP对应的出站配置 + local ip_exists=$(jq --arg ip "$ip" '.outbounds[] | select(.sendThrough == $ip) | .tag' "$config_file") + + if [[ -z "$ip_exists" ]]; then + echo "错误: IP $ip 在当前配置中未找到,停止脚本执行。" + exit 1 + fi + + # 找到对应的入站端口和标签 + local outbound_tags=$(jq -r --arg ip "$ip" '.outbounds[] | select(.sendThrough == $ip) | .tag' "$config_file") + + for outbound_tag in $outbound_tags; do + local port=$(echo $outbound_tag | cut -d'-' -f2) + local inbound_tag="in-$port" + + # 检查协议类型 + local protocol=$(jq -r --arg tag "$inbound_tag" '.inbounds[] | select(.tag == $tag) | .protocol' "$config_file") + + if [[ "$protocol" == "socks" ]]; then + # 更新socks协议的用户名和密码 + local username=$(generate_random_string) + local password=$(generate_random_string) + + jq --arg tag "$inbound_tag" --arg username "$username" --arg password "$password" ' + .inbounds[] |= if .tag == $tag then + .settings.accounts[0].user = $username | + .settings.accounts[0].pass = $password + else . end' "$config_file" > temp.json && mv temp.json "$config_file" + + # 找到对应的vless端口 + local vless_port=$((port + 1)) + local vless_tag="in-$vless_port" + + # 确认vless端口存在 + local vless_exists=$(jq --arg tag "$vless_tag" '.inbounds[] | select(.tag == $tag) | .tag' "$config_file") + + # 如果存在,更新vless协议的UUID + if [[ -n "$vless_exists" ]]; then + local uuid=$(generate_uuid) + + jq --arg tag "$vless_tag" --arg uuid "$uuid" ' + .inbounds[] |= if .tag == $tag then + .settings.clients[0].id = $uuid + else . end' "$config_file" > temp.json && mv temp.json "$config_file" + + # 构建vless链接,使用IP作为备注 + local vless_link="vless://$uuid@$ip:$vless_port?security=none&type=tcp#$ip" + + # 保存修改后的节点信息 + echo "$ip:$port:$username:$password————$vless_link" >> "$OUTPUT_FILE" + + echo "已修改 IP: $ip 的Socks5(端口:$port)和VLESS(端口:$vless_port)配置" + modify_success=true + else + echo "警告: 未找到IP $ip 对应的VLESS配置" + fi + fi + done + done < "$modify_file" + + if $modify_success; then + echo "节点修改完成,信息已保存到 $OUTPUT_FILE" + else + echo "未进行任何修改" + fi +} + +restart_xray() { + echo "正在重启 Xray 服务..." + if ! systemctl restart xray; then + echo "Xray 服务重启失败,请检查配置文件。" + exit 1 + fi + systemctl enable xray + echo "Xray 服务已重启。" +} + +# 显示交互式菜单 +show_menu() { + echo -e "\n\033[36m==== 站群多IP节点管理菜单 ====\033[0m" + echo -e "\033[33m1. 部署节点(首次部署)\033[0m" + echo -e "\033[33m2. 修改节点\033[0m" + echo -e "\033[33m3. 导出所有节点\033[0m" + echo -e "\033[33m4. 新增节点(自动添加未配置的IP)\033[0m" + echo -e "\033[33m0. 退出\033[0m" + echo -e "\033[36m==========================\033[0m" + + read -p "请输入选项 [0-4]: " choice + + case $choice in + 1) + if check_existing_nodes; then + echo -e "\033[31m警告: 检测到已有节点配置!\033[0m" + echo -e "\033[31m选择此选项将会清空所有现有节点并重新部署所有IP的节点\033[0m" + echo -e "\033[31m如果您只想添加新的IP节点,请使用选项4\033[0m" + read -p "是否确认清空所有节点并重新部署? (y/n): " confirm + if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then + echo "已取消操作" + show_menu + return + fi + fi + configure_xray + restart_xray + echo "节点部署完成" + ;; + 2) + echo "请确保 /home/xiugai.txt 文件中包含需要修改的IP地址列表,每行一个IP" + read -p "是否继续修改? (y/n): " confirm + if [[ "$confirm" == "y" || "$confirm" == "Y" ]]; then + modify_by_ip + restart_xray + echo "节点修改完成" + fi + ;; + 3) + export_all_nodes + ;; + 4) + add_new_nodes + if [ $? -eq 0 ]; then + restart_xray + echo "新节点添加完成" + fi + ;; + 0) + echo "退出程序" + exit 0 + ;; + *) + echo "无效选项,请重新选择" + show_menu + ;; + esac +} + +# 主函数 +main() { + # 检查是否已有节点配置 + if check_existing_nodes; then + echo "检测到已有节点配置,跳过依赖安装..." + show_menu + else + echo "未检测到节点配置,开始安装必要依赖..." + install_jq + install_xray + show_menu + fi +} + +main diff --git a/web/README.md b/web/README.md new file mode 100644 index 0000000..f48a5b7 --- /dev/null +++ b/web/README.md @@ -0,0 +1,25 @@ +### 完全卸载删除站点及环境 +- 删除站点文件 +``` +sudo rm -r /var/www/ +``` + +- 停止相关服务 +``` +sudo systemctl stop caddy apache2 mariadb +``` + +- 禁用开机自启 +``` +sudo systemctl disable caddy apache2 mariadb +``` + +- 卸载软件包 +``` +sudo apt remove --purge caddy apache2 php* mariadb-server mariadb-client -y +``` + +- 清理残留配置和依赖 +``` +sudo apt autoremove -y +``` diff --git a/web/acgfaka.sh b/web/acgfaka.sh new file mode 100644 index 0000000..04b9cf2 --- /dev/null +++ b/web/acgfaka.sh @@ -0,0 +1,196 @@ +#!/bin/bash +# 一键部署异次元发卡脚本 +# 项目地址https://github.com/sky22333/shell + +# 检查是否已经安装 acgfaka +if [ -d "/var/www/html/acgfaka" ]; then + echo -e "\033[32m检测到 acgfaka 已经安装。\033[0m" + echo -e "\033[33m如需重新安装,请删除站点文件:/var/www/html/acgfaka 并做好相关备份。\033[0m" + exit 0 +fi + +while true; do + echo -e "\033[33m请输入您的域名(确保已经解析到本机): \033[0m" + read DOMAIN + + echo -e "\033[32m您输入的域名是: $DOMAIN\033[0m" + echo -e "\033[33m请确认这个域名是否正确 (yes/no, 默认回车确认): \033[0m" + read CONFIRM + + # 如果用户按回车,则默认为确认 + if [[ -z "${CONFIRM// }" ]]; then + CONFIRM="yes" + fi + + if [[ "${CONFIRM,,}" == "yes" || "${CONFIRM,,}" == "y" ]]; then + echo -e "\033[32m域名确认成功: $DOMAIN\033[0m" + break + else + echo -e "\033[31m请重新输入域名。\033[0m" + fi +done + +# 安装必要的软件包 +echo -e "\033[32m安装必要的软件包...首次安装可能较慢...请耐心等待。。。\033[0m" + +# 创建 sources.list.d 目录(如果不存在的话) +if [ ! -d /etc/apt/sources.list.d/ ]; then + mkdir -p /etc/apt/sources.list.d/ +fi + +# 添加 Caddy 源和密钥 +sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl +curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg +curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list +sudo apt update -q +# 检查操作系统 +if grep -Eqi "debian" /etc/issue || grep -Eqi "debian" /proc/version; then + OS="debian" + # Debian 系统设置 PHP 仓库 + curl -sSLo /usr/share/keyrings/deb.sury.org-php.gpg https://packages.sury.org/php/apt.gpg + echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" >/etc/apt/sources.list.d/php.list +elif grep -Eqi "ubuntu" /etc/issue || grep -Eqi "ubuntu" /proc/version; then + OS="ubuntu" + # Ubuntu 系统设置 PHP PPA + sudo apt update -q + sudo apt install -yq software-properties-common + sudo add-apt-repository -y ppa:ondrej/php +else + echo "不支持的操作系统。本脚本仅支持 Debian 或 Ubuntu。" + exit 1 +fi + +# 项目地址https://github.com/sky22333/shell +# 更新源列表 +sudo apt update -q + +# 安装必要的软件包 +sudo apt install -yq mariadb-server php8.1 php8.1-mysql php8.1-fpm php8.1-curl php8.1-cgi php8.1-mbstring php8.1-xml php8.1-gd php8.1-xmlrpc php8.1-soap php8.1-intl php8.1-opcache php8.1-zip wget unzip socat curl caddy + +PHP_VERSION=$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;') +PHP_INI_FILE="/etc/php/${PHP_VERSION}/fpm/php.ini" +OPCACHE_FILE_CACHE_DIR="/var/cache/opcache" + +# 确保缓存目录存在并设置权限 +if [ ! -d "$OPCACHE_FILE_CACHE_DIR" ]; then + echo -e "\033[32m创建 OPcache 缓存目录...\033[0m" + sudo mkdir -p "$OPCACHE_FILE_CACHE_DIR" + sudo chown -R www-data:www-data "$OPCACHE_FILE_CACHE_DIR" +fi + +# 确保 OPcache 配置存在 +if ! grep -q "^opcache.enable=1" "$PHP_INI_FILE"; then + echo -e "\033[32m启用 OPcache 扩展...请稍等...\033[0m" + + # 写入 OPcache 配置 + sudo tee -a "$PHP_INI_FILE" > /dev/null < /dev/null < /dev/null 2>&1 +rm main.zip +mv acg-faka-main acgfaka + +sudo chown -R www-data:www-data /var/www/html/acgfaka +sudo find /var/www/html/acgfaka/ -type d -exec chmod 750 {} \; +sudo find /var/www/html/acgfaka/ -type f -exec chmod 640 {} \; + +# 配置 Caddyfile +CADDY_CONF="/etc/caddy/Caddyfile" +sudo tee $CADDY_CONF > /dev/null </tmp/.add_mysql.sql</etc/apt/sources.list.d/php.list +elif grep -Eqi "ubuntu" /etc/issue || grep -Eqi "ubuntu" /proc/version; then + OS="ubuntu" + # Ubuntu 系统设置 PHP PPA + sudo apt update -q + sudo apt install -yq software-properties-common + sudo add-apt-repository -y ppa:ondrej/php +else + echo "不支持的操作系统。本脚本仅支持 Debian 或 Ubuntu。" + exit 1 +fi + +# 更新源列表并安装必要的软件包 +sudo apt update -q +sudo apt install -yq mariadb-server php8.1 php8.1-mysql php8.1-fpm php8.1-curl php8.1-cgi php8.1-mbstring \ + php8.1-xml php8.1-gd php8.1-xmlrpc php8.1-soap php8.1-intl php8.1-opcache php8.1-zip unzip socat + +# 获取 PHP 版本信息 +PHP_VERSION=$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;') +PHP_INI_FILE="/etc/php/${PHP_VERSION}/fpm/php.ini" +OPCACHE_FILE_CACHE_DIR="/var/cache/opcache" + +# 创建 OPcache 缓存目录并设置权限 +if [ ! -d "$OPCACHE_FILE_CACHE_DIR" ]; then + echo -e "\033[32m创建 OPcache 缓存目录...\033[0m" + sudo mkdir -p "$OPCACHE_FILE_CACHE_DIR" + sudo chown -R www-data:www-data "$OPCACHE_FILE_CACHE_DIR" +fi + +# 配置 OPcache +if ! grep -q "^opcache.enable=1" "$PHP_INI_FILE"; then + echo -e "\033[32m启用 OPcache 扩展...请稍等...\033[0m" + sudo tee -a "$PHP_INI_FILE" > /dev/null < /dev/null +fi + +# 重启 PHP-FPM 服务 +sudo systemctl restart php${PHP_VERSION}-fpm + +echo -e "\033[32mPHP 配置已更新并重启 PHP-FPM 服务。\033[0m" diff --git a/web/wp.sh b/web/wp.sh new file mode 100644 index 0000000..45647b7 --- /dev/null +++ b/web/wp.sh @@ -0,0 +1,136 @@ +#!/bin/bash +# 一键部署WordPress脚本 +# 项目地址https://github.com/sky22333/shell + +# 检查是否已经安装 WordPress +if [ -d "/var/www/html/wordpress" ]; then + echo -e "\033[32m检测到 WordPress 已经安装。\033[0m" + echo -e "\033[33m如需重新安装,请删除站点文件:/var/www/html/wordpress 并做好相关备份。\033[0m" + exit 0 +fi + +while true; do + echo -e "\033[33m请输入您的域名(确保已经解析到本机): \033[0m" + read DOMAIN + + echo -e "\033[32m您输入的域名是: $DOMAIN\033[0m" + echo -e "\033[33m为防止输错,请核对域名是否正确 (yes/no?直接回车代表正确): \033[0m" + read CONFIRM + + # 如果用户按回车,则默认为确认 + if [[ -z "${CONFIRM// }" ]]; then + CONFIRM="yes" + fi + + if [[ "${CONFIRM,,}" == "yes" || "${CONFIRM,,}" == "y" ]]; then + echo -e "\033[32m域名确认成功: $DOMAIN\033[0m" + break + else + echo -e "\033[31m请重新输入域名。\033[0m" + fi +done + +echo -e "\033[32m更新系统包...首次更新可能较慢...请耐心等待。。。\033[0m" +sudo apt-get update -yq + +echo -e "\033[32m安装必要的软件包...首次安装可能较慢...请耐心等待。。。\033[0m" +sudo apt-get install -y -q mariadb-server php php-mysql php-fpm php-curl php-json php-cgi php-mbstring php-xml php-gd php-xmlrpc php-soap php-intl php-opcache php-zip wget unzip + +sudo systemctl start mariadb +sudo systemctl enable mariadb + +sudo mysql_secure_installation < /dev/null 2>&1 +rm latest-zh_CN.tar.gz + +sudo chown -R www-data:www-data /var/www/html/wordpress +sudo find /var/www/html/wordpress/ -type d -exec chmod 750 {} \; +sudo find /var/www/html/wordpress/ -type f -exec chmod 640 {} \; + +if [ ! -d /etc/apt/sources.list.d/ ]; then + sudo mkdir -p /etc/apt/sources.list.d/ +fi +sudo apt install -y -q debian-keyring debian-archive-keyring apt-transport-https +curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg +curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list +sudo apt update -yq +sudo apt install -y -q caddy + +PHP_VERSION=$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;') + +OPCACHE_FILE_CACHE_DIR="/var/www/html/wordpress/wp-content/opcache" +sudo mkdir -p $OPCACHE_FILE_CACHE_DIR +sudo chown www-data:www-data $OPCACHE_FILE_CACHE_DIR + +# 配置OPcache +sudo bash -c "cat > /etc/php/${PHP_VERSION}/fpm/conf.d/10-opcache.ini" < /etc/caddy/Caddyfile" <