【実務・中級編】 コンテナのメモリ・CPUリソース制限によるDoS攻撃対策 – インフラ・ネットワーク & クラウドセキュリティ防御ガイド

コンテナの「野放しリソース」は攻撃者の餌食だ。ResourceQuotaでクラスタを死守せよ

現場でインシデント対応をしていると、いまだに「コンテナのリソース制限」を後回しにしているチームに出くわす。正直に言おう。リソース制限のないコンテナを本番環境で動かすのは、鍵をかけずに高級車を路上に放置するようなものだ。

攻撃者は、あなたのアプリケーションの脆弱性を突いて「無限ループ」や「巨大なメモリ確保」を仕掛けるだけで、ホストOSを道連れにしてクラスタ全体をダウンさせることができる。これがリソース枯渇(DoS)攻撃のリアルだ。

今日は、なぜ「制限」が最強の防御なのか、そして具体的にどう設定すべきかを、現場の知見を交えて叩き込む。

—

1. なぜ「制限なし」が致命的なのか(攻撃者の視点)

攻撃者が狙うのはアプリケーションのバグだけではない。「インフラの設計ミス」だ。
例えば、画像処理を行うAPIに細工された巨大なファイルを送りつけ、メモリを食いつぶさせる攻撃は古典的だが、いまだに有効だ。

もしあなたのコンテナに limits が設定されていなければ、そのプロセスはホストOSのメモリ限界まで食らいつく。結果として、同じノードで動いている他の正常なコンテナまで道連れにしてOOM(Out of Memory)キラーが発動し、サービス全体が沈黙する。これを「ノイジーネイバー(騒がしい隣人)問題」と呼ぶが、攻撃者にとっては最高の「踏み台」兼「破壊工作」だ。

—

2. Kubernetesでの防御:ResourceQuotaとLimitRangeの実践

Kubernetesにおいて、リソース制限は「マナー」ではなく「規約」だ。以下の2つをセットで運用すること。

LimitRange:開発者の甘えを許さない

すべてのコンテナにデフォルトの制限を強制する。これがないと、開発者が設定を忘れた瞬間に「無制限コンテナ」がクラスタに紛れ込む。

# limit-range.yaml
apiVersion: v1
kind: LimitRange
metadata:
  name: default-limit-range
spec:
  limits:
  - default:
      memory: "256Mi"
      cpu: "500m"
    defaultRequest:
      memory: "128Mi"
      cpu: "250m"
    type: Container

*これを入れておけば、設定忘れのポッドも自動的にこの枠に押し込められる。*

ResourceQuota:名前空間ごとの「暴走防止装置」

名前空間(Namespace)全体で消費できるリソース量に上限を設ける。特定アプリがバグで暴走しても、クラスタ全体を巻き込む被害を最小限に抑えられる。

# namespace-quota.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: app-namespace-quota
spec:
  hard:
    requests.cpu: "4"         # 合計で4コアまで
    requests.memory: "8Gi"    # 合計で8GBまで
    limits.cpu: "10"          # バーストしても10コアまで
    limits.memory: "16Gi"     # バーストしても16GBまで

—

3. アプリ層での防御:Node.jsでのメモリ管理例

インフラ側の制限だけでなく、アプリケーションコード自体もリソースを食い散らかさない工夫が必要だ。特にNode.jsのようなイベントループ環境では、巨大なリクエストのバッファリングがメモリ枯渇のトリガーになる。

// リクエストのサイズを制限する実用的なミドルウェアの例
const express = require('express');
const app = express();

// 1MBを超えるリクエストは即座に拒否する
// これにより、巨大なパケットを送りつける攻撃を未然に遮断する
app.use(express.json({ limit: '1mb' }));
app.use(express.urlencoded({ limit: '1mb', extended: true }));

app.post('/upload', (req, res) => {
  // ここで処理を開始
  res.send('処理完了');
});

—

4. 監視なしの防御は「目隠し」と同じだ

ここからがプロの運用だ。制限をかけただけで満足してはいけない。PrometheusとGrafanaを使い、「制限値に近づいているポッド」をアラートさせる仕組みを構築しろ。

推奨するアラート条件はこれだ:

  • container_memory_working_set_bytes / container_spec_memory_limit_bytes > 0.8
  • 直訳:「制限値の80%を恒常的に超えているポッドがある」

このアラートが鳴った時、それは攻撃ではなく「ただのメモリリーク」かもしれない。しかし、放置すればいずれDoS攻撃と同じ結末を迎える。インシデントが起きる前に、そのポッドのメモリ使用率を可視化し、チューニングするか、制限値を緩和するかの判断を下すのが、我々エンジニアの仕事だ。

—

まとめ:防御の哲学

セキュリティ対策において「完璧」は存在しない。しかし、「被害を境界の内側に閉じ込める」ことは可能だ。

  • LimitRange で全コンテナの暴走を許さない。
  • ResourceQuota で名前空間を隔離する。
  • アプリ層のガードで無駄な消費をさせない。
  • 監視で限界を可視化する。

この4つを徹底するだけで、あなたのインフラは「攻撃者が最も手を出したくない、面倒で堅牢な要塞」へと進化する。

もし今、あなたのクラスタで resources: セクションが空欄のデプロイメント定義を見つけたら、それが今日やるべき最初の修正タスクだ。手を動かせ。守れるのは、自分たちのコードとインフラだけだ。

コメント

タイトルとURLをコピーしました