保护 Garry's Mod 服务器免受 DDoS 攻击

发布于 阅读时间 35 分钟

在 Garry's Mod 上,游戏流量和服务器查询都走 27015 这同一个端口。哪些规则在服务器上真正有效,如何加固 RCON 和 Lua 网络事件,以及攻击规模到什么程度就只能靠服务器前面的网络过滤。

一台 Garry's Mod 服务器如果在晚上八点消失三分钟然后又回来,问题很少出在硬件上。绝大多数情况下是有人正在攻击,而且它正好发生在玩家连接最多的时候。所以 Garry's Mod 的 DDoS 防护首先意味着:清楚哪些数据包才允许到达您的服务器。本文按这个顺序讲:接下来十分钟里您不花一分钱就能自己加固什么,这些措施在物理上到哪里为止,以及之后在服务器前面的网络里必须发生什么。

本文所有内容针对运行在 Debian 12、Debian 13、Ubuntu 22.04 LTS 或 Ubuntu 24.04 LTS 上的 srcds 服务器。配置文件位于 garrysmod/cfg/server.cfg,命令以 root 身份书写,普通用户请在前面加上 sudo。这里说的始终是在自己的根服务器或独立服务器上运行,而不是在某个游戏服务器商那里租一个槽位。

如果攻击正在进行:现在不要改动 server.cfg,也不要重启 srcds。请先保存测量数据(第 9 节),因为攻击结束后它们就没有了。重启会让您丢掉计数器,之后又把服务器送回同一场洪水里。

为什么 Garry's Mod 服务器需要 DDoS 防护

Garry's Mod 服务器会自己公开它的 IP 地址和端口。这不是疏忽,而是前提:不在服务器浏览器里出现,就不会有新玩家。这个条目之所以存在,是因为服务器会在 Steam 主服务器上注册,随后回答每一次从外面来的 A2S 查询。所以问题从来不是攻击者能不能找到您的地址,而只是他朝这个地址开火时会发生什么。

再加上社区的性质。Garry's Mod 大多不是按局来玩的,而是长期存在的世界:一个 DarkRP 社区会在数据库里连续几个月记录玩家账号、财产、职业和进度。因此周五晚上的一次宕机代价高于输掉一场对局,它让您失去常客。正因如此,竞争社区、被封禁的玩家和买来的服务器 booter(每月只要几欧元就能对任意地址发起攻击的服务)是最常见的三种起因。攻击者既不需要什么本事,也花不了多少钱。

技术上有三个特点凑在了一起。游戏流量走 UDP,而 UDP 没有可以强制要求的连接建立过程:源地址可以伪造。服务器查询和游戏在同一个端口上,所以一条粗暴的封禁总会同时打到两者。而在这一切之上还有 Lua:每一个 Steam 创意工坊 Addon 都会把自己的代码带进同一个进程,只要有一个没有保护的网络事件,单个客户端就能不用任何带宽把服务器拖慢。DDoS 攻击原则上是什么,请看文章 什么是 DDoS 攻击?。

Garry's Mod 上真正相关的那些端口

Garry's Mod 服务器默认在 27015 端口上启动,其中 UDP 承载游戏连同服务器查询,TCP 承载 RCON。端口号在启动时用 -port 改动,有多个实例时依次往上数(27016、27017 等等)。一条典型的启动命令是这样:

./srcds_run -game garrysmod -console \
  -port 27015 \
  +maxplayers 64 \
  +gamemode darkrp \
  +map rp_downtown_v4c_v2 \
  +sv_setsteamaccount YOUR_GSLT_TOKEN \
  +host_workshop_collection 123456789 \
  -authkey YOUR_STEAM_WEB_API_KEY

整个攻击面由此得出。下面这张表是后文每一条防火墙规则的基础:

端口和协议 用途 可通过什么更改 是否属于公网
27015/UDP 游戏流量和 A2S 查询在同一个端口上 -port 是,这是唯一真正必须开放的端口
27015/TCP RCON,也就是 Source 的 RCON 协议 -port(与游戏同一个号) 否,仅限您自己的地址
27005/UDP 客户端端口,从玩家那边发出 -clientport 否,服务器上不需要规则
27020/UDP SourceTV +tv_port 只有在您真的做转播时才需要
26901/UDP 在 Steam 主服务器上注册 出站 否,不需要入站规则
80/TCP 和 443/TCP 通过 sv_downloadurl 的 FastDL,如果网页服务器在同一台机器上 网页服务器 只有 FastDL 在那里时才需要(最好分开)
3306/TCP 供 DarkRP 和玩家数据使用的 MySQL(通过 mysqloo 模块) bind-address 否,只绑定 127.0.0.1
22/TCP SSH 访问 sshd_config 是,但要加限制

这八个条目里,只有一个可以毫无限制地放进公网:27015/UDP。其余都要么限制到您自己的地址,要么绑定到 127.0.0.1,要么干脆不启动。这个题目里代价最高的想法错误,是以为 Garry's Mod 有一个可以直接关掉的独立查询端口。这个端口并不存在。

在花钱之前,您自己能做的事

这一节最长,而且是故意的。配置干净的 Garry's Mod 服务器能靠自己扛住小型和中型攻击,无论它放在谁那里。这里没有一项要花钱,而且大部分一刻钟就能做完。

1. 清点:到底有什么在监听

在写下第一条规则之前,先看清楚您的服务器对外提供了什么。不要猜,要查:

ss -lntup

要看的是本地地址那一列。0.0.0.0:27015 和 [::]:27015 表示“整个互联网都能访问”,127.0.0.1:3306 表示“仅本机”,不需要防火墙规则。在一台长期发展起来的 DarkRP 服务器上,那里几乎总会有比预想更多的服务:MySQL、给 FastDL 用的网页服务器、一个面板、一个 Discord 机器人、27016 上的第二台测试服,以及一个被遗忘的语音服务。从外面做一次端口扫描,就能得到攻击者眼中的样子:

nmap -Pn -sU -sT -p- --min-rate 1000 YOUR.SERVER.IP.ADDRESS

2. 只让 srcds 真正需要的端口保持开放

对 Garry's Mod 来说,对外一条放行就够了,再加上 SSH 和受限的 RCON 访问。用 UFW 的写法如下,而且顺序必须完全照此执行,以免把自己关在门外:

ufw allow 22/tcp comment 'SSH'
ufw allow 27015/udp comment 'Garrys Mod 游戏和 A2S'
ufw allow from 203.0.113.10 to any port 27015 proto tcp comment 'RCON'
ufw default deny incoming
ufw default allow outgoing
ufw --force enable
ufw status verbose

请把 203.0.113.10 换成您自己的地址。27020/UDP 上的 SourceTV 只有在您真的做转播时才放行。包含自救办法的完整说明见 设置 UFW 防火墙而不把自己关在门外。万一还是出了事:KernelHost 的 KVM 根服务器和独立服务器没有 IPMI,也没有 iDRAC,您可以通过客户中心里的 VNC 控制台回来。它不挂在客户机系统的网络栈上,客户机里的防火墙规则拦不住它。

数据库在任何情况下都不该进公网。请在 /etc/mysql/mariadb.conf.d/50-server.cnf 里确认其中写着:

bind-address = 127.0.0.1

3. 限制 A2S 查询,又不从服务器列表里掉下去

这里正是让大多数 Garry's Mod 服务器付出代价的错误。因为游戏流量和服务器查询占用同一个端口,所以在 27015/UDP 上一刀切地封禁,或者把速率限制设得太严,都会把自己的玩家踢出去,等于替攻击者把事情做完。正确的着手点是区分查询数据包和游戏数据包。

引擎为此自带三个控制台变量,写在 server.cfg 里。它们的默认值很保守,但确实是设了的:

sv_max_queries_sec 3
sv_max_queries_sec_global 60
sv_max_queries_window 30

sv_max_queries_sec 限制按源地址回答的查询数(默认每秒 3 次),sv_max_queries_sec_global 给所有地址的总和设上限(默认每秒 60 次),sv_max_queries_window 规定平均窗口(默认 30 秒)。这些值保护 CPU 不去白白生成回应。它们并不能阻止数据包到达,而且把全局值拉得很紧的人,会在攻击期间从服务器浏览器里消失,因为列表网站的查询也一起得不到回答了。

再往下一层,就能干净地把查询流量分离出来。Source 引擎的所有无连接数据包都以四个全置位的字节(0xffffffff)开头,而已经连上的玩家的流量没有这个头。正好可以用 nftables 在这上面按源地址加一道速率限制:

table inet gmod {
    chain input {
        type filter hook input priority -10; policy accept;
        udp dport 27015 @th,64,32 0xffffffff \
            meter a2sflood { ip saddr limit rate over 8/second burst 20 packets } drop
    }
}

这个文件用 nft -f 加载。优先级 -10 让这条规则在 UFW 的过滤链之前生效,而 @th,64,32 读取 UDP 头之后的前四个字节。用传统的 iptables,一次 u32 匹配能做到同样的事:

iptables -A INPUT -p udp --dport 27015 \
  -m u32 --u32 "0>>22&0x3C@8=0xFFFFFFFF" \
  -m hashlimit --hashlimit-name gmod_a2s --hashlimit-mode srcip \
  --hashlimit-above 8/sec --hashlimit-burst 20 -j DROP

有一点几乎每篇网上的教程都不讲:无连接的不只是服务器查询,连接建立过程也是。一名正在加入的玩家在进入游戏之前会发出好几个带同样头部的数据包。所以界限设得太严会把新玩家挡在外面,尽管服务器仍然可达。请从宽开始(每个地址每秒 8 到 15 个数据包),等您在正常运行状态下测过一周之后,再把界限收紧。

4. 给 RCON 加固,或者干脆关掉

在 Source 服务器上,RCON 是个受欢迎的目标,而且三个原因同时成立。第一,它和游戏用同一个端口号,只是走 TCP,因此不用找就能找到。第二,Source 的 RCON 协议以明文传输密码,没有 TLS,也没有密钥交换:谁能读到流量,谁就有了密码。第三,收益最大,因为拿到 RCON 的人可以换地图、封禁所有玩家、改配置并停掉服务器。接管了 RCON 的攻击者,根本不再需要带宽。

请不要把 rcon_password 留空,也不要设成猜得到的值,用 openssl rand -base64 32 出来的一个值就够了。针对登录尝试,引擎自带一个刹车:

rcon_password "A_LONG_RANDOM_PASSWORD_HERE"
sv_rcon_minfailures 3
sv_rcon_maxfailures 5
sv_rcon_minfailuretime 30
sv_rcon_banpenalty 1440

这样一来,某个地址在 30 秒内失败三次之后,就会被封一天。有两点要提醒。第一,如果您自己的管理面板里存着旧密码,正是这套机制也会把它封掉:运营者报告的“RCON 突然不能用了”,多半是自己封的。第二,第 2 步里的防火墙限制更有效,因为它根本不让尝试到达应用层。只偶尔需要 RCON 的人,就把这个端口完全关掉,通过 SSH 端口转发来工作:

ssh -N -L 27015:127.0.0.1:27015 root@YOUR.SERVER.IP.ADDRESS

5. 限制 Lua 网络消息,最常见的自己造成的宕机

被报成 DDoS 的 Garry's Mod 宕机里,有相当一部分根本不是 DDoS。它们是 Lua 过载,由一个连上来的客户端用每秒几千比特就触发了。原因在 net 库的构造方式:只要某个 Addon 用 util.AddNetworkString 注册了一个网络事件,并用 net.Receive 监听它,任何客户端就都能在一个循环里不断触发这个事件。没有自己加限制,服务器就会执行每一条消息。Facepunch 在自己的问题记录里多次写到这一点,并且没有在引擎里安排解决办法,限制明确是 Addon 作者的任务。

所以请对每一个自写的和每一个买来的 Addon 检查三件事:每名玩家每秒的上限,对消息长度的检查,以及玩家是在服务端从第二个参数得出的,而不是从消息内容里取的。一个站得住的写法是这样:

util.AddNetworkString("khrp_buy")

local budget = {}

net.Receive("khrp_buy", function(len, ply)
    if not IsValid(ply) then return end
    if len > 256 then return end

    local now = CurTime()
    local b = budget[ply]

    if not b or now - b.start >= 1 then
        b = { start = now, count = 0 }
        budget[ply] = b
    end

    b.count = b.count + 1
    if b.count > 10 then return end

    KHRP.HandleBuy(ply, net.ReadString())
end)

hook.Add("PlayerDisconnected", "khrp_budget_cleanup", function(ply)
    budget[ply] = nil
end)

此外 server.cfg 里还需要两行。sv_allowcslua 在 Garry's Mod 里默认是 1,允许客户端用 lua_run_cl 和 lua_openscript_cl 执行自己的代码:对一台公开服务器,这个值该设成 0。而 sv_kickerrornum 会断开产生的客户端错误超过给定数量的客户端(默认 0,也就是关闭):

sv_allowcslua 0
sv_kickerrornum 25

6. 把创意工坊内容和 FastDL 从游戏服务器上分开

在 Garry's Mod 上,创意工坊 Addon 不是边角话题,而是常态:一个 DarkRP 社区用 +host_workshop_collection 引入自己的合集,客户端直接从 Steam 下载这些内容。这不占用您的线路。-authkey 里的密钥是一个 Steam Web API 密钥,要像密码一样对待:放进启动脚本,不要放进公开的代码仓库,也不要放进 Discord 频道。

花带宽的是第二条路。所有不来自创意工坊的东西(自制地图、声音、材质)都走下载通道。没有 sv_downloadurl,这条通道就走游戏端口本身,直接和游戏流量抢资源。有了 FastDL,它走 HTTP。如果这台网页服务器在同一台机器、同一个 IP 地址上,两者就共用同一条线路:一波加入或者一次针对 80/TCP 的攻击,因此也会打到游戏。下面这些值是合理的:

sv_downloadurl "https://fastdl.your-domain.com/garrysmod/"
sv_allowdownload 1
sv_allowupload 0
net_maxfilesize 64

sv_allowupload 0 取消客户端把自己的文件送到服务器的可能,从而堵住一条既不需要也不受控制的路。net_maxfilesize 以兆字节为单位限制通过游戏通道传输的文件大小。请尽量把 FastDL 放到另一台机器上,或者放到一个独立的域名后面,这样负载就不在游戏端口所在的那个地址上了。

7. 挡住连接洪水攻击和槽位耗尽

槽位耗尽是一种不需要带宽的攻击:攻击者用自动化的连接占满所有空位,让真正的玩家看到一个满员的服务器。在 Garry's Mod 上还有一层麻烦,就是每一次加入都要让服务器干活,因为资源列表和游戏模式要协商好,而这远在玩家进入游戏之前。

对此有四件事管用。第一,一个现实的上限:把 +maxplayers 设得比您的游戏模式撑得住的更高,只会扩大攻击面。第二,sv_timeout,它规定客户端在多少秒没有消息之后被断开(常见配置里是 120):想更快摆脱挂住的半开连接的人,就把这个值设小一些。第三,第 3 步里针对无连接数据包的速率限制,因为连接建立正好走的就是这条路。第四,对封闭的圈子来说,一个服务器密码:

sv_password "regulars_2026"
sv_timeout 90
sv_filterban 1
sv_region 3

Garry's Mod 本身不带真正的白名单,它通过 ULX 这类扩展,或者通过 CheckPassword 钩子里自己写的检查来实现。还有一点必须清楚:白名单保护的是您的游戏逻辑,不是您的线路。灌包攻击您服务器的人根本不想进服。他的数据包会被拒绝,但已经到达了,而这才是关键。

8. 给内核减负:连接跟踪和接收缓冲区

这一步解释的是那些看起来像流量型攻击、其实不是的宕机。内核会为 UDP 流量在连接跟踪(conntrack)里建立条目,而在源地址被伪造的情况下,每一个地址都意味着一个新条目。表一满,内核就不加区别地丢弃数据包:攻击和您的玩家会一起被踢出去,而系统日志里会出现“nf_conntrack: table full”。当前数量和上限用这条命令查看:

sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max

最有效的一步是干脆不去跟踪游戏流量,因为引擎自己管理它的会话:

table inet raw {
    chain prerouting {
        type filter hook prerouting priority raw; policy accept;
        udp dport { 27015, 27020 } notrack
    }
    chain output {
        type filter hook output priority raw; policy accept;
        udp sport { 27015, 27020 } notrack
    }
}

用 iptables,对应的写法是 iptables -t raw -A PREROUTING -p udp --dport 27015 -j NOTRACK,以及把 --sport 用在 OUTPUT 上的同一行。之后这个端口需要一条明确的放行规则,因为没有跟踪,任何检查既有状态的规则都不再生效。此外,如果数据包到达的速度比 srcds 取走它们的速度更快,接收缓冲区就会溢出,而在玩家看来,这就像线路空着却在丢包:

net.core.rmem_max = 16777216
net.core.rmem_default = 1048576
net.core.netdev_max_backlog = 16384

这些行应放进 /etc/sysctl.d/ 下的一个文件,用 sysctl --system 生效。它们是否有必要,内核自己会说:如果 nstat -az 中的 UdpRcvbufErrors 在上升,它们就起作用。如果这个计数器一直是零,这次调整什么都不会改变。这是余量,不是防护。

9. 在一切正常的时候先保存测量数据

最重要的一步是几乎没人事先做的那一步:建立一条基线。没有正常值,事后您就说不清每秒 4 万个数据包是很多,还是就是个普通的周六晚上。请把您服务器的正常值算一次:64 名玩家、cl_cmdrate 为 66 时,大约产生每秒 4200 个入站数据包,明显高于这个数的都需要解释。用 apt-get install -y vnstat sysstat 让测量常驻运行。事件发生时四条命令就够了:

sar -n DEV 1 10
ip -s link show eth0
nstat -az | grep -i udp
tcpdump -ni eth0 -c 200 "udp port 27015 and udp[8:4] = 0xffffffff"

第一条显示每秒的数据包数和字节数,第二条显示网卡的丢包计数器,第三条显示内核的 UDP 错误计数器。第四行只显示无连接的数据包,也就是查询洪水攻击滥用的正是这一类:如果在几乎没人连着的情况下计数器几秒钟就满了,您就有答案了。请始终用 -c 限制 tcpdump,满载状态下抓包会让本来已经过载的服务器更吃力。如何解读这些数值,见 在服务器上识别 DDoS 攻击。

什么是 A2S 反射攻击漏洞,它还影响我吗

A2S 反射攻击是一种攻击,其中您的服务器不是目标,而是工具。攻击者用伪造的源地址向数千台游戏服务器发送一个很小的查询,而这些服务器明显更大的回应全都汇到真正的受害者身上。历史上一次 A2S_INFO 请求是 25 字节(4 字节的 0xFFFFFFFF、1 字节的 0x54,加上字符串“Source Engine Query”的 20 字节),而回应有好几百字节。US-CERT 在它的放大攻击清单里给 Steam 协议记的系数是 5.5,意思是:攻击者那边的一个 Gbit,在受害者那边变成 5.5 Gbit。

Valve 从 2020 年 11 月起堵上了这个漏洞,走的是两条路。从那时起,无连接的查询数据包必须由发送方填充到 1200 字节,这样请求就比回应更大,放大系数降到 1 以下。在切换期间,运营者可以用环境变量 STEAM_GAMESERVER_MIN_CONNECTIONLESS_PACKET_SIZE=1200 提前强制采用更严格的行为。此外,对 A2S_PLAYER 和 A2S_RULES,服务器不再立刻回数据,而是回一个挑战值(S2C_CHALLENGE),提问方必须在第二次请求里把它送回来。伪造源地址的人永远看不到这个挑战值。

对您来说由此得出两点。请保持服务器程序为最新版本,因为这层防护在 Steam 游戏服务器的底层里,而不在您的配置里。另外,不要把反射攻击和针对您本身的查询洪水攻击搞混:对第二种,只有第 3 步里的速率限制有用,再往上就是服务器前面网络里的过滤。

这些措施到哪里为止:带宽和数据包速率

现在说没有任何配置文件能解决的那一部分。到此为止讲的一切都跑在您的服务器上,也就是线路的末端。防火墙规则处理的是已经跑过网线的数据包。您可以把它丢弃,但没法让它没被发出来。

一起算一下。一台典型的游戏服务器挂在 1 Gbit/s 上,也就是每秒 125 兆字节,只要有人发得更多,线路就满了。第二个量多半更早出事:在 64 字节的最小包情况下,1 Gbit/s 里能装进每秒约 149 万个数据包,10 Gbit/s 里约 1488 万个。普通的服务器内核视 CPU 和网卡而定,处理其中几十万个之后就会开始丢包。所以一次连您线路的三分之一都填不满的攻击,也可能让您的服务器瘫掉,因为算力都花在丢包上了。运营者的体验是“负载根本不高,可是所有人都有延迟尖峰”。

指标 数值
A2S_INFO 请求的历史大小 25 字节
Steam 协议的放大系数(US-CERT) 5.5
2020 年以来无连接查询数据包的最小大小 1200 字节
正常流量:64 名玩家,cmdrate 为 66 每秒约 4200 个入站数据包
1 Gbit/s,64 字节包大小 每秒约 149 万个数据包(每秒 125 兆字节)
10 Gbit/s,64 字节包大小 每秒约 1488 万个数据包
针对社区游戏服务器的典型攻击规模 5 到 50 Gbit/s
KernelHost 服务器上测到的峰值 473.4 Gbit/s,每秒 4150 万个数据包

为了说明现实中会出现哪些量级:KernelHost 的服务器上过滤过的攻击里,包括一次针对游戏服务器、超过 112.2 Gbit/s 且每秒超过 870 万个数据包的 UDP 洪水攻击,以及一次针对语音服务器、超过 473.4 Gbit/s 且每秒超过 4150 万个数据包的多向量攻击。473.4 Gbit/s 大约是 1 Gbit/s 接入的 470 倍,即便对 10 Gbit/s 的接入来说也还是大约 47 倍。对此没有任何本地设置可用。流量型攻击必须在服务器前面的网络里就被终结。

KernelHost 用什么来应对

每个服务器套餐里都包含的持续防护

KernelHost 的 DDoS 防护分两级,持续生效,您不需要开启、订购或配置任何东西:

  • 第 1 级:全球清洗网络中 17 Tbps 的清洗能力。流量型攻击在靠近来源的地方就被清洗掉,根本到不了数据中心。
  • 第 2 级:法兰克福的 Arbor 实时过滤,容量 3.2 Tbps。就在服务器前面,逐个数据包地识别并丢弃与协议相关的攻击特征。

有两点是关键。防护持续运行,不需要先对攻击做出反应,所以不存在一段让您玩家掉线的切换时间。而且不使用黑洞路由:您的 IP 地址留在网络里,被丢弃的只有恶意数据包。把 IP 地址从网络里撤下来的做法,对您而言和攻击者达到的结果一样。地点是法兰克福。哪些游戏和协议在覆盖范围内,见 实时游戏服务器 DDoS 防护。

针对长期被攻击社区的 Advanced DDoS Protection

有些项目不是偶尔被打,而是被有针对性地连打几周,手法不断变化,而且总是正好挑在高峰时段。为此有 Advanced DDoS Protection,每月 50.00 欧元起,PrePaid 预付费,没有最低合约期。区别不在于容量更大,而在于控制权:

  • 专用防护 IP,来自法兰克福的核心网络,您的服务器会在我们自己的网络内切换到它。您这边不需要做任何改造。
  • 按端口和协议自行管理的防护规则,就在客户中心里:您可以设置 27015/UDP 上允许什么、27015/TCP 上允许什么,而不用为此写一张工单。
  • 改动实时生效,因此您可以在攻击进行时随时调整,而不用等下一个维护窗口。
  • 与各款游戏匹配的防护策略,既适用于 Garry's Mod 和其余 Source 系列作品,也提供自由的 TCP 和 UDP 策略,用于经过修改的服务器和自研程序。

这里同样适用 PrePaid 预付费模式:没有最低合约期,没有退订通知期,没有合同,也没有开通费。攻击这一波过去了,您只要不续期就行。目前把 Garry's Mod 服务器放在别处的人,通过迁移到 KernelHost 获得这套防护,因为过滤发生在我们自己的网络里,而不在别人的基础设施上。

两级防护对比

对比项 标配的 DDoS 持续防护 Advanced DDoS Protection
价格 包含在每个服务器套餐中,不额外收费 每月 50.00 欧元起,PrePaid 预付费
启用 自服务器开通起即生效,无需设置 下单、获得防护 IP,服务器完成切换
过滤能力 17 Tbps 全球清洗,加上法兰克福 3.2 Tbps 的 Arbor 实时过滤 同样的两级过滤
IP 地址 您服务器本身的 IP 地址 额外的专用防护 IP
规则集 自动防护策略,无需配置 在客户中心里按端口和协议设置自己的规则
变更 自动跟进 实时生效,攻击进行时也可以
游戏策略 针对常见游戏的优化策略,Garry's Mod 也在其中 策略可按端口选择,也适用于经过修改的服务器
攻击期间的黑洞路由 否 否
合约期 与服务器套餐绑定 PrePaid 预付费,没有最低合约期,没有退订通知期,也没有开通费

对大多数 Garry's Mod 社区来说,标配的持续防护配上干净的服务器配置就够了。Advanced DDoS Protection 针对的是有人把这件事当成私人恩怨的情况。

常见错误及解决办法

“服务器在运行,可是从服务器浏览器里消失了”:多半是 27015/UDP 被一刀切地封了,或者速率限制设得太严。因为游戏流量和查询共用同一个端口,一条粗暴的规则会同时打到两者。请改用针对无连接数据包的匹配方式。如果端口可达而服务器仍然不可见,请检查 sv_setsteamaccount:没有有效的 Game Server Login Token,一台 Garry's Mod 服务器在列表里会被大幅降权,而且每台服务器都需要自己的令牌。

“我的 iptables 规则是对的,可还是不起作用”:常见原因有三个。规则排在 UFW 的链后面,永远轮不到它;规则在上次重启后就没了(这时用 apt-get install -y iptables-persistent 和 netfilter-persistent save,或者写进 /etc/ufw/before.rules);或者攻击是流量型的,而规则在一条已经满了的线路上正常工作。用 iptables -L INPUT -n -v 检查命中计数器是否在增长。如果一直是零,说明规则没有被匹配到。

“我的 DarkRP 服务器对所有人都在卡顿,可线路是空的”:这几乎总是 Lua,而不是针对线路的攻击。请在服务器日志里看哪个网络事件来得异常频繁,并检查对应的 Addon 有没有按玩家设上限。如果 sar -n DEV 1 10 和丢包计数器都没有异常,那就不是 DDoS 攻击。

“RCON 突然不能用了”:这不是 DDoS,多半是自己封的。一个存着旧密码的管理面板会触发 sv_rcon_minfailures,而 sv_rcon_banpenalty 会按设定的分钟数封掉这个地址。改正密码,解除封禁,然后把这个端口限制到自己的地址上。

“我换了 IP 地址,两天后又离线了”:这是常态。只要服务器重新在主服务器上注册,它就会自己公开新地址,而一台没有公开地址的游戏服务器不会有玩家。换地址能争取几小时到几天,它不是解决办法。

“我原来的主机商把我的 IP 地址封了”:那就是黑洞路由。主机商用它保护自己的网络,对您来说结果和一次成功的攻击完全一样,而且通常在攻击结束后还要持续几个小时。有疑问时请直接问清楚:是过滤还是黑洞路由。这个答案对您可用性的影响,比任何硬件参数都大。

“我在 tcpdump 里看不到任何异常”:如果流量已经在前面的网络里被过滤掉了,服务器上当然就什么都收不到。这在过滤正常工作时是常态。反过来说:线路一旦被打满,您甚至可能连用来测量的那条 SSH 会话都进不来。这时请使用客户中心里的 VNC 控制台。

要点总结

  • Garry's Mod 服务器只需要一个开放端口:27015/UDP。游戏流量和 A2S 查询在那里一起跑,并不存在独立的查询端口。
  • RCON 位于 27015/TCP,以明文传输密码,只该对自己的地址放行,或者通过 SSH 端口转发来访问。
  • 要限制的不是端口,而是带 0xffffffff 头的无连接数据包。在 27015/UDP 上一刀切地封禁会把自己的玩家踢出去。
  • 最常见的 Garry's Mod 宕机不是 DDoS 攻击,而是一个没有限制的网络事件:每一个用 util.AddNetworkString 注册的事件都需要一个按玩家每秒计的上限。
  • 在 64 字节的包大小下,一条 1 Gbit/s 的线路每秒承载约 149 万个数据包。超过之后,丢失发生在前面的路由器上,任何本地规则都会失效。
  • 在 KernelHost,两级持续防护包含在每个服务器套餐中,不额外收费:全球清洗网络中 17 Tbps 的清洗能力,以及法兰克福 3.2 Tbps 的 Arbor 实时过滤,并且不使用黑洞路由。
  • 长期被有针对性攻击的人,再加上每月 50.00 欧元起的 Advanced DDoS Protection:专用防护 IP,按端口和协议自行管理的规则,实时生效。

如果您的服务器已经放在 KernelHost,过滤就已经在工作,您什么都不用做。若仍然发现异常,请开一个 支持工单,以便为您的 IP 地址调整过滤规则。请在工单里一次写清四项信息:IP 地址、端口、按您所在时区的时间段,以及您看到的现象(玩家被踢下线、服务器不在浏览器里、延迟尖峰)。攻击正在进行时,您还可以通过 WhatsApp 紧急聊天联系我们:+43 650 8209883。

除 Garry's Mod 之外还运营其他 Source 系列作品的人,可以在 保护 CS2 和 Source 服务器免受 DDoS 攻击 里找到共同的基础,而底层该怎样干净地搭起来,见 用 SteamCMD 安装游戏服务器。

常见问题

我的 Garry's Mod 服务器刚刚离线了。这是 DDoS 攻击吗?
请先看数据包速率,而不是 CPU 负载。命令 sar -n DEV 1 10 显示每秒的数据包数和字节数,ip -s link show eth0 显示网卡的丢包计数器,nstat -az 显示内核的 UDP 错误计数器。如果入站数据包远远高于您的正常值,而几乎没人连着,那就是攻击。如果网络计数器没有异常,一切却仍然卡顿,原因几乎总在 Lua 上:这时是某个 Addon 或一个没有保护的网络事件在吃算力,任何过滤都改变不了这一点。
Garry's Mod 服务器真正需要哪些端口?
正好一个:27015/UDP,通过启动参数 -port 设置。游戏流量和服务器浏览器的 A2S 查询都在这一个端口上一起跑,Garry's Mod 并没有独立的查询端口。RCON 位于 27015/TCP,只该对您自己的地址放行。27020/UDP 只有在您通过 SourceTV 做转播时才需要。客户端端口 27005/UDP 是从玩家那边发出的,服务器上不需要规则。供 DarkRP 使用的 MySQL 该绑定到 127.0.0.1,绝不能进公网。
我能不能把查询端口封掉,让查询洪水攻击停下来?
不能,因为并不存在独立的查询端口。封掉 27015/UDP 或者对它一刀切地限速的人,会在同一个动作里把自己的玩家踢出去,并从服务器浏览器里消失。正确的做法是只针对无连接数据包的限制:Source 引擎的所有服务器查询和连接建立都以 0xffffffff 这四个字节开头,而已经连上的玩家的流量没有这个头。请用 nftables 或 iptables 正好对这个特征按源地址加一道限制,起始值大约是每秒八个数据包。
为什么 RCON 在 Garry's Mod 上是这么受欢迎的攻击目标?
因为收益最大而门槛很低。RCON 和游戏用同一个端口号,只是走 TCP,因此不用找就能找到。Source 的 RCON 协议以明文传输密码,没有 TLS,也没有密钥交换。而拿到 RCON 的人可以换地图、封禁所有玩家、改配置并停掉服务器,完全不需要带宽。所以请设一个长的随机密码,启用 sv_rcon_minfailures 和 sv_rcon_banpenalty,并且只对您自己的地址放行 27015/TCP。
什么是 A2S 反射攻击漏洞,它还影响我吗?
A2S 反射攻击是一种攻击,其中您的服务器不是目标,而是工具:攻击者用伪造的源地址查询数千台游戏服务器,而明显更大的回应全都汇到真正的受害者身上。历史上一次 A2S_INFO 请求是 25 字节,而 US-CERT 给 Steam 协议记的放大系数是 5.5。Valve 从 2020 年 11 月起堵上了这个漏洞:查询数据包必须填充到 1200 字节,而 A2S_PLAYER 和 A2S_RULES 需要一个挑战值。请保持服务器程序为最新版本,这层防护就会生效。
为什么我的防火墙规则在攻击期间没有用?
因为它只在数据包已经到达之后才生效。在 64 字节包大小时,一条 1 Gbit/s 的线路每秒承载约 149 万个数据包,一条 10 Gbit/s 的线路约 1488 万个。攻击一旦超过这个量,丢失就发生在前面的路由器上,您的规则永远不会被执行。而且远在线路被填满之前,CPU 就已经到顶了,因为每个数据包都要走一遍网络栈,即便随后被丢弃也一样。从这个点开始,只有服务器前面网络中的过滤才有用。
我的 DarkRP 服务器有延迟尖峰,可线路是空的。这是怎么回事?
那几乎总是 Lua,而不是针对线路的攻击。只要某个 Addon 用 util.AddNetworkString 注册了一个网络事件,并用 net.Receive 监听它,任何连上来的客户端都能在一个循环里不断触发这个事件,而服务器会执行每一条消息。一个每秒只有几千比特的玩家就足够了。解决办法在 Addon 里,而不在防火墙里:每名玩家每秒的上限,对消息长度的检查,以及在服务端得出玩家而不是从消息内容里取。
在 KernelHost,我的服务器会在攻击期间离线吗?
不会。我们不使用黑洞路由。您的 IP 地址留在网络里,被丢弃的只有恶意数据包。防护分两级:全球清洗网络中 17 Tbps 的清洗能力,以及法兰克福 3.2 Tbps 的 Arbor 实时过滤。它持续运行,不需要先对攻击做出反应,因此不存在一段让您玩家掉线的切换时间。这套持续防护包含在每个服务器套餐中,不额外收费,并且自服务器开通起即生效。
什么时候我还需要额外的 Advanced DDoS Protection?
当您的社区不是偶尔被打,而是被有针对性地连打几周,而且您想自己掌控过滤时。您会获得一个专用防护 IP,并在客户中心里自行按端口和协议管理防护规则,例如让 27015/UDP 和 27015/TCP 不一样。改动实时生效,因此您可以在攻击进行时随时调整。价格每月 50.00 欧元起,PrePaid 预付费,没有最低合约期,没有退订通知期,也没有开通费。

Garry's Mod Garry's Mod DDoS 防护 DarkRP Source 引擎 A2S 查询 游戏服务器防护 27015 端口 Advanced DDoS Protection