
Kubernetes標準のシークレット機能だけでは、安全な管理とは言えません。実体はBase64エンコードされた平文に過ぎず、稼働中のPodからそのまま参照できてしまいます。
免責事項:
本チュートリアルは、構築に必要な各コンポーネントを段階的に理解いただくことを目的としています。 terraform で一気に構築することももちろん可能ですが、これほど重要な基盤を中身を理解しないまま導入してしまうと、後々解決の難しいトラブルにつながりかねません。

確かにKubernetesのシークレットを暗号化することは可能ですが、暗号化されるのは保管時のみです。Podにマウントされた段階ではファイルや環境変数として簡単に取り出せるため、Pod自体やkubectl経由で当該namespaceにアクセスされた場合、データが漏洩する恐れがあります。
HashiCorp Vaultは、シークレットを安全に管理し、アクセスの監査や失効も行える仕組みです。とはいえ、Vaultを導入して使うことと、そのシークレットを実際にPodで利用することは別の課題です。
本記事ではTerraformとHelmを用いてGKEにVaultをインストールする方法を扱います。Pod側でシークレットを利用する手順については、別記事「PodからVaultのシークレットを透過的に利用する方法」をご参照ください。
Vaultだけを使いたい場合や、Consul template with VaultのようなConsulの高度な機能が不要な場合には、本記事の方法のほうが管理しやすいでしょう。
本チュートリアルでは、Google Cloud Storage(GCS)をVaultのバックエンドに据え、エンドツーエンドでTLSを有効化した高可用性Vaultを構築する手順を解説します。
注意:
アップグレード時の検証や環境間の分離をしやすくするため、Vaultは環境ごとに構築することを推奨します。
Vaultをサービスとして外部公開することは推奨しません。アクセスが必要な場合は、次のコマンドで接続してください:
$ kubectl port-forward vault-0 8200:8200そのうえで、後述のとおりhttps://127.0.0.1:8200からUIにアクセスします。
Vaultにアクセスする必要があるVMがある場合は、ServiceとPodがネイティブIPを持つためVPCピアリングを利用してください。本記事では扱いません。
チュートリアル
全体の流れ:
- Vault用のTLS証明書を作成
- VaultストレージバックエンドとしてGCSバケットを作成
- Vault自動アンシール用のKMSキーリングと暗号鍵を作成
- VaultがKMSとGCSバックエンドにアクセスするためのサービスアカウントを作成
- HashiCorp Vault公式HelmチャートをHelm tillerless経由でインストール
Vault用TLS証明書の作成

本番環境向けハードニングの推奨事項のひとつに、Vaultとクライアント間の通信を、受信・送信ともにTLSで暗号化することが挙げられます。
ここでは、次の用途で使用する証明書を作成します:
- Kubernetes Vaultサービスのアドレス
- 127.0.0.1
証明書の生成にはCloudFlare SSLツールキット(cfsslとcfssljson)を使用します。
インストールにはGo 1.12以上の動作環境と、適切に設定されたGOPATHが必要です。
重要: GOPATHのbinがPATHに含まれているか確認してください:
export PATH=$GOPATH/bin:$PATHCloudFlare SSLツールキットのインストール:
go get -u github.com/cloudflare/cfssl/cmd/cfsslgo get -u github.com/cloudflare/cfssl/cmd/cfssljson認証局(CA)の初期化:
$ mkdir vault-ca && cd vault-caCA関連ファイルを作成します:
有効期限5年のCA設定ファイル
$ cat <<EOF > ca-config.json{ "signing": { "default": { "expiry": "8760h" }, "profiles": { "default": { "usages": ["signing", "key encipherment", "server auth", "client auth"], "expiry": "8760h" } } }}EOFCA署名リクエスト:
$ cat <<EOF > ca-csr.json{ "hosts": [\ "cluster.local"\ ], "key": { "algo": "rsa", "size": 2048 }, "names": [\ {\ "C": "US",\ "L": "NewYork",\ "O": "Kubernetes",\ "OU": "CA",\ "ST": "NewYork"\ }\ ]}EOF上記のCAで署名するVAULT証明書署名リクエスト: 注意: Vaultをdefault以外のnamespaceで動かす場合は、namespaceを変更してください
$ cat <<EOF > vault-csr.json{ "CN": "Vault-GKE", "hosts": [\ "127.0.0.1",\ "vault.default.svc.cluster.local"\ ], "key": { "algo": "rsa", "size": 2048 }, "names": [\ {\ "C": "US",\ "L": "NewYork",\ "O": "Innovia",\ "OU": "Vault",\ "ST": "NewYork"\ }\ ]}EOFもちろん、下部の「names」セクションの証明書情報は適宜変更してかまいません。
編集したファイルを使ってCAを初期化するため、次のコマンドを実行します:
$ cfssl gencert -initca ca-csr.json | cfssljson -bare ca2019/11/12 16:35:01 [INFO] generating a new CA key and certificate from CSR2019/11/12 16:35:01 [INFO] generate received request2019/11/12 16:35:01 [INFO] received CSR2019/11/12 16:35:01 [INFO] generating key: rsa-20482019/11/12 16:35:01 [INFO] encoded CSR2019/11/12 16:35:01 [INFO] signed certificate with serial number 425581644650417483788325060652779897454211028144秘密鍵を作成し、TLS証明書に署名します:
$ cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -profile=default \ vault-csr.json | cfssljson -bare vault2019/11/12 16:36:33 [INFO] generate received request2019/11/12 16:36:33 [INFO] received CSR2019/11/12 16:36:33 [INFO] generating key: rsa-20482019/11/12 16:36:34 [INFO] encoded CSR2019/11/12 16:36:34 [INFO] signed certificate with serial number 311973563616303179057952194819087555625015840298ここまでで、作業ディレクトリに次のファイルが揃っているはずです:
ca-key.pemca.pemvault-key.pemvault.pemCA関連ファイルは安全に保管してください。証明書の有効期限が切れた際の再署名に必要です(CAは5年、Vaultは1年)。
Vault TLSとca.pem用のシークレットを作成します:
kubectl create secret generic vault-tls \ --from-file=ca.pem \ --from-file=vault.pem \ --from-file=vault-key.pemこのチュートリアルで使用するGCPプロジェクトを設定します:
$ export GCP_PROJECT=<your_project_id>本チュートリアルで必要なGCP APIを有効化:
$ gcloud services enable \ cloudapis.googleapis.com \ cloudkms.googleapis.com \ container.googleapis.com \ containerregistry.googleapis.com \ iam.googleapis.com \ --project ${GCP_PROJECT}Operation "operations/acf.8e126724-bbde-4c0d-b516-5dca5b8443ee" finished successfully.Vaultストレージバックエンド
Google Cloud Storage
HAモードで動作するVaultサーバーは、**standbyとactive**という2つの状態を追加で持ちます。Vaultクラスター内では1つのインスタンスのみが_active_となり、すべてのリクエスト(読み取り・書き込み)を処理します。_standby_ノードはリクエストを_active_ノードへリダイレクトします。
gsutilコマンドでGCS上にバケットを作成しましょう。バケット名はGoogle Cloud全体で一意である必要があるため、ユニークな名前を選んでください。
$ export GCS_BUCKET_NAME=mycompany-vault-data$ gsutil mb gs://$GCS_BUCKET_NAME$ gsutil versioning set on gs://$GCS_BUCKET_NAMEデータは転送時・保管時ともに暗号化されますが、露出を最小限に抑えるためバケットには適切な権限を設定してください。IAM権限を使い、VaultとGoogle Cloudのやり取りをこのストレージバケット内のオブジェクトに限定するサービスアカウントを作成することを推奨します。
Vault自動アンシール

Vaultは再起動するとシール状態かつ暗号化された状態で立ち上がります。利用するにはアンシールが必要ですが、CloudKMSからマスターキーとルートトークンを自動的に読み取る「自動アンシール」という新機能が使えます。
KMSキーリングと暗号鍵の作成:
このセクションでは、Vaultのマスターキーとルートトークンを暗号化・復号するためのKMSキーリングと鍵を作成します。
vault-helm-unseal-kr KMSキーリングを作成します:
$ gcloud kms keyrings create vault-helm-unseal-kr \ --location global \ --project ${GCP_PROJECT}暗号鍵を作成します:
$ gcloud kms keys create vault-helm-unseal-key \ --location global \ --keyring vault-helm-unseal-kr \ --purpose encryption \ --project ${GCP_PROJECT}Vault用GCPサービスアカウントとIAM権限の作成
変数を設定します:
$ export VAULT_SA_NAME=vault-server; export VAULT_SA=$VAULT_SA_NAME@$GCP_PROJECT.iam.gserviceaccount.comVaultサーバー用のサービスアカウントを作成します:
$ gcloud iam service-accounts create $VAULT_SA_NAME \ --display-name "Vault server service account" \ --project ${GCP_PROJECT}Vaultサーバー用のサービスアカウントキー(認証情報JSONファイル)を作成します:
$ gcloud iam service-accounts keys create \ --iam-account $VAULT_SA /tmp/vault_gcs_key.jsoncreated key [be22cfe6e30f3a3fcfc6ebaa23ca3ba905dd60ab] of type [json] as [/tmp/vault_gcs_key.json] for [[email protected]] VaultのGoogleサービスアカウントを格納するシークレットを作成$ kubectl create secret generic vault-gcs \ --from-file=/tmp/vault_gcs_key.jsonsecret/vault-gcs createdVaultストレージのGCSバケットへのアクセスを付与します:
$ gsutil iam ch \ serviceAccount:${VAULT_SA}:objectAdmin \ gs://${GCS_BUCKET_NAME}Vault KMS鍵へのアクセスを付与します:
$ gcloud kms keys add-iam-policy-binding \ vault-helm-unseal-key \ --location global \ --keyring vault-helm-unseal-kr \ --member serviceAccount:${VAULT_SA} \ --role roles/cloudkms.cryptoKeyEncrypterDecrypter \ --project ${GCP_PROJECT}Updated IAM policy for key [vault-helm-unseal-key].bindings:- members: - serviceAccount:[email protected] role: roles/cloudkms.cryptoKeyEncrypterDecrypteretag: BwWZ6sIYovk=version: 1注意:サービスアカウントを一度削除して再作成した場合は、鍵に紐づくIAMポリシーを削除する必要があります。該当しない場合は、HashiCorp Vault公式チャートの取得セクションへ進んでください。$ gcloud kms keys get-iam-policy vault-helm-unseal-key --location global --keyring vault-helm-unseal-kr > kms-policy.yaml ポリシーファイルを編集し、binding配下のmembersを削除して保存bindings:etag: BwWXQz4HjuI=version: 1ポリシーを再適用します:
$ gcloud kms keys set-iam-policy vault-helm-unseal-key --location global --keyring vault-helm-unseal-kr kms-policy.yamlHashiCorp Vault公式チャートの取得:

注意:
バージョン0.3.0以降、ボリューム上にファイルとしてシークレットを展開し、Podへ自動注入するKubernetes Vault連携機能が用意されています。Podへのシークレット注入をより安全に、かつ消費の自動化まで実現したい場合は、私が公開しているvault secrets webhookの利用を強く推奨します。
export CHART_VERSION=0.3.0チャートを取得して展開します:
$ wget https://github.com/hashicorp/vault-helm/archive/v$CHART_VERSION.tar.gz && tar zxf v$CHART_VERSION.tar.gz && rm v$CHART_VERSION.tar.gzチャート用values.yamlの設定:
下記のgistには、変数置換用のプレースホルダーが含まれています
global: tlsDisable: false
server: extraEnvironmentVars: GOOGLE_APPLICATION_CREDENTIALS: /vault/userconfig/vault-gcs/vault_gcs_key.json extraVolumes: - type: secret name: vault-gcs path: "/vault/userconfig" - type: secret name: vault-tls path: "/etc/tls"
authDelegator: enabled: true
ha: enabled: true config: | ui = true listener "tcp" { tls_disable = 0 tls_cert_file = "/etc/tls/vault-tls/vault.pem" tls_key_file = "/etc/tls/vault-tls/vault-key.pem" tls_client_ca_file = "/etc/tls/vault-tls/ca.pem" tls_min_version = "tls12" address = "[::]:8200" cluster_address = "[::]:8201" } storage "gcs" { bucket = "GCS_BUCKET_NAME" ha_enabled = "true" } seal "gcpckms" { project = "GCP_PROJECT" region = "global" key_ring = "vault-helm-unseal-kr" crypto_key = "vault-helm-unseal-key" }下記コマンドで_vault-gke-values.yaml_という新しいvaluesファイルを作成します
$ curl -s https://gist.githubusercontent.com/innovia/53c05bf69312706fc93ffe3bb685b223/raw/adc169605984da8ba82082191c8f631579b1b199/vault-gke-values.yaml | sed "s/GCP_PROJECT/$GCP_PROJECT/g" | sed "s/GCS_BUCKET_NAME/$GCS_BUCKET_NAME/g" > vault-helm-$CHART_VERSION/vault-gke-values.yaml生成されたファイルを確認し、プロジェクトとGCSバケットが正しいかチェックします。
$ cat vault-helm-$CHART_VERSION/vault-gke-values.yaml | grep -E 'bucket|project'bucket = "<COMPANY>-vault-data"project = "ami-playground"helm 2.xを使う場合:
クラスターにtillerが入っていない場合は、helm用のtillerlessプラグインを導入することでtillerのセットアップを省略できます。これによりローカルマシン上でtillerを起動し、helmからそれを参照できるようになります。すでに環境がある場合は、下のVaultチャートのインストールセクションへ進んでください。
tillerless helmプラグインのインストール:
helmが未インストールの場合は、以下でインストールします
$ brew install helm@2tillerサーバーをインストールしないよう、クライアントのみで初期化します
helm init --client-onlyhelm-tillerlessプラグインをインストールhelm plugin install https://github.com/rimusz/helm-tillerhelm経由でtillerを起動$ helm tiller startInstalled Helm version v2.16.1Copied found /usr/local/bin/tiller to helm-tiller/binHelm and Tiller are the same version!Starting Tiller...Tiller namespace: kube-systemhelm 3を使う場合
$ brew install helmVaultチャートのインストール:
注意: helm 3を使う場合、出力にリソース一覧は表示されません。
$ helm upgrade --install vault -f vault-helm-$CHART_VERSION/vault-gke-values.yaml vault-helm-$CHART_VERSIONrelease "vault" does not exist. Installing it now.NAME: vaultLAST DEPLOYED: Wed Nov 13 15:41:55 2019NAMESPACE: defaultSTATUS: DEPLOYEDRESOURCES:==> v1/ConfigMapNAME AGEvault-config 0s==> v1/ServiceNAME AGEvault 0s==> v1/ServiceAccountNAME AGEvault 0s==> v1/StatefulSetNAME AGEvault 0s==> v1beta1/ClusterRoleBindingNAME AGEvault-server-binding 0s==> v1beta1/PodDisruptionBudgetNAME AGEvault 0sNOTES:Thank you for installing HashiCorp Vault!Now that you have deployed Vault, you should look over the docs on usingVault with Kubernetes available here:https://www.vaultproject.io/docs/Your release is named vault. To learn more about the release, try:$ helm status vault $ helm get vaultVaultが起動し、未初期化状態になっているはずです。Vaultが未初期化の段階では、以下の警告が出ても問題ありません:
=> Vault server started! Log data will stream in below:2019-12-17T19:07:37.937Z [INFO] proxy environment: http_proxy= https_proxy= no_proxy=2019-12-17T19:07:38.909Z [INFO] core: stored unseal keys supported, attempting fetch2019-12-17T19:07:39.037Z [WARN] failed to unseal core: error="stored unseal keys are supported, but none were found"2019-12-17T19:07:44.038Z [INFO] core: stored unseal keys supported, attempting fetch2019-12-17T19:07:44.080Z [INFO] core: autoseal: seal configuration missing, but cannot check old path as core is sealed: seal_type=recovery2019-12-17T19:07:44.174Z [WARN] failed to unseal core: error="stored unseal keys are supported, but none were found"---kubectl describe pod vault-0Events: Type Reason Age From Message ---- ------ ---- ---- ------- ... Warning Unhealthy 3s (x9 over 27s) kubelet, minikube Readiness probe failed: Key ValueKMS自動アンシールでVaultを初期化
次のコマンドでVaultへのポートフォワードを開始します:
$ kubectl port-forward vault-0 8200:8200 > /dev/null & export PID=$!; echo "vault port-forward pid: $PID"ca.pem証明書を使ってVaultに接続します
$ export VAULT_ADDR=https://127.0.0.1:8200; export VAULT_CACERT=$PWD/ca.pemmy vault ca.pem for example is at:VAULT_CACERT: /Users/ami/vault-gke-medium/ca.pemVaultクライアントをインストールします(クライアントとサーバーのバージョンを揃えてください)
$ brew install vaultステータスを確認します:
$ vault statusKey Value--- -----Recovery Seal Type gcpckmsInitialized falseSealed trueTotal Recovery Shares 0Threshold 0Unseal Progress 0/0Unseal Nonce n/aVersion n/aHA Enabled trueVaultを初期化します:
vault operator initRecovery Key 1: 33nCanHWgYMR/VPj6bNQdHXJiayL6WeB8Ourx4kHYNaXRecovery Key 2: IMf7RjptFxtGQUbEWUWehanCBiSY7VhElkM7rRVxczGcRecovery Key 3: zGuzk/PhNet9OHL4cW2H7d3XypDxfwWXkmajclLPklK4Recovery Key 4: nCFS0dt0cNGB2LWk0F+3Vmz9TbVNpeIsXbIXDbRarlnTRecovery Key 5: 9GxXr/6T8OJWJrWqyHQxayR0BAK+WTdbT870AzKEFl2VInitial Root Token: s.1ukhSgycySjZUJRD0bZjSEitSuccess! Vault is initializedRecovery key initialized with 5 key shares and a key threshold of 3. Pleasesecurely distribute the key shares printed above.これらのキーは厳重に保管してください。
自己署名認証局を信頼する:
ca.pemは自前で作成したため、OS標準のCAバンドルには含まれておらず、そのままでは信頼されません。
OSごとに次の手順で信頼を追加できます。
Mac OS:
CAを「常に信頼」に設定すると、ブラウザでVault UIをエラーなく開けるようになります:
$ sudo security add-trusted-cert -d -k /Library/Keychains/System.keychain $VAULT_CACERTWindows 10:
下記の手順に従い、信頼された発行元に証明書を追加してください:
VaultでKubernetesバックエンド認証を設定
Vaultの起動と高可用性化が済んだら、次はVaultとKubernetesを連携させます。
VaultからKubernetesへの初回ログインにはサービスアカウントを利用します。
このサービスアカウントのトークンは、Vault CLIを使ってVault側に登録します。
このサービスアカウントには「system:auth-delegator」という特別な権限が付与されており、これによりVaultはPodのサービスアカウントをKubernetesへ渡して認証を行えます。認証が通ると、VaultはVaultにアクセスして必要なシークレットを取得するクライアントへログイントークンを返します。
クライアントはそのログイントークンでVaultにログインし、シークレットを取得します。
Vaultは、Vaultロール・サービスアカウント・namespace・ポリシーのマッピングを照合してアクセスを許可または拒否します。
では、vault-reviewer用のサービスアカウントを作成しましょう
https://gist.github.com/innovia/5435f2336e4dd0045dbb5842880b3334#file-vault-reviewer-yaml
注意: Vaultを別のnamespaceで構築している場合は、このファイルも同様に更新してください。
kubectl apply -f vault-reviewer.yamlKubernetes認証バックエンドを有効化します:
$ vault login$ vault auth enable kubernetesSuccess! Enabled kubernetes auth method at: kubernetes/vault-reviewerトークンとCAでVaultを設定:
注意: Vaultを別のnamespaceで構築している場合は、各_kubectl_コマンドの後に_-n_フラグを付けてください
$ VAULT_SA_TOKEN_NAME=$(kubectl get sa vault-reviewer -o jsonpath="{.secrets[*]['name']}")$ SA_JWT_TOKEN=$(kubectl get secret "$VAULT_SA_TOKEN_NAME" -o jsonpath="{.data.token}" | base64 --decode; echo)$ SA_CA_CRT=$(kubectl get secret "$VAULT_SA_TOKEN_NAME" -o jsonpath="{.data['ca\.crt']}" | base64 --decode; echo)$ vault write auth/kubernetes/config token_reviewer_jwt="$SA_JWT_TOKEN" kubernetes_host=https://kubernetes.default kubernetes_ca_cert="$SA_CA_CRT"Success! Data written to: auth/kubernetes/configPodがシークレットにアクセスするための前提条件:
- Podにサービスアカウントが設定されていること
- Podが動作するnamespaceにVaultのca.pemシークレットが存在すること
- シークレットへの最低限の読み取り権限を持つポリシーが存在すること
path "secret/foo" { capabilities = ["read"]}- VaultにVaultロールが作成されていること:
vault write auth/kubernetes/role/<role_name> \ bound_service_account_names=<service_account_name> \ bound_service_account_namespaces=<service_account_namespace> \ policies=<policy_name>以上で、GKE上のHashiCorp Vaultのセットアップは完了です。最後に、わずかなアノテーションを付けるだけでVaultからシームレスにシークレットを利用できるvault secrets webhooksの導入を強くおすすめします。
ロードバランサー経由でIdentity-Aware Proxy(IAP)付きVault UIを構築する手順

Identity-Aware Proxyは、VPNやSSH Bastionを用意することなくユーザー認証を実現する仕組みです。
Identity-Aware Proxy付きのロードバランサーを構築したい場合は、以下の手順に従ってください。それ以外の場合は、kubectl port-forward vault-0 8200でVault UIにアクセスできます。
なお、この手順はGoogleユーザーをVaultに紐付けるものではなく、あくまで多要素認証として機能します。Vault認証にJWTを使う方法もありますが、その場合はドメイン内の任意のユーザーがロールを選択できてしまうためセキュリティ面で劣ります…
注意:
Vaultサービス自体には引き続き自己署名証明書が必要です。ロードバランサー用の証明書は、IAPとhttpsを有効化するために別途必要になります。
前提条件:
- ロードバランサー用の証明書を、Google Managed certificateで作成するか、Kubernetesシークレットとして作成しておくこと。
https://cloud.google.com/load-balancing/docs/ssl-certificates
- ドメインがGoogle webmaster toolsで検証済みであること
- グローバル静的IPの作成と、DNSレコードの設定が完了していること
(externalDNSサービスを利用している場合は不要です)
$ gcloud compute addresses create vault-ui --global下記のYAMLで証明書を作成できます
https://gist.github.com/innovia/71c219692b003e97bc72feeeb5bc8442
$ kubectl apply -f managed-cert.yaml作成後、ステータスが_Proviosning_から_Active_に変わるまで15〜20分ほど待ちます
証明書のステータスを確認します:
$ kubectl describe ManagedCertificate vault-ui-certificateName: vault-ui-certificateNamespace: defaultLabels: <none>Annotations: kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"networking.gke.io/v1beta1","kind":"ManagedCertificate","metadata":{"annotations":{},"name":"vault-ui-certificate","namespac...API Version: networking.gke.io/v1beta1Kind: ManagedCertificateMetadata: Creation Timestamp: 2020-01-13T23:10:28Z Generation: 3 Resource Version: 7120865 Self Link: /apis/networking.gke.io/v1beta1/namespaces/default/managedcertificates/vault-ui-certificate UID: e35e7a1b-3659-11ea-ae90-42010aa80174Spec: Domains: vault.ami-playground.doit-intl.comStatus: Certificate Name: mcrt-9462e1f4-6dd6-4cf2-8769-9693ba29789e Certificate Status: Active Domain Status: Domain: vault.ami-playground.doit-intl.com Status: Active Expire Time: 2020-04-12T15:12:29.000-07:00Events: <none>GKE向けIAPの設定:
下記の要約版ではなく、公式の完全な手順に従っても構いません:
Oauth consent screenでドメインのIAPを設定し、クライアント認証情報を作成します。
クライアント作成後、クライアントIDをコピーし、承認済みリダイレクトURI欄に次の形式で追加します:
https://iap.googleapis.com/v1/oauth/clientIds/<CLIENT_ID>:handleRedirectバックエンド設定で使うシークレットを作成します:
kubectl create secret generic my-secret --from-literal=client_id=client_id_key \ --from-literal=client_secret=client_secret_keyIAP用のバックエンド設定を作成します:
https://gist.github.com/innovia/4485a253f15cd824d0e6d2a19230a603
vault-gke.yamlの末尾セクションを有効化し、グローバル静的IPおよびホストのDNSが正しい値に更新されているか確認してください
注意:
GKE Ingressは既存Ingressの更新がうまくいかないため、一度Vaultをアンインストールしてからhelmで再作成する必要があります。
values yamlの設定内容まとめ:
- Vault UIサービスをポート443で有効化し、NodePort経由で公開
- バックエンド設定を介してVaultサービスにIAPを適用
- 静的グローバルIPと、それに紐づくホストとしてDNSを指定してIngressを有効化
- ロードバランサー上のHTTPを無効化
- ロードバランサーとVault Pod間の通信をhttpsのみに限定
- ロードバランサーがHTTPSリスナーとして動作するよう、マネージド証明書を設定
デプロイ後、IAPページを確認すると次のエラーや警告が表示されます(共有VPCネットワーキングを使っている場合は、両方のバックエンドサービスがERRORとなることもあります。最終的な確認はブラウザでVault UIのURLを開いて行ってください)

1つ目のエラーはデフォルトバックエンド(404を返すもの)に対するものです。これは404ページにIAPが適用されないことを示しているだけで、想定どおりの挙動です。
もう1つは単なる警告で、クリックすると次のような表示が出ます:

これは、内部ネットワークやロードバランサーからVaultバックエンドへの通信など、IAPをバイパスするファイアウォールルールがあることをGCPが検出したという意味にすぎません。
IAPページでdefault/vaultを選択し、左側の情報パネルから、ロードバランサー経由でvault-uiにアクセスさせたいメンバーを追加します
該当ユーザーがVault UIにアクセスできるよう、メンバーには「IAP-secured Web App User」権限を付与してください。