安装 v2ray
Ubuntu 安装 v2ray
(弃用)apt 安装
apt 安装
Ubuntu 22.04 下安装的版本 4.34.0 有问题,例如用 curl --proxy 无法正常连接到代理端口
sudo apt install v2ray下列文件将被安装:
/usr/bin/v2ray/v2ray: V2Ray executable/usr/bin/v2ray/v2ctl: Utility/etc/v2ray/config.json: Config file/usr/bin/v2ray/geoip.dat: IP data file/usr/bin/v2ray/geosite.dat: domain data file
# 如果已经这样装了,请卸载
sudo apt autoremove v2raySee: v2ray 4.34.0-5 in ubuntu22.04 bug · Issue #3005 · v2ray/v2ray-core
Did you install V2Ray via the
sudo apt install v2raycommand? I think this V2Ray 4.34.0 version is NOT stable.When I downgraded to V2Ray
4.28.2, the problem was solved.
See: v2ray - Ubuntu PPA
(弃用)解压缩安装
解压缩安装
根本无法启动
wget https://githubfast.com/v2ray/v2ray-core/releases/download/v4.28.2/v2ray-linux-64.zip -O v2ray-linux-64.zip
unzip v2ray-linux-64.zip -d /usr/bin/v2ray
# add to path
export PATH=$PATH:/usr/bin/v2raySee: v2fly/v2ray-core
See: Installation - v2fly.org
See: Install on Linux - v2ray.com
脚本安装
修改后的脚本附在文末: v2ray 完整安装脚本
简单修改
直接拿下面这个脚本安装是不行的,因为 github.com 被墙了
所以需要修改几个地方:
把脚本中的
github.com替换为githubfast.com,这是一个国内能访问的 github 镜像shdownload_v2ray() { DOWNLOAD_LINK="https://githubfast.com/v2fly/v2ray-core/releases/download/$RELEASE_VERSION/v2ray-linux-$MACHINE.zip" ... }获取最新版本时需要访问
api.github.com,但这个接口没有对应的githubfast镜像,所以需要注释掉联网部分,手动指定版本号v4.28.2:sh# # Get V2Ray release version number # TMP_FILE="$(mktemp)" # if ! curl -x "${PROXY}" -sS -i -H "Accept: application/vnd.github.v3+json" -o "$TMP_FILE" 'https://api.github.com/repos/v2fly/v2ray-core/releases/latest'; then # "rm" "$TMP_FILE" # echo 'error: Failed to get release list, please check your network.' # exit 1 # fi # HTTP_STATUS_CODE=$(awk 'NR==1 {print $2}' "$TMP_FILE") # if [[ $HTTP_STATUS_CODE -lt 200 ]] || [[ $HTTP_STATUS_CODE -gt 299 ]]; then # "rm" "$TMP_FILE" # echo "error: Failed to get release list, GitHub API response code: $HTTP_STATUS_CODE" # exit 1 # fi # RELEASE_LATEST="$(sed 'y/,/\n/' "$TMP_FILE" | grep 'tag_name' | awk -F '"' '{print $4}')" # "rm" "$TMP_FILE" # RELEASE_VERSION="v${RELEASE_LATEST#v}" RELEASE_VERSION="v4.28.2" # <--- 添加这里download_v2ray()函数中要下载验证文件,但是.dsgt文件在githubfast.com中也没有,所以要注释掉这部分:shdownload_v2ray() { DOWNLOAD_LINK="https://githubfast.com/v2fly/v2ray-core/releases/download/$RELEASE_VERSION/v2ray-linux-$MACHINE.zip" echo "Downloading V2Ray archive: $DOWNLOAD_LINK" if ! curl -x "${PROXY}" -R -H 'Cache-Control: no-cache' -o "$ZIP_FILE" "$DOWNLOAD_LINK"; then echo 'error: Download failed! Please check your network or try again.' return 1 fi # echo "Downloading verification file for V2Ray archive: $DOWNLOAD_LINK.dgst" # if ! curl -x "${PROXY}" -sSR -H 'Cache-Control: no-cache' -o "$ZIP_FILE.dgst" "$DOWNLOAD_LINK.dgst"; then # echo 'error: Download failed! Please check your network or try again.' # return 1 # fi # if [[ "$(cat "$ZIP_FILE".dgst)" == 'Not Found' ]]; then # echo 'error: This version does not support verification. Please replace with another version.' # return 1 # fi # # Verification of V2Ray archive # CHECKSUM=$(awk -F '= ' '/256=/ {print $2}' < "${ZIP_FILE}.dgst") # LOCALSUM=$(sha256sum "$ZIP_FILE" | awk '{printf $1}') # if [[ "$CHECKSUM" != "$LOCALSUM" ]]; then # echo 'error: SHA256 check failed! Please check your network or try again.' # return 1 # fi }
See: fhs-install-v2ray/install-release.sh
一键安装
其实就是下载和运行上面修改后的脚本:
wget https://raw.staticdn.net/Hansimov/blog/main/docs/notes/scripts/v2ray-install-release.sh -O ./v2ray-install-release.sh && chmod +x ./v2ray-install-release.sh && sudo ./v2ray-install-release.sh如果局域网内其他设备有这个安装脚本,可以直接下载:
cd ~/downloads
scp asimov@[host]:/home/asimov/repos/blog/docs/notes/scripts/v2ray-install-release.sh ./
chmod +x ./v2ray-install-release.sh && sudo ./v2ray-install-release.sh下载 geoip 和 geosite
类似上面的,也需要把 github.com 替换为 githubfast.com:
sudo wget https://githubfast.com/v2fly/geoip/releases/latest/download/geoip.dat -O /usr/local/share/v2ray/geoip.dat
sudo wget https://githubfast.com/v2fly/domain-list-community/releases/latest/download/dlc.dat -O /usr/local/share/v2ray/geosite.datSee: fhs-install-v2ray/install-dat-release.sh
Windows 安装 v2ray
下载 release:
- https://github.com/v2ray/v2ray-core/releases
- https://github.com/v2fly/v2ray-core/releases/download/v4.31.0/v2ray-windows-64.zip
解压,参考下面的样例修改 config.json,然后运行 v2ray.exe。
或者创建 launch_v2ray.bat 文件,内容如下。双击启动:
v2ray.exe run --config=config.json若要开机自启,创建快捷方式,发送到下面路径即可:
C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp- 又名:
C:\ProgramData\Microsoft\Windows\[开始]菜单\程序\启动
配置 server 和 client
配置 server 的 X-UI
MHSanaei/3x-ui
https://github.com/MHSanaei/3x-uihttps://github.com/MHSanaei/3x-ui/wiki/Installation#install-in-one-line-recommended
在远端代理服务器安装 x-ui:
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)安装时会提示设置 port。注意需要在防火墙中开放该端口。同时还会生成 Username 和 Password。输出形如:
Username: **********
Password: **********
Port: 9999
WebBasePath: ******************
Access URL: http://XXX.XXX.XXX.XXX:9999/******************查看设置:
x-ui settings输出形如:
The OS release is: ubuntu
[INF] current panel settings as follows:
Warning: Panel is not secure with SSL
hasDefaultCredential: false
port: 9999
webBasePath: /******************/
Access URL: http://XXX.XXX.XXX.XXX:9999/******************/访问 Access URL,输入之前命令行的 Username 和 Password,进入 x-ui 的 dashboard。
在入站列表添加一个 vmess 节点。
这几个信息后面会用到:address, port, users (id, alterId)。
配置 client 的 config.json
完整的样例附在文末:config.json 完整样例
v2ray 默认调用的配置文件位于:
/usr/local/etc/v2ray/config.json
需要修改配置文件中的:
inbounds:socks和http的portoutbounds:vnext>address,port,users(id,alterId)
如果局域网内其他设备有这个配置文件,可以直接下载:
scp asimov@[host]:/usr/local/etc/v2ray/config.json /usr/local/etc/v2ray/
# scp asimov@[host]:/usr/local/etc/v2ray/new.json /usr/local/etc/v2ray/See: Client Configuration - V2Fly.org
运行 client
sudo systemctl enable v2ray
sudo systemctl start v2ray显示服务状态:
sudo systemctl status v2ray测试代理:
curl --proxy http://127.0.0.1:11111 http://ifconfig.me/ip && echo ""查看日志:
journalctl -u v2ray.serviceSee: systemd - How to see full log from systemctl status service? - Unix & Linux Stack Exchange
运行多个 v2ray 服务
假如想要添加的新服务对应的配置文件为 new.json。同时不想改动原有的 v2ray 的服务,这时可以采用 v2ray@service 这个模板单元。
cat /etc/systemd/system/v2ray@.service[Unit]
Description=V2Ray Service
Documentation=https://www.v2fly.org/
After=network.target nss-lookup.target
[Service]
User=nobody
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
NoNewPrivileges=true
ExecStart=/usr/local/bin/v2ray -config /usr/local/etc/v2ray/%i.json
Restart=on-failure
RestartPreventExitStatus=23
[Install]
WantedBy=multi-user.targetsudo cp /usr/local/etc/v2ray/config.json /usr/local/etc/v2ray/new.json
sudo nano /usr/local/etc/v2ray/new.json修改 new.json 中的对应内容:
inbounds:port(socks + http)outbounds:address,port,id
如果局域网内其他设备有这个配置文件,可以直接下载:
scp asimov@[host]:/usr/local/etc/v2ray/new.json /usr/local/etc/v2ray/重载 systemd 配置:
sudo systemctl daemon-reload设置开机自启,并启动:
sudo systemctl enable v2ray@new
sudo systemctl start v2ray@new查看服务状态:
sudo systemctl status v2ray@new测试新代理:
curl --proxy http://127.0.0.1:11119 http://ifconfig.me/ip && echo ""在直连与本机前置代理间切换
如果 11119 对应的远端节点只能经本机 11111 访问,可以在其出站项使用 proxySettings.tag 指向一个 127.0.0.1:11111 的 HTTP outbound。两种模式分别是:
direct:应用 → 本机11119→ 原远端节点 → 目标网站。relay:应用 → 本机11119→ 本机11111的远端节点 → 原远端节点 → 目标网站。
这里的 direct 仍使用 11119 原有的远端代理和认证,只是取消前置中转; 不会把所有请求改成 freedom,也不会清除已有的分流规则。
在 VM 中安装切换脚本:
sudo install -m 755 v2ray_11119_route.py /usr/local/sbin/v2ray-11119-route# 查看当前模式
sudo v2ray-11119-route status
# 独立测试直连,不修改生产配置
sudo v2ray-11119-route test-direct
# 测试成功后切换为直连
sudo v2ray-11119-route direct
# 切换回经 11111 中转
sudo v2ray-11119-route relay脚本默认读取 /usr/local/etc/v2ray/new.json,管理 v2ray@new.service, 从原配置保留远端地址、端口和凭据。测试进程使用服务自身的 Unix 用户、独立的 loopback 临时端口,并强制探测请求经过所选代理,防止分流规则造成误判。 只有两轮 HTTPS 检查全部通过才应用配置;切换后的服务或请求检查失败会恢复 原配置并重启服务。已处于目标模式时,测试通过后不会重复重启。
原配置备份保存在 VM 内 /var/backups/v2ray-route/,权限为 0600,目录为 0700。备份包含真实代理凭据,不能提交到 public 仓库;仓库中的脚本不包含 真实上游地址或认证信息。依赖 Python 3.9+、curl、systemd 和现有 V2Ray 4.x。
这份脚本针对现有 V2Ray 4.28 的普通 TCP 配置。若使用 WebSocket/TLS 或更新核心, 应重新核对传输层中转行为;transportLayer 从 4.35 才加入,不能直接套到旧版。 参见 V2Fly 出站代理配置。
2026-09-05 在 ai122 的复测中,原上游 TCP 端口能够连接,但完整直连请求仍出现 长延迟和超时;延长等待后一个请求约 26 秒才成功,另一个仍超时。经 11111 中转的四次请求均成功,约 0.8–3.1 秒,因此保留中转。不能仅凭 TCP 端口恢复 连接就自动切回直连,也不能据此断定具体是哪一家运营商或哪一跳的问题。
#!/usr/bin/env python3
"""Switch an existing V2Ray 4.x TCP outbound between direct and local relay.
Run inside the VM. Reads the existing private config; no upstream credentials
are embedded here. Candidate probes use a separate loopback listener and the
service's Unix account. Failed changes restore the previous config and service.
"""
import argparse
import copy
import concurrent.futures
import datetime
import fcntl
import grp
import json
import os
from pathlib import Path
import pwd
import socket
import stat
import subprocess
import tempfile
import time
RELAY_TAG = "via-local-v2ray-http-11111"
CHECKS = (
("https://www.google.com/generate_204", "204"),
("https://www.cloudflare.com/cdn-cgi/trace", "200"),
)
def command(argv, timeout=35):
return subprocess.run(argv, capture_output=True, text=True, timeout=timeout,
stdin=subprocess.DEVNULL)
def service_account(service):
user = command(["systemctl", "show", service, "-p", "User", "--value"]).stdout.strip()
group = command(["systemctl", "show", service, "-p", "Group", "--value"]).stdout.strip()
account = pwd.getpwnam(user or "root")
gid = grp.getgrnam(group).gr_gid if group else account.pw_gid
return account.pw_uid, gid
def primary(config, tag):
outbounds = config.get("outbounds", [])
matches = [item for item in outbounds if item.get("tag") == tag] if tag else outbounds[:1]
if len(matches) != 1 or matches[0].get("protocol") not in (
"vmess", "vless", "trojan", "shadowsocks"):
raise RuntimeError("Select exactly one existing remote proxy outbound with --outbound-tag.")
stream = matches[0].get("streamSettings", {})
if stream.get("sockopt", {}).get("dialerProxy"):
raise RuntimeError("dialerProxy is configured; this V2Ray 4.x helper cannot change it.")
return matches[0]
def candidate(config, mode, tag):
result = copy.deepcopy(config)
target = primary(result, tag)
if mode == "direct":
target.pop("proxySettings", None)
return result
stream = target.get("streamSettings", {})
if stream.get("network", "tcp") != "tcp" or stream.get("security", "none") != "none":
raise RuntimeError("Relay mode supports raw TCP only; retain transport settings on newer cores separately.")
relay = {"tag": RELAY_TAG, "protocol": "http", "settings": {
"servers": [{"address": "127.0.0.1", "port": 11111}]}}
existing = [item for item in result["outbounds"] if item.get("tag") == RELAY_TAG]
if existing and (len(existing) != 1 or existing[0] != relay):
raise RuntimeError("Existing relay tag has different settings; refusing to overwrite it.")
if not existing:
result["outbounds"].append(relay)
target["proxySettings"] = {"tag": RELAY_TAG}
return result
def check_http(port, url, expected):
result = command(["curl", "--silent", "--show-error", "--noproxy", "",
"--proxy", f"http://127.0.0.1:{port}", "--connect-timeout", "10",
"--max-time", "20", "--output", "/dev/null", "--write-out",
"%{http_code} %{time_total}", url], timeout=24)
code = result.stdout.split()[0] if result.stdout.split() else "000"
ok = result.returncode == 0 and code == expected
print(f"{'OK' if ok else 'FAIL'} {url}: {result.stdout.strip()} curl={result.returncode}", flush=True)
return ok
def check_proxy(port):
# Two rounds avoid accepting a single successful request on a flaky route.
results = []
for _ in range(2):
with concurrent.futures.ThreadPoolExecutor(max_workers=len(CHECKS)) as pool:
futures = [pool.submit(check_http, port, url, status) for url, status in CHECKS]
results.extend(job.result() for job in futures)
return all(results)
def validate(binary, path):
# Matches the existing V2Ray 4.x systemd ExecStart, without upgrading it.
result = command([binary, "-test", "-config", str(path)])
if result.returncode:
raise RuntimeError("V2Ray configuration validation failed (private config output suppressed).")
def probe(config, args, uid, gid, directory):
probe_config = copy.deepcopy(config)
target = primary(probe_config, args.outbound_tag)
with socket.socket() as listener:
listener.bind(("127.0.0.1", 0))
port = listener.getsockname()[1]
# Make the selected remote outbound the default and force probe requests to it;
# production split-routing rules must not create a false successful direct test.
probe_config["outbounds"].remove(target)
probe_config["outbounds"].insert(0, target)
probe_config["inbounds"] = [{"listen": "127.0.0.1", "port": port,
"protocol": "http", "settings": {}}]
probe_config["routing"] = {"domainStrategy": "AsIs", "rules": []}
probe_config["log"] = {"loglevel": "warning"}
for key in ("api", "reverse", "observatory", "burstObservatory"):
probe_config.pop(key, None)
path = directory / "probe.json"
path.write_text(json.dumps(probe_config), encoding="utf-8")
os.chown(path, 0, gid)
path.chmod(0o640)
validate(args.binary, path)
with (directory / "probe.log").open("wb") as log:
process = subprocess.Popen([args.binary, "-config", str(path)],
stdin=subprocess.DEVNULL, stdout=log, stderr=log,
cwd="/", user=uid, group=gid, extra_groups=[])
try:
deadline = time.monotonic() + 5
while time.monotonic() < deadline:
if process.poll() is not None:
raise RuntimeError("Isolated V2Ray probe exited before becoming ready.")
try:
with socket.create_connection(("127.0.0.1", port), timeout=0.2):
break
except OSError:
time.sleep(0.1)
else:
raise RuntimeError("Isolated V2Ray probe listener did not become ready.")
return check_proxy(port) and process.poll() is None
finally:
if process.poll() is None:
process.terminate()
try:
process.wait(timeout=5)
except subprocess.TimeoutExpired:
process.kill()
process.wait(timeout=5)
def atomic_write(path, data, metadata):
fd, temporary = tempfile.mkstemp(prefix=".v2ray-route-", suffix=".json", dir=path.parent)
try:
with os.fdopen(fd, "wb") as output:
output.write(data)
output.flush()
os.fsync(output.fileno())
os.fchown(output.fileno(), metadata.st_uid, metadata.st_gid)
os.fchmod(output.fileno(), stat.S_IMODE(metadata.st_mode))
os.replace(temporary, path)
finally:
if os.path.exists(temporary):
os.unlink(temporary)
def restart(service):
if command(["systemctl", "restart", service]).returncode:
raise RuntimeError("Service restart failed.")
if command(["systemctl", "is-active", "--quiet", service]).returncode:
raise RuntimeError("Service is not active after restart.")
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("mode", choices=("status", "test-direct", "direct", "relay"))
parser.add_argument("--config", type=Path, default=Path("/usr/local/etc/v2ray/new.json"))
parser.add_argument("--service", default="v2ray@new.service")
parser.add_argument("--binary", default="/usr/local/bin/v2ray")
parser.add_argument("--outbound-tag", default=None)
parser.add_argument("--backup-dir", type=Path, default=Path("/var/backups/v2ray-route"))
args = parser.parse_args()
if os.geteuid() != 0:
parser.error("Run with sudo inside the VM.")
args.config = args.config.resolve(strict=True)
with open("/run/lock/v2ray-11119-route.lock", "a") as lock:
fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
raw = args.config.read_bytes()
metadata = args.config.stat()
config = json.loads(raw)
current = primary(config, args.outbound_tag).get("proxySettings", {}).get("tag")
route = "direct" if not current else ("relay via 127.0.0.1:11111" if current == RELAY_TAG else "other relay")
print(f"Current route: {route}", flush=True)
if args.mode == "status":
print("Service: " + command(["systemctl", "is-active", args.service]).stdout.strip())
return
if not any(item.get("protocol") == "http" and item.get("port") == 11119
for item in config.get("inbounds", [])):
raise RuntimeError("Expected HTTP inbound on 11119 is absent.")
if command(["systemctl", "is-active", "--quiet", args.service]).returncode:
raise RuntimeError("Start the existing service before testing or switching routes.")
mode = "direct" if args.mode == "test-direct" else args.mode
changed = candidate(config, mode, args.outbound_tag)
uid, gid = service_account(args.service)
with tempfile.TemporaryDirectory(prefix="v2ray-route-", dir="/run") as temp:
directory = Path(temp)
os.chown(directory, 0, gid)
directory.chmod(0o710)
print(f"Testing {mode} with an isolated listener as service uid={uid}...", flush=True)
if not probe(changed, args, uid, gid, directory):
raise RuntimeError("Candidate route failed; production config and service were left unchanged.")
if args.mode == "test-direct":
print("Direct route passed. Use 'direct' to switch production.")
return
if config == changed:
print("Already using the requested route; no restart needed.")
return
candidate_path = directory / "candidate.json"
encoded = (json.dumps(changed, ensure_ascii=False, indent=2) + "\n").encode()
candidate_path.write_bytes(encoded)
candidate_path.chmod(0o600)
validate(args.binary, candidate_path)
if args.config.read_bytes() != raw:
raise RuntimeError("Config changed during the probe; rerun against the new version.")
backup_dir = args.backup_dir
backup_dir.mkdir(mode=0o700, parents=True, exist_ok=True)
backup_dir.chmod(0o700)
stamp = datetime.datetime.now().strftime("%Y%m%d-%H%M%S-%f")
backup = backup_dir / f"{args.config.name}.{stamp}.bak"
fd = os.open(backup, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
with os.fdopen(fd, "wb") as output:
output.write(raw)
output.flush()
os.fsync(output.fileno())
print(f"Private backup: {backup}", flush=True)
atomic_write(args.config, encoded, metadata)
try:
restart(args.service)
if not check_proxy(11119):
raise RuntimeError("Production HTTP checks failed.")
except BaseException as problem:
atomic_write(args.config, raw, metadata)
restart(args.service)
restored = check_proxy(11119)
raise RuntimeError(f"Previous config restored; previous route healthy={restored}.") from problem
print(f"Switched to {mode}; production HTTPS checks passed.")
if __name__ == "__main__":
try:
main()
except (RuntimeError, OSError, ValueError, subprocess.SubprocessError) as error:
# Config parsing and process errors may contain private values: do not dump
# the config, V2Ray log, subprocess output, or Python exception traceback.
message = str(error) if isinstance(error, RuntimeError) else type(error).__name__
raise SystemExit("ERROR: " + message)附录
v2ray 完整安装脚本
#!/usr/bin/env bash
# shellcheck disable=SC2268
# The files installed by the script conform to the Filesystem Hierarchy Standard:
# https://wiki.linuxfoundation.org/lsb/fhs
# The URL of the script project is:
# https://github.com/v2fly/fhs-install-v2ray
# The URL of the script is:
# https://raw.githubusercontent.com/v2fly/fhs-install-v2ray/master/install-release.sh
# If the script executes incorrectly, go to:
# https://github.com/v2fly/fhs-install-v2ray/issues
# You can set this variable whatever you want in shell session right before running this script by issuing:
# export DAT_PATH='/usr/local/share/v2ray'
DAT_PATH=${DAT_PATH:-/usr/local/share/v2ray}
# You can set this variable whatever you want in shell session right before running this script by issuing:
# export JSON_PATH='/usr/local/etc/v2ray'
JSON_PATH=${JSON_PATH:-/usr/local/etc/v2ray}
# Set this variable only if you are starting v2ray with multiple configuration files:
# export JSONS_PATH='/usr/local/etc/v2ray'
# Set this variable only if you want this script to check all the systemd unit file:
# export check_all_service_files='yes'
curl() {
$(type -P curl) -L -q --retry 5 --retry-delay 10 --retry-max-time 60 "$@"
}
systemd_cat_config() {
if systemd-analyze --help | grep -qw 'cat-config'; then
systemd-analyze --no-pager cat-config "$@"
echo
else
echo "${aoi}~~~~~~~~~~~~~~~~"
cat "$@" "$1".d/*
echo "${aoi}~~~~~~~~~~~~~~~~"
echo "${red}warning: ${green}The systemd version on the current operating system is too low."
echo "${red}warning: ${green}Please consider to upgrade the systemd or the operating system.${reset}"
echo
fi
}
check_if_running_as_root() {
# If you want to run as another user, please modify $UID to be owned by this user
if [[ "$UID" -ne '0' ]]; then
echo "WARNING: The user currently executing this script is not root. You may encounter the insufficient privilege error."
read -r -p "Are you sure you want to continue? [y/n] " cont_without_been_root
if [[ x"${cont_without_been_root:0:1}" = x'y' ]]; then
echo "Continuing the installation with current user..."
else
echo "Not running with root, exiting..."
exit 1
fi
fi
}
identify_the_operating_system_and_architecture() {
if [[ "$(uname)" == 'Linux' ]]; then
case "$(uname -m)" in
'i386' | 'i686')
MACHINE='32'
;;
'amd64' | 'x86_64')
MACHINE='64'
;;
'armv5tel')
MACHINE='arm32-v5'
;;
'armv6l')
MACHINE='arm32-v6'
grep Features /proc/cpuinfo | grep -qw 'vfp' || MACHINE='arm32-v5'
;;
'armv7' | 'armv7l')
MACHINE='arm32-v7a'
grep Features /proc/cpuinfo | grep -qw 'vfp' || MACHINE='arm32-v5'
;;
'armv8' | 'aarch64')
MACHINE='arm64-v8a'
;;
'mips')
MACHINE='mips32'
;;
'mipsle')
MACHINE='mips32le'
;;
'mips64')
MACHINE='mips64'
;;
'mips64le')
MACHINE='mips64le'
;;
'ppc64')
MACHINE='ppc64'
;;
'ppc64le')
MACHINE='ppc64le'
;;
'riscv64')
MACHINE='riscv64'
;;
's390x')
MACHINE='s390x'
;;
*)
echo "error: The architecture is not supported."
exit 1
;;
esac
if [[ ! -f '/etc/os-release' ]]; then
echo "error: Don't use outdated Linux distributions."
exit 1
fi
# Do not combine this judgment condition with the following judgment condition.
## Be aware of Linux distribution like Gentoo, which kernel supports switch between Systemd and OpenRC.
### Refer: https://github.com/v2fly/fhs-install-v2ray/issues/84#issuecomment-688574989
if [[ -f /.dockerenv ]] || grep -q 'docker\|lxc' /proc/1/cgroup && [[ "$(type -P systemctl)" ]]; then
true
elif [[ -d /run/systemd/system ]] || grep -q systemd <(ls -l /sbin/init); then
true
else
echo "error: Only Linux distributions using systemd are supported."
exit 1
fi
if [[ "$(type -P apt)" ]]; then
PACKAGE_MANAGEMENT_INSTALL='apt -y --no-install-recommends install'
PACKAGE_MANAGEMENT_REMOVE='apt purge'
package_provide_tput='ncurses-bin'
elif [[ "$(type -P dnf)" ]]; then
PACKAGE_MANAGEMENT_INSTALL='dnf -y install'
PACKAGE_MANAGEMENT_REMOVE='dnf remove'
package_provide_tput='ncurses'
elif [[ "$(type -P yum)" ]]; then
PACKAGE_MANAGEMENT_INSTALL='yum -y install'
PACKAGE_MANAGEMENT_REMOVE='yum remove'
package_provide_tput='ncurses'
elif [[ "$(type -P zypper)" ]]; then
PACKAGE_MANAGEMENT_INSTALL='zypper install -y --no-recommends'
PACKAGE_MANAGEMENT_REMOVE='zypper remove'
package_provide_tput='ncurses-utils'
elif [[ "$(type -P pacman)" ]]; then
PACKAGE_MANAGEMENT_INSTALL='pacman -Syu --noconfirm'
PACKAGE_MANAGEMENT_REMOVE='pacman -Rsn'
package_provide_tput='ncurses'
else
echo "error: The script does not support the package manager in this operating system."
exit 1
fi
else
echo "error: This operating system is not supported."
exit 1
fi
}
## Demo function for processing parameters
judgment_parameters() {
while [[ "$#" -gt '0' ]]; do
case "$1" in
'--remove')
if [[ "$#" -gt '1' ]]; then
echo 'error: Please enter the correct parameters.'
exit 1
fi
REMOVE='1'
;;
'--version')
VERSION="${2:?error: Please specify the correct version.}"
break
;;
'-c' | '--check')
CHECK='1'
break
;;
'-f' | '--force')
FORCE='1'
break
;;
'-h' | '--help')
HELP='1'
break
;;
'-l' | '--local')
LOCAL_INSTALL='1'
LOCAL_FILE="${2:?error: Please specify the correct local file.}"
break
;;
'-p' | '--proxy')
if [[ -z "${2:?error: Please specify the proxy server address.}" ]]; then
exit 1
fi
PROXY="$2"
shift
;;
*)
echo "$0: unknown option -- -"
exit 1
;;
esac
shift
done
}
install_software() {
package_name="$1"
file_to_detect="$2"
type -P "$file_to_detect" > /dev/null 2>&1 && return
if ${PACKAGE_MANAGEMENT_INSTALL} "$package_name"; then
echo "info: $package_name is installed."
else
echo "error: Installation of $package_name failed, please check your network."
exit 1
fi
}
get_current_version() {
if /usr/local/bin/v2ray -version > /dev/null 2>&1; then
VERSION="$(/usr/local/bin/v2ray -version | awk 'NR==1 {print $2}')"
else
VERSION="$(/usr/local/bin/v2ray version | awk 'NR==1 {print $2}')"
fi
CURRENT_VERSION="v${VERSION#v}"
}
get_version() {
# 0: Install or update V2Ray.
# 1: Installed or no new version of V2Ray.
# 2: Install the specified version of V2Ray.
if [[ -n "$VERSION" ]]; then
RELEASE_VERSION="v${VERSION#v}"
return 2
fi
# Determine the version number for V2Ray installed from a local file
if [[ -f '/usr/local/bin/v2ray' ]]; then
get_current_version
if [[ "$LOCAL_INSTALL" -eq '1' ]]; then
RELEASE_VERSION="$CURRENT_VERSION"
return
fi
fi
# # Get V2Ray release version number
# TMP_FILE="$(mktemp)"
# if ! curl -x "${PROXY}" -sS -i -H "Accept: application/vnd.github.v3+json" -o "$TMP_FILE" 'https://api.github.com/repos/v2fly/v2ray-core/releases/latest'; then
# "rm" "$TMP_FILE"
# echo 'error: Failed to get release list, please check your network.'
# exit 1
# fi
# HTTP_STATUS_CODE=$(awk 'NR==1 {print $2}' "$TMP_FILE")
# if [[ $HTTP_STATUS_CODE -lt 200 ]] || [[ $HTTP_STATUS_CODE -gt 299 ]]; then
# "rm" "$TMP_FILE"
# echo "error: Failed to get release list, GitHub API response code: $HTTP_STATUS_CODE"
# exit 1
# fi
# RELEASE_LATEST="$(sed 'y/,/\n/' "$TMP_FILE" | grep 'tag_name' | awk -F '"' '{print $4}')"
# "rm" "$TMP_FILE"
# RELEASE_VERSION="v${RELEASE_LATEST#v}"
RELEASE_VERSION="v4.28.2"
# Compare V2Ray version numbers
if [[ "$RELEASE_VERSION" != "$CURRENT_VERSION" ]]; then
RELEASE_VERSIONSION_NUMBER="${RELEASE_VERSION#v}"
RELEASE_MAJOR_VERSION_NUMBER="${RELEASE_VERSIONSION_NUMBER%%.*}"
RELEASE_MINOR_VERSION_NUMBER="$(echo "$RELEASE_VERSIONSION_NUMBER" | awk -F '.' '{print $2}')"
RELEASE_MINIMUM_VERSION_NUMBER="${RELEASE_VERSIONSION_NUMBER##*.}"
# shellcheck disable=SC2001
CURRENT_VERSION_NUMBER="$(echo "${CURRENT_VERSION#v}" | sed 's/-.*//')"
CURRENT_MAJOR_VERSION_NUMBER="${CURRENT_VERSION_NUMBER%%.*}"
CURRENT_MINOR_VERSION_NUMBER="$(echo "$CURRENT_VERSION_NUMBER" | awk -F '.' '{print $2}')"
CURRENT_MINIMUM_VERSION_NUMBER="${CURRENT_VERSION_NUMBER##*.}"
if [[ "$RELEASE_MAJOR_VERSION_NUMBER" -gt "$CURRENT_MAJOR_VERSION_NUMBER" ]]; then
return 0
elif [[ "$RELEASE_MAJOR_VERSION_NUMBER" -eq "$CURRENT_MAJOR_VERSION_NUMBER" ]]; then
if [[ "$RELEASE_MINOR_VERSION_NUMBER" -gt "$CURRENT_MINOR_VERSION_NUMBER" ]]; then
return 0
elif [[ "$RELEASE_MINOR_VERSION_NUMBER" -eq "$CURRENT_MINOR_VERSION_NUMBER" ]]; then
if [[ "$RELEASE_MINIMUM_VERSION_NUMBER" -gt "$CURRENT_MINIMUM_VERSION_NUMBER" ]]; then
return 0
else
return 1
fi
else
return 1
fi
else
return 1
fi
elif [[ "$RELEASE_VERSION" == "$CURRENT_VERSION" ]]; then
return 1
fi
}
download_v2ray() {
DOWNLOAD_LINK="https://githubfast.com/v2fly/v2ray-core/releases/download/$RELEASE_VERSION/v2ray-linux-$MACHINE.zip"
echo "Downloading V2Ray archive: $DOWNLOAD_LINK"
if ! curl -x "${PROXY}" -R -H 'Cache-Control: no-cache' -o "$ZIP_FILE" "$DOWNLOAD_LINK"; then
echo 'error: Download failed! Please check your network or try again.'
return 1
fi
# echo "Downloading verification file for V2Ray archive: $DOWNLOAD_LINK.dgst"
# if ! curl -x "${PROXY}" -sSR -H 'Cache-Control: no-cache' -o "$ZIP_FILE.dgst" "$DOWNLOAD_LINK.dgst"; then
# echo 'error: Download failed! Please check your network or try again.'
# return 1
# fi
# if [[ "$(cat "$ZIP_FILE".dgst)" == 'Not Found' ]]; then
# echo 'error: This version does not support verification. Please replace with another version.'
# return 1
# fi
# # Verification of V2Ray archive
# CHECKSUM=$(awk -F '= ' '/256=/ {print $2}' < "${ZIP_FILE}.dgst")
# LOCALSUM=$(sha256sum "$ZIP_FILE" | awk '{printf $1}')
# if [[ "$CHECKSUM" != "$LOCALSUM" ]]; then
# echo 'error: SHA256 check failed! Please check your network or try again.'
# return 1
# fi
}
decompression() {
if ! unzip -q "$1" -d "$TMP_DIRECTORY"; then
echo 'error: V2Ray decompression failed.'
"rm" -r "$TMP_DIRECTORY"
echo "removed: $TMP_DIRECTORY"
exit 1
fi
echo "info: Extract the V2Ray package to $TMP_DIRECTORY and prepare it for installation."
}
install_file() {
NAME="$1"
if [[ "$NAME" == 'v2ray' ]] || [[ "$NAME" == 'v2ctl' ]]; then
install -m 755 "${TMP_DIRECTORY}/$NAME" "/usr/local/bin/$NAME"
elif [[ "$NAME" == 'geoip.dat' ]] || [[ "$NAME" == 'geosite.dat' ]]; then
install -m 644 "${TMP_DIRECTORY}/$NAME" "${DAT_PATH}/$NAME"
fi
}
install_v2ray() {
# Install V2Ray binary to /usr/local/bin/ and $DAT_PATH
install_file v2ray
if [[ -f "${TMP_DIRECTORY}/v2ctl" ]]; then
install_file v2ctl
else
if [[ -f '/usr/local/bin/v2ctl' ]]; then
rm '/usr/local/bin/v2ctl'
fi
fi
install -d "$DAT_PATH"
# If the file exists, geoip.dat and geosite.dat will not be installed or updated
if [[ ! -f "${DAT_PATH}/.undat" ]]; then
install_file geoip.dat
install_file geosite.dat
fi
# Install V2Ray configuration file to $JSON_PATH
# shellcheck disable=SC2153
if [[ -z "$JSONS_PATH" ]] && [[ ! -d "$JSON_PATH" ]]; then
install -d "$JSON_PATH"
echo "{}" > "${JSON_PATH}/config.json"
CONFIG_NEW='1'
fi
# Install V2Ray configuration file to $JSONS_PATH
if [[ -n "$JSONS_PATH" ]] && [[ ! -d "$JSONS_PATH" ]]; then
install -d "$JSONS_PATH"
for BASE in 00_log 01_api 02_dns 03_routing 04_policy 05_inbounds 06_outbounds 07_transport 08_stats 09_reverse; do
echo '{}' > "${JSONS_PATH}/${BASE}.json"
done
CONFDIR='1'
fi
# Used to store V2Ray log files
if [[ ! -d '/var/log/v2ray/' ]]; then
if id nobody | grep -qw 'nogroup'; then
install -d -m 700 -o nobody -g nogroup /var/log/v2ray/
install -m 600 -o nobody -g nogroup /dev/null /var/log/v2ray/access.log
install -m 600 -o nobody -g nogroup /dev/null /var/log/v2ray/error.log
else
install -d -m 700 -o nobody -g nobody /var/log/v2ray/
install -m 600 -o nobody -g nobody /dev/null /var/log/v2ray/access.log
install -m 600 -o nobody -g nobody /dev/null /var/log/v2ray/error.log
fi
LOG='1'
fi
}
install_startup_service_file() {
get_current_version
if [[ "$(echo "${CURRENT_VERSION#v}" | sed 's/-.*//' | awk -F'.' '{print $1}')" -gt "4" ]]; then
START_COMMAND="/usr/local/bin/v2ray run"
else
START_COMMAND="/usr/local/bin/v2ray"
fi
install -m 644 "${TMP_DIRECTORY}/systemd/system/v2ray.service" /etc/systemd/system/v2ray.service
install -m 644 "${TMP_DIRECTORY}/systemd/system/v2ray@.service" /etc/systemd/system/v2ray@.service
mkdir -p '/etc/systemd/system/v2ray.service.d'
mkdir -p '/etc/systemd/system/v2ray@.service.d/'
if [[ -n "$JSONS_PATH" ]]; then
"rm" -f '/etc/systemd/system/v2ray.service.d/10-donot_touch_single_conf.conf' \
'/etc/systemd/system/v2ray@.service.d/10-donot_touch_single_conf.conf'
echo "# In case you have a good reason to do so, duplicate this file in the same directory and make your customizes there.
# Or all changes you made will be lost! # Refer: https://www.freedesktop.org/software/systemd/man/systemd.unit.html
[Service]
ExecStart=
ExecStart=${START_COMMAND} -confdir $JSONS_PATH" |
tee '/etc/systemd/system/v2ray.service.d/10-donot_touch_multi_conf.conf' > '/etc/systemd/system/v2ray@.service.d/10-donot_touch_multi_conf.conf'
else
"rm" -f '/etc/systemd/system/v2ray.service.d/10-donot_touch_multi_conf.conf' \
'/etc/systemd/system/v2ray@.service.d/10-donot_touch_multi_conf.conf'
echo "# In case you have a good reason to do so, duplicate this file in the same directory and make your customizes there.
# Or all changes you made will be lost! # Refer: https://www.freedesktop.org/software/systemd/man/systemd.unit.html
[Service]
ExecStart=
ExecStart=${START_COMMAND} -config ${JSON_PATH}/config.json" > '/etc/systemd/system/v2ray.service.d/10-donot_touch_single_conf.conf'
echo "# In case you have a good reason to do so, duplicate this file in the same directory and make your customizes there.
# Or all changes you made will be lost! # Refer: https://www.freedesktop.org/software/systemd/man/systemd.unit.html
[Service]
ExecStart=
ExecStart=${START_COMMAND} -config ${JSON_PATH}/%i.json" > '/etc/systemd/system/v2ray@.service.d/10-donot_touch_single_conf.conf'
fi
echo "info: Systemd service files have been installed successfully!"
echo "${red}warning: ${green}The following are the actual parameters for the v2ray service startup."
echo "${red}warning: ${green}Please make sure the configuration file path is correctly set.${reset}"
systemd_cat_config /etc/systemd/system/v2ray.service
# shellcheck disable=SC2154
if [[ x"${check_all_service_files:0:1}" = x'y' ]]; then
echo
echo
systemd_cat_config /etc/systemd/system/v2ray@.service
fi
systemctl daemon-reload
SYSTEMD='1'
}
start_v2ray() {
if [[ -f '/etc/systemd/system/v2ray.service' ]]; then
if systemctl start "${V2RAY_CUSTOMIZE:-v2ray}"; then
echo 'info: Start the V2Ray service.'
else
echo 'error: Failed to start V2Ray service.'
exit 1
fi
fi
}
stop_v2ray() {
V2RAY_CUSTOMIZE="$(systemctl list-units | grep 'v2ray@' | awk -F ' ' '{print $1}')"
if [[ -z "$V2RAY_CUSTOMIZE" ]]; then
local v2ray_daemon_to_stop='v2ray.service'
else
local v2ray_daemon_to_stop="$V2RAY_CUSTOMIZE"
fi
if ! systemctl stop "$v2ray_daemon_to_stop"; then
echo 'error: Stopping the V2Ray service failed.'
exit 1
fi
echo 'info: Stop the V2Ray service.'
}
check_update() {
if [[ -f '/etc/systemd/system/v2ray.service' ]]; then
get_version
local get_ver_exit_code=$?
if [[ "$get_ver_exit_code" -eq '0' ]]; then
echo "info: Found the latest release of V2Ray $RELEASE_VERSION . (Current release: $CURRENT_VERSION)"
elif [[ "$get_ver_exit_code" -eq '1' ]]; then
echo "info: No new version. The current version of V2Ray is $CURRENT_VERSION ."
fi
exit 0
else
echo 'error: V2Ray is not installed.'
exit 1
fi
}
remove_v2ray() {
if systemctl list-unit-files | grep -qw 'v2ray'; then
if [[ -n "$(pidof v2ray)" ]]; then
stop_v2ray
fi
if ! ("rm" -r '/usr/local/bin/v2ray' \
"$DAT_PATH" \
'/etc/systemd/system/v2ray.service' \
'/etc/systemd/system/v2ray@.service' \
'/etc/systemd/system/v2ray.service.d' \
'/etc/systemd/system/v2ray@.service.d'); then
echo 'error: Failed to remove V2Ray.'
exit 1
else
echo 'removed: /usr/local/bin/v2ray'
if [[ -f '/usr/local/bin/v2ctl' ]]; then
rm '/usr/local/bin/v2ctl'
echo 'removed: /usr/local/bin/v2ctl'
fi
echo "removed: $DAT_PATH"
echo 'removed: /etc/systemd/system/v2ray.service'
echo 'removed: /etc/systemd/system/v2ray@.service'
echo 'removed: /etc/systemd/system/v2ray.service.d'
echo 'removed: /etc/systemd/system/v2ray@.service.d'
echo 'Please execute the command: systemctl disable v2ray'
echo "You may need to execute a command to remove dependent software: $PACKAGE_MANAGEMENT_REMOVE curl unzip"
echo 'info: V2Ray has been removed.'
echo 'info: If necessary, manually delete the configuration and log files.'
if [[ -n "$JSONS_PATH" ]]; then
echo "info: e.g., $JSONS_PATH and /var/log/v2ray/ ..."
else
echo "info: e.g., $JSON_PATH and /var/log/v2ray/ ..."
fi
exit 0
fi
else
echo 'error: V2Ray is not installed.'
exit 1
fi
}
# Explanation of parameters in the script
show_help() {
echo "usage: $0 [--remove | --version number | -c | -f | -h | -l | -p]"
echo ' [-p address] [--version number | -c | -f]'
echo ' --remove Remove V2Ray'
echo ' --version Install the specified version of V2Ray, e.g., --version v4.18.0'
echo ' -c, --check Check if V2Ray can be updated'
echo ' -f, --force Force installation of the latest version of V2Ray'
echo ' -h, --help Show help'
echo ' -l, --local Install V2Ray from a local file'
echo ' -p, --proxy Download through a proxy server, e.g., -p http://127.0.0.1:8118 or -p socks5://127.0.0.1:1080'
exit 0
}
main() {
check_if_running_as_root
identify_the_operating_system_and_architecture
judgment_parameters "$@"
install_software "$package_provide_tput" 'tput'
red=$(tput setaf 1)
green=$(tput setaf 2)
aoi=$(tput setaf 6)
reset=$(tput sgr0)
# Parameter information
[[ "$HELP" -eq '1' ]] && show_help
[[ "$CHECK" -eq '1' ]] && check_update
[[ "$REMOVE" -eq '1' ]] && remove_v2ray
# Two very important variables
TMP_DIRECTORY="$(mktemp -d)"
ZIP_FILE="${TMP_DIRECTORY}/v2ray-linux-$MACHINE.zip"
# Install V2Ray from a local file, but still need to make sure the network is available
if [[ "$LOCAL_INSTALL" -eq '1' ]]; then
echo 'warn: Install V2Ray from a local file, but still need to make sure the network is available.'
echo -n 'warn: Please make sure the file is valid because we cannot confirm it. (Press any key) ...'
read -r
install_software 'unzip' 'unzip'
decompression "$LOCAL_FILE"
else
# Normal way
install_software 'curl' 'curl'
get_version
NUMBER="$?"
if [[ "$NUMBER" -eq '0' ]] || [[ "$FORCE" -eq '1' ]] || [[ "$NUMBER" -eq 2 ]]; then
echo "info: Installing V2Ray $RELEASE_VERSION for $(uname -m)"
download_v2ray
if [[ "$?" -eq '1' ]]; then
"rm" -r "$TMP_DIRECTORY"
echo "removed: $TMP_DIRECTORY"
exit 1
fi
install_software 'unzip' 'unzip'
decompression "$ZIP_FILE"
elif [[ "$NUMBER" -eq '1' ]]; then
echo "info: No new version. The current version of V2Ray is $CURRENT_VERSION ."
exit 0
fi
fi
# Determine if V2Ray is running
if systemctl list-unit-files | grep -qw 'v2ray'; then
if [[ -n "$(pidof v2ray)" ]]; then
stop_v2ray
V2RAY_RUNNING='1'
fi
fi
install_v2ray
install_startup_service_file
echo 'installed: /usr/local/bin/v2ray'
if [[ -f '/usr/local/bin/v2ctl' ]]; then
echo 'installed: /usr/local/bin/v2ctl'
fi
# If the file exists, the content output of installing or updating geoip.dat and geosite.dat will not be displayed
if [[ ! -f "${DAT_PATH}/.undat" ]]; then
echo "installed: ${DAT_PATH}/geoip.dat"
echo "installed: ${DAT_PATH}/geosite.dat"
fi
if [[ "$CONFIG_NEW" -eq '1' ]]; then
echo "installed: ${JSON_PATH}/config.json"
fi
if [[ "$CONFDIR" -eq '1' ]]; then
echo "installed: ${JSON_PATH}/00_log.json"
echo "installed: ${JSON_PATH}/01_api.json"
echo "installed: ${JSON_PATH}/02_dns.json"
echo "installed: ${JSON_PATH}/03_routing.json"
echo "installed: ${JSON_PATH}/04_policy.json"
echo "installed: ${JSON_PATH}/05_inbounds.json"
echo "installed: ${JSON_PATH}/06_outbounds.json"
echo "installed: ${JSON_PATH}/07_transport.json"
echo "installed: ${JSON_PATH}/08_stats.json"
echo "installed: ${JSON_PATH}/09_reverse.json"
fi
if [[ "$LOG" -eq '1' ]]; then
echo 'installed: /var/log/v2ray/'
echo 'installed: /var/log/v2ray/access.log'
echo 'installed: /var/log/v2ray/error.log'
fi
if [[ "$SYSTEMD" -eq '1' ]]; then
echo 'installed: /etc/systemd/system/v2ray.service'
echo 'installed: /etc/systemd/system/v2ray@.service'
fi
"rm" -r "$TMP_DIRECTORY"
echo "removed: $TMP_DIRECTORY"
if [[ "$LOCAL_INSTALL" -eq '1' ]]; then
get_version
fi
echo "info: V2Ray $RELEASE_VERSION is installed."
echo "You may need to execute a command to remove dependent software: $PACKAGE_MANAGEMENT_REMOVE curl unzip"
if [[ "$V2RAY_RUNNING" -eq '1' ]]; then
start_v2ray
else
echo 'Please execute the command: systemctl enable v2ray; systemctl start v2ray'
fi
}
main "$@"config.json 完整样例
{
"log": {
"loglevel": "warning"
},
"inbounds": [
{
"port": 11110,
"listen": "127.0.0.1",
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true
},
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls"]
}
},
{
"port": 11111,
"listen": "127.0.0.1",
"protocol": "http",
"settings": {
"auth": "noauth",
"udp": false
},
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls"]
}
}
],
"outbounds": [
{
"protocol": "vmess",
"settings": {
"vnext": [
{
"address": "***.***.***.***",
"port": 9999,
"users": [
{
"id": "********-****-****-****-************",
"alterId": 0
}
]
}
]
}
},
{
"protocol": "freedom",
"tag": "direct",
"settings": {}
}
],
"routing": {
"domainStrategy": "IPOnDemand",
"rules": [
{
"type": "field",
"ip": ["geoip:private"],
"outboundTag": "direct"
}
]
}
}