プライベート CA 証明書を使用して限定公開レジストリにアクセスする


このページでは、レジストリの証明書を発行した認証局(CA)の公開鍵を使用して、Google Kubernetes Engine(GKE)で実行されているワークロードが限定公開イメージ レジストリにアクセスできるようにする方法について説明します。

仕組み

限定公開レジストリの証明書の発行に使用する CA の公開鍵を Secret Manager に保存し、その公開鍵を証明書の検証に使用するレジストリの完全修飾ドメイン名(FQDN)を構成します。GKE は、ノード ブートストラップ中に鍵を自動的に取得し、コンテナ ランタイム レジストリ構成を更新します。プライベート レジストリのコンテナ イメージを使用するワークロードをデプロイすると、次の手順が実行されます。

  1. ノード上の kubelet が限定公開レジストリからイメージを pull しようとします。
  2. レジストリがサーバーサイドの TLS 証明書を提示します。
  3. コンテナ ランタイムはレジストリ証明書を暗号的に検証し、FQDN が指定されたものと一致することを確認します。
  4. 検証に合格すると、GKE はイメージを pull し、ワークロードをスケジュールします。

利点

プライベート レジストリにアクセスするこの方法には、次のような利点があります。

  1. コンテナ ランタイム構成の信頼性の向上を実現する: DaemonSet などのメソッドを使用して containerd 構成を設定すると、競合状態が発生するリスクが高まり、構成 DaemonSet の前に他の DaemonSet が実行される可能性があります。
  2. 権限昇格攻撃に対する脆弱性を軽減する: コンテナ ランタイム構成を変更する特権 DaemonSet を実行する必要がなくなります。
  3. 管理オーバーヘッドを削減: Secret Manager を使用すると、CA 公開鍵を一元管理された場所に保存して、IAM を使用して鍵へのアクセスを管理し、バージョン管理とアノテーションを実装できます。詳細については、Secret Manager プロダクトの概要をご覧ください。
  4. 監査可能性の改善: Cloud Logging は、証明書がクラスタに追加されたときや、GKE ノードがイメージを pull するときなど、すでにログを収集しています。

料金

このドキュメントでは、Google Cloud の次の課金対象のコンポーネントを使用します。

  • GKE
  • Secret Manager
  • ロギング: GKE は管理アクティビティの監査ログを生成し、有効にするとこの機能のデータアクセスの監査ログも生成します。さまざまな種類の監査ログについては、GKE 監査ロギングをご覧ください。

料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを生成できます。

始める前に

始める前に、次の作業が完了していることを確認してください。

  • Google Kubernetes Engine API を有効にする。
  • Google Kubernetes Engine API の有効化
  • このタスクに Google Cloud CLI を使用する場合は、gcloud CLI をインストールして初期化する。すでに gcloud CLI をインストールしている場合は、gcloud components update を実行して最新のバージョンを取得する。
  • Enable the Secret Manager API.

    Enable the API

  • レジストリにアクセスするには、プライベート レジストリとプライベート CA 証明書を取得済みであることが必要です。このガイドでは、限定公開レジストリの設定や証明書の作成については説明しません。

要件

限定公開 CA 公開鍵を使用して限定公開レジストリにアクセスするには、次の要件を満たす必要があります。

  • クラスタは、GKE バージョン 1.27.3-gke.1700 以降を使用する必要があります。
  • containerd を含む Container-Optimized OS ノードイメージを使用する必要があります。これは、すべての GKE クラスタのデフォルトです。containerd を含む Ubuntu ノードイメージはサポートされていません。Windows Server ノードイメージはサポートされていません。
  • ノードが証明書をダウンロードするには、ノードプールに cloud-platform アクセス スコープが必要です。詳細については、GKE のデフォルトのアクセス スコープをご覧ください。このドキュメントでは、クラスタまたはノードプールの作成時にアクセス スコープを設定する手順について説明します。

制限事項

次の制限事項を考慮してください。

  • Ubuntu ノードイメージではプライベート CA 証明書を使用できません。
  • Windows Server ノードではプライベート CA 証明書を使用できません。
  • 各クラスタは、プライベート レジストリのプライベート CA 証明書を最大 5 つサポートします。
  • 各証明書には、最大 25 個の完全修飾ドメイン名(FQDN)を含めることができます。
  • 各ドメインは 1 つの証明書ファイルでのみ使用できます。ただし、証明書のバンドルはサポートされています。
  • 証明書は PEM エンコードされている必要があります。
  • サーバーは証明書を自動的にローテーションしません。詳細については、このドキュメントのプライベート CA 証明書をローテーションするをご覧ください。
  • FQDN には次の制限があります。
    • FQDN の最大長は 255 文字(特殊文字を含む)です。
    • FQDN では、英字、数字、ダッシュ(-)のみを使用できます。
    • Punycode はサポートされていません。
    • ワイルドカード文字はサポートされていません。

構成 DaemonSet から移行する

GKE Standard クラスタでは、特権 DaemonSet をデプロイして、コンテナのランタイム構成を変更できます。このメソッドでは、各ノードの containerd 構成を直接変更します。

特権 DaemonSet を使用して限定公開レジストリへのアクセスを構成する場合は、このドキュメントを使用する前に、次の点を考慮してください。

  • Secret Manager にプライベート CA 公開鍵を保存すると、プライベート レジストリへのアクセスの構成のみが行われます。その他のレジストリ関連の構成はサポートされていません。
  • この機能を有効にすると、クラスタは containerd の CRI hostpath 構成モデルを使用します。これは以前の構成モデルとは互換性がありません。安全でない限定公開レジストリ、ミラー、プロキシなど、containerd ホスト構成を変更する DaemonSet がある場合は、CRI hostpath モデルを使用するように DaemonSet を更新します。

    CRI hostpath モデルで使用可能なフィールドについては、containerd GitHub リポジトリのレジストリ構成をご覧ください。

この機能を有効にすると、GKE はクラスタ内の新しいノードに CRI hostpath 構成モデルを適用します。既存のノードは、アップグレードなどのイベント中に再作成されるまで、以前の構成モデルを引き続き使用します。

両方の構成モデルをサポートするように DaemonSet を更新する

特定の構成モデルをサポートするノードで構成 DaemonSet が機能しないリスクを軽減するには、DaemonSet が、ノード上の containerd 構成ファイルに応じて、条件付きで特定の構成モデルを使用するようにします。この条件付きロジックを実装する DaemonSet の例については、GoogleCloudPlatform/k8s-node-tools GitHub リポジトリの insecure-registry-config.yaml マニフェストをご覧ください。

CA の公開鍵を Secret Manager に保存する

限定公開レジストリ証明書を発行するプライベート CA の公開鍵をシークレットとして Secret Manager に保存します。手順については、Secret Manager ドキュメントのシークレットを作成するをご覧ください。

GKE から Secret Manager へのアクセスを構成する

クラスタの IAM サービス アカウントに Secret Manager からシークレットを pull するために必要な権限が付与されていることを確認するには、クラスタの IAM サービス アカウントにシークレットに対する次の IAM ロールを付与するよう管理者に依頼します。

ロールの付与については、プロジェクト、フォルダ、組織へのアクセス権の管理をご覧ください。

これらの事前定義ロールには、Secret Manager からシークレットを pull するために必要な権限が含まれています。必要とされる正確な権限については、「必要な権限」セクションを開いてご確認ください。

必要な権限

Secret Manager からシークレットを pull するには、次の権限が必要です。

  • resourcemanager.projects.get
  • resourcemanager.projects.list
  • secretmanager.secrets.get
  • secretmanager.secrets.list
  • secretmanager.versions.get
  • secretmanager.versions.list
  • secretmanager.versions.access

管理者は、カスタムロールや他の事前定義ロールを使用してクラスタの IAM サービス アカウントにこれらの権限を付与することもできます。

カスタム IAM サービス アカウントをクラスタまたはノードプールに関連付けていない場合(おすすめの方法)、クラスタは Compute Engine のデフォルトのサービス アカウントを使用します。可能であれば、最小限の権限を持つ IAM サービス アカウントを使用してクラスタとノードプールを構成することをおすすめします。手順については、最小権限のサービス アカウントを使用するをご覧ください。

ランタイム構成ファイルを作成する

GKE のプライベート レジストリにプライベート CA 証明書を使用する機能を有効にするには、YAML ファイルを作成して containerd 構成を変更します。

  1. Google Cloud プロジェクト番号を取得します。

    gcloud projects describe PROJECT_ID \
        --format="value(projectNumber)"
    

    出力は数値のプロジェクト番号です。

  2. 次の構成を containerd-configuration.yaml として保存します。

    privateRegistryAccessConfig:
      certificateAuthorityDomainConfig:
      - gcpSecretManagerCertificateConfig:
          secretURI: "projects/PROJECT_NUMBER/secrets/SECRET_NAME/versions/SECRET_VERSION"
        fqdns:
          - "FQDN1"
          - "FQDN2"
      enabled: true
    

    次のように置き換えます。

    • PROJECT_NUMBER: 前のステップで取得したプロジェクト番号。
    • SECRET_VERSION: Secret Manager のシークレットのバージョン番号。必要に応じてバージョン エイリアスを使用することもできますが、管理が複雑にならないように、バージョン番号を使用することをおすすめします。
    • FQDN1FQDN2: 限定公開レジストリの完全修飾ドメイン名。証明書が IPv4 アドレス用に発行されている場合は IPv4 アドレスを使用することもできますが、おすすめしません。

これらのフィールドの説明については、使用可能な containerd 構成オプションの表の privateRegistryAccessConfig をご覧ください。

新しいクラスタに containerd 構成を適用する

このセクションでは、新しい GKE クラスタの作成時に containerd 構成ファイルを適用する方法について説明します。

次のコマンドを実行します。

gcloud container clusters create-auto CLUSTER_NAME \
    --location=LOCATION \
    --scopes="cloud-platform" \
    --containerd-config-from-file="PATH_TO_CONFIG_FILE"

次のように置き換えます。

  • CLUSTER_NAME: 新しいクラスタの名前。
  • LOCATION: 新しいクラスタの Compute Engine のロケーション。
  • PATH_TO_CONFIG_FILE: 作成した構成ファイルのパス(~/containerd-configuration.yaml など)。

新しい Standard クラスタで限定公開レジストリ構成を有効にするには、同じオプションを指定して gcloud container clusters create コマンドを実行します。

既存のクラスタに containerd 構成を適用する

このセクションでは、既存のクラスタとノードに containerd 構成を適用する方法について説明します。

アクセス スコープを確認する

この機能を使用するには、既存のクラスタに cloud-platform アクセス スコープが必要です。このセクションでは、アクセス スコープを確認し、新規または変更された限定公開レジストリ構成ファイルを使用して既存のクラスタを更新する方法について説明します。

新しいクラスタのデフォルトのアクセス スコープの詳細については、GKE のアクセス スコープをご覧ください。

Autopilot のアクセス スコープを確認する

次のコマンドを実行します。

gcloud container clusters describe CLUSTER_NAME \
    --location=LOCATION \
    --flatten=nodeConfig \
    --format='csv[delimiter="\\n",no-heading](oauthScopes)'

クラスタに https://www.googleapis.com/auth/cloud-platform アクセス スコープがない場合は、このアクセス スコープを持つ新しいクラスタを作成します。

Standard アクセス スコープを確認する

Standard クラスタのアクセス スコープを確認するには、ノードプールを確認します。

gcloud container node-pools describe NODE_POOL_NAME \
    --cluster=CLUSTER_NAME \
    --location=LOCATION \
    --flatten=nodeConfig \
    --format='csv[delimiter="\\n",no-heading](oauthScopes)'

NODE_POOL_NAME は、ノードプールの名前に置き換えます。

クラスタに https://www.googleapis.com/auth/cloud-platform アクセス スコープがない場合は、cloud-platform アクセス スコープで新しいノードプールを作成し、既存のノードプールを削除します。

構成ファイルを使用するようにクラスタを更新する

次のコマンドを実行します。

gcloud container clusters update CLUSTER_NAME \
    --location=LOCATION \
    --containerd-config-from-file="PATH_TO_CONFIG_FILE"

Standard クラスタでノードを再作成する

Standard クラスタで自動アップグレードを使用しない場合は、ノードプールを手動で再作成して新しい構成を適用する必要があります。ノードの手動再作成をトリガーするには、クラスタがすでに使用している GKE バージョンにクラスタをアップグレードします。

gcloud container clusters upgrade CLUSTER_NAME \
    --location=LOCATION \
    --cluster-version=VERSION

VERSION は、クラスタがすでに使用している GKE パッチ バージョンに置き換えます。

クラスタが限定公開レジストリにアクセスできることを確認する

次のコマンドを実行します。

gcloud container clusters describe CLUSTER_NAME \
    --location=LOCATION \
    --flatten="nodePoolDefaults.nodeConfigDefaults.containerdConfig"

出力は次のようになります。

    containerdConfig:
      privateRegistryAccessConfig:
        certificateAuthorityDomainConfig:
        - fqdns:
          - 203.0.113.105
          gcpSecretManagerCertificateConfig:
            secretUri: projects/123456789012/secrets/example-secret-name/versions/1
        enabled: true

限定公開イメージにアクセスするワークロードをデプロイする

このセクションでは、プライベート レジストリのイメージを参照する静的 Pod をデプロイします。

  1. 次のマニフェストを private-registry-pod.yaml として保存します。

    apiVersion: v1
    kind: Pod
    metadata:
      name: private-registry-pod
    spec:
      containers:
      - name: private-image
        image: IMAGE_NAME
    

    IMAGE_NAME は、限定公開イメージの名前に置き換えます。

  2. Pod をデプロイします。

    kubectl create -f private-registry-pod.yaml
    

プライベート CA 証明書をローテーションする

Secret Manager と GKE は、Secret Manager の CA 証明書を自動的にローテーションできません。証明書のローテーションを行うには、次の手順を行います。この手順では、既存のノードを 2 回再作成する必要があります。ワークロードの中断による影響を最小限に抑えるため、計画的ダウンタイム中に証明書のローテーションを行うことをおすすめします。

  1. 古い証明書と新しい証明書の両方を含む、PEM でエンコードされた証明書バンドルを作成します。
  2. Secret Manager で新しいシークレット バージョンとしてバンドルを追加します
  3. ランタイム構成ファイルの secretURI フィールドを新しいシークレット バージョン番号で更新します。
  4. 新しいシークレット バージョンを使用するようにクラスタを更新します
  5. 更新オペレーションのタイムスタンプを取得します。

    gcloud container operations list \
        --filter="operationType ~ UPDATE_CLUSTER AND targetLink ~ CLUSTER_NAME" \
        --sort-by=startTime \
        --limit=1 \
        --format='value(endTime)'
    

    出力は次のようになります。

    2024-01-31T09:27:30.864308964Z
    
  6. 更新オペレーションが完了する前に作成されたノードを探します。

    kubectl get nodes -o json | jq ".items[] |
    select(.metadata.creationTimestamp | fromdateiso8601 < $(date -d
    CLUSTER_UPDATE_TIMESTAMP +%s)) | .metadata.name"
    

    CLUSTER_UPDATE_TIMESTAMP は、前のステップで取得したタイムスタンプに置き換えます。

    出力は、更新された構成で再作成されていないノード名のリストです。出力が空白の場合は、次のステップに進みます。

  7. Secret Manager で、新しい証明書のみを使用してシークレットの新しいバージョンを作成します。

  8. 上記の手順を繰り返してクラスタを更新し、オペレーションのタイムスタンプを取得して、ノードで新しいシークレット バージョンが使用されていることを確認します。

  9. Secret Manager から古いシークレット バージョンを削除します。

Logging で監査ログを表示する

このセクションでは、Logging を使用して、GKE がシークレット バージョンをノードにインストールしたかどうかを確認する方法について説明します。

  1. Google Cloud コンソールの [ログ エクスプローラ] ページに移動します。

    [ログ エクスプローラ] に移動

  2. 次のクエリを指定します。

    resource.type="gce_instance"
    textPayload:"Installed certificate \\\"projects/PROJECT_NUMBER/secrets/SECRET_NAME/versions/SECRET_VERSION\\\""
    

    証明書のインストールが成功した場合、出力は次のようになります。

    "Installed certificate "projects/PROJECT_NUMBER/secrets/SECRET_NAME/versions/SECRET_VERSION""
    

    証明書のインストールに失敗した場合、出力は次のようになります。

    "Failed to install certificate "projects/PROJECT_NUMBER/secrets/SECRET_NAME/versions/SECRET_VERSION""
    

ベスト プラクティス

この機能を使用する場合は、次のベスト プラクティスを使用することをおすすめします。

  • Secret Manager のシークレット バージョンにエイリアスを使用しないでください。シークレット バージョンごとに自動生成されたバージョン番号を使用します。エイリアスが時間の経過とともに異なる証明書バージョンを指す可能性があるため、ワークロードが使用する特定のバージョンを追跡することが複雑になる可能性があります。
  • メンテナンスの時間枠と除外を使用して、GKE がノードを再作成して更新された containerd 構成を適用できるタイミングを制御します。
  • プロジェクト レベルではなく、シークレット レベルでシークレットへのアクセス権を付与します。

containerd 構成オプションを無効にする

カスタム構成を削除する手順は次のとおりです。

  1. 次の例に示すように、構成ファイルを更新して、無効にする構成アイテムに enabled: false を指定し、アイテム内の他のフィールドを削除します。以下に例を示します。

    privateRegistryAccessConfig:
      enabled: false
  2. 更新された構成ファイルをクラスタに適用します。手順については、既存のクラスタに containerd 構成を適用するをご覧ください。

トラブルシューティング

トラブルシューティングの手順については、コンテナ ランタイムのトラブルシューティングをご覧ください。