AWS
PR

AWS Load Balancer Controller で ALB を IP 制限するときの注意点(source-ip の上限・優先度・反映中の 404)

saratogax
記事内に商品プロモーションを含む場合があります

EKS で動かしている API を、決まった接続元の IP からだけ使えるようにしたいことはありませんか?

ALB を AWS Load Balancer Controller で作っているなら、Ingress の annotation でリスナールールに接続元の IP の条件(source-ip)を付けられます。

設定そのものは数行足すだけですが、実際に入れてみると気を付けるところがいくつかありました。

結論から書くと、気を付けるところは次の 3 つです。

  • source-ip は 1 つの条件に 3 つまでで、超える分はルールを分ける
  • ルールの優先度は、Ingress の paths の並び順で決まる
  • ルールの条件を書き換えると、反映中に一時的に 404 になる場合がある

3 つ目は、Load Balancer Controller のソースを読んで、どういうときに起きるのかを確かめました。

この記事では、それぞれの理由と、本番環境で 404 を出さずに反映した手順を紹介します。

確認した環境

  • AWS Load Balancer Controller v2.17.1(EKS で動かしているもの)
  • ソースは v2.17.1 と、執筆時点で最新の v3.6.2(2026 年 10 月 25 日公開)を読み比べた

この記事で触れる部分は、2 つのバージョンで同じ動きでした。

基本の設定: conditions と actions を組み合わせる

Load Balancer Controller では、alb.ingress.kubernetes.io/conditions.<名前> の annotation で、Ingress のパスにリスナールールの条件を足せます(公式ドキュメント)。

接続元の IP で絞るときは、source-ip の条件を使います。

転送先は alb.ingress.kubernetes.io/actions.<名前> に書き、paths の backend からは use-annotation で参照します。

例として、api.example.com の API を 3 つの IP からだけ使えるようにする設定です。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api-ingress
  annotations:
    alb.ingress.kubernetes.io/actions.allow-rule-1: >
      {"Type":"forward","ForwardConfig":{"TargetGroups":[{"ServiceName":"api-backend","ServicePort":"80"}]}}
    alb.ingress.kubernetes.io/conditions.allow-rule-1: >
      [{"Field":"source-ip","SourceIpConfig":{"Values":["203.0.113.10/32","203.0.113.11/32","203.0.113.12/32"]}}]
spec:
  ingressClassName: alb
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /*
            pathType: ImplementationSpecific
            backend:
              service:
                name: allow-rule-1
                port:
                  name: use-annotation

これで、次の 3 つがそろったときに当たるリスナールールが 1 つできます。

  • ホストが api.example.com
  • パスが /*
  • 接続元が 3 つの IP のどれか

許可していない IP からのアクセスは、どのルールにも当たらず、リスナーのデフォルトアクションに回ります。

私の環境では、許可していない IP から curl でアクセスして、デフォルトアクションの 404 が返ることを確かめました。

なお、source-ip の判定に使われるのは、ALB から見た接続元の IP です。

間にプロキシーがあるとプロキシーの IP で判定され、X-Forwarded-For ヘッダーの IP は見ません(AWS のドキュメント)。

注意点 1: source-ip は 1 つの条件に 3 つまで

許可したい IP が 4 つ以上あると、1 つのルールには入りません。

AWS のドキュメントには、条件に指定する値の数について、次の 2 つの決まりがあります。

  • 1 つの条件に 3 つまで(up to three match evaluations per condition)
  • 1 つのルール全体で 5 つまで(up to five match evaluations per rule)

Quotas のページにも「Condition Values per Rule」が 5 とあり、引き上げはできません。

ルール全体の上限 5 だけを見て、source-ip に 4 つ入れないように注意してください。

上の例では、ホストの 1 つとパスの 1 つも値として数えるので、source-ip に使えるのは残りの 3 つです。

ホストを指定しないルールでも、1 つの条件に 3 つまでの決まりがあるので、source-ip は 3 つまでです。

4 つ以上許可したいときは、同じパスのルールを分けて、IP を 3 つずつ入れます。

metadata:
  annotations:
    alb.ingress.kubernetes.io/actions.allow-rule-1: >
      {"Type":"forward","ForwardConfig":{"TargetGroups":[{"ServiceName":"api-backend","ServicePort":"80"}]}}
    alb.ingress.kubernetes.io/conditions.allow-rule-1: >
      [{"Field":"source-ip","SourceIpConfig":{"Values":["203.0.113.10/32","203.0.113.11/32","203.0.113.12/32"]}}]
    alb.ingress.kubernetes.io/actions.allow-rule-2: >
      {"Type":"forward","ForwardConfig":{"TargetGroups":[{"ServiceName":"api-backend","ServicePort":"80"}]}}
    alb.ingress.kubernetes.io/conditions.allow-rule-2: >
      [{"Field":"source-ip","SourceIpConfig":{"Values":["198.51.100.20/32","198.51.100.21/32"]}}]
spec:
  ingressClassName: alb
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /*
            pathType: ImplementationSpecific
            backend:
              service:
                name: allow-rule-1
                port:
                  name: use-annotation
          - path: /*
            pathType: ImplementationSpecific
            backend:
              service:
                name: allow-rule-2
                port:
                  name: use-annotation

同じパスのルールが並んでいても、条件が違えば問題ありません。

ALB はルールを優先度の順に評価し、条件に合ったルールで止まります。

ルールの数は、1 つの ALB に 100 個まで(引き上げ可能)なので、IP が多いとルールの数も増えていく点は覚えておくとよいです。

注意点 2: ルールの優先度は paths の並び順で決まる

ルールを分けると、次に気になるのが優先度(ルールを評価する順番)です。

Load Balancer Controller には、Ingress の中のルールの優先度を、番号で指定する設定がありません。

alb.ingress.kubernetes.io/group.order はありますが、これは複数の Ingress を 1 つの ALB にまとめるときの、Ingress 同士の順番です。

ソース(pkg/ingress/model_build_listener_rules.go)を読むと、優先度は次のように振られていました。

  1. Ingress の rules と paths を、上から順に並べる
  2. 並べた順に、1 から番号を振る

ただし、paths は pathType で並べ替えられてから番号が振られます。

  • Exact のパスが先頭
  • 次に Prefix のパスを、パスの長い順に並べる
  • 最後に ImplementationSpecific(または pathType の指定なし)のパスを、書いた順のまま並べる

書いた順がそのまま優先度になるのは、ImplementationSpecific のパスだけです。

この記事の例は、すべて ImplementationSpecific で書いています。

注意点 3: 反映中に一時的に 404 になる場合がある

Ingress を変えると、Load Balancer Controller は ALB に設定済みのルールと Ingress から作ったルールを突き合わせます。

そのうえで、違うところだけを ALB に反映します。

ソース(pkg/deploy/elbv2/listener_rule_synthesizer.go)では、次の順番で処理していました。

  1. 内容(条件と転送先)がまったく同じルールは、番号だけを付け替える(SetRulePriorities をまとめて 1 回呼ぶ)
  2. 内容が違い、番号が同じルールは、その場で書き換える(ModifyRule)
  3. 新しく必要になったルールを作る(CreateRule)
  4. 要らなくなったルールを消す(DeleteRule)

1 の番号の付け替えは、2025 年 3 月の変更(#4039)で入り、v2.12.0 から含まれています。

それより前のバージョンは番号だけで突き合わせていました。

処理の順番も違い、要らないルールを先に消してから新しいルールを作り、最後に同じ番号のルールを書き換えていました。

v2.12.0 以降でも、ルールの内容が変わると 2 の「その場での書き換え」になり、番号の小さいルールから 1 つずつ書き換わります。

そのため、書き換えの途中や 3 で新しいルールができるまでの間に、許可したい IP がどのルールにも当たらない瞬間ができることがあります。

その間のアクセスは、デフォルトアクションの 404 になります。

私が気付いたのは、次の 2 つの場面です。

条件のないルールに、あとから条件を付けるとき

IP 制限を入れる前は、/* のルールに条件がなく、どこからでも使える状態でした。

        paths:
          - path: /*
            pathType: ImplementationSpecific
            backend:
              service:
                name: api-backend
                port:
                  number: 80

これを、注意点 1 の 2 つのルールの形に 1 回で変えるとします。

番号 1 のルールは「内容が違い、番号が同じ」なので、その場で書き換わります。

書き換えたあとは、203.0.113.x の 3 つだけを許可するルールになります。

番号 2 のルールはまだないので、作られるまでの間、198.51.100.x からのアクセスは 404 になります。

IP を別のルールへ移すとき

前のルールの IP を後ろのルールへ移すと、両方のルールの内容が変わります。

どちらも「その場での書き換え」になり、番号の小さい前のルールから書き換わります。

前のルールから IP が消えてから、後ろのルールに IP が足されるまでの間、その IP はどのルールにも入っていません。

逆向き(後ろから前へ)なら隙間はできませんが、向きを気にしながら作業するより、移さないと決めてしまうほうが簡単です。

404 を出さずに IP 制限を入れるには: 2 段階に分けて反映する

開発環境では、数秒の 404 は許容して、1 回で反映しました。

本番環境は止めたくなかったので、次の 2 段階に分けました。

  1. 条件のない /* のルールを先頭に残したまま、後ろに許可するルールを足す
  2. 先頭のルールに source-ip の条件を付ける

1 段階目の paths は、次のようになります。

        paths:
          # 今までどおり条件なし。2 段階目で allow-rule-1 に変える
          - path: /*
            pathType: ImplementationSpecific
            backend:
              service:
                name: api-backend
                port:
                  number: 80
          # 1 段階目で足す
          - path: /*
            pathType: ImplementationSpecific
            backend:
              service:
                name: allow-rule-2
                port:
                  name: use-annotation

1 段階目では、既存のルールは内容も番号も変わらず、新しいルールが後ろに作られるだけです。

すべてのアクセスが先頭のルールに当たるので、この時点ではまだ制限はかかっていません。

2 段階目で先頭のルールを allow-rule-1 に変えると、注意点 1 の形になります。

このときの変更は先頭のルールの書き換え 1 回だけで、ほかの許可するルールはすでにあるので、隙間はできません。

本番環境の反映では、許可した接続元からのアクセスが 404 になることはありませんでした。

IP を変えるときの決まりを書いておく

IP 制限は、入れたあとも IP の追加や削除が続きます。

そこで、Ingress の YAML のコメントに、次の決まりを書いておきました。

  • 既存の IP を別のルールへ移さない
  • IP を足すときは、末尾のルールに足すか、末尾にルールを増やす
  • IP を消しても、空いたところを詰めない
  • 変更の多い接続元のルールほど、paths の後ろに置く

1 つ目は、上の「IP を別のルールへ移すとき」を避けるためです。

2〜4 つ目は、v2.12.0 以降なら番号の付け替えで済むので、必ず守らないといけないわけではありません。

それでも、変更のたびに差分が小さくなり、どのルールが書き換わるのかを読みやすくなります。

v2.11 以前のバージョンでは、途中にルールを足したり消したりすると後ろのルールがすべて「その場での書き換え」になります。

そのため、この決まりがそのまま効きます。

まとめ

  • source-ip は 1 つの条件に 3 つまでで、ルール全体の 5 つとは別の決まり
  • 優先度は paths の順に 1 から振られ、番号は指定できない(pathType による並べ替えもある)
  • 内容が変わるルールは、反映中に一時的に 404 になる場合がある
  • 条件のないルールから切り替えるときは、2 段階に分けると隙間ができない

IP 制限は入れたあとも IP の変更が続くので、最初に変更の決まりを決めておくと安心です。

ABOUT ME
saratoga
saratoga
フリーランスエンジニア
仕事にも趣味にも IT を駆使するフリーランスエンジニア。技術的な TIPS や日々の生活の中で深堀りしてみたくなったことを備忘録として残していきます。
記事URLをコピーしました