コンテナの「野放しリソース」は攻撃者の餌食だ。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: セクションが空欄のデプロイメント定義を見つけたら、それが今日やるべき最初の修正タスクだ。手を動かせ。守れるのは、自分たちのコードとインフラだけだ。
コメント