3x-ui-setup

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

VPN Server Setup (3x-ui)

VPN服务器搭建(3x-ui)

Complete setup: fresh VPS from provider → secured server → working VPN with Hiddify client.
完整搭建流程:从服务商提供的全新VPS → 安全加固服务器 → 配置可正常使用的VPN并搭配Hiddify客户端。

Workflow Overview

工作流程概览

ЧАСТЬ 1: Настройка сервера
  Fresh VPS (IP + root + password)
    → Determine execution mode (remote or local)
    → Generate SSH key / setup access
    → Connect as root
    → Update system
    → Create non-root user + sudo
    → Install SSH key
    → TEST new user login (critical!)
    → Firewall (ufw)
    → Kernel hardening
    → Time sync + packages
    → Configure local ~/.ssh/config
    → ✅ Server secured

ЧАСТЬ 2: Установка VPN (3x-ui)
    → Install 3x-ui panel
    → Enable BBR (TCP optimization)
    → Disable ICMP (stealth)
    → Reality: scanner → create inbound → get link
    → Install Hiddify client
    → Verify connection
    → Generate guide file (credentials + instructions)
    → Install fail2ban + lock SSH (after key verified)
    → ✅ VPN working

第一部分:服务器配置
  全新VPS(IP + root账号 + 密码)
    → 确定执行模式(远程或本地)
    → 生成SSH密钥 / 配置访问权限
    → 以root身份连接服务器
    → 更新系统
    → 创建非root用户并赋予sudo权限
    → 安装SSH密钥
    → 测试新用户登录(至关重要!)
    → 配置防火墙(ufw)
    → 内核加固
    → 时间同步与基础软件包安装
    → 配置本地~/.ssh/config文件
    → ✅ 服务器已安全加固

第二部分:安装VPN(3x-ui)
    → 安装3x-ui面板
    → 启用BBR(TCP优化)
    → 禁用ICMP(隐身模式)
    → Reality:扫描器 → 创建入站规则 → 获取连接链接
    → 安装Hiddify客户端
    → 验证连接有效性
    → 生成指南文件(包含凭证与操作说明)
    → 安装fail2ban并锁定SSH(密钥验证通过后执行)
    → ✅ VPN已正常运行

PART 1: Server Hardening

第一部分:服务器安全加固

Secure a fresh server from provider credentials to production-ready state.
将服务商提供的全新服务器配置为生产就绪状态。

Step 0: Collect Information

步骤0:收集信息

First, determine execution mode:
Где запущен Claude Code?
  • На локальном компьютере (Remote mode) -- настраиваем удалённый сервер через SSH
  • На самом сервере (Local mode) -- настраиваем этот же сервер напрямую
首先,确定执行模式
Claude Code运行在何处?
  • 本地电脑(远程模式)——通过SSH配置远程服务器
  • 服务器本地(本地模式)——直接配置当前服务器

Remote Mode -- ASK the user for:

远程模式 —— 向用户询问:

  1. Server IP -- from provider email
  2. Root password -- from provider email
  3. Desired username -- for the new non-root account
  4. Server nickname -- for SSH config (e.g.,
    myserver
    ,
    vpn1
    )
  5. Has domain? -- if unsure, recommend "no" (Reality path, simpler)
  6. Domain name (if yes to #5) -- must already point to server IP
  1. 服务器IP —— 来自服务商邮件
  2. Root密码 —— 来自服务商邮件
  3. 期望的用户名 —— 用于创建新的非root账号
  4. 服务器别名 —— 用于SSH配置(例如:
    myserver
    vpn1
  5. 是否拥有域名? —— 若不确定,建议选择“否”(Reality路径,设置更简单)
  6. 域名(若第5项选“是”)—— 必须已解析至服务器IP

Local Mode -- ASK the user for:

本地模式 —— 向用户询问:

  1. Desired username -- for the new non-root account
  2. Server nickname -- for future SSH access from user's computer (e.g.,
    myserver
    ,
    vpn1
    )
  3. Has domain? -- if unsure, recommend "no" (Reality path, simpler)
  4. Domain name (if yes to #3) -- must already point to server IP
In Local mode, get server IP automatically:
bash
curl -4 -s ifconfig.me
If user pastes the full provider email, extract the data from it.
Recommend Reality (no domain) for beginners. Explain:
  • Reality: works without domain, free, simpler setup, great performance
  • TLS: needs domain purchase (~$10/year), more traditional, allows fallback site
  1. 期望的用户名 —— 用于创建新的非root账号
  2. 服务器别名 —— 后续从用户电脑通过SSH访问时使用(例如:
    myserver
    vpn1
  3. 是否拥有域名? —— 若不确定,建议选择“否”(Reality路径,设置更简单)
  4. 域名(若第3项选“是”)—— 必须已解析至服务器IP
本地模式下,自动获取服务器IP:
bash
curl -4 -s ifconfig.me
若用户粘贴了完整的服务商邮件,从中提取所需信息。
推荐新手使用Reality模式(无需域名),说明如下:
  • Reality:无需域名即可使用,免费,设置更简单,性能出色
  • TLS:需购买域名(约10美元/年),更传统,支持 fallback 站点

Execution Modes

执行模式适配

All commands in this skill are written for Remote mode (via SSH). For Local mode, adapt as follows:
StepRemote Mode (default)Local Mode
Step 1Generate SSH key on LOCAL machineSKIP -- user creates key on laptop later (Step 22)
Step 2
ssh root@{SERVER_IP}
Already on server. If not root:
sudo su -
Steps 3-4Run on server via root SSHRun directly (already on server)
Step 5Install local public key on serverSKIP -- user sends .pub via SCP later (Step 22)
Step 6SSH test from LOCAL:
ssh -i ... user@IP
Switch user:
su - {username}
, then
sudo whoami
Step 7SKIP -- lockdown deferred to Step 22SKIP -- lockdown deferred to Step 22
Steps 8-11
sudo
on server via SSH
sudo
directly (no SSH prefix)
Step 12Write
~/.ssh/config
on LOCAL
SKIP -- user does this from guide file (Step 22)
Step 13Verify via
ssh {nickname}
Run audit directly, skip SSH lockdown checks
Part 2
ssh {nickname} "sudo ..."
sudo ...
directly (no SSH prefix)
Step 17AScanner via
ssh {nickname} '...'
Scanner runs directly (no SSH wrapper) -- see Step 17A for both commands
Panel accessVia SSH tunnelDirect:
https://127.0.0.1:{panel_port}/{web_base_path}
Step 22Generate guide + fail2ban + lock SSHGenerate guide → SCP download → SSH key setup → fail2ban + lock SSH
IMPORTANT: In both modes, the end result is the same -- user has SSH key access to the server from their local computer via
ssh {nickname}
, password auth disabled, root login disabled.
本技能中的所有命令默认针对远程模式(通过SSH执行)。 针对本地模式,按如下方式调整:
步骤远程模式(默认)本地模式
步骤1在本地电脑生成SSH密钥跳过 —— 用户后续在笔记本电脑创建密钥(步骤22)
步骤2
ssh root@{SERVER_IP}
已在服务器上。若当前不是root用户:
sudo su -
步骤3-4通过root SSH在服务器上执行直接执行(已在服务器上)
步骤5在服务器上安装本地公钥跳过 —— 用户后续通过SCP发送.pub文件(步骤22)
步骤6从本地测试SSH:
ssh -i ... user@IP
切换用户:
su - {username}
,然后执行
sudo whoami
步骤7跳过 —— 锁定操作推迟至步骤22跳过 —— 锁定操作推迟至步骤22
步骤8-11通过SSH在服务器上使用
sudo
执行
直接使用
sudo
执行(无需SSH前缀)
步骤12在本地编写
~/.ssh/config
跳过 —— 用户通过指南文件完成此操作(步骤22)
步骤13通过
ssh {nickname}
验证
直接运行审计,跳过SSH锁定检查
第二部分
ssh {nickname} "sudo ..."
直接执行
sudo ...
(无需SSH前缀)
步骤17A通过
ssh {nickname} '...'
运行扫描器
直接运行扫描器(无需SSH包装)—— 查看步骤17A中的两种命令
面板访问通过SSH隧道直接访问:
https://127.0.0.1:{panel_port}/{web_base_path}
步骤22生成指南文件 + 安装fail2ban + 锁定SSH生成指南文件 → 通过SCP下载 → 配置SSH密钥 → 安装fail2ban + 锁定SSH
重要提示: 两种模式的最终结果一致——用户可通过本地电脑的
ssh {nickname}
命令,使用SSH密钥访问服务器,密码认证已禁用,root登录已禁用。

Step 1: Generate SSH Key (LOCAL)

步骤1:生成SSH密钥(本地电脑)

Run on the user's LOCAL machine BEFORE connecting to the server:
bash
ssh-keygen -t ed25519 -C "{username}@{nickname}" -f ~/.ssh/{nickname}_key -N ""
Save the public key content for later:
bash
cat ~/.ssh/{nickname}_key.pub
在连接服务器前,先在用户的本地电脑上执行:
bash
ssh-keygen -t ed25519 -C "{username}@{nickname}" -f ~/.ssh/{nickname}_key -N ""
保存公钥内容备用:
bash
cat ~/.ssh/{nickname}_key.pub

Step 2: First Connection as Root

步骤2:首次以Root身份连接服务器

bash
ssh root@{SERVER_IP}
bash
ssh root@{SERVER_IP}

Handling forced password change

处理强制修改密码

Many providers force a password change on first login. Signs:
  • Prompt: "You are required to change your password immediately"
  • Prompt: "Current password:" followed by "New password:"
  • Prompt: "WARNING: Your password has expired"
If this happens:
  1. Enter the current (provider) password
  2. Enter a new strong temporary password (this is temporary -- SSH keys will replace it)
  3. You may be disconnected -- reconnect with the new password
If connection drops after password change -- this is normal. Reconnect:
bash
ssh root@{SERVER_IP}
许多服务商要求首次登录时修改密码,特征如下:
  • 提示:"You are required to change your password immediately"
  • 提示:"Current password:" 随后是 "New password:"
  • 提示:"WARNING: Your password has expired"
若出现此情况:
  1. 输入当前(服务商提供的)密码
  2. 设置一个新的强临时密码(此密码为临时使用——后续将被SSH密钥替代)
  3. 可能会断开连接——使用新密码重新连接
若修改密码后连接断开——此为正常现象,重新连接:
bash
ssh root@{SERVER_IP}

Step 3: System Update (as root on server)

步骤3:系统更新(服务器上以root身份执行)

bash
apt update && DEBIAN_FRONTEND=noninteractive NEEDRESTART_MODE=a apt upgrade -y
bash
apt update && DEBIAN_FRONTEND=noninteractive NEEDRESTART_MODE=a apt upgrade -y

Step 4: Create Non-Root User

步骤4:创建非Root用户

bash
useradd -m -s /bin/bash {username}
echo "{username}:{GENERATE_STRONG_PASSWORD}" | chpasswd
usermod -aG sudo {username}
Generate a strong random password. Tell the user to save it (needed for sudo). Then:
bash
undefined
bash
useradd -m -s /bin/bash {username}
echo "{username}:{GENERATE_STRONG_PASSWORD}" | chpasswd
usermod -aG sudo {username}
生成一个强随机密码,告知用户保存(执行sudo时需要)。然后:
bash
undefined

Verify

验证

groups {username}
undefined
groups {username}
undefined

Step 5: Install SSH Key for New User

步骤5:为新用户安装SSH密钥

bash
mkdir -p /home/{username}/.ssh
echo "{PUBLIC_KEY_CONTENT}" > /home/{username}/.ssh/authorized_keys
chmod 700 /home/{username}/.ssh
chmod 600 /home/{username}/.ssh/authorized_keys
chown -R {username}:{username} /home/{username}/.ssh
bash
mkdir -p /home/{username}/.ssh
echo "{PUBLIC_KEY_CONTENT}" > /home/{username}/.ssh/authorized_keys
chmod 700 /home/{username}/.ssh
chmod 600 /home/{username}/.ssh/authorized_keys
chown -R {username}:{username} /home/{username}/.ssh

Step 6: TEST New User Login -- CRITICAL CHECKPOINT

步骤6:测试新用户登录 —— 至关重要的检查点

DO NOT proceed without successful test!
Open a NEW connection (keep root session alive):
bash
ssh -i ~/.ssh/{nickname}_key {username}@{SERVER_IP}
Verify sudo works:
bash
sudo whoami
未通过测试前请勿继续!
打开一个新的连接(保持root会话处于活跃状态):
bash
ssh -i ~/.ssh/{nickname}_key {username}@{SERVER_IP}
验证sudo权限是否正常:
bash
sudo whoami

Must output: root

输出必须为:root


**If this fails** -- debug permissions, do NOT disable root login:
```bash

**若测试失败**——调试权限,请勿禁用root登录:
```bash

Check on server as root:

在服务器上以root身份检查:

ls -la /home/{username}/.ssh/ cat /home/{username}/.ssh/authorized_keys
ls -la /home/{username}/.ssh/ cat /home/{username}/.ssh/authorized_keys

Fix ownership:

修复权限:

chown -R {username}:{username} /home/{username}/.ssh
undefined
chown -R {username}:{username} /home/{username}/.ssh
undefined

Step 7: Lock Down SSH — DEFERRED

步骤7:锁定SSH —— 推迟执行

Оба режима: ПРОПУСКАЕМ. Блокировка SSH и установка fail2ban выполняются в самом конце (Step 22), после того как SSH-ключ проверен. Это предотвращает случайную блокировку доступа во время настройки.
两种模式均跳过此步骤。SSH锁定与fail2ban安装将在最后执行(步骤22),且需在SSH密钥验证通过后进行。这可避免在配置过程中意外锁定访问权限。

Step 8: Firewall

步骤8:配置防火墙

bash
sudo apt install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw --force enable
sudo ufw status
bash
sudo apt install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw --force enable
sudo ufw status

Step 9: fail2ban — DEFERRED

步骤9:安装fail2ban —— 推迟执行

Пропущен. fail2ban устанавливается в конце настройки (Step 22) вместе с блокировкой SSH, чтобы не заблокировать пользователя во время настройки.
跳过此步骤。fail2ban将在配置末尾(步骤22)与SSH锁定一同安装,避免在配置过程中锁定用户。

Step 10: Kernel Hardening

步骤10:内核加固

bash
sudo tee /etc/sysctl.d/99-security.conf << 'EOF'
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.tcp_syncookies = 1
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
EOF
sudo sysctl -p /etc/sysctl.d/99-security.conf
bash
sudo tee /etc/sysctl.d/99-security.conf << 'EOF'
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.tcp_syncookies = 1
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
EOF
sudo sysctl -p /etc/sysctl.d/99-security.conf

Step 11: Time Sync + Base Packages

步骤11:时间同步与基础软件包

bash
sudo apt install -y chrony curl wget unzip net-tools
sudo systemctl enable chrony
bash
sudo apt install -y chrony curl wget unzip net-tools
sudo systemctl enable chrony

Step 12: Configure Local SSH Config

步骤12:配置本地SSH配置

On the user's LOCAL machine:
bash
cat >> ~/.ssh/config << 'EOF'

Host {nickname}
    HostName {SERVER_IP}
    User {username}
    IdentityFile ~/.ssh/{nickname}_key
    IdentitiesOnly yes
EOF
Tell user: Теперь подключайся командой
ssh {nickname}
-- без пароля и IP.
在用户的本地电脑上执行:
bash
cat >> ~/.ssh/config << 'EOF'

Host {nickname}
    HostName {SERVER_IP}
    User {username}
    IdentityFile ~/.ssh/{nickname}_key
    IdentitiesOnly yes
EOF
告知用户:现在你可以使用
ssh {nickname}
命令连接服务器——无需输入密码和IP。

Step 13: Final Verification

步骤13:最终验证

Connect as new user and run quick audit:
bash
ssh {nickname}
以新用户身份连接并运行快速审计:
bash
ssh {nickname}

Then on server:

然后在服务器上执行:

sudo ufw status sudo sysctl net.ipv4.conf.all.rp_filter

Expected: ufw active, rp_filter = 1.

**Note:** SSH lockdown и fail2ban проверяются в конце (Step 22) после подтверждения работы SSH-ключа.

**Часть 1 завершена. Базовая настройка сервера готова. Переходим к установке VPN.**

---
sudo ufw status sudo sysctl net.ipv4.conf.all.rp_filter

预期结果:ufw处于活跃状态,rp_filter = 1。

**注意:** SSH锁定与fail2ban的检查将在末尾(步骤22)完成,需在SSH密钥确认可用后执行。

**第一部分完成。服务器基础配置已就绪。接下来进行VPN安装。**

---

PART 2: VPN Installation (3x-ui)

第二部分:VPN安装(3x-ui)

All commands from here use
ssh {nickname}
-- the shortcut configured in Part 1.
从本部分开始,所有命令均使用
ssh {nickname}
——即第一部分中配置的快捷方式。

Step 14: Install 3x-ui

步骤14:安装3x-ui

3x-ui install script requires root. Run with sudo:
bash
ssh {nickname} "curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh -o /tmp/3x-ui-install.sh && echo 'n' | sudo bash /tmp/3x-ui-install.sh"
The
echo 'n'
answers "no" to port customization prompt -- a random port and credentials will be generated.
Note: Do NOT use
sudo bash <(curl ...)
-- process substitution does not work with sudo (file descriptors are not inherited).
IMPORTANT: Capture the output! It contains:
  • Generated username
  • Generated password
  • Panel port
  • Panel web base path
Extract and save these values. Show them to the user:
Данные панели 3x-ui (СОХРАНИ!):
  Username: {panel_username}
  Password: {panel_password}
  Port:     {panel_port}
  Path:     {web_base_path}
  URL:      https://127.0.0.1:{panel_port}/{web_base_path} (через SSH-туннель)
Verify 3x-ui is running:
bash
ssh {nickname} "sudo x-ui status"
If not running:
ssh {nickname} "sudo x-ui start"
Panel port is NOT opened in firewall intentionally -- access panel only via SSH tunnel for security.
3x-ui安装脚本需要root权限,使用sudo执行:
bash
ssh {nickname} "curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh -o /tmp/3x-ui-install.sh && echo 'n' | sudo bash /tmp/3x-ui-install.sh"
echo 'n'
用于回答端口自定义提示为“否”——将随机生成端口和凭证。
注意: 请勿使用
sudo bash <(curl ...)
——进程替换与sudo不兼容(文件描述符不会被继承)。
重要提示: 保存输出内容!其中包含:
  • 生成的用户名
  • 生成的密码
  • 面板端口
  • 面板web基础路径
提取并保存这些值,展示给用户:
3x-ui面板信息(务必保存!):
  用户名: {panel_username}
  密码: {panel_password}
  端口:     {panel_port}
  路径:     {web_base_path}
  URL:      https://127.0.0.1:{panel_port}/{web_base_path}(通过SSH隧道访问)
验证3x-ui是否运行:
bash
ssh {nickname} "sudo x-ui status"
若未运行:
ssh {nickname} "sudo x-ui start"
面板端口未在防火墙上开放——为安全起见,仅通过SSH隧道访问面板。

Step 14b: Enable BBR

步骤14b:启用BBR

BBR (Bottleneck Bandwidth and RTT) dramatically improves TCP throughput, especially on lossy links -- critical for VPN performance.
bash
ssh {nickname} 'current=$(sysctl -n net.ipv4.tcp_congestion_control); echo "Current: $current"; if [ "$current" != "bbr" ]; then echo "net.core.default_qdisc=fq" | sudo tee -a /etc/sysctl.conf && echo "net.ipv4.tcp_congestion_control=bbr" | sudo tee -a /etc/sysctl.conf && sudo sysctl -p && echo "BBR enabled"; else echo "BBR already active"; fi'
Verify:
bash
ssh {nickname} "sysctl net.ipv4.tcp_congestion_control net.core.default_qdisc"
Expected:
net.ipv4.tcp_congestion_control = bbr
,
net.core.default_qdisc = fq
.
BBR(Bottleneck Bandwidth and RTT)可显著提升TCP吞吐量,尤其是在丢包链路中——对VPN性能至关重要。
bash
ssh {nickname} 'current=$(sysctl -n net.ipv4.tcp_congestion_control); echo "当前配置: $current"; if [ "$current" != "bbr" ]; then echo "net.core.default_qdisc=fq" | sudo tee -a /etc/sysctl.conf && echo "net.ipv4.tcp_congestion_control=bbr" | sudo tee -a /etc/sysctl.conf && sudo sysctl -p && echo "BBR已启用"; else echo "BBR已处于活跃状态"; fi'
验证:
bash
ssh {nickname} "sysctl net.ipv4.tcp_congestion_control net.core.default_qdisc"
预期结果:
net.ipv4.tcp_congestion_control = bbr
net.core.default_qdisc = fq

Step 15: Disable ICMP (Stealth)

步骤15:禁用ICMP(隐身模式)

Makes server invisible to ping scans:
bash
ssh {nickname} "sudo sed -i 's/-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT/-A ufw-before-input -p icmp --icmp-type echo-request -j DROP/' /etc/ufw/before.rules && sudo sed -i 's/-A ufw-before-forward -p icmp --icmp-type echo-request -j ACCEPT/-A ufw-before-forward -p icmp --icmp-type echo-request -j DROP/' /etc/ufw/before.rules && sudo ufw reload"
Verify:
bash
ping -c 2 -W 2 {SERVER_IP}
Expected: no response (timeout).
使服务器对ping扫描不可见:
bash
ssh {nickname} "sudo sed -i 's/-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT/-A ufw-before-input -p icmp --icmp-type echo-request -j DROP/' /etc/ufw/before.rules && sudo sed -i 's/-A ufw-before-forward -p icmp --icmp-type echo-request -j ACCEPT/-A ufw-before-forward -p icmp --icmp-type echo-request -j DROP/' /etc/ufw/before.rules && sudo ufw reload"
验证:
bash
ping -c 2 -W 2 {SERVER_IP}
预期结果:无响应(超时)。

Step 16: Branch -- Reality or TLS

步骤16:分支选择——Reality或TLS

Path A: VLESS Reality (NO domain needed) -- RECOMMENDED

路径A:VLESS Reality(无需域名)——推荐使用

Go to Step 17A.
进入步骤17A。

Path B: VLESS TLS (domain required)

路径B:VLESS TLS(需域名)

Go to
references/vless-tls.md
.
参考
references/vless-tls.md

Step 17A: Find Best SNI with Reality Scanner

步骤17A:使用Reality扫描器寻找最佳SNI

Scan the server's /24 subnet to find real websites on neighboring IPs that support TLS 1.3, H2 (HTTP/2), and X25519 -- the exact stack Reality needs to mimic a genuine TLS handshake. The found domain becomes the masquerade target (SNI/dest), making VPN traffic indistinguishable from regular HTTPS to a neighboring site on the same hosting.
Why subnet scanning matters:
  • Reality reproduces a real TLS 1.3 handshake with the dest server -- the dest must support TLS 1.3 + H2 + X25519, or Reality won't work
  • RealiTLScanner (from the XTLS project) checks exactly this -- it only outputs servers compatible with Reality
  • DPI sees the SNI in TLS ClientHello and can probe the IP to verify the domain actually lives there
  • Popular domains (microsoft.com, google.com) are often on CDN IPs far from the VPS -- active probing catches this
  • A small unknown site on a neighboring IP (e.g.,
    shop.finn-auto.fi
    ) is ideal -- nobody filters it, and it's in the same subnet
  • Do NOT manually pick an SNI without the scanner -- a random domain may not support TLS 1.3 or may be on a different IP range
Download and run Reality Scanner against the /24 subnet:
Remote mode (Claude Code on user's laptop):
bash
ssh {nickname} 'ARCH=$(dpkg --print-architecture); case "$ARCH" in amd64) SA="64";; arm64|aarch64) SA="arm64-v8a";; *) SA="$ARCH";; esac && curl -sL "https://github.com/XTLS/RealiTLScanner/releases/latest/download/RealiTLScanner-linux-${SA}" -o /tmp/scanner && chmod +x /tmp/scanner && file /tmp/scanner | grep -q ELF || { echo "ERROR: scanner binary not valid for this architecture"; exit 1; }; MY_IP=$(curl -4 -s ifconfig.me); SUBNET=$(echo $MY_IP | sed "s/\.[0-9]*$/.0\/24/"); echo "Scanning subnet: $SUBNET"; timeout 120 /tmp/scanner --addr "$SUBNET" 2>&1 | head -80'
Local mode (Claude Code on the VPS itself):
bash
ARCH=$(dpkg --print-architecture); case "$ARCH" in amd64) SA="64";; arm64|aarch64) SA="arm64-v8a";; *) SA="$ARCH";; esac && curl -sL "https://github.com/XTLS/RealiTLScanner/releases/latest/download/RealiTLScanner-linux-${SA}" -o /tmp/scanner && chmod +x /tmp/scanner && file /tmp/scanner | grep -q ELF || { echo "ERROR: scanner binary not valid for this architecture"; exit 1; }; MY_IP=$(curl -4 -s ifconfig.me); SUBNET=$(echo $MY_IP | sed "s/\.[0-9]*$/.0\/24/"); echo "Scanning subnet: $SUBNET"; timeout 120 /tmp/scanner --addr "$SUBNET" 2>&1 | head -80
Note: The commands are identical — Local mode simply runs without the
ssh {nickname}
wrapper since Claude Code is already on the VPS. GitHub releases use non-standard arch names (
64
instead of
amd64
,
arm64-v8a
instead of
arm64
). The
case
block maps them. The
file | grep ELF
check ensures the download is a real binary, not a 404 HTML page. Timeout is 120s because scanning 254 IPs takes longer than a single IP.
扫描服务器的**/24子网**,寻找相邻IP上支持TLS 1.3、H2(HTTP/2)和X25519的真实网站——这些是Reality模拟真实TLS握手所需的精确协议栈。找到的域名将作为伪装目标(SNI/目标),使VPN流量与访问同一主机上相邻站点的常规HTTPS流量无法区分。
为什么子网扫描很重要:
  • Reality会与目标服务器重现真实的TLS 1.3握手——目标服务器必须支持TLS 1.3 + H2 + X25519,否则Reality无法正常工作
  • RealiTLScanner(来自XTLS项目)会精确检查这些条件——仅输出与Reality兼容的服务器
  • DPI会在TLS ClientHello中查看SNI,并可探测IP以验证域名是否确实存在于该IP上
  • 热门域名(如microsoft.com、google.com)通常位于远离VPS的CDN IP上——主动探测会发现这一点
  • 同一子网中的小型未知站点(例如
    shop.finn-auto.fi
    )是理想选择——无人会过滤此类站点,且与VPS位于同一子网
  • 请勿手动选择SNI而不使用扫描器——随机域名可能不支持TLS 1.3,或位于不同的IP段
下载并运行Reality扫描器对/24子网进行扫描:
远程模式(Claude Code在用户笔记本上):
bash
ssh {nickname} 'ARCH=$(dpkg --print-architecture); case "$ARCH" in amd64) SA="64";; arm64|aarch64) SA="arm64-v8a";; *) SA="$ARCH";; esac && curl -sL "https://github.com/XTLS/RealiTLScanner/releases/latest/download/RealiTLScanner-linux-${SA}" -o /tmp/scanner && chmod +x /tmp/scanner && file /tmp/scanner | grep -q ELF || { echo "错误:扫描器二进制文件与当前架构不兼容"; exit 1; }; MY_IP=$(curl -4 -s ifconfig.me); SUBNET=$(echo $MY_IP | sed "s/\.[0-9]*$/.0\/24/"); echo "正在扫描子网: $SUBNET"; timeout 120 /tmp/scanner --addr "$SUBNET" 2>&1 | head -80'
本地模式(Claude Code在VPS上):
bash
ARCH=$(dpkg --print-architecture); case "$ARCH" in amd64) SA="64";; arm64|aarch64) SA="arm64-v8a";; *) SA="$ARCH";; esac && curl -sL "https://github.com/XTLS/RealiTLScanner/releases/latest/download/RealiTLScanner-linux-${SA}" -o /tmp/scanner && chmod +x /tmp/scanner && file /tmp/scanner | grep -q ELF || { echo "错误:扫描器二进制文件与当前架构不兼容"; exit 1; }; MY_IP=$(curl -4 -s ifconfig.me); SUBNET=$(echo $MY_IP | sed "s/\.[0-9]*$/.0\/24/"); echo "正在扫描子网: $SUBNET"; timeout 120 /tmp/scanner --addr "$SUBNET" 2>&1 | head -80
注意: 两种模式的命令完全相同——本地模式仅需去掉
ssh {nickname}
包装,因为Claude Code已在VPS上运行。GitHub发布使用非标准架构名称(
64
代替
amd64
arm64-v8a
代替
arm64
)。
case
块用于映射这些名称。
file | grep ELF
检查确保下载的是真实的二进制文件,而非404 HTML页面。超时设置为120秒,因为扫描254个IP所需时间较长。

Choosing the best SNI from scan results

从扫描结果中选择最佳SNI

Every domain in the scanner output already supports TLS 1.3 + H2 + X25519 (the scanner filters for this). From those results, prefer domains in this order:
  1. Small unknown sites on neighboring IPs (e.g.,
    shop.finn-auto.fi
    ,
    portal.company.de
    ) -- ideal, not filtered by DPI
  2. Regional/niche services (e.g., local hosting panels, small business sites) -- low profile
  3. Well-known tech sites (e.g.,
    github.com
    ,
    twitch.tv
    ) -- acceptable but less ideal
AVOID these as SNI:
  • www.google.com
    ,
    www.microsoft.com
    ,
    googletagmanager.com
    -- commonly blacklisted by DPI, people in Amnezia chats report these stop working
  • Any domain behind a CDN (Cloudflare, Akamai, Fastly) -- the IP won't match the CDN edge, active probing detects this
  • Domains that resolve to a completely different IP range than the VPS
How to verify a candidate SNI: The scanner output shows which IP responded with which domain. Pick a domain where the responding IP is in the same /24 as the VPS.
If scanner finds nothing or times out -- some providers (e.g., OVH) have sparse subnets. Try scanning a wider range
/23
(512 IPs):
Remote mode:
bash
ssh {nickname} 'MY_IP=$(curl -4 -s ifconfig.me); SUBNET=$(echo $MY_IP | sed "s/\.[0-9]*$/.0\/23/"); timeout 180 /tmp/scanner --addr "$SUBNET" 2>&1 | head -80'
Local mode:
bash
MY_IP=$(curl -4 -s ifconfig.me); SUBNET=$(echo $MY_IP | sed "s/\.[0-9]*$/.0\/23/"); timeout 180 /tmp/scanner --addr "$SUBNET" 2>&1 | head -80
If still nothing, use
www.yahoo.com
as a last-resort fallback -- it supports TLS 1.3 and resolves to many IPs globally, and is less commonly filtered than google/microsoft. But always prefer a real neighbor from the scan -- a neighbor is guaranteed to be in the same subnet and verified by the scanner for TLS 1.3 + H2 + X25519 compatibility.
Save the best SNI for the next step.
扫描结果中的每个域名均已支持TLS 1.3 + H2 + X25519(扫描器已过滤)。从这些结果中,优先选择以下顺序的域名:
  1. 相邻IP上的小型未知站点(例如
    shop.finn-auto.fi
    portal.company.de
    )——理想选择,不会被DPI过滤
  2. 区域/ niche服务(例如本地主机面板、小型企业网站)——低关注度
  3. 知名科技站点(例如
    github.com
    twitch.tv
    )——可接受但并非最佳选择
避免选择以下SNI:
  • www.google.com
    www.microsoft.com
    googletagmanager.com
    ——常被DPI列入黑名单,Amnezia聊天用户反馈这些域名会失效
  • 任何位于CDN后的域名(Cloudflare、Akamai、Fastly)——IP与CDN边缘不匹配,主动探测会发现这一点
  • 解析到与VPS完全不同IP段的域名
如何验证候选SNI: 扫描结果会显示哪个IP响应了哪个域名。选择与VPS位于同一/24子网的域名。
若扫描器未找到结果或超时——部分服务商(例如OVH)的子网较为稀疏。尝试扫描更广泛的范围
/23
(512个IP):
远程模式:
bash
ssh {nickname} 'MY_IP=$(curl -4 -s ifconfig.me); SUBNET=$(echo $MY_IP | sed "s/\.[0-9]*$/.0\/23/"); timeout 180 /tmp/scanner --addr "$SUBNET" 2>&1 | head -80'
本地模式:
bash
MY_IP=$(curl -4 -s ifconfig.me); SUBNET=$(echo $MY_IP | sed "s/\.[0-9]*$/.0\/23/"); timeout 180 /tmp/scanner --addr "$SUBNET" 2>&1 | head -80
若仍未找到结果,可使用
www.yahoo.com
作为最后的备选——它支持TLS 1.3,且在全球拥有多个解析IP,相比谷歌/微软域名被过滤的概率更低。但始终优先选择扫描到的真实相邻站点——相邻站点确保与VPS位于同一子网,且已被扫描器验证支持TLS 1.3 + H2 + X25519。
保存最佳SNI以备下一步使用。

Step 18A: Create VLESS Reality Inbound via API

步骤18A:通过API创建VLESS Reality入站规则

Pre-check: Verify port 443 is not occupied by another service (some providers pre-install apache2/nginx):
bash
ssh {nickname} "ss -tlnp | grep ':443 '"
If something is listening on 443, stop and disable it first (e.g.,
sudo systemctl stop apache2 && sudo systemctl disable apache2
). Otherwise the VLESS inbound will silently fail to bind.
3x-ui has an API. Since v2.8+, the installer auto-configures SSL, so the panel runs on HTTPS. Use
-k
to skip certificate verification (self-signed cert on localhost).
First, get session cookie:
bash
ssh {nickname} 'PANEL_PORT={panel_port}; curl -sk -c /tmp/3x-cookie -b /tmp/3x-cookie -X POST "https://127.0.0.1:${PANEL_PORT}/{web_base_path}/login" -H "Content-Type: application/x-www-form-urlencoded" -d "username={panel_username}&password={panel_password}"'
Generate keys for Reality:
bash
ssh {nickname} "sudo /usr/local/x-ui/bin/xray-linux-* x25519"
This outputs two lines:
PrivateKey
= private key,
Password
= public key (confusing naming by xray). Save both.
Generate UUID for the client:
bash
ssh {nickname} "sudo /usr/local/x-ui/bin/xray-linux-* uuid"
Generate random Short ID:
bash
ssh {nickname} "openssl rand -hex 8"
Create the inbound:
bash
ssh {nickname} 'PANEL_PORT={panel_port}; curl -sk -c /tmp/3x-cookie -b /tmp/3x-cookie -X POST "https://127.0.0.1:${PANEL_PORT}/{web_base_path}/panel/api/inbounds/add" -H "Content-Type: application/json" -d '"'"'{
  "up": 0,
  "down": 0,
  "total": 0,
  "remark": "vless-reality",
  "enable": true,
  "expiryTime": 0,
  "listen": "",
  "port": 443,
  "protocol": "vless",
  "settings": "{\"clients\":[{\"id\":\"{CLIENT_UUID}\",\"flow\":\"xtls-rprx-vision\",\"email\":\"user1\",\"limitIp\":0,\"totalGB\":0,\"expiryTime\":0,\"enable\":true}],\"decryption\":\"none\",\"fallbacks\":[]}",
  "streamSettings": "{\"network\":\"tcp\",\"security\":\"reality\",\"externalProxy\":[],\"realitySettings\":{\"show\":false,\"xver\":0,\"dest\":\"{BEST_SNI}:443\",\"serverNames\":[\"{BEST_SNI}\"],\"privateKey\":\"{PRIVATE_KEY}\",\"minClient\":\"\",\"maxClient\":\"\",\"maxTimediff\":0,\"shortIds\":[\"{SHORT_ID}\"],\"settings\":{\"publicKey\":\"{PUBLIC_KEY}\",\"fingerprint\":\"chrome\",\"serverName\":\"\",\"spiderX\":\"/\"}},\"tcpSettings\":{\"acceptProxyProtocol\":false,\"header\":{\"type\":\"none\"}}}",
  "sniffing": "{\"enabled\":true,\"destOverride\":[\"http\",\"tls\",\"quic\",\"fakedns\"],\"metadataOnly\":false,\"routeOnly\":false}",
  "allocate": "{\"strategy\":\"always\",\"refresh\":5,\"concurrency\":3}"
}'"'"''
If API approach fails -- tell user to access panel via SSH tunnel (Step 18A-alt).
预检查: 验证端口443未被其他服务占用(部分服务商预装了apache2/nginx):
bash
ssh {nickname} "ss -tlnp | grep ':443 '"
若443端口被占用,先停止并禁用该服务(例如
sudo systemctl stop apache2 && sudo systemctl disable apache2
)。否则VLESS入站规则将静默绑定失败。
3x-ui提供API。从v2.8+版本开始,安装程序会自动配置SSL,因此面板运行在HTTPS上。使用
-k
跳过证书验证(本地主机使用自签名证书)。
首先,获取会话cookie:
bash
ssh {nickname} 'PANEL_PORT={panel_port}; curl -sk -c /tmp/3x-cookie -b /tmp/3x-cookie -X POST "https://127.0.0.1:${PANEL_PORT}/{web_base_path}/login" -H "Content-Type: application/x-www-form-urlencoded" -d "username={panel_username}&password={panel_password}"'
生成Reality密钥:
bash
ssh {nickname} "sudo /usr/local/x-ui/bin/xray-linux-* x25519"
输出包含两行:
PrivateKey
= 私钥,
Password
= 公钥(xray的命名方式易混淆)。保存这两个值。
为客户端生成UUID:
bash
ssh {nickname} "sudo /usr/local/x-ui/bin/xray-linux-* uuid"
生成随机Short ID:
bash
ssh {nickname} "openssl rand -hex 8"
创建入站规则:
bash
ssh {nickname} 'PANEL_PORT={panel_port}; curl -sk -c /tmp/3x-cookie -b /tmp/3x-cookie -X POST "https://127.0.0.1:${PANEL_PORT}/{web_base_path}/panel/api/inbounds/add" -H "Content-Type: application/json" -d '"'"'{
  "up": 0,
  "down": 0,
  "total": 0,
  "remark": "vless-reality",
  "enable": true,
  "expiryTime": 0,
  "listen": "",
  "port": 443,
  "protocol": "vless",
  "settings": "{\"clients\":[{\"id\":\"{CLIENT_UUID}\",\"flow\":\"xtls-rprx-vision\",\"email\":\"user1\",\"limitIp\":0,\"totalGB\":0,\"expiryTime\":0,\"enable\":true}],\"decryption\":\"none\",\"fallbacks\":[]}",
  "streamSettings": "{\"network\":\"tcp\",\"security\":\"reality\",\"externalProxy\":[],\"realitySettings\":{\"show\":false,\"xver\":0,\"dest\":\"{BEST_SNI}:443\",\"serverNames\":[\"{BEST_SNI}\"],\"privateKey\":\"{PRIVATE_KEY}\",\"minClient\":\"\",\"maxClient\":\"\",\"maxTimediff\":0,\"shortIds\":[\"{SHORT_ID}\"],\"settings\":{\"publicKey\":\"{PUBLIC_KEY}\",\"fingerprint\":\"chrome\",\"serverName\":\"\",\"spiderX\":\"/\"}},\"tcpSettings\":{\"acceptProxyProtocol\":false,\"header\":{\"type\":\"none\"}}}",
  "sniffing": "{\"enabled\":true,\"destOverride\":[\"http\",\"tls\",\"quic\",\"fakedns\"],\"metadataOnly\":false,\"routeOnly\":false}",
  "allocate": "{\"strategy\":\"always\",\"refresh\":5,\"concurrency\":3}"
}'"'"''
若API方法失败——告知用户通过SSH隧道访问面板(步骤18A-alt)。

Step 18A-alt: SSH Tunnel to Panel (manual fallback)

步骤18A-alt:通过SSH隧道访问面板(手动备选方案)

If API fails, user can access panel in browser:
bash
ssh -L {panel_port}:127.0.0.1:{panel_port} {nickname}
Then open in browser:
https://127.0.0.1:{panel_port}/{web_base_path}
(browser will warn about self-signed cert -- accept it)
Guide user through the UI:
  1. Login with generated credentials
  2. Inbounds -> Add Inbound
  3. Protocol: VLESS
  4. Port: 443
  5. Security: Reality
  6. Client Flow: xtls-rprx-vision
  7. Target & SNI: paste the best SNI from scanner
  8. Click "Get New Cert" for keys
  9. Create
若API失败,用户可在浏览器中访问面板:
bash
ssh -L {panel_port}:127.0.0.1:{panel_port} {nickname}
然后在浏览器中打开:
https://127.0.0.1:{panel_port}/{web_base_path}
(浏览器会警告自签名证书——接受即可)
引导用户完成UI操作:
  1. 使用生成的凭证登录
  2. 进入Inbounds(入站规则)→ Add Inbound(添加入站规则)
  3. 协议:VLESS
  4. 端口:443
  5. 安全:Reality
  6. 客户端流:xtls-rprx-vision
  7. 目标与SNI:粘贴扫描得到的最佳SNI
  8. 点击"Get New Cert"(获取新证书)生成密钥
  9. 创建

Step 19: Get Connection Link

步骤19:获取连接链接

Get the client connection link from 3x-ui API:
bash
ssh {nickname} 'PANEL_PORT={panel_port}; curl -sk -b /tmp/3x-cookie "https://127.0.0.1:${PANEL_PORT}/{web_base_path}/panel/api/inbounds/list" | python3 -c "
import json,sys
data = json.load(sys.stdin)
for inb in data.get(\"obj\", []):
    if inb.get(\"protocol\") == \"vless\":
        settings = json.loads(inb[\"settings\"])
        stream = json.loads(inb[\"streamSettings\"])
        client = settings[\"clients\"][0]
        uuid = client[\"id\"]
        port = inb[\"port\"]
        security = stream.get(\"security\", \"none\")
        if security == \"reality\":
            rs = stream[\"realitySettings\"]
            sni = rs[\"serverNames\"][0]
            pbk = rs[\"settings\"][\"publicKey\"]
            sid = rs[\"shortIds\"][0]
            fp = rs[\"settings\"].get(\"fingerprint\", \"chrome\")
            flow = client.get(\"flow\", \"\")
            link = f\"vless://{uuid}@$(curl -4 -s ifconfig.me):{port}?type=tcp&security=reality&pbk={pbk}&fp={fp}&sni={sni}&sid={sid}&spx=%2F&flow={flow}#vless-reality\"
            print(link)
            break
"'
Show the link to the user. This is what they'll paste into Hiddify.
IMPORTANT: Terminal line-wrap fix. Long VLESS links break when copied from terminal. ALWAYS provide the link in TWO formats:
  1. The raw link (for reference)
  2. A ready-to-copy block with LLM cleanup prompt:
Скопируй всё ниже и вставь в любой LLM (ChatGPT, Claude) чтобы получить чистую ссылку:

Убери все переносы строк и лишние пробелы из этой ссылки, выдай одной строкой:

{VLESS_LINK}
Also save the link to a file for easy access:
bash
ssh {nickname} "echo '{VLESS_LINK}' > ~/vpn-link.txt"
Tell the user: Ссылка также сохранена в файл ~/vpn-link.txt
Cleanup session cookie:
bash
ssh {nickname} "rm -f /tmp/3x-cookie"
从3x-ui API获取客户端连接链接:
bash
ssh {nickname} 'PANEL_PORT={panel_port}; curl -sk -b /tmp/3x-cookie "https://127.0.0.1:${PANEL_PORT}/{web_base_path}/panel/api/inbounds/list" | python3 -c "
import json,sys
data = json.load(sys.stdin)
for inb in data.get(\"obj\", []):
    if inb.get(\"protocol\") == \"vless\":
        settings = json.loads(inb[\"settings\"])
        stream = json.loads(inb[\"streamSettings\"])
        client = settings[\"clients\"][0]
        uuid = client[\"id\"]
        port = inb[\"port\"]
        security = stream.get(\"security\", \"none\")
        if security == \"reality\":
            rs = stream[\"realitySettings\"]
            sni = rs[\"serverNames\"][0]
            pbk = rs[\"settings\"][\"publicKey\"]
            sid = rs[\"shortIds\"][0]
            fp = rs[\"settings\"].get(\"fingerprint\", \"chrome\")
            flow = client.get(\"flow\", \"\")
            link = f\"vless://{uuid}@$(curl -4 -s ifconfig.me):{port}?type=tcp&security=reality&pbk={pbk}&fp={fp}&sni={sni}&sid={sid}&spx=%2F&flow={flow}#vless-reality\"
            print(link)
            break
"'
将链接展示给用户。用户需要将此链接粘贴到Hiddify客户端中。
重要提示:修复终端换行问题。长VLESS链接从终端复制时会断裂。请始终以两种格式提供链接:
  1. 原始链接(仅供参考)
  2. 可直接复制的块,附带LLM清理提示:
复制以下所有内容并粘贴到任意LLM(ChatGPT、Claude)中以获取干净的链接:

请移除该链接中的所有换行符和多余空格,以单行形式输出:

{VLESS_LINK}
同时将链接保存到文件中以便访问:
bash
ssh {nickname} "echo '{VLESS_LINK}' > ~/vpn-link.txt"
告知用户:链接已保存到文件~/vpn-link.txt中
清理会话cookie:
bash
ssh {nickname} "rm -f /tmp/3x-cookie"

Step 20: Guide User -- Install Hiddify Client

步骤20:引导用户安装Hiddify客户端

Tell the user:
Теперь установи клиент Hiddify на своё устройство:

Android:  Google Play -> "Hiddify" или https://github.com/hiddify/hiddify-app/releases
iOS:      App Store -> "Hiddify"
Windows:  https://github.com/hiddify/hiddify-app/releases (скачай .exe)
macOS:    https://github.com/hiddify/hiddify-app/releases (скачай .dmg)
Linux:    https://github.com/hiddify/hiddify-app/releases (.deb или .AppImage)

После установки:
1. Открой Hiddify
2. Нажми "+" или "Add Profile"
3. Выбери "Add from clipboard" (ссылка уже скопирована)
4. Или отсканируй QR-код (я могу его показать)
5. Нажми кнопку подключения (большая кнопка в центре)
6. Готово! Проверь IP на сайте: https://2ip.ru
告知用户:
现在请在你的设备上安装Hiddify客户端:

Android:  Google Play搜索"Hiddify",或访问https://github.com/hiddify/hiddify-app/releases
iOS:      App Store搜索"Hiddify"
Windows:  访问https://github.com/hiddify/hiddify-app/releases(下载.exe文件)
macOS:    访问https://github.com/hiddify/hiddify-app/releases(下载.dmg文件)
Linux:    访问https://github.com/hiddify/hiddify-app/releases(下载.deb或.AppImage文件)

安装完成后:
1. 打开Hiddify
2. 点击"+"或"Add Profile"(添加配置文件)
3. 选择"Add from clipboard"(从剪贴板添加)(链接已复制)
4. 或扫描QR码(我可以为你展示)
5. 点击连接按钮(中央的大按钮)
6. 完成!访问https://2ip.ru验证IP

Step 21: Verify Connection Works

步骤21:验证连接是否正常工作

After user connects via Hiddify, verify:
bash
ssh {nickname} "sudo x-ui status && ss -tlnp | grep -E '443|{panel_port}'"
用户通过Hiddify连接后,进行验证:
bash
ssh {nickname} "sudo x-ui status && ss -tlnp | grep -E '443|{panel_port}'"

Step 22: Generate Guide File & Finalize SSH Access

步骤22:生成指南文件并完成SSH访问配置

This step generates a comprehensive guide file with all credentials and instructions, then finalizes SSH key-based access.
本步骤将生成包含所有凭证和说明的综合指南文件,然后完成基于SSH密钥的访问配置。

Remote Mode

远程模式

22R-1: Generate guide file locally
Use the Write tool to create
~/vpn-{nickname}-guide.md
on the user's local machine. Use the Guide File Template below, substituting all
{variables}
with actual values.
Tell user: Методичка сохранена в ~/vpn-{nickname}-guide.md — там все пароли, доступы и инструкции.
22R-2: Final lockdown — fail2ban + SSH
Verify SSH key access works:
bash
ssh {nickname} "echo 'SSH key access OK'"
If successful, install fail2ban and lock SSH:
bash
ssh {nickname} 'sudo apt install -y fail2ban && sudo tee /etc/fail2ban/jail.local << JAILEOF
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5

[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 24h
JAILEOF
sudo systemctl enable fail2ban && sudo systemctl restart fail2ban'
bash
ssh {nickname} 'sudo sed -i "s/^#\?PermitRootLogin.*/PermitRootLogin no/" /etc/ssh/sshd_config && sudo sed -i "s/^#\?PasswordAuthentication.*/PasswordAuthentication no/" /etc/ssh/sshd_config && sudo systemctl restart sshd'
Verify lockdown + SSH still works:
bash
ssh {nickname} "grep -E 'PermitRootLogin|PasswordAuthentication' /etc/ssh/sshd_config && sudo systemctl status fail2ban --no-pager -l && echo 'Lockdown OK'"
22R-1:在本地生成指南文件
使用写入工具在用户本地电脑创建
~/vpn-{nickname}-guide.md
。使用以下指南文件模板,将所有
{variables}
替换为实际值。
告知用户:指南文件已保存到~/vpn-{nickname}-guide.md中——其中包含所有密码、访问信息和操作说明。
22R-2:最终锁定——安装fail2ban并配置SSH
验证SSH密钥访问是否正常:
bash
ssh {nickname} "echo 'SSH密钥访问正常'"
若验证成功,安装fail2ban并锁定SSH:
bash
ssh {nickname} 'sudo apt install -y fail2ban && sudo tee /etc/fail2ban/jail.local << JAILEOF
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5

[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 24h
JAILEOF
sudo systemctl enable fail2ban && sudo systemctl restart fail2ban'
bash
ssh {nickname} 'sudo sed -i "s/^#\?PermitRootLogin.*/PermitRootLogin no/" /etc/ssh/sshd_config && sudo sed -i "s/^#\?PasswordAuthentication.*/PasswordAuthentication no/" /etc/ssh/sshd_config && sudo systemctl restart sshd'
验证锁定配置 + SSH是否仍正常工作:
bash
ssh {nickname} "grep -E 'PermitRootLogin|PasswordAuthentication' /etc/ssh/sshd_config && sudo systemctl status fail2ban --no-pager -l && echo '锁定配置正常'"

Local Mode

本地模式

In Local mode, Claude Code runs on the server. SSH lockdown was skipped (Step 7), so password auth still works. The flow:
本地模式下,Claude Code运行在服务器上。SSH锁定已被跳过(步骤7),因此密码认证仍可用。流程如下:

22L-1: Generate guide file on server

22L-1:在服务器上生成指南文件

Use the Write tool to create
/home/{username}/vpn-guide.md
on the server. Use the Guide File Template below, substituting all
{variables}
with actual values.
使用写入工具在服务器上创建
/home/{username}/vpn-guide.md
。使用以下指南文件模板,将所有
{variables}
替换为实际值。

22L-2: User downloads guide via SCP

22L-2:用户通过SCP下载指南文件

Tell the user:
Методичка готова! Скачай её на свой компьютер.
Открой НОВЫЙ терминал на своём ноутбуке и выполни:

scp {username}@{SERVER_IP}:~/vpn-guide.md ./

Пароль: {sudo_password}

Файл сохранится в текущую папку. Открой его -- там все пароли и инструкции.
Fallback: If SCP doesn't work (Windows without OpenSSH, network issues), show the full guide content directly in chat.
告知用户:
指南文件已准备好!请将其下载到你的电脑上。
在你的笔记本电脑上打开一个**新终端**并执行:

scp {username}@{SERVER_IP}:~/vpn-guide.md ./

密码:{sudo_password}

文件将保存到当前目录。打开文件——其中包含所有密码和操作说明。
备选方案: 若SCP无法工作(Windows未安装OpenSSH、网络问题),直接在聊天中展示完整的指南内容。

22L-3: User creates SSH key on their laptop

22L-3:用户在笔记本电脑上创建SSH密钥

Tell the user:
Теперь создай SSH-ключ на своём компьютере.
Есть два варианта:

Вариант А: Следуй инструкциям из раздела "SSH Key Setup" в методичке.

Вариант Б (автоматический): Установи Claude Code на ноутбуке
  (https://claude.ai/download) и скинь ему файл vpn-guide.md --
  он сам всё настроит по инструкциям из раздела "Instructions for Claude Code".

После создания ключа отправь публичный ключ на сервер (следующий шаг).
告知用户:
现在请在你的电脑上创建SSH密钥。
有两种选择:

选项A:按照指南文件中"SSH密钥配置"部分的说明操作。

选项B(自动方式):在笔记本电脑上安装Claude Code
  (https://claude.ai/download),然后将vpn-guide.md文件发送给它——
  它会根据指南文件中"Claude Code操作说明"部分自动完成所有配置。

创建密钥后,将公钥发送到服务器(下一步)。

22L-4: User sends public key to server via SCP

22L-4:用户通过SCP将公钥发送到服务器

Tell the user:
Отправь публичный ключ на сервер (из терминала на ноутбуке):

scp ~/.ssh/{nickname}_key.pub {username}@{SERVER_IP}:~/

Пароль: {sudo_password}
Wait for user confirmation before proceeding.
告知用户:
将公钥发送到服务器(在笔记本电脑的终端中执行):

scp ~/.ssh/{nickname}_key.pub {username}@{SERVER_IP}:~/

密码:{sudo_password}
等待用户确认后再继续。

22L-5: Install key + verify

22L-5:安装密钥 + 验证

bash
mkdir -p /home/{username}/.ssh
cat /home/{username}/{nickname}_key.pub >> /home/{username}/.ssh/authorized_keys
chmod 700 /home/{username}/.ssh
chmod 600 /home/{username}/.ssh/authorized_keys
chown -R {username}:{username} /home/{username}/.ssh
rm -f /home/{username}/{nickname}_key.pub
Tell user to test from their laptop:
Проверь подключение с ноутбука:
ssh -i ~/.ssh/{nickname}_key {username}@{SERVER_IP}

Должно подключиться без пароля.
Wait for user confirmation that SSH key works before proceeding!
bash
mkdir -p /home/{username}/.ssh
cat /home/{username}/{nickname}_key.pub >> /home/{username}/.ssh/authorized_keys
chmod 700 /home/{username}/.ssh
chmod 600 /home/{username}/.ssh/authorized_keys
chown -R {username}:{username} /home/{username}/.ssh
rm -f /home/{username}/{nickname}_key.pub
告知用户在笔记本电脑上测试:
验证从笔记本电脑的连接:
ssh -i ~/.ssh/{nickname}_key {username}@{SERVER_IP}

应无需输入密码即可连接。
等待用户确认SSH密钥正常工作后再继续!

22L-6: Final lockdown — fail2ban + SSH

22L-6:最终锁定——安装fail2ban并配置SSH

Only after user confirms key-based login works!
Install fail2ban:
bash
sudo apt install -y fail2ban
sudo tee /etc/fail2ban/jail.local << 'EOF'
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5

[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 24h
EOF
sudo systemctl enable fail2ban
sudo systemctl restart fail2ban
Lock SSH:
bash
sudo sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
sudo sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo systemctl restart sshd
Verify:
bash
grep -E "PermitRootLogin|PasswordAuthentication" /etc/ssh/sshd_config
sudo systemctl status fail2ban --no-pager
Expected:
PermitRootLogin no
,
PasswordAuthentication no
, fail2ban active.
Tell user to verify SSH still works from laptop:
Проверь, что SSH-ключ всё ещё работает:
ssh {nickname}
Если подключился — всё настроено!
仅在用户确认基于密钥的登录正常工作后执行!
安装fail2ban:
bash
sudo apt install -y fail2ban
sudo tee /etc/fail2ban/jail.local << 'EOF'
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5

[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 24h
EOF
sudo systemctl enable fail2ban
sudo systemctl restart fail2ban
锁定SSH:
bash
sudo sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
sudo sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo systemctl restart sshd
验证:
bash
grep -E "PermitRootLogin|PasswordAuthentication" /etc/ssh/sshd_config
sudo systemctl status fail2ban --no-pager
预期结果:
PermitRootLogin no
PasswordAuthentication no
,fail2ban处于活跃状态。
告知用户在笔记本电脑上验证SSH是否仍正常工作:
验证SSH密钥是否仍正常工作:
ssh {nickname}
若能连接——则所有配置已完成!

22L-7: User configures SSH config

22L-7:用户配置SSH配置

Tell the user:
Последний шаг! Добавь на ноутбуке в файл ~/.ssh/config:

Host {nickname}
    HostName {SERVER_IP}
    User {username}
    IdentityFile ~/.ssh/{nickname}_key
    IdentitiesOnly yes

Теперь подключайся просто: ssh {nickname}
告知用户:
最后一步!在笔记本电脑的~/.ssh/config文件中添加以下内容:

Host {nickname}
    HostName {SERVER_IP}
    User {username}
    IdentityFile ~/.ssh/{nickname}_key
    IdentitiesOnly yes

现在你可以使用简单的命令连接:ssh {nickname}

22L-8: Delete guide file from server

22L-8:从服务器删除指南文件

bash
rm -f /home/{username}/vpn-guide.md
Tell user: Методичка удалена с сервера. Убедись, что она сохранена на твоём компьютере.

bash
rm -f /home/{username}/vpn-guide.md
告知用户:指南文件已从服务器删除。请确保已将其保存到你的电脑上。

Guide File Template

指南文件模板

Generate this file using the Write tool, substituting all
{variables}
with actual values collected during setup.
markdown
undefined
使用写入工具生成此文件,将所有
{variables}
替换为配置过程中收集的实际值。
markdown
undefined

Методичка VPN-сервера — {nickname}

VPN服务器指南 —— {nickname}

Дата создания: {current_date}
创建日期:{current_date}

1. Подключение к серверу

1. 服务器连接

ПараметрЗначение
IP
{SERVER_IP}
Пользователь
{username}
Пароль sudo
{sudo_password}
SSH-ключ
~/.ssh/{nickname}_key
Быстрое подключение
ssh {nickname}
参数
IP
{SERVER_IP}
用户
{username}
sudo密码
{sudo_password}
SSH密钥
~/.ssh/{nickname}_key
快速连接命令
ssh {nickname}

2. Панель 3x-ui

2. 3x-ui面板

ПараметрЗначение
URL
https://127.0.0.1:{panel_port}/{web_base_path}
Логин
{panel_username}
Пароль
{panel_password}
Доступ через SSH-туннель:
ssh -L {panel_port}:127.0.0.1:{panel_port} {nickname}
Затем открой:
https://127.0.0.1:{panel_port}/{web_base_path}
参数
URL
https://127.0.0.1:{panel_port}/{web_base_path}
登录账号
{panel_username}
密码
{panel_password}
通过SSH隧道访问:
ssh -L {panel_port}:127.0.0.1:{panel_port} {nickname}
然后打开:
https://127.0.0.1:{panel_port}/{web_base_path}

3. VPN-подключение

3. VPN连接

ПараметрЗначение
ПротоколVLESS Reality
Порт443
SNI
{best_sni}
КлиентHiddify
Ссылка VLESS:
{VLESS_LINK}
参数
协议VLESS Reality
端口443
SNI
{best_sni}
客户端Hiddify
VLESS链接:
{VLESS_LINK}

4. Настройка SSH-ключа

4. SSH密钥配置

Если у тебя ещё нет SSH-ключа, следуй инструкциям для своей ОС:
若你尚未拥有SSH密钥,请根据你的操作系统遵循以下说明:

macOS / Linux

macOS / Linux

bash
undefined
bash
undefined

Создать ключ

创建密钥

ssh-keygen -t ed25519 -C "{username}@{nickname}" -f ~/.ssh/{nickname}_key -N ""
ssh-keygen -t ed25519 -C "{username}@{nickname}" -f ~/.ssh/{nickname}_key -N ""

Отправить публичный ключ на сервер

将公钥发送到服务器

scp /.ssh/{nickname}_key.pub {username}@{SERVER_IP}:/
scp /.ssh/{nickname}_key.pub {username}@{SERVER_IP}:/

Установить права

设置权限

chmod 600 ~/.ssh/{nickname}_key
chmod 600 ~/.ssh/{nickname}_key

Добавить в SSH-конфиг

添加到SSH配置

cat >> ~/.ssh/config << 'SSHEOF'
Host {nickname} HostName {SERVER_IP} User {username} IdentityFile ~/.ssh/{nickname}_key IdentitiesOnly yes SSHEOF
cat >> ~/.ssh/config << 'SSHEOF'
Host {nickname} HostName {SERVER_IP} User {username} IdentityFile ~/.ssh/{nickname}_key IdentitiesOnly yes SSHEOF

Проверить подключение

验证连接

ssh {nickname}
undefined
ssh {nickname}
undefined

Windows (PowerShell)

Windows(PowerShell)

powershell
undefined
powershell
undefined

Создать ключ

创建密钥

ssh-keygen -t ed25519 -C "{username}@{nickname}" -f $HOME.ssh{nickname}_key -N '""'
ssh-keygen -t ed25519 -C "{username}@{nickname}" -f $HOME.ssh{nickname}_key -N '""'

Отправить публичный ключ на сервер

将公钥发送到服务器

scp $HOME.ssh{nickname}_key.pub {username}@{SERVER_IP}:~/
scp $HOME.ssh{nickname}_key.pub {username}@{SERVER_IP}:~/

Добавить в SSH-конфиг

添加到SSH配置

Add-Content $HOME.ssh\config @"
Host {nickname} HostName {SERVER_IP} User {username} IdentityFile ~/.ssh/{nickname}_key IdentitiesOnly yes "@
Add-Content $HOME.ssh\config @"
Host {nickname} HostName {SERVER_IP} User {username} IdentityFile ~/.ssh/{nickname}_key IdentitiesOnly yes "@

Проверить подключение

验证连接

ssh {nickname}

Примечание: `chmod` не нужен на Windows. SSH использует ACL автоматически.
ssh {nickname}

注意:Windows无需使用`chmod`。SSH会自动使用ACL。

5. Частые команды

5. 常用命令

bash
ssh {nickname}                           # подключиться к серверу
ssh {nickname} "sudo x-ui status"        # статус панели
ssh {nickname} "sudo x-ui restart"       # перезапустить панель
ssh {nickname} "sudo x-ui log"           # логи
ssh {nickname} "sudo x-ui setting -reset" # сбросить пароль панели
Добавить нового VPN-клиента: Открой панель через SSH-туннель → Inbounds → ... → Add Client → отправь ссылку/QR.
bash
ssh {nickname}                           # 连接服务器
ssh {nickname} "sudo x-ui status"        # 查看面板状态
ssh {nickname} "sudo x-ui restart"       # 重启面板
ssh {nickname} "sudo x-ui log"           # 查看日志
ssh {nickname} "sudo x-ui setting -reset" # 重置面板密码
添加新VPN客户端: 通过SSH隧道打开面板 → Inbounds(入站规则)→ ... → Add Client(添加客户端)→ 发送链接/QR码。

6. Статус безопасности

6. 安全状态

ПараметрСтатус
Вход под rootОтключён
Вход по паролюОтключён
Файрвол UFWВключён (SSH, 80, 443)
fail2banВключён (3 попытки → бан 24ч)
Усиление ядраВключено (sysctl)
BBRВключён
ICMP (ping)Отключён
参数状态
Root登录已禁用
密码登录已禁用
UFW防火墙已启用(开放SSH、80、443端口)
fail2ban已启用(3次失败尝试 → 封禁24小时)
内核加固已启用(sysctl)
BBR已启用
ICMP(ping)已禁用

7. Решение проблем

7. 故障排除

ПроблемаРешение
Connection refused
ssh {nickname} "sudo x-ui status"
— перезапусти если остановлен
Permission denied (publickey)Проверь путь и права ключа:
ls -la ~/.ssh/{nickname}_key
Host key verification failed
ssh-keygen -R {SERVER_IP}
и переподключись
Панель недоступнаИспользуй SSH-туннель (см. раздел 2)
VPN не подключаетсяНеверный SNI или сервер лежит — проверь
sudo x-ui log
Забыл пароль панели
ssh {nickname} "sudo x-ui setting -reset"
问题解决方案
Connection refused(连接被拒绝)
ssh {nickname} "sudo x-ui status"
—— 若已停止则重启
Permission denied (publickey)(公钥权限被拒绝)检查密钥路径和权限:
ls -la ~/.ssh/{nickname}_key
Host key verification failed(主机密钥验证失败)执行
ssh-keygen -R {SERVER_IP}
然后重新连接
面板无法访问使用SSH隧道(参见第2部分)
VPN无法连接SNI错误——重新运行扫描器,尝试其他域名
Hiddify显示错误将Hiddify更新到最新版本,重新添加链接
"connection refused"(连接被拒绝)检查x-ui是否运行:
sudo x-ui status
忘记面板密码执行
ssh {nickname} "sudo x-ui setting -reset"

8. Инструкции для Claude Code

8. Claude Code操作说明

Этот раздел для автоматической настройки через Claude Code на твоём ноутбуке. Скопируй всё ниже и вставь в Claude Code на ноутбуке.

ЗАДАЧА: Настроить SSH-ключ и завершить настройку безопасности VPN-сервера
Данные сервера:
  • SERVER_IP: {SERVER_IP}
  • USERNAME: {username}
  • PASSWORD: {sudo_password}
  • NICKNAME: {nickname}
Шаги:
Часть А: Установка sshpass (нужен для автоматической передачи пароля)
  1. Определи ОС и установи sshpass:
    • macOS:
      brew install hudochenkov/sshpass/sshpass
    • Linux (Debian/Ubuntu):
      sudo apt install -y sshpass
Часть Б: Создание и установка SSH-ключа 2. Создай SSH-ключ:
ssh-keygen -t ed25519 -C "{username}@{nickname}" -f ~/.ssh/{nickname}_key -N ""
3. Установи ключ на сервере одной командой:
sshpass -p '{sudo_password}' ssh-copy-id -i ~/.ssh/{nickname}_key.pub -o StrictHostKeyChecking=accept-new {username}@{SERVER_IP}
  1. Проверь вход по ключу (должен работать БЕЗ пароля):
    ssh -i ~/.ssh/{nickname}_key {username}@{SERVER_IP} "echo 'SSH key OK'"
  2. Добавь SSH-конфиг:
    cat >> ~/.ssh/config << 'EOF'
    
    Host {nickname}
        HostName {SERVER_IP}
        User {username}
        IdentityFile ~/.ssh/{nickname}_key
        IdentitiesOnly yes
    EOF
  3. Проверь подключение через конфиг:
    ssh {nickname} "echo 'Config OK'"
Часть В: Финальная защита сервера (fail2ban + блокировка пароля) 7. Установи fail2ban:
ssh {nickname} 'sudo apt install -y fail2ban && sudo tee /etc/fail2ban/jail.local << JAILEOF
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5

[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 24h
JAILEOF
sudo systemctl enable fail2ban && sudo systemctl restart fail2ban'
  1. Заблокируй парольный вход и root:
    ssh {nickname} 'sudo sed -i "s/^#\?PermitRootLogin.*/PermitRootLogin no/" /etc/ssh/sshd_config && sudo sed -i "s/^#\?PasswordAuthentication.*/PasswordAuthentication no/" /etc/ssh/sshd_config && sudo systemctl restart sshd'
  2. Проверь что SSH-ключ всё ещё работает после блокировки:
    ssh {nickname} "grep -E 'PermitRootLogin|PasswordAuthentication' /etc/ssh/sshd_config && sudo systemctl status fail2ban --no-pager && echo 'Сервер полностью защищён!'"
  3. Скажи пользователю: "Готово! SSH-ключ настроен, fail2ban установлен, парольный вход отключён. Подключайся: ssh {nickname}"

---
本部分用于通过笔记本电脑上的Claude Code自动完成配置。 复制以下所有内容并粘贴到笔记本电脑上的Claude Code中。

任务:配置SSH密钥并完成VPN服务器的安全配置
服务器信息:
  • SERVER_IP: {SERVER_IP}
  • USERNAME: {username}
  • PASSWORD: {sudo_password}
  • NICKNAME: {nickname}
步骤:
部分A:安装sshpass(用于自动传递密码)
  1. 确定操作系统并安装sshpass:
    • macOS:
      brew install hudochenkov/sshpass/sshpass
    • Linux(Debian/Ubuntu):
      sudo apt install -y sshpass
部分B:创建并安装SSH密钥 2. 创建SSH密钥:
ssh-keygen -t ed25519 -C "{username}@{nickname}" -f ~/.ssh/{nickname}_key -N ""
3. 一键在服务器上安装密钥:
sshpass -p '{sudo_password}' ssh-copy-id -i ~/.ssh/{nickname}_key.pub -o StrictHostKeyChecking=accept-new {username}@{SERVER_IP}
  1. 验证密钥登录是否正常(应无需输入密码):
    ssh -i ~/.ssh/{nickname}_key {username}@{SERVER_IP} "echo 'SSH密钥正常'"
  2. 添加SSH配置:
    cat >> ~/.ssh/config << 'EOF'
    
    Host {nickname}
        HostName {SERVER_IP}
        User {username}
        IdentityFile ~/.ssh/{nickname}_key
        IdentitiesOnly yes
    EOF
  3. 验证通过配置文件的连接:
    ssh {nickname} "echo '配置正常'"
部分C:服务器最终安全配置(fail2ban + 密码锁定) 7. 安装fail2ban:
ssh {nickname} 'sudo apt install -y fail2ban && sudo tee /etc/fail2ban/jail.local << JAILEOF
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5

[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 24h
JAILEOF
sudo systemctl enable fail2ban && sudo systemctl restart fail2ban'
  1. 禁用密码登录和root登录:
    ssh {nickname} 'sudo sed -i "s/^#\?PermitRootLogin.*/PermitRootLogin no/" /etc/ssh/sshd_config && sudo sed -i "s/^#\?PasswordAuthentication.*/PasswordAuthentication no/" /etc/ssh/sshd_config && sudo systemctl restart sshd'
  2. 验证锁定后SSH密钥是否仍正常工作:
    ssh {nickname} "grep -E 'PermitRootLogin|PasswordAuthentication' /etc/ssh/sshd_config && sudo systemctl status fail2ban --no-pager && echo '服务器已完全加固!'"
  3. 告知用户:"完成!SSH密钥已配置,fail2ban已安装,密码登录已禁用。连接命令:ssh {nickname}"

---

Completion Summary

完成总结

Print this summary for the user:
VPN-сервер полностью настроен и работает!

Подключение к серверу:
   Команда:     ssh {nickname}
   IP:          {SERVER_IP}
   Пользователь: {username}
   SSH-ключ:    ~/.ssh/{nickname}_key
   Пароль sudo: {sudo_password}

Безопасность сервера:
   Root-вход отключён
   Парольный вход отключён
   Файрвол включён (порты: SSH, 80, 443)
   fail2ban защищает от брутфорса
   Ядро усилено (sysctl)
   BBR включён (TCP-оптимизация)
   ICMP отключён (сервер не пингуется)

Панель 3x-ui:
   URL:      https://127.0.0.1:{panel_port}/{web_base_path} (через SSH-туннель)
   Login:    {panel_username}
   Password: {panel_password}

VPN-подключение:
   Протокол:  VLESS Reality
   Порт:      443
   SNI:       {best_sni}

Клиент:
   Hiddify -- ссылка добавлена

Управление (через SSH):
   ssh {nickname}                           # подключиться к серверу
   ssh {nickname} "sudo x-ui status"        # статус панели
   ssh {nickname} "sudo x-ui restart"       # перезапустить панель
   ssh {nickname} "sudo x-ui log"           # логи

SSH-туннель к админке:
   ssh -L {panel_port}:127.0.0.1:{panel_port} {nickname}
   Затем открыть: https://127.0.0.1:{panel_port}/{web_base_path}

Добавить нового клиента:
   Открой админку -> Inbounds -> ... -> Add Client
   Скинь ссылку или QR-код другому человеку

Методичка: ~/vpn-{nickname}-guide.md
   Все пароли, инструкции и команды в одном файле
向用户展示以下总结:
VPN服务器已完全配置并正常运行!

服务器连接信息:
   命令:     ssh {nickname}
   IP:          {SERVER_IP}
   用户: {username}
   SSH密钥:    ~/.ssh/{nickname}_key
   sudo密码: {sudo_password}

服务器安全状态:
   Root登录已禁用
   密码登录已禁用
   防火墙已启用(开放端口:SSH、80、443)
   fail2ban已安装以防止暴力破解
   内核已加固(sysctl)
   BBR已启用(TCP优化)
   ICMP已禁用(服务器无法被ping通)

3x-ui面板信息:
   URL:      https://127.0.0.1:{panel_port}/{web_base_path}(通过SSH隧道访问)
   登录账号:    {panel_username}
   密码: {panel_password}

VPN连接信息:
   协议:  VLESS Reality
   端口:      443
   SNI:       {best_sni}

客户端信息:
   Hiddify -- 链接已添加

管理命令(通过SSH):
   ssh {nickname}                           # 连接服务器
   ssh {nickname} "sudo x-ui status"        # 查看面板状态
   ssh {nickname} "sudo x-ui restart"       # 重启面板
   ssh {nickname} "sudo x-ui log"           # 查看日志

面板SSH隧道:
   ssh -L {panel_port}:127.0.0.1:{panel_port} {nickname}
   然后打开:https://127.0.0.1:{panel_port}/{web_base_path}

添加新客户端:
   打开面板 → Inbounds(入站规则)→ ... → Add Client(添加客户端)
   将链接或QR码发送给其他人

指南文件:~/vpn-{nickname}-guide.md
   所有密码、操作说明和命令均包含在此文件中

Critical Rules

关键规则

Part 1 (Server)

第一部分(服务器)

  1. NEVER skip Step 6 (test login) -- user can be locked out permanently
  2. NEVER disable root before confirming new user works
  3. NEVER store passwords in files -- only display once to user
  4. If connection drops after password change -- reconnect, this is normal
  5. If Step 6 fails -- fix it before proceeding, keep root session open
  6. Generate SSH key BEFORE first connection -- more efficient workflow
  7. All operations after Step 6 use sudo -- not root
  8. Steps 7 and 9 are DEFERRED -- SSH lockdown and fail2ban are installed at the very end (Step 22)
  1. 绝不要跳过步骤6(测试登录)——用户可能会被永久锁定在外
  2. 在确认新用户正常工作前,绝不要禁用root登录
  3. 绝不要将密码存储在文件中——仅向用户显示一次
  4. 若修改密码后连接断开——重新连接,此为正常现象
  5. 若步骤6失败——修复问题后再继续,保持root会话处于活跃状态
  6. 在首次连接前生成SSH密钥——更高效的工作流程
  7. 步骤6之后的所有操作均使用sudo——不要使用root
  8. 步骤7和9推迟执行——SSH锁定和fail2ban安装将在最后执行(步骤22)

Part 2 (VPN)

第二部分(VPN)

  1. NEVER expose panel to internet -- access only via SSH tunnel
  2. NEVER skip firewall configuration -- only open needed ports
  3. ALWAYS save panel credentials -- show them once, clearly
  4. ALWAYS verify connection works before declaring success
  5. Ask before every destructive or irreversible action
  6. ALWAYS generate guide file (Step 22) -- the user's single source of truth
  7. Lock SSH + install fail2ban LAST (Step 22) -- only after SSH key access is verified in BOTH modes
  8. NEVER leave password auth enabled after setup is complete
  1. 绝不要将面板暴露在公网中——仅通过SSH隧道访问
  2. 绝不要跳过防火墙配置——仅开放必要端口
  3. 务必保存面板凭证——清晰地向用户显示一次
  4. 在宣布成功前务必验证连接是否正常工作
  5. 在执行任何破坏性或不可逆操作前询问用户
  6. 务必生成指南文件(步骤22)——这是用户的唯一参考来源
  7. 最后执行SSH锁定 + 安装fail2ban(步骤22)——仅在两种模式下均确认SSH密钥访问正常后执行
  8. 配置完成后绝不要保留密码登录功能

Troubleshooting

故障排除

ProblemSolution
Connection drops after password changeNormal -- reconnect with new password
Permission denied (publickey)Check key path and permissions (700/600)
Host key verification failed
ssh-keygen -R {SERVER_IP}
then reconnect
x-ui install fails
sudo apt update && sudo apt install -y curl tar
Panel not accessibleUse SSH tunnel:
ssh -L {panel_port}:127.0.0.1:{panel_port} {nickname}
Reality not connectingWrong SNI -- re-run scanner, try different domain
Hiddify shows errorUpdate Hiddify to latest version, re-add link
"connection refused"Check x-ui is running:
sudo x-ui status
Forgot panel password
sudo x-ui setting -reset
SCP fails (Windows)Install OpenSSH: Settings → Apps → Optional Features → OpenSSH Client
SCP fails (connection refused)Check UFW allows SSH:
sudo ufw status
, verify sshd running
BBR not active after rebootRe-check:
sysctl net.ipv4.tcp_congestion_control
-- re-apply if needed
问题解决方案
修改密码后连接断开正常现象——使用新密码重新连接
Permission denied (publickey)(公钥权限被拒绝)检查密钥路径和权限(700/600)
Host key verification failed(主机密钥验证失败)执行
ssh-keygen -R {SERVER_IP}
然后重新连接
x-ui安装失败执行
sudo apt update && sudo apt install -y curl tar
面板无法访问使用SSH隧道:
ssh -L {panel_port}:127.0.0.1:{panel_port} {nickname}
Reality无法连接SNI错误——重新运行扫描器,尝试其他域名
Hiddify显示错误将Hiddify更新到最新版本,重新添加链接
"connection refused"(连接被拒绝)检查x-ui是否运行:
sudo x-ui status
忘记面板密码执行
sudo x-ui setting -reset
SCP失败(Windows)安装OpenSSH:设置 → 应用 → 可选功能 → OpenSSH客户端
SCP失败(连接被拒绝)检查UFW是否允许SSH:
sudo ufw status
,验证sshd是否运行
重启后BBR未激活重新检查:
sysctl net.ipv4.tcp_congestion_control
——若未激活则重新应用

x-ui CLI Reference

x-ui CLI参考

bash
x-ui start          # start panel
x-ui stop           # stop panel
x-ui restart        # restart panel
x-ui status         # check status
x-ui setting -reset # reset username/password
x-ui log            # view logs
x-ui cert           # manage SSL certificates
x-ui update         # update to latest version
bash
x-ui start          # 启动面板
x-ui stop           # 停止面板
x-ui restart        # 重启面板
x-ui status         # 检查状态
x-ui setting -reset # 重置用户名/密码
x-ui log            # 查看日志
x-ui cert           # 管理SSL证书
x-ui update         # 更新到最新版本