GKE で BackendConfig を使用して Ingress に Cloud Armor セキュリティポリシーを構成する

記事タイトルとURLをコピーする

G-gen の佐々木です。当記事では Google Cloud ( 旧称 GCP ) の Google Kubernetes Engine ( GKE ) で作成した Ingress に対して、Cloud Armor セキュリティポリシー を構成することで、ワークロードに対するアクセス元 IP アドレス制限を実装します。

GKE & Cloud Armor

前提知識

Google Kubernetes Engine とは

Google Kubernetes Engine (以下、GKE ) は、Google Cloud のインフラストラクチャ上に構築された マネージドな Kubernetes クラスタ を利用することができるサービスです。
サービスの詳細は以下の記事で解説しています。

blog.g-gen.co.jp

Ingress とは

Ingress は Kubernetes の API リソースの 1 つで、Kubernetes クラスタ内のワークロードに対する L7 ロードバランシングを提供します。
Ingress は Service に関連付けることで、その背後にある Pod にデプロイされたアプリケーションを外部に公開することができます。

GKE では、組み込みのコントローラである GKE Ingress Controller によって、Kubernetes によって管理される HTTP(S) ロードバランサを GKE クラスタ外に作成することができます。

Ingressを用いたL7ロードバランサによるワークロードの外部公開

当記事では省略しますが、Ingress によって作成される HTTP(S) ロードバランサで HTTPS を使用する場合、Ingress と SSL 証明書を紐づける設定をする必要があります。
詳細な手順については以下の記事をご一読ください。

blog.g-gen.co.jp

Cloud Armor とは

Cloud Armor は Google 製のクラウド型 WAF であり、Cloud Armor の セキュリティポリシー を HTTP(S) ロードバランサのバックエンドに紐づけることで、バックエンドに対するアクセス元の制限をかけることができます。
Cloud Armor の詳細については、以下の記事で解説しています。

blog.g-gen.co.jp

GKE クラスタの準備

当記事では Autopilot モードの限定公開クラスタを使用していきます。
クラスタの作成方法については 公式ドキュメント、または Terraform を使用した以下の記事を参照してください。

blog.g-gen.co.jp

GKE クラスタにサンプルアプリケーションをデプロイする

Google Cloud が提供するサンプルのコンテナイメージを使用し、アプリケーションを GKE クラスタに登録します。
マニフェストファイルは以下のようになります。

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: hello
  template:
    metadata:
      labels:
        app: hello
    spec:
      containers:
      - name: hello
        image: us-docker.pkg.dev/google-samples/containers/gke/hello-app:1.0
        ports:
        - containerPort: 8080
          protocol: TCP

続いて、アプリケーションを公開するための Service を GKE クラスタに登録します。
Service のマニフェストファイルは後に編集するため、当記事ではファイル名を service.yaml と定めます。

# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: hello
  namespace: default
  annotations:
    cloud.google.com/neg: '{"ingress": true}'
spec:
  ports:
  - port: 8080
    protocol: TCP
    targetPort: 8080
  selector:
    app: hello
  type: NodePort

それぞれのプロビジョニングが完了するまで待ちます。

$ kubectl get deployments hello
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
hello   3/3     3            3           15m

$ kubectl get services hello
NAME    TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
hello   NodePort   172.31.236.34   <none>        8080:32678/TCP   10m

Ingress を作成してアプリケーションを公開する

まずは Cloud Armor のセキュリティポリシーを構成せずに Ingress を作成してみます。
この Ingress によって作成される HTTP(S) ロードバランサはアクセス元の制限がされておらず、ロードバランサの IP アドレスを知っていれば誰でもアプリケーションにアクセスすることができてしまいます。

# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hello-ingress
  namespace: default
  annotations:
    kubernetes.io/ingress.class: "gce"  # 外部 HTTP(S) ロードバランサを指定
spec:
  defaultBackend:
    service:
      name: hello
      port:
        number: 8080

Ingress によって作成された HTTP(S) ロードバランサの外部 IP アドレスを確認し、ブラウザからアクセスしてみます。

$ kubectl get ingresses hello-ingress
NAME            CLASS    HOSTS   ADDRESS          PORTS   AGE
hello-ingress   <none>   *       34.117.255.169   80      19m

サンプルアプリケーションの画面

Cloud Armor のセキュリティポリシーを作成する

アプリケーションに対するアクセス元 IP アドレスを制限するセキュリティポリシーを作成します。
ここで設定したセキュリティポリシーの名前は後で使用します。

セキュリティポリシーは Cloud Armor の機能ですが、gcloud compute コマンドグループを使用して作成、編集を行うことができます。
当記事では hello-sec-policy という名前のセキュリティポリシーを作成します。

# セキュリティポリシーの作成
$ gcloud compute security-policies create hello-sec-policy

ポリシーのデフォルトルール(優先度 2,147,483,647 )を更新し、すべてのアクセス元を拒否します。

# デフォルトルールの更新
$ gcloud compute security-policies rules update 2147483647 \
    --security-policy hello-sec-policy \
    --action "deny-403"

特定の IP アドレス範囲からのアクセスを許可するルールを追加します。
ルールの優先度はデフォルトのルールより高ければよいので、ここでは優先度 1000 で設定します。

# 許可ルールの追加
$ gcloud compute security-policies rules create 1000 \
    --security-policy hello-sec-policy \
    --src-ip-ranges "<許可する IP アドレス範囲>" \
    --action "allow"

BackendConfig を作成する

Ingress に対して Cloud Armor セキュリティポリシーを紐づけるため、GKE のカスタムリソースである BackendConfig リソースをクラスタに登録します。
BackendConfig リソースを使用することで、セキュリティポリシーだけではなく、Cloud CDN や Identity-Aware Proxy など、HTTP(S) ロードバランサで利用できる様々な機能を Kubernetes から有効化することができます。

BackendConfig のマニフェストファイルでは、先ほど作成したセキュリティポリシーの名前を使用します。

# backendconfig.yaml
apiVersion: cloud.google.com/v1
kind: BackendConfig
metadata:
  name: hello-backend-config
spec:
  securityPolicy:
    name: hello-sec-policy  # 作成したセキュリティポリシーの名前

Service を更新して BackendConfig に紐づける

BackendConfig は、Ingress に対してではなく、HTTP(S) ロードバランサのバックエンドとなる Service に対して紐づけます。
バックエンドが使用するポートと BackendConfig を組み合わせた cloud.google.com/backend-config アノテーションを service.yaml に追記します。

annotations のキー
cloud.google.com/backend-config '{"ports": {"8080":"hello-backend-config"}}'
# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: hello
  namespace: default
  annotations:
    cloud.google.com/backend-config: '{"ports": {"8080":"hello-backend-config"}}'  # 追記
    cloud.google.com/neg: '{"ingress": true}' 
spec:
  ports:
  - port: 8080
    protocol: TCP
    targetPort: 8080
  selector:
    app: hello
  type: NodePort

許可していない IP アドレスからアクセスしてみる

Ingress によって作成された HTTP(S) ロードバランサに Cloud Armor セキュリティポリシーが構成されたので、ポリシーで許可していない IP アドレス範囲からのアクセスが拒否されることを確認します。
ブラウザで、HTTP(S) ロードバランサの外部 IP アドレスに再度アクセスしてみます。

許可されていない IP アドレス範囲からのアクセスは拒否される

最後に、ポリシーで許可された IP アドレスからアクセスし、サンプルアプリケーションの画面が表示されることを確認します。

許可された IP アドレス範囲からのアクセス

佐々木 駿太 (記事一覧)

G-gen 最北端、北海道在住のクラウドソリューション部エンジニア。
2022 年 6 月に G-gen にジョイン。Google Cloud All Certifications Engineer。

好きな Google Cloud プロダクトは Cloud Run。