Garry's Mod サーバーを DDoS 攻撃から守る

公開日 読了時間 45 分

Garry's Mod ではゲームトラフィックとサーバーへの問い合わせが同じポート 27015 を流れます。サーバー上でどのルールが本当に効くのか、RCON と Lua のネットワークイベントをどう守るか、そしてどの攻撃規模から手前のネットワークでのフィルタリングしか効かなくなるかを解説します。

夜の 20 時に 3 分だけ消えて、そのあと戻ってくる Garry's Mod サーバーに、ハードウェアの問題があることはまずありません。ほとんどの場合は攻撃が走っており、しかもプレイヤーが最も多く接続している時間帯を狙って走ります。Garry's Mod の DDoS 対策とは、したがってまず、どのパケットならお使いのサーバーに届いてよいのかを把握することです。本記事ではこの順序で、これから 10 分のあいだに 1 セントもかけずにご自身で固められる範囲、その対策が物理的にどこで終わるか、そしてそのあとサーバーの手前のネットワークで何が起きる必要があるかを示します。

本記事の記述はすべて、Debian 12、Debian 13、Ubuntu 22.04 LTS、Ubuntu 24.04 LTS 上で動く srcds サーバーを前提としています。設定ファイルは garrysmod/cfg/server.cfg にあり、コマンドは root 向けに書かれているため、一般ユーザーの場合は先頭に sudo を付けてください。想定しているのは常に、ご自身のルートサーバーまたは Dedicated Server 上での運用で、ゲームサーバー事業者から借りた 1 スロットではありません。

攻撃がいま進行中の場合は、server.cfg を変更せず、srcds も再起動しないでください。まずは測定値を保存してください(第 9 節)。攻撃が終わってからでは、もう残っていません。再起動はカウンターを失わせ、そのあとサーバーを同じ攻撃の波の中へ戻すだけです。

Garry's Mod サーバーに DDoS 対策が必要な理由

Garry's Mod サーバーは、自分の IP アドレスとポートを自分で公開します。これは手落ちではなく前提条件です。サーバーブラウザーに載らなければ、新しいプレイヤーは来ません。この登録は、サーバーが Steam マスターサーバーに登録し、そのあと外から来るすべての A2S クエリに応答することで生まれます。ですから問いは、攻撃側がお使いのアドレスを見つけるかどうかではなく、そこに撃ち込まれたときに何が起きるかだけです。

そこにコミュニティの性質が加わります。Garry's Mod は主にラウンド単位で遊ばれるのではなく、継続する世界で遊ばれます。DarkRP のコミュニティは、プレイヤーのアカウント、所持物、職業、進行状況を数か月にわたってデータベースで管理します。金曜の夜の障害は、1 試合の敗北より高くつきます。常連プレイヤーを失うからです。だからこそ、競合するコミュニティ、BAN されたプレイヤー、購入されたサーバー booter(月にわずか数ユーロで任意のアドレスへの攻撃を発動させるサービス)が、最も多い 3 つの引き金になります。攻撃側には技術もまとまった金額も必要ありません。

技術面では 3 つの特徴が重なります。ゲームトラフィックは UDP で流れ、UDP には要求できるような接続確立の手順がなく、送信元アドレスは偽装できます。サーバーへの問い合わせはゲームと同じポートに載っているため、大まかな遮断は常に両方に当たります。そしてそのすべての上に Lua があります。Workshop の Addon はどれも独自のコードを同じプロセスに持ち込み、保護のないネットワークイベントが 1 つあるだけで、1 人のクライアントが帯域幅をまったく使わずにサーバーを失速させられます。DDoS 攻撃が根本的に何であるかは、記事 DDoS 攻撃とは何か で解説しています。

Garry's Mod で実際に問題になるポート

Garry's Mod サーバーは初期設定でポート 27015 で起動します。ゲームとサーバーへの問い合わせには UDP、RCON には TCP を使います。番号は起動時に -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 はい。ただし限定して

この 8 行のうち、制限なしでインターネットに開けてよいのはちょうど 1 つ、27015/UDP です。それ以外はご自身のアドレスに限定するか、127.0.0.1 にバインドするか、そもそも起動しません。この分野で最も高くつく思い違いは、Garry's Mod には単純に閉じられる独立したクエリポートがある、という思い込みです。そんなものはありません。

費用をかける前に自分でできること

この節が最も長いのは意図的です。きちんと設定された Garry's Mod サーバーは、どこに置かれていようと、小規模から中規模の攻撃を自力で耐えます。どれも費用はかからず、大半は 15 分で終わります。

1. 現状把握:そもそも何が待ち受けているか

ルールを 1 つ書く前に、お使いのサーバーが外に何を提供しているかを確認します。推測せず、実際に見てください。

ss -lntup

注目するのはローカルアドレスの列です。0.0.0.0:27015 と [::]:27015 は「インターネット全体から到達できる」、127.0.0.1:3306 は「ローカルのみ」で、ファイアウォールルールは不要という意味です。長く育ってきた DarkRP サーバーでは、そこに予想より多くのサービスが並んでいることがほぼ常です。MySQL、FastDL 用のウェブサーバー、パネル、Discord ボット、27016 で動く 2 台目のテストサーバー、そして忘れられたボイスサービスです。攻撃側からの見え方は、外部からのポートスキャンで分かります。

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

2. srcds が本当に必要とするポートだけを開けたままにする

Garry's Mod では外に向けた開放は 1 つで足り、それに 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 ルートサーバーと Dedicated Server には IPMI も iDRAC もないため、カスタマーエリアの VNC コンソールから戻ります。このコンソールはゲストシステムのネットワークスタックに依存しないので、ゲスト側のファイアウォールルールで遮断されることはありません。

データベースは、どんな場合でもインターネットに出してはいけません。/etc/mysql/mariadb.conf.d/50-server.cnf に次の記述があるか確認してください。

bind-address = 127.0.0.1

3. サーバーリストから落ちずに A2S クエリを制限する

ここに、最も多くの Garry's Mod サーバーを失わせている間違いがあります。ゲームトラフィックとサーバーへの問い合わせが同じポートを占めているため、27015/UDP への一律の遮断や厳しすぎるレート制限は、自分のプレイヤーを追い出し、攻撃側の仕事を自分で仕上げてしまいます。正しい着眼点は、問い合わせのパケットとゲームのパケットを区別することです。

エンジンはそのためにコンソール変数を 3 つ用意しており、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 を守ります。パケットが届くこと自体は防げませんし、グローバルの値をとても厳しく絞ると、一覧ページからの問い合わせにも応答しなくなるため、攻撃の最中にサーバーブラウザーから消えます。

1 つ下の層では、問い合わせのトラフィックをきれいに切り分けられます。Source エンジンのコネクションレスパケットはすべて、4 バイトすべてが立った値(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 ヘッダーの直後の 4 バイトを読みます。従来の 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

ネット上のほぼすべての手順書が黙っている点が 1 つあります。コネクションレスなのはサーバーへの問い合わせだけでなく、接続の確立もそうです。参加しようとしているプレイヤーは、ゲームに入る前に同じヘッダーを持つパケットを複数送ります。ですから上限が厳しすぎると、サーバーは到達できるままなのに新しいプレイヤーが締め出されます。まずはゆるく始めて(アドレスごとに毎秒 8 から 15 パケット)、平常時を 1 週間測ってから上限を絞ってください。

4. RCON を固めるか、完全に切る

Source サーバーでは RCON が好まれる標的で、しかも 3 つの理由が同時に成り立ちます。第一に、ゲームと同じポート番号の TCP 側にあるため、探さずに見つかります。第二に、Source の RCON プロトコルはパスワードを平文で送り、TLS も鍵交換もありません。トラフィックを読める者は、それを手に入れます。第三に、得られるものが最大です。RCON を握った者はマップを変え、全プレイヤーを BAN し、設定を変更し、サーバーを停止できます。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 秒以内に 3 回失敗したアドレスが 1 日遮断されます。注意が 2 つあります。第一に、まさにこの仕組みは、古いパスワードが保存されているご自身の管理パネルも遮断します。運用者が「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 の過負荷であり、毎秒わずか数キロビットの、接続済みのクライアント 1 台が引き起こしています。原因は net ライブラリの作りにあります。ある Addon が util.AddNetworkString でネットワークイベントを登録し、net.Receive でそれを待ち受けた時点から、どのクライアントもそのイベントをループで発火させられます。独自の制限がなければ、サーバーはメッセージを 1 件ずつすべて実行します。Facepunch はこれを自らの不具合報告で何度も記録していますが、エンジン側での解決は用意しておらず、制限は Addon の作者の仕事だと明示しています。

ですから、自作と購入のどちらの Addon についても 3 点を確認してください。プレイヤーごと 1 秒ごとの上限、メッセージ長の検査、そしてプレイヤーがメッセージの内容からではなく、サーバー側で第 2 引数から取得されていることです。通用する書き方は次のようになります。

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 に 2 行が必要です。sv_allowcslua は Garry's Mod では初期設定で 1 になっており、クライアントが lua_run_cl と lua_openscript_cl で独自のコードを実行できます。公開サーバーではこの値を 0 にします。そして sv_kickerrornum は、指定した数を超えるクライアント側のエラーを出したクライアントを切断します(初期値 0、つまり無効)。

sv_allowcslua 0
sv_kickerrornum 25

6. Workshop の内容と FastDL をゲームサーバーから分ける

Garry's Mod では Workshop の Addon は周辺的な話題ではなく、ごく普通のことです。DarkRP のコミュニティは +host_workshop_collection で自分のコレクションを組み込み、クライアントはその内容を Steam から直接ダウンロードします。これはお使いの回線に負荷をかけません。-authkey の鍵は Steam Web API キーであり、パスワードと同じように扱うものです。起動スクリプトの中に置き、公開リポジトリや Discord のチャンネルには置きません。

帯域幅を食うのは 2 つ目の経路です。Workshop から来ないものはすべて、つまり独自のマップ、サウンド、マテリアルはダウンロードの経路を通ります。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 は、クライアントが自分のファイルをサーバーへ送る可能性を取り除き、必要でも制御されてもいない経路を 1 つ閉じます。net_maxfilesize は、ゲームの経路で転送されるファイルのサイズをメガバイト単位で制限します。FastDL は可能なら別のホストか、独自の名前の背後に置いてください。そうすれば負荷はゲームポートと同じアドレスに乗りません。

7. 参加フラッドとスロット枯渇を受け止める

スロット枯渇は帯域幅を必要としない攻撃です。攻撃側は自動化された接続で空いている席をすべて埋め、本物のプレイヤーには満員の状態が見えるようにします。Garry's Mod ではさらに、参加 1 件ごとにサーバーが作業を負う点が事情を悪くします。プレイヤーがゲームに入るはるか前に、リソースリストとゲームモードのやり取りが行われるからです。

これに対しては 4 つが効きます。第一に、現実的な上限です。+maxplayers をご自身のゲームモードが耐えられる以上に設定すると、攻撃対象領域だけが広がります。第二に sv_timeout で、メッセージのない状態が何秒続いたらクライアントを切断するかを決めます(広く使われている設定では 120)。中途半端に残った接続をより早く手放したい場合は、値を下げます。第三に、手順 3 のコネクションレスパケットへのレート制限です。接続の確立はまさにそこを通るからです。第四に、閉じたグループ向けにはサーバーパスワードです。

sv_password "regulars_2026"
sv_timeout 90
sv_filterban 1
sv_region 3

本物の許可リストは Garry's Mod には付属しません。ULX のような拡張機能か、フック CheckPassword での独自の検査で実現します。そしてもう 1 つはっきりさせておきます。許可リストが守るのはゲームのロジックであって、お使いの回線ではありません。サーバーをあふれさせる攻撃側は、そもそも参加する気がありません。そのパケットは拒否されますが、それでも届いてしまっており、まさにそれが問題なのです。

8. カーネルの負担を下げる:接続追跡と受信バッファー

この手順は、ボリューム型攻撃に見えるのに実はそうではない障害を説明します。カーネルは UDP のトラフィックについても接続追跡(conntrack)にエントリーを作り、送信元アドレスが偽装されていると、アドレス 1 つごとに新しいエントリーになります。テーブルが埋まると、カーネルは区別なくパケットを破棄します。攻撃とお使いのプレイヤーが一緒に落ち、システムログには「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 が増えているなら、これらが効きます。カウンターが 0 のままなら、この調整は何も変えません。これは余裕の確保であって、保護ではありません。

9. すべてが平常に動いているうちに測定値を残す

最も重要なのは、ほとんど誰も事前にやらない手順、つまり基準値を作っておくことです。平常時の値がなければ、障害のあとで毎秒 4 万パケットが多かったのか、それとも単に土曜の夜だったのかを判断できません。ご自身のサーバーの平常値は一度計算しておいてください。cl_cmdrate が 66 のプレイヤー 64 人は、毎秒およそ 4200 の受信パケットを生みます。それを大きく超えるものはすべて説明を要します。apt-get install -y vnstat sysstat で測定は常に走り続けます。障害の最中は 4 つのコマンドで足ります。

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"

1 つ目は毎秒のパケット数とバイト数を、2 つ目はインターフェースの破棄パケットのカウンターを、3 つ目はカーネルの UDP エラーカウンターを表示します。4 行目はコネクションレスパケットだけを表示します。つまりクエリフラッドが悪用するまさにそのクラスです。ほとんど誰も接続していないのに数秒でカウンターが埋まるなら、答えは出ています。tcpdump は必ず -c で件数を区切ってください。全負荷の状態での取得は、すでに過負荷のサーバーにさらに負荷をかけます。測定値の読み方は サーバーで DDoS 攻撃を検知する にあります。

A2S リフレクションの穴とは何で、いまも自分に関係があるのか

A2S リフレクションとは、お使いのサーバーが標的ではなく道具になる攻撃です。攻撃側は送信元アドレスを偽装した小さな問い合わせを数千のゲームサーバーに送り、それらのはるかに大きな応答が、すべて本来の被害者のところに集まります。歴史的に A2S_INFO の要求は 25 バイトでした(0xFFFFFFFF の 4 バイト、0x54 の 1 バイト、それに「Source Engine Query」という文字列の 20 バイト)が、応答は数百バイトに及びました。US-CERT は増幅攻撃の一覧で Steam プロトコルを増幅率 5.5 として挙げており、これは攻撃側の 1 ギガビットが被害者のところで 5.5 ギガビットになるという意味です。

Valve はこの穴を 2020 年 11 月から 2 つの方法でふさぎました。コネクションレスの問い合わせパケットは、それ以降、送信側で 1200 バイトまで埋める必要があります。これで要求が応答より大きくなり、増幅率は 1 を下回ります。移行の期間中は、運用者が環境変数 STEAM_GAMESERVER_MIN_CONNECTIONLESS_PACKET_SIZE=1200 で厳しい挙動を先に強制できました。あわせてサーバーは A2S_PLAYER と A2S_RULES に対して、すぐデータで応答するのではなくチャレンジ(S2C_CHALLENGE)で応答し、問い合わせた側は 2 回目の要求でそれを送り返す必要があります。送信元アドレスを偽装した者は、このチャレンジを目にすることがありません。

ここからご自身にとって 2 つのことが導かれます。サーバーバイナリを最新に保ってください。この保護はお使いの設定ではなく、Steam のゲームサーバー基盤の中にあります。そして、リフレクションとご自身に向けられたクエリフラッドを混同しないでください。後者に効くのは手順 3 のレート制限だけで、それを超えたところではサーバーの手前のネットワークでのフィルタリングです。

これらの対策が終わるところ:帯域幅とパケットレート

ここからは、どの設定ファイルでも解決できない部分です。ここまで説明したものはすべてお使いのサーバー上、つまり回線の末端で動きます。ファイアウォールのルールが判断するのは、すでにケーブルを通り終えたパケットです。破棄はできますが、送られなかったことにはできません。

一度計算してみてください。一般的なゲームサーバーは 1 Gbit/s につながっており、これは毎秒 125 メガバイトで、誰かがそれより多く送った時点で回線は埋まります。2 つ目の量のほうが先に効くことが多いです。最小の 64 バイトのパケットなら、1 Gbit/s に毎秒約 149 万パケット、10 Gbit/s に約 1488 万パケットが入ります。通常のサーバーのカーネルが破棄を始める前に処理できるのは、CPU とネットワークカードによりますがそのうち数十万です。ですからお使いの回線を 3 分の 1 も埋めない攻撃でも、破棄のための計算時間が食われるため、サーバーを止められます。運用者にはこれが「使用率はまったく高くなかったのに、それでも全員にラグスパイクが出た」という形で見えます。

指標 値
A2S_INFO の要求、歴史的なサイズ 25 バイト
Steam プロトコルの増幅率(US-CERT) 5.5
2020 年以降のコネクションレスな問い合わせパケットの最小サイズ 1200 バイト
平常時のトラフィック:cmdrate 66 のプレイヤー 64 人 毎秒およそ 4200 の受信パケット
64 バイトのパケットでの 1 Gbit/s 毎秒約 149 万パケット(毎秒 125 メガバイト)
64 バイトのパケットでの 10 Gbit/s 毎秒約 1488 万パケット
コミュニティのゲームサーバーに対する一般的な攻撃規模 5 から 50 Gbit/s
KernelHost のサーバーで測定した最大値 毎秒 4150 万パケットで 473.4 Gbit/s

実際にどの規模が起きるのかの目安として。KernelHost のサーバーでは、ゲームサーバーに対する毎秒 870 万パケット超で 112.2 Gbit/s 超の UDP フラッド、そしてボイスサーバーに対する毎秒 4150 万パケット超で 473.4 Gbit/s 超のマルチベクター攻撃などを除去しました。473.4 Gbit/s は 1 Gbit/s の回線のおよそ 470 倍で、10 Gbit/s の回線でもなおおよそ 47 倍です。これに対するローカルの設定は存在しません。ボリューム型攻撃は、サーバーの手前のネットワークで終わらせる必要があります。

KernelHost が用意している対策

すべてのサーバープランに含まれる常時保護

KernelHost の DDoS 対策は 2 段階で構成されており、有効化も注文も設定もなしに常時動いています。

  • 第 1 段階:グローバルなスクラビングネットワークにおける 17 Tbps の緩和能力。 ボリューム型攻撃は、データセンターに届く前に、発生源の近くで除去されます。
  • 第 2 段階:フランクフルトにおける 3.2 Tbps の Arbor リアルタイムフィルタリング。 サーバーの直前で、プロトコル固有のパターンを検知し、パケット単位で破棄します。

決定的な性質が 2 つあります。保護は常時動いており、攻撃を受けてから反応する必要がありません。つまり、その間にプレイヤーが落ちるような切り替え時間がありません。そしてヌルルーティングは使いません。お使いの IP アドレスはネットワークに残り、破棄されるのは有害なパケットだけです。IP アドレスをネットワークから外す事業者は、ご自身にとって攻撃側と同じ結果をもたらします。設置場所はフランクフルトです。どのゲームとプロトコルが対象かは ゲームサーバーの DDoS 対策をリアルタイムで にまとめています。

継続的に攻撃されるコミュニティ向けの Advanced DDoS Protection

プロジェクトによっては、たまたまではなく、狙って何週間も攻撃されます。パターンを変えながら、しかも必ずちょうど最も混み合う時間に来ます。そのために Advanced DDoS Protection があり、月額 50.00 EUR から、PrePaid、最低利用期間なしで利用できます。違いは容量の大きさではなく、制御できることにあります。

  • 専用の保護 IP をフランクフルトの中核から割り当て、お使いのサーバーを当社自身のネットワーク内で切り替えます。ご自身の側で作り直す作業はありません。
  • ポートとプロトコルごとに自分で管理できる保護ルールをカスタマーエリアで設定できます。27015/UDP で何を許可するか、27015/TCP で何を許可するかを、チケットを書かずに指定できます。
  • 変更はリアルタイムで反映されます。そのため、次のメンテナンス枠を待つのではなく、攻撃が進行している最中に調整できます。
  • それぞれのゲームに合わせた保護プロファイルを用意しており、Garry's Mod とほかの Source 系タイトル向けのものに加えて、改造したサーバーや独自のアプリケーション向けの自由な TCP と UDP のプロファイルもあります。

ここでも PrePaid の仕組みです。最低利用期間なし、解約予告期間なし、契約なし、初期費用なしです。攻撃の波が過ぎたら、単に更新しなければよいだけです。Garry's Mod サーバーをこれまでほかの場所で動かしてきた場合、この保護は KernelHost への移転によって手に入ります。フィルタリングは当社自身のネットワークで行われ、他社のインフラ上では行われないからです。

2 つの保護段階の比較

項目 標準で含まれる DDoS 常時保護 Advanced DDoS Protection
料金 すべてのサーバープランに含まれ、追加料金なし 月額 50.00 EUR から、PrePaid
有効化 提供開始時点から有効、設定するものはない 注文し、保護 IP を受け取り、サーバーが切り替えられる
フィルタリング能力 17 Tbps のグローバルなスクラビングと、フランクフルトにおける 3.2 Tbps の Arbor リアルタイムフィルタリング 同じ 2 段階のフィルタリング
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 のルールは正しいのに、それでも効かない」:よくある原因は 3 つです。ルールが UFW のチェーンの後ろにあって到達しない、前回の再起動で消えていた(この場合は apt-get install -y iptables-persistent と netfilter-persistent save、または /etc/ufw/before.rules への記述が助けになります)、あるいは攻撃がボリューム型で、ルールはすでに埋まった回線の上で正しく動いている、のいずれかです。iptables -L INPUT -n -v で一致カウンターが増えているか確認してください。0 のままなら、そのルールには届いていません。

「DarkRP サーバーが全員に対して重いのに、回線は空いている」:これはほぼ常に Lua であり、回線への攻撃ではありません。サーバーのログでどのネットワークイベントが目立って多く届いているかを確認し、対応する Addon にプレイヤーごとの制限があるかを調べてください。sar -n DEV 1 10 と破棄パケットのカウンターに異常がなければ、DDoS 攻撃ではありませんでした。

「RCON が突然使えなくなった」:DDoS ではなく、たいてい自分で作った遮断です。古いパスワードが入った管理パネルが sv_rcon_minfailures を発動させ、sv_rcon_banpenalty が設定された分数だけそのアドレスを遮断します。パスワードを直し、遮断を解除して、そのあとポートをご自身のアドレスに限定してください。

「IP アドレスを変えたのに 2 日後にまたオフラインになった」:それが通常の姿です。お使いのサーバーは、マスターサーバーへの登録が済んだ時点で新しいアドレスを自分で公開しますし、公開アドレスのないゲームサーバーにはプレイヤーが来ません。アドレスの変更は数時間から数日を稼ぐだけで、解決ではありません。

「これまで使っていた事業者に IP アドレスを遮断された」:それがヌルルーティングです。事業者はそれで自分のネットワークを守りますが、ご自身にとっての結果は攻撃の成功と同じで、多くの場合そのあと数時間続きます。判断に迷うときは、フィルタリングなのかヌルルーティングなのかを尋ねてください。その答えは、どんなハードウェアの仕様よりも可用性を左右します。

「tcpdump で見ても、おかしなところがない」:手前のネットワークですでにトラフィックが除去されている場合、サーバーには何も届かないのが当然です。フィルタリングが機能しているときの通常の姿です。逆に回線が飽和していると、測定に使おうとした SSH セッションさえ届かないことがあります。その場合は、カスタマーエリアの VNC コンソールを使ってください。

まとめ

  • Garry's Mod サーバーが必要とする開いたポートはちょうど 1 つ、27015/UDP です。ゲームトラフィックと A2S クエリはそこを一緒に流れ、独立したクエリポートはありません。
  • RCON は 27015/TCP にあり、パスワードを平文で送るため、ご自身のアドレスだけに開放するか、SSH ポートフォワーディング経由で到達するべきものです。
  • 制限するのはポートではなく、ヘッダーが 0xffffffff のコネクションレスパケットです。27015/UDP への一律の遮断は、自分のプレイヤーを追い出します。
  • 最も多い Garry's Mod の障害は DDoS 攻撃ではなく、制限のないネットワークイベントです。util.AddNetworkString で登録したイベントには、プレイヤーごと 1 秒ごとの上限が必要です。
  • 64 バイトのパケットなら、1 Gbit/s の回線は毎秒約 149 万パケットを運びます。それを超えると損失はその手前のルーターで生まれ、ローカルのルールはどれも効かなくなります。
  • KernelHost では、2 段階の常時保護がすべてのサーバープランに追加料金なしで含まれます。グローバルなスクラビングネットワークにおける 17 Tbps の緩和能力と、フランクフルトにおける 3.2 Tbps の Arbor リアルタイムフィルタリングで、ヌルルーティングは行いません。
  • 継続的に狙って攻撃される場合は、月額 50.00 EUR からの Advanced DDoS Protection を加えます。専用の保護 IP、ポートとプロトコルごとに自分で管理できるルール、そしてリアルタイムでの反映です。

サーバーをすでに KernelHost で動かしている場合、フィルタリングは何もしなくても有効です。それでも異常に気づいたときは サポートチケット を開いてください。お使いの IP アドレス向けにフィルタールールを調整します。その際は 4 つの情報をすぐに添えてください。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 サーバーが本当に必要とするポートはどれですか?
ちょうど 1 つです。起動パラメーター -port で設定する 27015/UDP です。この 1 つのポートを、ゲームトラフィックとサーバーブラウザーの A2S クエリが一緒に流れ、Garry's Mod に独立したクエリポートはありません。RCON は 27015/TCP にあり、ご自身のアドレスだけに開放するものです。27020/UDP が必要なのは SourceTV で配信する場合だけです。クライアントポートの 27005/UDP はプレイヤー側から出るので、サーバー側にルールは要りません。DarkRP 用の MySQL は 127.0.0.1 にバインドし、インターネットには決して出しません。
クエリフラッドを止めるために、クエリポートを遮断できますか?
いいえ。独立したクエリポートが存在しないからです。27015/UDP を遮断したり一律でレート制限したりすると、同じ動きで自分のプレイヤーを追い出し、サーバーブラウザーからも消えます。正しいのは、コネクションレスパケットだけに当たる制限です。Source エンジンのサーバーへの問い合わせと接続の確立はすべて 4 バイトの 0xffffffff で始まり、すでに接続しているプレイヤーのトラフィックにはこのヘッダーがありません。まさにこのパターンに対して、nftables か iptables で送信元アドレスごとの上限をかけます。出発点としては毎秒 8 パケットほどです。
Garry's Mod で RCON がこれほど好まれる攻撃目標になるのはなぜですか?
得られるものが最大で、ハードルが低いからです。RCON はゲームと同じポート番号の TCP 側にあるため、探さずに見つかります。Source の RCON プロトコルはパスワードを平文で送り、TLS も鍵交換もありません。そして RCON を奪った者は、帯域幅をまったく使わずにマップを変え、全プレイヤーを BAN し、設定を変更し、サーバーを停止できます。ですから長いランダムなパスワードを設定し、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 が限界に達します。パケットは 1 つごとにネットワークスタックを通す分の負荷がかかり、そのあと破棄される場合でも同じです。この地点からは、サーバーの手前のネットワークでのフィルタリングだけが効きます。
DarkRP サーバーにラグスパイクが出るのに、回線は空いています。原因は何ですか?
それならほぼ常に Lua であり、回線への攻撃ではありません。ある Addon が util.AddNetworkString でネットワークイベントを登録し、net.Receive でそれを待ち受けた時点から、接続しているどのクライアントもそのイベントをループで発火させられ、サーバーはメッセージを 1 件ずつすべて実行します。毎秒わずか数キロビットのプレイヤー 1 人で足ります。解決策はファイアウォールではなく Addon の側にあります。プレイヤーごと 1 秒ごとの上限、メッセージ長の検査、そしてプレイヤーをメッセージの内容からではなくサーバー側で取得することです。
KernelHost のサーバーは、攻撃を受けている間オフラインになりますか?
いいえ。ヌルルーティングは使いません。お使いの IP アドレスはネットワークに残り、破棄されるのは有害なパケットだけです。保護は 2 段階で構成されています。グローバルなスクラビングネットワークにおける 17 Tbps の緩和能力と、フランクフルトにおける 3.2 Tbps の Arbor リアルタイムフィルタリングです。常時動いており、攻撃を受けてから反応する必要がないので、その間にプレイヤーが落ちるような切り替え時間がありません。この常時保護はすべてのサーバープランに追加料金なしで含まれ、提供開始時点から有効です。
Advanced DDoS Protection を追加で必要とするのはどんなときですか?
コミュニティがたまたまではなく、狙って何週間も攻撃され、フィルタリングをご自身で制御したい場合です。専用の保護 IP を受け取り、保護ルールをポートとプロトコルごとに、たとえば 27015/UDP と 27015/TCP を分けて、カスタマーエリアで自分で管理できます。変更はリアルタイムで反映されるため、攻撃が進行している最中に調整できます。料金は月額 50.00 EUR から、PrePaid、最低利用期間なし、解約予告期間なし、初期費用なしです。

Garry's Mod Garry's Mod の DDoS 対策 DarkRP Source エンジン A2S クエリ ゲームサーバー保護 ポート 27015 Advanced DDoS Protection