服务器入坑指北:配置 NFTables 防御服务器攻击

举报
Tulpartner 发表于 2026/10/18 08:25:50 2026/10/18
【摘要】 公网机器仅靠 fail2ban、禁用密码登录等常规措施不足:分散式扫描不易触发阈值,公开恶意 IP 也可能尚未被日志记录;更关键的是出方向风险,机器一旦被当作跳板,入站规则就失效,必须限制其连接已知恶意端点。作者的做法是拉取多份开源威胁名单,生成 nftables 的四个集合——白名单兜底、入站两层、出站一层——并用脚本每天自动更新,加载失败则回滚;文中名单条数和端口号仅为示例,需按实际环境调整。

公网机器上线之后,多数人的安全动作到"装个 fail2ban、关掉密码登录"就停了——看日志里安静了些,就当没事了。真正在打你的东西往往不出现在那几行日志里:成百上千个来源分散扫描、扫完就走,阈值还没触发,那一轮已经结束了;还有些 IP 早就因为攻击别人被记在公开名单上,你的机器还没被探到,它们的名字已经在册了。
更麻烦的一层在出方向:机器一旦被当成跳板,入站规则就再也没有意义——它要主动往外连。这时候能限制它的,只剩"不许它跟已知的恶意端点通信"这一条。
这篇是我自己在用的做法:把几份开源威胁名单拉下来,做成 nftables 的四个集合——白名单兜底、入站两层、出站一层;再让脚本每天自动更新,加载失败就回滚。下面的名单条数、端口号都是示例,按自己的机器改。

为什么只装 fail2ban 不够

fail2ban 的机制是"读本机服务日志 → 发现连续失败 → 临时封禁来源"。它的观察面是本机日志,介入时机是攻击已经打到服务上之后:对单一来源反复试密码很有效,但面对分散来源就吃力——每个 IP 只试几次就换下一个,等不到阈值;而对"这批网段早就在公开名单里"这类情报,它没有数据来源。
nftables 集合方案补的是另一个环节:在数据包进入内核转发路径时,用一份外部持续更新的名单做丢弃判定。区别可以这么看——fail2ban 是"被打之后记账",名单是"进门之前查册"。两者不冲突,同时开着也合理;只是后者的覆盖面不依赖你的机器是否已经被探到。

四层设计:白名单 + 入站两层 + 出站一层

层 集合名 名单来源 抓什么 用在哪
白名单 blk_allow 本机与内网段 永不拦截 input / output 前置放行
入站 · SSH 层 blk_ssh 正在尝试 SSH 口令认证的来源、SSH 攻击上报、扫描爆破汇总 口令爆破与端口扫描来源 入站丢弃
入站 · Web 与综合层 blk_web Web 攻击上报、机器人 IP、综合恶意网段 Web 攻击来源、已知机器人、综合黑名单 入站丢弃
出站层 blk_egress 「一个 web 客户端永远不该与之通信的 IP」 含 cybercrime 成分的恶意端点 仅出站丢弃

出站层为什么是灵魂

入站规则的前提是"攻击从外面进来"。可如果机器已经被拿下、正在被人当跳板,它的行为是由内向外的:扫描别人的端口、往外发垃圾流量、回连自己的控制端。这些包在入站链上根本不会经过。
出站这一层做的事就一句话:即使机器已经沦陷,它仍然无法与已知的恶意端点通信。落到规则上,它是 output 链里针对 ip daddr 的丢弃——方向必须是出站,写成入站就完全失效,这是个方向性的错误,不是程度问题。
它的名单也必须是"客户端不该通信的 IP"这一类,而不是随便一份黑名单:出站丢弃会直接影响本机自己发起的连接,选错名单会把自己的正常访问一起掐掉(见下面关于 firehol_level1 的说明)。

为什么入站只留两层

按来源拆得更细当然可以,代价是处处要跟着改:多一个集合、多一组来源数组、多一条规则,丢弃时的可观测粒度是细了,但维护的来源清单也更多、更容易漂移。这里把"Web 攻击来源"与"综合黑名单"合并成 blk_web,保留"入站两层 + 出站一层 + 白名单"的结构——想更细,照同样的写法拆开即可,不是必须四层。

为什么用集合而不是逐条规则

名单动辄几万到几十万条。写成逐条规则的话,匹配是逐行线性走,规则数量还会把规则集的加载和更新拖垮,转发路径上的每个包都要多走一遍长链。
nftables 的命名集合由内核索引,查找代价与条目数近似无关:非区间集合走哈希表,区间集合(也就是我们这里用的 flags interval)走红黑树,复杂度是 O(log n)——名单从几千条涨到几十万条,每个包的成本只是缓慢上升,远优于把上万条写成逐条规则。这就是这件事能从"手工维护不现实"变成"每天自动更新"的前提。

firehol_level1 只能用于入站

firehol_level1 是综合黑名单,特点是防护面大、误报少,但它的内容本身就包含 bogon 与内网段——0.0.0.0/8、10.0.0.0/8、127.0.0.0/8、192.168.0.0/16 这类都在里面。
所以它只能放在入站方向:入站方向上"来自内网段的包"本来就不该从公网进来,丢掉没有副作用。而如果把它用在出站,你的机器发往自己内网的连接会被它自己的防火墙丢掉——这就是脚本清洗管线里"排除内网与保留段"那一步存在的因果:不排除,综合名单会把内网通信一起封死。

初始配置:从装包到第一次生效

从零搭起来的完整顺序就在下面。每一步都有命令和判据,按顺序做完,再考虑自动化。
1. 装 nftables,并让它开机加载。

# Debian / Ubuntu
apt install -y nftables
# RHEL 系
dnf install -y nftables
systemctl enable --now nftables

判据:systemctl is-enabled nftables 返回 enabled。这一步最容易被跳过,但它和后面的「重启炸弹」直接相关——开机是由 nftables.service 读 /etc/nftables.conf 加载规则的,不启用的话,现在手工生效的规则在重启后就全没了。
2. 建目录。

mkdir -p /etc/nftables/sets

判据:ls -d /etc/nftables/sets 能列出。生成文件要写进这个目录,目录不存在,脚本落盘那一步就会失败。
3. 写主配置 /etc/nftables.conf。

#!/usr/sbin/nft -f
# /etc/nftables.conf —— include 放最前:生成文件里是 define 变量,必须先于 set 声明出现
include "/etc/nftables/sets/blocklist_sets.nft"
table inet filter {
    set blk_allow  { type ipv4_addr; flags interval; auto-merge; elements = $blk_allow  }
    set blk_ssh    { type ipv4_addr; flags interval; auto-merge; elements = $blk_ssh    }
    set blk_web    { type ipv4_addr; flags interval; auto-merge; elements = $blk_web    }
    set blk_egress { type ipv4_addr; flags interval; auto-merge; elements = $blk_egress }
    chain input {
        type filter hook input priority filter; policy drop;
        ct state established,related accept
        iif lo accept
        ip saddr @blk_allow accept
        tcp dport 22 accept
        ip saddr @blk_ssh counter drop
        ip saddr @blk_web counter drop
    }
    chain output {
        type filter hook output priority filter; policy accept;
        ip daddr @blk_allow accept
        ip daddr @blk_egress counter drop
        ct state established,related accept
    }
}

几个要点:

  • include 必须放在最前面:生成文件里是一组 define 变量,变量要先于使用它们的 set 声明出现。
  • 四个 set 的统一写法是 type ipv4_addr; flags interval; auto-merge; elements = $同名变量。flags interval 允许 CIDR 区间;auto-merge 不能省——名单里全是重叠区间,不加它整份规则的加载会失败(报 conflicting intervals specified)。
  • counter 只计数、不影响判定,用来观察哪一层命中最多。
  • output 里的顺序是白名单 → 出站丢弃 → 放行已建立连接。出站丢弃必须排在 ct state established,related accept 之前:nftables 按序求值,accept 一命中就终止判定,已经建立的出站连接同样要先过黑名单——机器沦陷时,现存的回连与对外攻击会话正好属于"已建立连接",从那条 accept 漏过去,这一层就白设了。
  • 先把 tcp dport 改成你真正的 SSH 端口,确认 ct state established,related accept 与 iif lo accept 都在,并且保留一个已登录会话与云控制台/VNC 通道,再让默认 drop 生效。 顺序错了就是当场失联。
  • 兼容性备注:如果你的 nft 版本不接受 elements = $变量,两种等价做法——在规则里直接引用变量(ip saddr $blk_ssh counter drop),或者取消 elements = 那一行、让生成文件直接输出整段 set 声明。
    判据:这一步先别纠结 nft -c -f 报错——生成文件还没被创建,include 自然找不到;下一步跑完脚本它就存在了。
    4. 把脚本装到固定路径,然后首次运行——这一步就是「构建」。
install -m 0755 nft-blocklist-sync.sh /usr/local/sbin/nft-blocklist-sync.sh
/usr/local/sbin/nft-blocklist-sync.sh

先把它装到 /usr/local/sbin:第 6 步的 cron 与 systemd timer 都是按这个路径调用的,脚本留在当前目录不搬过去,定时任务只会静默地什么都不做(cron 的失败不会报到你的终端上)。install -m 0755 一步完成复制与可执行位设置,等价于 cp 之后再 chmod +x。
它做的是:抓取名单 → 清洗 → 生成 blocklist_sets.nft → 备份上一版 → 落盘 → nft -f 原子加载。首次运行前生成文件不存在是正常的,脚本会把它建出来;需要 root 权限。
判据:ls -l /usr/local/sbin/nft-blocklist-sync.sh 能看到文件且带 x 位(command -v nft-blocklist-sync.sh 也应有输出);脚本在终端上逐层报出条数,最后是「更新完成,规则已加载」;ls -l /etc/nftables/sets/blocklist_sets.nft 能看到一份非空文件。
5. 验证。

nft -c -f /etc/nftables.conf                     # 干跑语法检查:只解析,不加载
nft list set inet filter blk_ssh | head          # 看集合里有没有条目
nft list ruleset | head -40                      # 看规则顺序:egress 的 drop 在放行之前
nft list table inet filter                       # 带 counter 时能看到每条规则的命中次数

判据:干跑没有输出(通过);集合有内容;output 链里 egress 的丢弃在放行之前;跑一阵子后入站两条 drop 的 counter 开始涨。
6. 上定时任务做生产维护。
二选一即可,仍然用同一个脚本,不要再写第二个:

# /etc/cron.d/nft-blocklist-sync
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
17 4 * * * root /usr/local/sbin/nft-blocklist-sync.sh >>/var/log/nft-blocklist-sync.log 2>&1
# /etc/systemd/system/nft-blocklist-sync.service
[Unit]
Description=Refresh nftables blocklist sets
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/nft-blocklist-sync.sh
Nice=10
# /etc/systemd/system/nft-blocklist-sync.timer
[Unit]
Description=Daily refresh of nftables blocklist sets
[Timer]
OnCalendar=*-*-* 04:17:00
RandomizedDelaySec=30m
Persistent=true
[Install]
WantedBy=timers.target

unit 文件放好不等于生效,timer 要显式启用:

systemctl daemon-reload
systemctl enable --now nft-blocklist-sync.timer

判据:cron 方案等了第一次触发后 tail /var/log/nft-blocklist-sync.log 能看到这一轮输出;timer 方案先看 systemctl is-enabled nft-blocklist-sync.timer 返回 enabled,再用 systemctl list-timers nft-blocklist-sync.timer 看到下次触发时间,用 journalctl -u nft-blocklist-sync.service 回看每轮结果。RandomizedDelaySec 让各机器错开拉取,减少对名单源的瞬时压力;Persistent=true 让关机错过的任务在开机后补跑一次。
7. 回滚与失联预案。

  • 脚本在 nft -f 失败时会回滚磁盘文件:这一步防的是「重启炸弹」——加载失败的当下业务没断(内核还是旧规则),但磁盘上留着一份坏配置,下次重启 nftables.service 加载失败,机器就会不带任何防火墙启动;首次部署没有备份可回滚时,它会把坏文件删掉,同样是为了不把炸弹留给下一次重启。
  • 动手前保留一个已登录的 SSH 会话,并确认云控制台/VNC 那条带外通道可用——规则改错时,它是你唯一的入口。
  • nft flush ruleset 是最后手段:它会清空全部规则(包括 SSH 放行),只在带外通道下用来紧急恢复。

完整脚本

构建(首次生成)与日常维护用的是同一个脚本:第一次执行它就是「构建」,之后交给定时任务做日常维护,不需要也不建议拆成第二个。

#!/usr/bin/env bash
#
# nft-blocklist-sync
# 抓取开源威胁情报名单 → 清洗 → 生成 nftables 集合定义 → 原子加载,失败回滚。
#
# 部署前请自行确认(脚本不代改你的主配置):
#   1) /etc/nftables.conf 中有:include "/etc/nftables/sets/blocklist_sets.nft"
#   2) 其中对应的 set 声明带:flags interval; auto-merge;
# 只处理 IPv4。
set -uo pipefail
# ============================== 配置区 ==============================
OUT_FILE="/etc/nftables/sets/blocklist_sets.nft"   # 生成文件(被主配置 include)
STAGE_FILE="/tmp/nft-blocklist.stage"               # 生成中的临时文件
ROLLBACK_FILE="${OUT_FILE}.last-good"               # 上一版备份(加载失败回滚用)
NFT_CONF="/etc/nftables.conf"
FEED_BASE="https://iplists.firehol.org/files"
# 永远放行:本机与内网
ALLOW_RANGES="127.0.0.1, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16"
# 集合名
SET_ALLOW="blk_allow"
SET_SSH="blk_ssh"
SET_WEB="blk_web"
SET_EGRESS="blk_egress"
# 各层来源(加来源就往数组里加一项)
SSH_FEEDS=(
  "dataplane_sshpwauth.ipset"   # SSH 口令爆破来源
  "blocklist_de_ssh.ipset"      # Blocklist.de 上报的 SSH 攻击来源
  "greensnow.ipset"             # 扫描与爆破来源
)
WEB_FEEDS=(
  "blocklist_de_apache.ipset"   # Blocklist.de 上报的 Web 攻击来源
  "botscout_1d.ipset"           # 机器人 IP(1 天窗口)
  "firehol_level1.netset"       # 综合黑名单:最大防护、最少误报
)
EGRESS_FEEDS=(
  "firehol_webclient.netset"    # 出站层:客户端永远不该通信的 IP
)
# ============================== 抓取 + 清洗 + 生成 ==============================
# 用法:build_layer <集合名> <来源...>;成功输出一行 define,失败返回非 0
build_layer() {
  local set_name="$1"; shift
  local pool failed=0 src ips
  pool=$(mktemp)
  for src in "$@"; do
    if curl -fsSL --retry 2 --max-time 20 "${FEED_BASE}/${src}" >>"$pool" 2>/dev/null; then
      echo "  下载 OK   ${src}" >&2
    else
      echo "  下载失败 ${src}" >&2
      failed=$((failed + 1))
    fi
  done
  # 熔断:全部来源失败就放弃本层,绝不用空名单去覆盖
  if [ "$failed" -eq "$#" ]; then
    echo "  ${set_name}: 全部来源失败,放弃本层" >&2
    rm -f "$pool"
    return 1
  fi
  # 去注释与空行 → 只留 IPv4/CIDR → 排除内网与保留段 → 去重
  # 排除内网段是必需的:综合黑名单本身就带 10./192.168./0./127. 这些段
  ips=$(grep -vE '^[[:space:]]*(#|$)' "$pool" \
    | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}(/[0-9]{1,2})?' \
    | grep -vE '^(0\.|10\.|127\.|169\.254\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.)' \
    | sort -u)
  rm -f "$pool"
  if [ -z "$ips" ]; then
    echo "  ${set_name}: 清洗后为空,放弃本层" >&2
    return 1
  fi
  # 一行 define;paste 拼接,末尾不带逗号(nftables 对尾逗号敏感)
  printf 'define %s = { %s }\n' "$set_name" "$(printf '%s\n' "$ips" | paste -sd, -)"
  echo "  ${set_name}: $(printf '%s\n' "$ips" | wc -l) 条" >&2
}
# ============================== 主流程 ==============================
[ "$(id -u)" -eq 0 ] || { echo "需要 root 权限" >&2; exit 1; }
mkdir -p "$(dirname "$OUT_FILE")"
FAIL=0
{
  echo "# 由 nft-blocklist-sync 生成于 $(date '+%F %T'),请勿手工编辑"
  printf 'define %s = { %s }\n' "$SET_ALLOW" "$ALLOW_RANGES"
} >"$STAGE_FILE"
build_layer "$SET_SSH"    "${SSH_FEEDS[@]}"    >>"$STAGE_FILE" || FAIL=1
build_layer "$SET_WEB"    "${WEB_FEEDS[@]}"    >>"$STAGE_FILE" || FAIL=1
build_layer "$SET_EGRESS" "${EGRESS_FEEDS[@]}" >>"$STAGE_FILE" || FAIL=1
# 任一层失败都整体放弃:不做部分更新
if [ "$FAIL" -ne 0 ]; then
  echo "至少一层不可用,本次不更新" >&2
  rm -f "$STAGE_FILE"
  exit 1
fi
# 备份 → 落盘 → 原子加载 → 失败回滚
[ -f "$OUT_FILE" ] && cp -a "$OUT_FILE" "$ROLLBACK_FILE"
mv "$STAGE_FILE" "$OUT_FILE" && chmod 644 "$OUT_FILE"
if nft -f "$NFT_CONF"; then
  rm -f "$ROLLBACK_FILE"
  echo "更新完成,规则已加载" >&2
else
  echo "加载失败:回滚磁盘文件" >&2
  echo "(否则重启后 nftables.service 会因坏文件加载失败,机器将不带防火墙启动)" >&2
  if [ -f "$ROLLBACK_FILE" ]; then
    mv "$ROLLBACK_FILE" "$OUT_FILE"
  else
    rm -f "$OUT_FILE"
  fi
  exit 1
fi

脚本逐段讲:机制与判断

1. 配置区:加一层只改三处。 生成文件与临时文件的路径、回滚备份路径、名单源基址、ALLOW_RANGES 白名单、四个集合名、三层来源数组,都在文件开头。白名单是最高优先级、永远放行;要新增一层,只需三处改动——配置区加一个来源数组、主流程加一行 build_layer、nftables.conf 里加 set 与规则。这是它可维护的原因:分层是数据,不是逻辑。
2. 抓取 build_layer。 每个来源用 curl -fsSL --retry 2 --max-time 20 下载:失败会重试、超时会中断,而且不带 -k——不为了「能下下来」就关掉证书校验。单个来源失败只记一条告警;全部来源失败,整层放弃——绝不能拿一份空名单去覆盖现网,那等于把自己的规则一夜清空。
3. 清洗管线。 顺序是:去掉注释与空行 → 用正则提纯 IPv4 与 CIDR → 排除内网与保留段(0.、10.、127.、169.254.、192.168.、172.16–172.31)→ 排序去重。排除这一步不是洁癖:综合名单本身就带内网与 bogon 段,留下去就会掐断自己的内网通信(前面讲过因果)。
4. 空值检查。 清洗完为空,与「全部来源失败」同样处理:放弃本层。空集合本身不会报错,但会让这一层静默失效——把它当异常拦住,比事后从日志里找原因便宜得多。
5. 生成格式。 每层输出一行 define,用 paste -sd, - 把 IP 拼成逗号分隔,末尾不带逗号——nftables 对尾逗号敏感,带上就解析失败,整份规则都进不去。
6. 整体放弃。 主流程用一个失败标记累积三层的返回值,任何一层失败就删掉临时文件、本次不更新。不做部分更新的理由很实际:规则看着能用,实际名单新旧不一,出问题时你很难想到是「这次只更新了一部分」。
7. 备份 → 落盘 → 原子加载 → 失败回滚。 cp -a 备份上一版 → mv 落盘 → nft -f 原子加载(整份解析通过才生效,失败时内核仍在使用旧规则)→ 成功删除备份;失败则把磁盘文件回滚。这里最容易被忽略的是失败之后为什么必须回滚磁盘文件:nft -f 失败时业务并没有断,看起来「没生效而已」,但磁盘上已经留下了那份坏配置;下次机器重启,nftables.service 按坏文件加载会失败,结果是机器在一次重启之后不带任何防火墙启动。首次部署时没有备份可回滚,脚本会删掉坏文件——防的是同一件事。
8. 部署顺序。 先按上一节把 nftables.conf 写好(include + 四个 set 声明 + input/output 规则),再跑脚本:首次运行即完成构建,之后把它交给 cron 或 systemd timer 做日常维护。构建与维护是同一个脚本,不需要也不建议拆成两个——拆开之后,「生成规则」和「加载规则」的清单很容易各自漂移。

日常观测:怎么知道它在干活

nft -c -f /etc/nftables.conf          # 干跑语法检查:只解析,不加载
nft list set inet filter blk_ssh | head
nft list ruleset | head -40            # 确认 egress 的丢弃在放行之前
nft list table inet filter             # 带 counter 时看每条规则命中次数

观测建议:入站两条丢弃规则的 counter 一直涨,说明确实在拦;某一层长期不命中,可以考虑精简它的来源。出站层反过来用——先只计数、不丢弃(把规则里的 drop 去掉,只留 counter),观察一段时间,确认没有误伤自己的正常访问,再打开丢弃。
日志看两处:cron 方案在 /var/log/nft-blocklist-sync.log;systemd timer 方案用 journalctl -u nft-blocklist-sync.service。

踩坑清单

  • 先把 SSH 端口和已建立连接放行,再把 input 策略改成 drop;改动前保留一个已登录会话与控制台通道,否则改错就是失联。
  • input 改成 drop 之后,已发布端口上的新连接也会被一起拦掉:外部流量经 DNAT 转到容器或服务,之后走的是 input 钩子,并不在 established,related 或 iif lo 的豁免里。机器上跑着 Docker 这类发布端口的服务时,先把要保留的端口写成放行规则(或临时把 policy 留在 accept、只加 counter 观察一段时间再收紧);现象是「防火墙没报错、本机访问正常、外部连不上」,很容易误判成服务挂了。
  • 改完先 nft -c -f 干跑,再 nft -f。干跑只解析、不加载,能挡住绝大多数语法错误。
  • 别把坏文件留在磁盘上又开着 nftables.service 自动加载:那是重启炸弹——加载失败的当下业务还在跑,重启之后机器不带防火墙。
  • firehol_level1 含内网与 bogon 段,只能用于入站;出站名单要选"客户端不该通信的 IP"这类,否则掐断自己的内网。
  • 白名单必须包含 127.0.0.1 与自己的内网段,漏了会掐断本机与内网通信。
  • 名单会变、也会误报:出站层先只计数不丢弃,观察一段时间再正式启用。
  • 集合声明少写 flags interval 或 auto-merge:重叠区间会让整份规则加载失败(报 conflicting intervals specified)。
  • 生成的 define 末尾带逗号:nftables 解析不过,整份加载失败。
  • 某一层失败时做部分更新:规则看起来能用,实际名单新旧不一,排障时很难想到这一层。
  • 本方案是纯 IPv4;IPv6 要另做一套 ip6 表,别以为一个 inet 表就都管了。
  • 名单源是第三方公益服务,可能限速或临时不可用——熔断、整体放弃与回滚因此是必需项,不是可选项。
  • 下载失败去加 -k 关掉证书校验:这是把可用性问题换成了信任问题,别这么干。
  • 只装了 nftables 却没 systemctl enable --now nftables:当前规则是生效的,重启之后全没了。
  • 第一次运行前发现生成文件不存在就手工去建:它是脚本的产物,跑一次脚本自然会出现。
  • 把 nft flush ruleset 当日常手段:它会清空全部规则(包括 SSH 放行),只在带外通道下作紧急恢复用。
  • 装了 fail2ban 就以为不用管已知恶意网段:两者观察的环节不同,不能互相替代。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。