GKE に Nginx でレート制限を入れる時の注意点

こんにちは。 vivit で SRE をやっている 宮本 です!

今回はサービスを Dos 攻撃から守るためにレート制限を導入した際に得られた知見をまとめてみたいと思います。

vivit の各サービスは GKE 上で動いており、 GKE Ingress から最初に転送する Pod は基本的に Nginx のリバースプロキシです。 Nginx からユーザーが利用するフロントや API などにアクセスします。

図にすると、以下のようになります。

f:id:tatsurom:20220310155842p:plain

どうやったのか

やり方自体はシンプルで、 Nginx が提供しているレート制限の仕組みをそのまま利用しただけです。 こちらの Nginx 公式ブログ が分かりやすいです。

一部省略や加工を行っていますが、以下のように設定を入れます。

http {
  limit_req_zone $http_x_forwarded_for zone=limit_x_forwarded:50m rate=3r/s;
server {
  listen 80;
  server_name example.com;
  location / {
    limit_req zone=limit_x_forwarded burst=1 nodelay;
    limit_req_status 429;

これだけです。

レート制限の動作確認は必須です。 検証環境への意図的な攻撃には ab を利用しました。

結果の確認は全て Datadog で行っているので簡単でした。 ホストや UA、IP アドレスなどで絞ることで簡単に、どの程度ステータスコード429 で返せたかが分かります。

f:id:tatsurom:20220310162941p:plain

Datadog は決して安くないツールですが、 vivit の開発部では必要で価値あるものにはしっかり投資する文化なのでケチらず使っています。
SRE だけでなく各チームの開発者も日常的に使っており、トラブルシューティングや開発に大いに役立てています!!

この構成の何が問題か

Nginx は Deployment で管理しており Pod のレプリカ数は2以上ですが、レート制限に引っかかるかの閾値はそれぞれの Pod の中だけで判断しています 😇

つまり L4 Service から Pod へのトラフィックは、Pod 間でほぼ等しく分散されるので、意図した数以上のアクセスを許可してしまいかねません。

当然ではありますが、最初は盲点でした。。。

だからといって、例えばレプリカ数が 4 であることを前提として閾値をチューニングすると、それはレプリカ数に依存した設定になってしまい、Pod を気軽に増減できなくなってしまい Kubernetes の良さを一部捨てることになります 🥲

レート制限においては、Nginx は Kubernetes の考え方と相性が悪いとも言えそうです。

結局どうしたのか

かかる工数や、現状受けている攻撃の規模や傾向、Nginx Pod の負荷などを総合的に判断して、今回はこのまま Nginx を使ってレート制限をすることにしました。

適切にチューニングを行った結果、完璧ではないものの、ある程度攻撃に強い構成にできたので目的は達成です!!

改善するなら

Kubernetes 環境でレート制限を入れたいケースは珍しくなく、そのような場合の最も多い構成はサービスメッシュで入れることかなと思います。

Istio を使っている企業さんは多く見受けられます。 公式チュートリアル もありました。

Istio ベースのマネージドな Anthos Service Mesh を検討する方も多そうです。

入れるとなると Kubernetes クラスター全体に影響があることなので今回は見送りましたが、もっと大規模になってきたら検討しても良いのかもしれません。

最後に

vivit では一緒に働くエンジニアを大募集しています 🎉

www.wantedly.com

少しでも興味を持って頂いた方は、是非カジュアル面談の応募をお待ちしております!