Skip to content
Published on

ingress-nginx セキュリティハードニング — WAF、mTLS、アノテーションリスクの遮断

Authors

はじめに

Ingress コントローラはクラスタに入るすべての外部トラフィックの入口です。入口が破られればその先のすべてのサービスが危険にさらされるため、ingress-nginx のセキュリティ設定は単なる付加機能ではなく、クラスタセキュリティの最前線です。

特に 2025 年に報告された一連の CVE(いわゆる IngressNightmare 系)は、アノテーションと admission webhook の処理経路を悪用すればコントローラ権限でのコード実行が可能であることを示しました。コントローラは通常クラスタ全体の Secret(TLS 証明書を含む)を読めるため、コントローラの掌握はクラスタの掌握に直結し得ます。

本記事は ingress-nginx を安全に運用するためのハードニング手法を一度に整理します。危険なアノテーションの遮断から WAF 連携、mTLS、external auth、TLS ポリシー、マルチテナント分離、CVE 対応、そして運用チェックリストまで扱います。

snippet アノテーションの無効化と RBAC

まず最初に閉じるべきは snippet アノテーションです。configuration-snippet、server-snippet といったアノテーションは任意の nginx 設定をコントローラの nginx.conf に注入でき、事実上コントローラ権限で任意コードを実行できる通路になります。

apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
  namespace: ingress-nginx
data:
  allow-snippet-annotations: "false"
  annotations-risk-level: "Critical"
  strict-validate-path-type: "true"

最新バージョンでは allow-snippet-annotations のデフォルトが false です。annotations-risk-level は危険度の高いアノテーションの許可範囲を制限し、Critical にすると最も危険な分類を遮断します。

次の肝は RBAC です。Ingress を作成できる権限はルーティングを変えられる権限なので、信頼できる主体だけに限定する必要があります。

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: ingress-editor
  namespace: team-a
rules:
  - apiGroups: ["networking.k8s.io"]
    resources: ["ingresses"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]

Ingress 権限はネームスペース単位で付与し、snippet が必要な特殊ケースは別途のレビュー手続きとポリシーエンジン(例: OPA Gatekeeper、Kyverno)で統制してください。

ModSecurity / Coraza WAF 連携

Web アプリケーションファイアウォール(WAF)は SQL インジェクション、XSS、パストラバーサルといった攻撃をリクエスト段階で遮断します。ingress-nginx は ModSecurity による WAF をサポートします。なお ModSecurity は徐々に後継エンジンの Coraza へ移行する流れですが、運用概念は類似しています。

apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
  namespace: ingress-nginx
data:
  enable-modsecurity: "true"
  enable-owasp-modsecurity-crs: "true"
  modsecurity-snippet: |
    SecRuleEngine On
    SecRequestBodyAccess On

OWASP Core Rule Set(CRS)も併せて有効化すると、一般的な攻撃パターンを広範に遮断できます。導入初期は遮断(On)ではなく検知専用(DetectionOnly)で運用し、誤検知を取り除くのが安全です。誤検知が見つかったら特定のルールを例外処理したうえで、段階的に遮断モードへ切り替えてください。

クライアント証明書 mTLS

内部サービスやパートナー API のように呼び出し主体を強く制限すべき経路には、クライアント証明書ベースの相互 TLS(mTLS)を適用します。信頼する CA 証明書を Secret として登録し、auth-tls アノテーションで検証を有効にします。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: secure-api
  annotations:
    nginx.ingress.kubernetes.io/auth-tls-verify-client: "on"
    nginx.ingress.kubernetes.io/auth-tls-secret: "default/client-ca"
    nginx.ingress.kubernetes.io/auth-tls-verify-depth: "1"
    nginx.ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream: "true"
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - api.example.com
      secretName: api-tls
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: api-svc
                port:
                  number: 443

auth-tls-verify-client を on にすると、有効なクライアント証明書のないリクエストは拒否されます。pass-certificate-to-upstream を有効にすると、バックエンドが証明書情報をヘッダで受け取り、追加の認可に活用できます。

external auth と oauth2-proxy

認証をコントローラ外の認証サービスへ委譲したい場合は external auth を使います。auth-url で指定したエンドポイントが毎リクエストを検証し、その結果に応じて通過/遮断します。oauth2-proxy と組み合わせれば、OIDC ベースの SSO を Ingress レベルで強制できます。

metadata:
  annotations:
    nginx.ingress.kubernetes.io/auth-url: "https://oauth2-proxy.example.com/oauth2/auth"
    nginx.ingress.kubernetes.io/auth-signin: "https://oauth2-proxy.example.com/oauth2/start?rd=https://$host$request_uri"

ここで使われた変数表記(host、request_uri)は nginx 変数であり、必ずコードフェンス内でのみ使わないと MDX ビルドが壊れます。external auth はすべてのリクエストに追加の往復を生むので、auth-cache 関連オプションでキャッシュを検討し、認証サービスの可用性がそのまま全体の可用性になる点を念頭に置いてください。

TLS ポリシー — プロトコルと cipher

TLS 設定はコンプライアンスに直結します。古いプロトコル(TLS 1.0/1.1)や弱い cipher は、グローバル ConfigMap で明示的に遮断します。

data:
  ssl-protocols: "TLSv1.2 TLSv1.3"
  ssl-ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384"
  ssl-prefer-server-ciphers: "true"
  hsts: "true"
  hsts-max-age: "31536000"
  hsts-include-subdomains: "true"

HSTS を有効にするとブラウザが以降の接続を常に HTTPS で強制します。証明書自体は cert-manager で自動発行/更新し、失効事故を防いでください。

ヘッダセキュリティ

セキュリティ関連のレスポンスヘッダはグローバルに追加できます。snippet を切った環境では custom-http-errors や別途のヘッダ ConfigMap を活用します。

data:
  hide-headers: "Server,X-Powered-By"
  add-headers: "ingress-nginx/custom-headers"
apiVersion: v1
kind: ConfigMap
metadata:
  name: custom-headers
  namespace: ingress-nginx
data:
  X-Frame-Options: "SAMEORIGIN"
  X-Content-Type-Options: "nosniff"
  Referrer-Policy: "strict-origin-when-cross-origin"

サーバのバージョン露出(Server、X-Powered-By)を隠せば、攻撃者に与える情報を減らせます。クリックジャッキング防止の X-Frame-Options、MIME スニッフィング遮断の X-Content-Type-Options はデフォルトで有効にしておくのがよいでしょう。

レートリミットで濫用を防御

認証されていないエンドポイントやログイン経路は、ブルートフォースと DoS の標的です。レートリミットで基本防御線を張ります。

metadata:
  annotations:
    nginx.ingress.kubernetes.io/limit-rps: "10"
    nginx.ingress.kubernetes.io/limit-connections: "5"
    nginx.ingress.kubernetes.io/limit-whitelist: "10.0.0.0/8"

内部ネットワーク帯域は whitelist で除外し、外部公開経路には保守的な上限を置いてください。より精緻な全体制限やボット遮断が必要なら、WAF または別の API Gateway と組み合わせます。

CVE 対応運用

IngressNightmare 系 CVE の教訓は明確です。コントローラを最新パッチバージョンに保ち、攻撃表面を減らし、権限を最小化せよ、ということです。

[ CVE 対応運用フロー ]

1. バージョンインベントリ ── 現在のコントローラバージョン/イメージダイジェスト把握
2. パッチモニタリング ──── セキュリティ勧告/リリースノートを購読
3. 迅速なアップグレード ── カナリア/ステージング後に素早くロールアウト
4. 表面の縮小 ────────── snippet 無効化、admission webhook 露出制限
5. 権限の最小化 ──────── コントローラ RBAC/ネットワークポリシーで Secret アクセス統制
6. 検知 ──────────────── 異常な設定変更/リクエストの監視

特に admission webhook は外部から直接到達できないようネットワークポリシーで保護し、コントローラが読める Secret の範囲を可能な限り狭めるべきです。

IP アクセス制御と実際のクライアント IP

管理コンソールや社内ツールのようにアクセス主体が限定的な経路には、ソース IP ベースのアクセス制御が有効です。

metadata:
  annotations:
    nginx.ingress.kubernetes.io/whitelist-source-range: "10.0.0.0/8,192.168.0.0/16"

ただし IP 制御が正しく動作するには、コントローラが実際のクライアント IP を知る必要があります。クラウド LB や CDN の背後にあると、コントローラが見るソース IP はプロキシの IP かもしれません。その場合は信頼するプロキシ帯域を登録し、forwarded ヘッダを信頼するよう設定します。

apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
  namespace: ingress-nginx
data:
  use-forwarded-headers: "true"
  proxy-real-ip-cidr: "10.0.0.0/8"
  enable-real-ip: "true"

forwarded ヘッダの信頼は諸刃の剣です。信頼帯域を狭く取らないと、攻撃者が X-Forwarded-For を偽造して IP ホワイトリストを回避できるので、proxy-real-ip-cidr は必ず実際のプロキシ帯域に限定してください。AWS NLB のように proxy protocol を使う場合は、use-proxy-protocol を有効にして元の IP を保持します。

監査ロギングと検知

セキュリティは遮断だけでなく検知も含みます。アクセスログを構造化すれば、異常の兆候を SIEM で分析できます。

data:
  log-format-escape-json: "true"
  log-format-upstream: '{"time":"$time_iso8601","remote_addr":"$remote_addr","status":"$status","request":"$request","upstream_status":"$upstream_status"}'

ここで使われた変数表記はすべて nginx 変数であり、コードフェンス内でのみ安全です。JSON 構造化ログは、異常なステータスコードの急増、特定経路のスキャン、異常な User-Agent パターンの検知に有用です。WAF の検知ログと併せて監視すれば、攻撃の試みを早期に捉えられます。

マルチテナント分離

複数のチームが 1 つのクラスタを共有するなら、Ingress 分離が重要です。肝心な道具は IngressClass 分離、ネームスペース RBAC、そしてポリシーエンジンです。

分離手段目的メカニズム
IngressClass 分離チームごとのコントローラ分離ingressClassName ごとのコントローラ
ネームスペース RBACIngress 作成権限の制限Role/RoleBinding
ポリシーエンジン危険アノテーションの遮断Kyverno/OPA Gatekeeper
ホスト検証ホスト乗っ取りの防止admission ポリシーで host 制限

ポリシーエンジンで危険なアノテーション(snippet、任意ホストなど)を遮断する例は次のとおりです。

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: block-snippet-annotations
spec:
  validationFailureAction: Enforce
  rules:
    - name: no-configuration-snippet
      match:
        any:
          - resources:
              kinds: ["Ingress"]
      validate:
        message: "configuration-snippet annotation is not allowed"
        pattern:
          metadata:
            =(annotations):
              X(nginx.ingress.kubernetes.io/configuration-snippet): "null"

運用チェックリスト

最後にハードニングの点検項目をチェックリストにまとめます。

  • allow-snippet-annotations が false で annotations-risk-level が適切に設定されている
  • Ingress 作成権限が RBAC で最小化されている
  • ポリシーエンジンで危険アノテーション遮断ルールが適用されている
  • WAF(ModSecurity/Coraza + CRS)が少なくとも検知モードで動作中
  • 機密経路に mTLS または external auth が適用されている
  • TLS は 1.2/1.3 のみ許可、弱い cipher 遮断、HSTS 有効
  • セキュリティヘッダ追加、サーババージョンヘッダ隠蔽
  • 外部公開経路にレートリミット適用
  • コントローラが最新パッチバージョン、admission webhook をネットワーク保護
  • マルチテナントなら IngressClass/ネームスペース分離を適用

Secret 管理と証明書の自動化

コントローラは TLS 証明書を Secret として読み込みます。証明書が失効すると全サービスが TLS エラーで停止するため、自動化がそのまま安定性になります。cert-manager で発行と更新を自動化し、失効間近をアラートで検知してください。

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: api-tls
  namespace: default
spec:
  secretName: api-tls
  duration: 2160h
  renewBefore: 360h
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  dnsNames:
    - api.example.com

renewBefore を十分に取れば、更新失敗時にも再試行の余裕が生まれます。また、コントローラが読める Secret を最小化すべきです。コントローラが全ネームスペースの Secret を読めると、コントローラ掌握時にクラスタ全体の証明書が露出します。できれば証明書を特定ネームスペースに限定し、RBAC でコントローラの Secret アクセス範囲を狭めてください。

サプライチェーンとイメージセキュリティ

コントローラ自体の完全性もセキュリティの一部です。信頼できるレジストリから署名済みイメージを使い、ダイジェストで固定して改ざんを防ぎます。

# イメージダイジェスト固定の例
image: registry.k8s.io/ingress-nginx/controller@sha256:abcdef...

イメージをタグ(latest など)ではなくダイジェストで固定すると、同じタグが別のイメージに差し替わるリスクを防げます。ポリシーエンジンで署名検証(例: cosign/sigstore)を強制し、コントローラ Pod に securityContext で権限を最小化してください。

# コントローラ Pod securityContext の例
securityContext:
  runAsNonRoot: true
  readOnlyRootFilesystem: true
  allowPrivilegeEscalation: false
  capabilities:
    drop: ["ALL"]
    add: ["NET_BIND_SERVICE"]

readOnlyRootFilesystem と capability drop は、コンテナが侵害されても攻撃者ができることを大きく減らします。NET_BIND_SERVICE だけ追加するのは、80/443 のような特権ポートのバインドのためです。

ネットワークポリシーで分離

最後の防御線はネットワークポリシーです。コントローラへ入る・出るトラフィックを明示的に制限し、侵害時の横方向移動(lateral movement)を防ぎます。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: ingress-nginx-restrict
  namespace: ingress-nginx
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: ingress-nginx
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - ipBlock:
            cidr: 0.0.0.0/0
      ports:
        - protocol: TCP
          port: 443
  egress:
    - to:
        - namespaceSelector: {}
      ports:
        - protocol: TCP
          port: 8080

admission webhook ポートは外部から直接到達できないよう ingress ルールから除外し、egress はバックエンドサービスがあるネームスペースだけに許可するように狭めてください。ネットワークポリシーは CNI(Calico、Cilium など)が対応していて初めて適用されます。

Gateway API との関係

セキュリティの観点でも 2026 年の方向性は重要です。ingress-nginx のアノテーション/snippet モデルは、セキュリティ表面が広く標準化が難しいという構造的限界があります。Ingress API が凍結され ingress-nginx がメンテナンスモードへ移行した今、後継標準の Gateway API はセキュリティポリシーを標準 CRD やポリシーリソース(例: BackendTLSPolicy、ReferenceGrant など)で表現し、役割分離と権限統制をより明確にします。既存 Ingress のセキュリティは上記チェックリストで維持しつつ、新規設計は Gateway API のポリシーモデルの上でハードニングするほうが長期的に安全です。

侵害が疑われる場合の対応手順

ハードニングをしっかり行っても、侵害の可能性はゼロではありません。コントローラが侵害された兆候(異常な設定変更、未知の snippet、予期しない外部通信)が見えたときに備えて、手順をあらかじめ定めておいてください。

[ Ingress コントローラ侵害対応フロー ]

1. 隔離 ─────── ネットワークポリシーで疑わしい Pod の egress を遮断
2. 証拠保全 ── Pod ログ/nginx.conf/イベントのスナップショットを確保
3. 影響評価 ── 露出し得る Secret/証明書の範囲を見積もる
4. 資格情報の回転 ── コントローラがアクセスしていた証明書/トークンを即座に回転
5. 再デプロイ ── 検証済みダイジェストイメージでコントローラを再生成
6. 事後分析 ── 侵入経路を把握し、ポリシー/RBAC を強化

最も重要なステップは資格情報の回転です。コントローラが読めていた TLS 証明書とトークンはすでに露出したと仮定し、すべて回転すべきです。だからこそ、先ほどの「コントローラが読む Secret の範囲を最小化」が単なるベストプラクティスではなく、事故時の被害範囲を直接減らす統制であることが分かります。

定期的なセキュリティ点検ルーティン

セキュリティは一度設定して終わりではなく、継続的な運用です。次を定期ルーティンにしてください。

周期点検項目
常時異常なステータスコード/リクエストパターンのアラート
週次新規 CVE/セキュリティ勧告の確認
月次コントローラバージョンの最新化検討
四半期RBAC/ネットワークポリシー/証明書範囲の監査

こうしたルーティンを自動化すれば(例: バージョンドリフトのアラート、ポリシー違反レポート)、人の注意力に依存せずにセキュリティ水準を維持できます。セキュリティは結局、一度きりの設定ではなく運用文化の問題です。

おわりに

ingress-nginx セキュリティハードニングの核心は、攻撃表面を減らし権限を最小化することです。危険な snippet を切って RBAC とポリシーエンジンで統制し、WAF と mTLS、external auth で防御層を積み、TLS とヘッダポリシーで基礎を固め、CVE に迅速に対応する運用ルーティンを作ってください。コントローラはクラスタの入口であり、入口のセキュリティはそのまま全体のセキュリティです。

参考資料