- TablePlusは数週間続くDDoSの試みにもかかわらず、IP遮断やCloudflareの「Under Attack」モードを使わず、サーバーの状態を観察している
- 直近30日間のインストーラダウンロード試行は約600万回、直近5日間だけで800,126回に達し、ファイルサイズは1回あたり約200MB
- 攻撃中でもサーバーのCPU使用率はほとんど**0〜1%**にとどまり、キャッシュなしでも約8つのAPIサービスとデータベースが月間数十億リクエストを処理できるよう構成されている
- バックエンドはアプリごとのモノリシックサービスとしてまとめ、Docker・Kubernetes・ランタイム環境なしで単一バイナリを新しいVPSへデプロイする方式を使っている
- Go/Rustフレームワーク、データベースのインデックス化、ログDBの分離、Nginxリバースプロキシ、Cloudflare CDN/R2、メール送信のthrottlingといったシンプルな構成が、攻撃コストと運用の複雑さを下げている
数週間続いたDDoSの試み
- 何者かがTablePlusサーバーに数週間にわたり数億件のリクエストを送り、インストーラを数百万回ダウンロードしようと試みている
- インストーラのダウンロード試行は直近5日間で800,126回、直近30日では約600万回に達する
- 直近30日のAPI呼び出しベースでは、トラフィックの大半はEU、特にGermanyとUnited Kingdomから発生している
- この数値にはダウンロードリクエストとCDNトラフィックは含まれない
- 記事執筆時点でも攻撃は続いている
実際の対応: 遮断より耐える構造
- 攻撃元IPアドレスはブロックしていない
- Cloudflareは使っているが、「Under Attack」モードは有効にしていない
- 攻撃中でもサーバーCPUはほとんど**0〜1%**で、ほぼアイドル状態にある
- サービスが月間数十億リクエストを問題なく処理でき、コスト負担も大きくないため、特別な対策はほとんどしていない
シンプルなバックエンド設計
- TablePlusのアプリ設計はシンプルさを重視しており、バックエンドサービスも可能な限り最小構成に保っている
- VercelやNetlifyのようなサードパーティのレンダリングサービスは、攻撃時にボトルネックや想定外の請求を招く可能性があるため使っていない
- 自前のWebサーバーを使えばこうした制約を避けられると考えている
- 過去には弱いVPSやプロセッサのためにモノリスがボトルネックになることもあったが、現在のVPSはマルチコアCPU、大容量RAM、高速SSDを提供している
- 高速SSDとRAMはデータベース性能を大きく向上させる
- 正しく実装すれば、単一インスタンスのモノリシックサービスでも月間数十億リクエストを処理できる
アプリごとのモノリス運用方式
- 各アプリに必要なAPI、Webサイト、メール、決済などを1つのサービスに統合している
- デプロイは設定ファイル1つ、ビルド、デプロイだけで完了する
- 別のクラウドベンダーへサービスを移したり、新規デプロイしたりする際にも素早く対応できる
- 依存関係が少ないため、デバッグやボトルネックの特定が容易
- エラーが発生しても確認すべきサービスは1つ、または少数で済む
- 選択肢となるモノリス向けフレームワークの例は次の通り
- Golang: Echo, Gin
- PHP: Laravel
- Ruby: Rails
- Rust: Actix, Rocket, Warp
月間数十億リクエストのための構成原則
- 高性能なWebフレームワークを選び、TablePlusはGolangとRustを好んでいる
- データセットが大きくなるほど検索時間を短縮するため、データベースにインデックスを設定する
- 中核ビジネスの性能を守るため、メインデータベースとログ・利用量データベースを分離する
- メインデータベースは、変化しないか時間が経ってもサイズが増えないデータ向け
- ログ・利用量データベースは、時間とともに増え続けるデータ向け
- 中核APIの前段にはリバースプロキシを置き、リクエストを処理・分散する
- 複数サーバーが必要な場合に役立つ
- TablePlusはNginxを使っている
- すべてをCloudflareの背後に置き、キャッシュ、Argo、Cloudflareとサーバー間のfull SSLを適切に設定する
- DDoS保護付きのCDNを使う
- TablePlusはコスト削減と保護のため、Amazon CloudFront + S3よりCloudflare R2とCDNを好んでいる
- 大きなダウンロードファイルをCDNやキャッシュなしでVPSに置くと、帯域をすぐに消費してしまう
- TablePlusは帯域無制限のCloudflare CDNを使っている
- 同一ドメインでArgoを有効にすると、Cloudflare CDNトラフィックがArgoを経由する可能性があり、Argoの帯域は無料ではないためコストが大きくなり得る
- パスワード再設定のようにサーバーからメール送信が必要なリクエストは、throttlingでメーラーを保護する
デプロイ方式: コンテナなしでバイナリを実行
- TablePlusはDocker、Kubernetes、コンテナ、別途ランタイム環境の設定なしで、デプロイ手順を可能な限りシンプルに保っている
- デプロイ単位はバイナリである
- Linuxサーバーにコピーした後、プロセスとして実行する
- macOSでTablePlusアプリを実行するのと似た感覚で扱っている
- Linux Systemctlにプロセス監視を任せ、致命的なエラーが発生したら再起動させることができる
- VM、仮想化、サードパーティのインフラ管理ツールといった中間レイヤーにCPU/RAMを無駄遣いしないよう、ネイティブ方式で実行している
- GoとRustは高性能な言語であり、デプロイ用バイナリファイルを生成できるため採用している
Vercel利用時に有効化しておくべき保護機能
- Vercelはこのような状況でサイトを保護する機能として、Spend ManagementとAttack Challenge Modeを提供している
- Spend Management: softまたはhardの支出上限を設定できる
- Attack Challenge Mode: Cloudflareの「Under Attack」モードに似ている
- Vercelを使うなら、これらの機能を有効にしておく必要がある
1件のコメント
Hacker News のコメント
この記事は自画自賛がひどすぎるように感じる。「月10億リクエスト」は秒間数百リクエストにすぎず、取るに足らない規模で、DDoSと呼ぶのも難しい
しかもサイトは CDN である Cloudflare の背後にあるのに、パフォーマンス面で何かすごいことをしたと見る理由がなおさら理解できない
たとえば 200MB のファイルが CDN にキャッシュされて配信されない合理的な状況はない。アプリケーションサーバーがダウンロードのたびにディスクから 200MB を読み、クライアントへコピーしていないことを誇るような話ではなく、そうしていたら明らかに悪い設計だ
話している200MB のファイルが https://tableplus.com/download の TablePlus クライアントアプリのダウンロードなら、Windows 版は 183MB で、実際に Cloudflare にキャッシュされている
# curl -v https://files.tableplus.com/windows/5.9.2/TablePlusSetup.exec > /dev/null< cache-control: max-age=691200< cf-cache-status: HIT< age: 2980普通こういうのは、FAANG 企業なら開発者1000人以上が必要だと考えそうなことではないのか。大企業でも定期的にはできていないことを、どうして自画自賛とだけ言えるのか分からない
秒間数百リクエストだけと見るのは難しい。リクエスト密度は時間的に均等ではなく、平均の20倍まで上がることもよくある
月4TBは、実際には DDoS 攻撃とは言いにくいのでは? 時間あたり 4TB なら DDoS と言えるかもしれないが、月 4TB は秒間 1.5MB にすぎない
月600万リクエストも秒間2リクエストにすぎない。この規模では、モノリスサービスで運用しているという事実はあまり重要に見えないし、特に Cloudflare が CDN キャッシュで大半のリクエストを処理していると考えればなおさらだ
Web の圧倒的大多数は月5〜20ドルのマルチテナント型ホスティングに載った WordPress サイトで、より大きな組織なら Drupal の場合が多い。キャッシュも導入せずデータベースに直接つながっているため、インターネット上の大半のサイトは秒間4リクエストだけでも落ちる可能性がある
ばかげて聞こえるかもしれないが、現実はそうだ。以前 Cloudflare で DDoS 防御、WAF、ファイアウォール、顧客向けセキュリティプロジェクトを管理していたが、Web スクレイピングやごく小さく低い HTTP リクエスト率だけで顧客サイトが落ちるのを頻繁に見た
大きな数字が見出しを飾るが、ほとんどの人が実際に体感するのは小さな数字だ
サービス拒否攻撃は、時間あたり数 KB だけでも可能だ。対象サービスの重い API エンドポイントを叩けば、それだけでサービスを落とせるし、Distributed とは複数のマシンが同時に攻撃するという意味にすぎない
DDoS に必ずしも巨大なスループットが必要なわけではない。ただ、ニュースに出るのはたいてい大規模な攻撃だ
こうした攻撃の目的は、オリジンサーバーの帯域割り当てを使い切らせること、または帯域コストを負担できなくさせることかもしれない。単一または少数の攻撃マシンでも非常に安価に実行できる。ほとんどのデータセンターやホスティング事業者には帯域制限があるか、一定量を超えると課金されるが、攻撃を受けた会社は支払いきれない請求書を受け取って初めて気づくことがあまりにも多い
総プレイヤー数が50人ほどのゲームダウンロードサイトがある。誰も使っておらず、実際の人間がゲームをダウンロードするのは月に数回程度だと思う
ところが2GB の zip ファイル1つで先月 5TB のトラフィックが発生し、これが何年も続いている。これは DDoS ではなく、リンクをすべてたどり、ダウンロードをキャンセルできない設定ミスのボット/クローラーにすぎない
1日あたり1TB程度のようだが、それでもまだ非常に小さい部類だ
トラフィックがどれほど不規則かによるように思う
これはデスクトップアプリ向けの静的なマーケティングサイトにすぎない。ディスカッションフォーラムもなく、フィードバックは GitHub Issues で処理している
静的なマーケティングサイトのデプロイがどれほど単純か、静的ファイルが1日に数百万回ダウンロードされても耐えられることを誇るのはかなり奇妙だ。そしてここで緩和作業をしているのは Cloudflare であって、彼らではない。そもそもこの程度の小さなトラフィックに緩和が必要なら、という話だが
自分がこういう「攻撃」を受けていたとしても、Cloudflare が毎月送ってくる「X TB 転送、ほぼ 100% の帯域削減」というメールを受け取るまでは気づきもしなかったと思う
アプリ自体は好きだし、おすすめできる
これは DDoS 攻撃というより、説明上は月 8TB ほどの追加トラフィックによる「迷惑で無意味なサービス濫用」に近く見える
そうした濫用がコストやリソース枯渇の問題を生まない限り無視してもよいが、「実はオートスケーリングフリート容量の80%が何の有用な仕事もしていなかった」といった話は憂鬱なほどよくあるので、少なくとも監視はする必要がある
こうした濫用で最も厄介なのは主にログだ。ログの大半が、同じホスト群が同じように無意味な行動を繰り返す内容で埋まる。ログストレージが安く十分にあるなら大きな問題ではないが、特定のトラフィックを自動的に濫用と分類し、日常的な処理を抑制する方法は間違いなく良いアイデアだ
ただし言うほど簡単ではない。インバウンド SMTP サーバーに、露骨でばかげた濫用を捕まえる分類ロジックを何度追加したか数え切れないが、そのたびに境界線上では有効なシナリオまで引っかかってしまった
連続するウサギ穴にあまりにも多くの時間を費やすと、実際の仕事ができなくなりがちだ。だから外注するか、それも面倒すぎたり高すぎたりするなら、腹立たしくても濫用をそのまま無視したほうがよい場合もある
DigitalOcean はたとえば s-4vcpu-8gb-intel に含まれる 5TB を超える 3TB 分に30ドルを払い、Linode は 3TB 超過分が15ドルだ。だから記事の要旨には意味があると思う
これは注目に値する「攻撃」ではありません。誰かのマシンで暴走した単一の bash スクリプトだけでも可能です
「英国から月5,000万リクエスト」は、平均すると秒間20リクエストにもなりません。単一の Go サーバー1台でも、大きな最適化なしにその250倍のリクエスト量は処理できると期待します
助言そのものが必ずしも悪いわけではありませんが、その数字は助言の根拠にはなりません。Go HTTP API サービスなら、小〜中規模の VPS で、データベース処理や JSON フォーマットが多少あっても、最適化なしで秒間5,000リクエストは処理できると期待します。これは、1日に数十億リクエストを受け、ピーク時には秒間50万リクエストを超えていた場所で、似たようなサービスを数十個デプロイした経験に基づく数字です
これが被害を与えていないのはよいことですが、これを助言として受け取るには抜けている部分が多く、少し不安です
Docker よりバイナリ配布を好むというのは構いませんが、そのバイナリが動くホストはどうするのでしょうか。コンテナを使う理由の1つは、セキュリティ強化の設定をデプロイに同梱し、後でスケールする必要が出たときにもノード間でセキュリティ設定が同一だと確信するためです
ここで言うモノリスは単一の VPS に載せられますし、それは良く、安くもあります。しかしクラッシュしたり、何らかの理由でハードウェアが故障したりすると、かなり大きなダウンタイムが発生する可能性があります
もう1つ懸念しているのは、すべてをモノリスにまとめると、アプリケーションスタックの多層防御を失うという点です。誰かがフロントエンド経由でアプリを突破すると、バックエンドのデータストアまでまっすぐ到達できてしまいます。だから多くの人はデータストアを内部 Web サービスの背後に置き、フロントエンドから離れたプライベートネットワークのセキュリティグループで遮断して、攻撃面をブラウザで実行可能な操作の範囲に制限します
攻撃面を増やしてセキュリティが向上する世界はありません
Docker をインストールすればホストがセキュアになると思っているなら、それは間違いです。スケール時に追加するホストはどう設定するのでしょうか。
Docker を使うなら、コンテナだけでなくホスト、つまりコンテナを実行するサービスまで安全である必要があります。スケールしてホストを追加デプロイする場合も、同じように安全でなければなりません
Infrastructure as Code と Configuration as Code を使っているなら、システム設定の後にバイナリをデプロイするのか Docker をデプロイするのかに本質的な違いはありません
「ベアメタル」設定をある程度再現可能にするツールはあります。たとえば NixOS、Ansible、Amazon AMI イメージビルドがあります
「フロントエンド経由でアプリを突破する」という表現がいつも理解できません。SQL インジェクションはリモートコード実行なしにデータストアを直接触りますし、XSS は他のユーザーに水平的に影響します
では、フロントエンドからバックエンド全体へ自由にどうやって到達するのでしょうか。人々は Go API サービスの中でシェルアクセス権のある JavaScript インタプリタを動かし、ユーザー入力に対して
evalを呼んでいるのでしょうか。技術的にかなり無理があるように感じます結局、曖昧さによるセキュリティではないのでしょうか。複雑にしすぎて、自分たちにも理解できなければ他人にも理解できないだろう、ということなのでしょうか。
重要なのは壁を増やすことではなく、壁を破れなくすることです。インターフェースは性能を下げ、複雑さを増やします
個人的に引っかかる表現です。「月10億リクエスト」はおおよそ秒間370リクエストです。適切に設定されたサーバー1台でも処理可能な水準で、確実に10台未満で足ります
単一の悪意ある bash スクリプト1つでも、その程度のトラフィックは生成できます
現実感覚がずれているのかもしれませんが、月に数十億リクエストというのは大したことがないように聞こえます。これは大規模な DDoS 攻撃と見なされるのでしょうか。
「各アプリごとに、デプロイと保守が容易なモノリスサービスを作る。Docker も Kubernetes も依存関係もランタイム環境もなく、新しく作った VPS のどこにでもデプロイできるバイナリファイル1つだけ」という部分は良かったです
TablePlus を直接使ってはいません。これがマーケティング Web サイトの話なら、Kubernetes が不要なのは当然です
アプリケーションの話なら、依存関係がないというのは不思議です。データを保存したりログを残したりしないのでしょうか。
これは Golang の本当に素晴らしい利点です。VPS を立ち上げ、妥当なデフォルトを適用したうえで、クロスコンパイルしてバイナリを実行すればよいのです
Python、Node、PHP のデプロイと比べると、不要な複雑さがはるかに少なくなります
データベースサーバーを実行し、稼働させ続けることもこれくらい単純ならよいのですが
タイトルを見て、もう少し swole doge にふさわしい内容を期待していました。助言のかなりの部分には同意しますが、Cloudflare の背後にいるというのは、決して何もしていないことではありません
トラフィック分布によっては、Cloudflare なしで VPS だけでも十分持ちこたえられたかもしれませんし、規模もそれほど大きく見えません。秒間リクエスト数や、Cloudflare がオリジン到達前にどれだけ遮断したのかといった、より詳しい統計があれば興味深かったでしょう
私が知っているスウェーデン企業向けのロシア発の L7 DDoS は、主要プロバイダーが容量問題に突き当たって倒れるほど大規模でした。Verizon、Azure Frontdoor、Cloudflare、GCP ロードバランサーまで含まれます。その規模を相手に、この戦略は絶対に通用しません