文章分类: Linux

sing-box AnyTLS 客户端指南

本文档介绍如何在 Linux 系统上安装、配置并使用 sing-box 作为本地代理客户端

下载与安装

# 下载二进制包
wget https://ap-us-files-prod.neworld.today/new/linux/sing-box-1.13.11-linux-amd64.tar.gz

# 解压
tar -xzf sing-box-1.13.11-linux-amd64.tar.gz

# 安装到系统路径
mv sing-box-1.13.11-linux-amd64/sing-box /usr/local/bin/

# 验证安装
sing-box version

# 清理临时文件
rm -rf sing-box-1.13.11-linux-amd64.tar.gz sing-box-1.13.11-linux-amd64/

正常输出(部分省略)

sing-box version 1.13.11

Environment: go1.25.9 linux/amd64
Tags: ...
Revision: ...
CGO: disabled

创建 systemd 服务

创建服务配置目录及 systemd 单元文件:

# 创建配置目录
mkdir -p /etc/sing-box

# 写入 systemd 服务文件
cat > /etc/systemd/system/sing-box.service << 'EOF'
[Unit]
Description=sing-box service
After=network.target

[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/sing-box run -c /etc/sing-box/config.json
Restart=on-failure
RestartSec=3s

[Install]
WantedBy=multi-user.target
EOF

# 重新加载 systemd
systemctl daemon-reload

自动写入配置

复制并执行下面的内容,执行后的后续操作,请看代码后的说明

cat > /tmp/setup-singbox.py << 'PYEOF'
import base64
import binascii
import json
import os
import sys
import subprocess
import urllib.parse
import urllib.request

CONFIG_PATH = "/etc/sing-box/config.json"
SOCKS_PORT = 3080
HTTP_PORT = 3081


# ───────────────────────────────────────────
# 输入订阅链接
# ───────────────────────────────────────────
print("=" * 65)
print(" sing-box AnyTLS 订阅一键配置工具")
print("=" * 65)

try:
SUB_URL = input("📎 请粘贴订阅链接: ").strip()
except (EOFError, KeyboardInterrupt):
print("\n❌ 已取消")
sys.exit(1)

if not SUB_URL:
print("❌ 订阅链接不能为空")
sys.exit(1)

if not SUB_URL.startswith(("http://", "https://")):
print("❌ 请输入 http:// 或 https:// 开头的订阅链接")
sys.exit(1)


# ───────────────────────────────────────────
# 获取订阅
# ───────────────────────────────────────────
print()
print("🌐 正在获取订阅...")

try:
req = urllib.request.Request(
SUB_URL,
headers={
"User-Agent": "sing-box",
"Accept": "*/*"
}
)

with urllib.request.urlopen(req, timeout=20) as resp:
raw_content = resp.read()

except Exception as e:
print(f"❌ 获取订阅失败: {e}")
sys.exit(1)

if not raw_content:
print("❌ 订阅返回内容为空")
sys.exit(1)

print(f"✅ 订阅获取成功,共 {len(raw_content)} Bytes")

raw_content = raw_content.strip()

# 去 UTF-8 BOM
if raw_content.startswith(b"\xef\xbb\xbf"):
raw_content = raw_content[3:]


# ───────────────────────────────────────────
# 节点列表
# ───────────────────────────────────────────
nodes = []


# ───────────────────────────────────────────
# 方法 1:解析 sing-box JSON
# ───────────────────────────────────────────
def parse_singbox_json(data):
result = []

try:
text = data.decode("utf-8-sig")
config = json.loads(text)
except Exception:
return result

outbounds = config.get("outbounds", [])

if not isinstance(outbounds, list):
return result

for outbound in outbounds:

if not isinstance(outbound, dict):
continue

if outbound.get("type") != "anytls":
continue

server = outbound.get("server")
port = outbound.get("server_port")
password = outbound.get("password")

if not all([server, port, password]):
continue

tls = outbound.get("tls") or {}

name = outbound.get("tag") or f"{server}:{port}"

sni = (
tls.get("server_name")
or server
)

insecure = bool(
tls.get("insecure", False)
)

utls = tls.get("utls") or {}

fingerprint = (
utls.get("fingerprint")
or ""
)

alpn = tls.get("alpn") or []

result.append({
"name": name,
"server": server,
"port": int(port),
"password": password,
"sni": sni,
"insecure": insecure,
"fingerprint": fingerprint,
"alpn": alpn,
})

return result


# ───────────────────────────────────────────
# 方法 2:解析 anytls:// 链接
# ───────────────────────────────────────────
def parse_anytls_links(text):
result = []

for line in text.splitlines():
line = line.strip()

if not line:
continue

if not line.lower().startswith("anytls://"):
continue

try:
p = urllib.parse.urlparse(line)

params = dict(
urllib.parse.parse_qsl(
p.query,
keep_blank_values=True
)
)

password = urllib.parse.unquote(
p.username or ""
)

server = p.hostname
port = p.port

name = (
urllib.parse.unquote(p.fragment)
or f"{server}:{port}"
)

sni = (
params.get("sni")
or params.get("peer")
or params.get("serverName")
or server
)

insecure = (
params.get("insecure", "0").lower()
in ("1", "true", "yes")
or
params.get("allowInsecure", "0").lower()
in ("1", "true", "yes")
)

fingerprint = (
params.get("fp")
or params.get("fingerprint")
or ""
)

alpn_raw = params.get("alpn", "")

alpn = []

if alpn_raw:
alpn = [
x.strip()
for x in urllib.parse.unquote(
alpn_raw
).split(",")
if x.strip()
]

if not all([password, server, port]):
continue

result.append({
"name": name,
"server": server,
"port": port,
"password": password,
"sni": sni,
"insecure": insecure,
"fingerprint": fingerprint,
"alpn": alpn,
})

except Exception:
continue

return result


# ───────────────────────────────────────────
# 自动识别订阅类型
# ───────────────────────────────────────────
text_preview = raw_content.lstrip()

# 1. JSON
if text_preview.startswith(b"{"):

print("📄 检测到 sing-box JSON 订阅")

nodes = parse_singbox_json(raw_content)

# 2. 明文 anytls://
elif b"anytls://" in raw_content:

print("📄 检测到明文 AnyTLS 订阅")

decoded = raw_content.decode(
"utf-8-sig",
errors="ignore"
)

nodes = parse_anytls_links(decoded)

# 3. Base64
else:

print("📄 检测到 Base64 订阅")

try:
b64_content = b"".join(
raw_content.split()
)

padding = len(b64_content) % 4

if padding:
b64_content += b"=" * (4 - padding)

decoded_bytes = base64.b64decode(
b64_content
)

decoded = decoded_bytes.decode(
"utf-8-sig",
errors="ignore"
)

nodes = parse_anytls_links(decoded)

except Exception as e:
print(f"❌ Base64 解码失败: {e}")
sys.exit(1)


# ───────────────────────────────────────────
# 检查解析结果
# ───────────────────────────────────────────
if not nodes:

print()
print("❌ 订阅中没有找到有效的 AnyTLS 节点")

print()
print("订阅内容前 500 个字符:")
print("─" * 65)

try:
print(
raw_content[:500].decode(
"utf-8",
errors="replace"
)
)
except Exception:
print(repr(raw_content[:500]))

print("─" * 65)

sys.exit(1)


# ───────────────────────────────────────────
# 显示节点
# ───────────────────────────────────────────
print()
print("=" * 80)
print(f"📦 共发现 {len(nodes)} 个 AnyTLS 节点")
print("=" * 80)

for i, node in enumerate(nodes, 1):

print(
f"{i:>3}. "
f"{node['name']} "
f"[{node['server']}:{node['port']}]"
)

print("=" * 80)


# ───────────────────────────────────────────
# 选择节点
# ───────────────────────────────────────────
while True:

try:
choice = input(
"👉 请输入需要使用的节点序号: "
).strip()

index = int(choice)

if 1 <= index <= len(nodes):

node = nodes[index - 1]
break

print(
f"❌ 请输入 1 - {len(nodes)} 之间的数字"
)

except ValueError:
print("❌ 请输入数字")

except (EOFError, KeyboardInterrupt):
print("\n❌ 已取消")
sys.exit(1)


# ───────────────────────────────────────────
# TLS 配置
# ───────────────────────────────────────────
tls_config = {
"enabled": True,
"server_name": node["sni"],
"insecure": node["insecure"]
}

if node["fingerprint"]:

tls_config["utls"] = {
"enabled": True,
"fingerprint": node["fingerprint"]
}

if node["alpn"]:

if isinstance(node["alpn"], list):
tls_config["alpn"] = node["alpn"]

else:
tls_config["alpn"] = [
x.strip()
for x in str(
node["alpn"]
).split(",")
if x.strip()
]


# ───────────────────────────────────────────
# 生成配置
# ───────────────────────────────────────────
config = {
"log": {
"level": "info"
},

"inbounds": [
{
"type": "socks",
"tag": "socks-in",
"listen": "127.0.0.1",
"listen_port": SOCKS_PORT
},

{
"type": "http",
"tag": "http-in",
"listen": "127.0.0.1",
"listen_port": HTTP_PORT
}
],

"outbounds": [
{
"type": "anytls",
"tag": "proxy",
"server": node["server"],
"server_port": node["port"],
"password": node["password"],
"tls": tls_config
},

{
"type": "direct",
"tag": "direct"
}
],

"route": {
"final": "proxy"
}
}


# ───────────────────────────────────────────
# 写入配置
# ───────────────────────────────────────────
try:

os.makedirs(
os.path.dirname(CONFIG_PATH),
exist_ok=True
)

with open(
CONFIG_PATH,
"w",
encoding="utf-8"
) as f:

json.dump(
config,
f,
indent=2,
ensure_ascii=False
)

except PermissionError:

print(
"❌ 无写入权限,请使用 root 用户运行"
)

sys.exit(1)

except Exception as e:

print(f"❌ 写入失败: {e}")

sys.exit(1)


# ───────────────────────────────────────────
# 显示配置
# ───────────────────────────────────────────
print()
print("=" * 65)
print("✅ 节点配置已生成")
print("=" * 65)

print(f"📌 节点名称 : {node['name']}")
print(f"🌐 服务器 : {node['server']}:{node['port']}")
print(f"🔒 SNI : {node['sni']}")
print(f"⚠️ insecure : {node['insecure']}")
print(f"🧦 SOCKS5 : 127.0.0.1:{SOCKS_PORT}")
print(f"🌍 HTTP : 127.0.0.1:{HTTP_PORT}")


# ───────────────────────────────────────────
# 检查 sing-box
# ───────────────────────────────────────────
try:

version = subprocess.run(
["sing-box", "version"],
capture_output=True,
text=True
)

except FileNotFoundError:

print()
print("❌ 未找到 sing-box")

sys.exit(1)


# ───────────────────────────────────────────
# 校验配置
# ───────────────────────────────────────────
print()
print("─" * 65)
print("🔍 正在校验配置文件...")

check = subprocess.run(
[
"sing-box",
"check",
"-c",
CONFIG_PATH
],
capture_output=True,
text=True
)

if check.returncode != 0:

print(
"❌ 配置校验失败,已终止重启:"
)

print(
check.stderr
or check.stdout
)

sys.exit(1)

print("✅ 配置校验通过")


# ───────────────────────────────────────────
# 启动服务
# ───────────────────────────────────────────
print("─" * 65)
print("🔄 正在重启 sing-box 服务...")

for cmd in [
["systemctl", "enable", "sing-box"],
["systemctl", "restart", "sing-box"],
]:

ret = subprocess.run(
cmd,
capture_output=True,
text=True
)

label = " ".join(
cmd[1:]
)

if ret.returncode == 0:

print(
f"✅ systemctl {label}"
)

else:

print(
f"⚠️ systemctl {label} 失败:"
)

print(
ret.stderr
or ret.stdout
)


# ───────────────────────────────────────────
# 显示状态
# ───────────────────────────────────────────
print()
print("─" * 65)

status = subprocess.run(
[
"systemctl",
"status",
"sing-box",
"--no-pager",
"-l"
],
capture_output=True,
text=True
)

print(
status.stdout
or status.stderr
)

PYEOF

python3 /tmp/setup-singbox.py

脚本说明
脚本会自动完成以下操作:

  1. 解析粘贴的节点链接(支持 AnyTLS 格式)
  2. 生成 /etc/sing-box/config.json
  3. 校验配置合法性
  4. 重启并启用 sing-box 服务

导入配置
执行脚本时会遇到脚本要求粘贴节点链接

root@debian:~# python3 /tmp/setup-singbox.py
=======================================================
sing-box AnyTLS 一键配置工具
=======================================================
📎 请粘贴节点链接:

内容是在用户中心的节点列表获取,点击如图所示的复制按钮


正常输出(部分内容使用 xxx 隐去)

配置已写入 : /etc/sing-box/config.json
📌 节点名称 : California (美国) B26
🌐 服务器 : xxx:xxx
🔑 密码 : xxx
🔒 TLS : True insecure: False
🧦 SOCKS5 : 127.0.0.1:3080
🌍 HTTP 代理 : 127.0.0.1:3081

───────────────────────────────────────────────────────
🔍 正在校验配置文件...
✅ 配置校验通过
───────────────────────────────────────────────────────
🔄 正在重启 sing-box 服务...
✅ systemctl enable sing-box
✅ systemctl restart sing-box
───────────────────────────────────────────────────────
● sing-box.service - sing-box service
Loaded: loaded (/etc/systemd/system/sing-box.service; enabled; vendor preset: enabled)
Active: active (running) since Thu 2026-05-14 07:56:01 UTC; 19ms ago
Main PID: 92970 (sing-box)
Tasks: 6 (limit: 515)
Memory: 2.1M
CPU: 11ms
CGroup: /system.slice/sing-box.service
└─92970 /usr/local/bin/sing-box run -c /etc/sing-box/config.json

May 14 07:56:01 krc01 systemd[1]: Started sing-box service.

此时,客户端已经在工作了,可以使用下面的命令检查工作状态

curl myip.ipip.net --socks5 127.0.0.1:3080

正常输出(根据选择节点的不同,内容将有差异,是正常情况)

当前 IP:154.xx.xx.xx  来自于:美国 加利福尼亚州 洛杉矶  cogentco.com

服务启动后将监听以下本地端口:

协议

地址

用途

SOCKS5

127.0.0.1:3080

通用代理,支持 TCP/UDP

HTTP

127.0.0.1:3081

HTTP/HTTPS 代理

温馨提示

如何为 git wget curl 配置代理,或是你需要走代理的程序配置代理,可以询问各大 AI

启动与管理服务

温馨提示
下面列出的命令不需要执行,仅做说明之用

# 启动并设置开机自启
systemctl enable --now sing-box

# 查看运行状态
systemctl status sing-box

# 重启服务
systemctl restart sing-box

# 停止服务
systemctl stop sing-box

# 实时查看日志
journalctl -u sing-box -f

# 查看最近 100 行日志
journalctl -u sing-box -n 100 --no-pager

卸载与清理

注意
执行卸载操作将停止代理服务并删除所有相关文件,请确认不再需要后再操作。

第一步:停止并禁用服务

systemctl stop sing-box
systemctl disable sing-box

第二步:删除 systemd 服务文件

rm -f /etc/systemd/system/sing-box.service
systemctl daemon-reload
systemctl reset-failed

第三步:删除二进制文件与配置

# 删除主程序
rm -f /usr/local/bin/sing-box

# 删除配置目录(包含 config.json)
rm -rf /etc/sing-box/

第四步:清理临时脚本(如有)

rm -f /tmp/setup-singbox.py

验证卸载完成

# 确认二进制已删除
which sing-box && echo "未删除" || echo "✅ 已删除"

# 确认服务已移除
systemctl status sing-box 2>&1 | grep -q "not found" && echo "✅ 服务已移除"

# 确认配置目录已删除
[ -d /etc/sing-box ] && echo "目录仍存在" || echo "✅ 配置目录已删除"

更新于: 04/09/2026

这篇文章有帮助吗?

分享您的反馈意见

取消

谢谢!