サードパーティの ID を証明する証明書を発行する

このチュートリアルでは、ID リフレクションWorkload Identity プールを使用してサードパーティの ID を証明する証明書を発行する方法について説明します。

ID リフレクションを使用すると、証明書リクエスト元の検証済みの ID と一致する証明書を作成できます。ID リフレクションを使用すると、認証情報の ID に対応するサブジェクト代替名(SAN)を持つ証明書のみをリクエストするように、権限のない証明書リクエスト元を制限できます。

目標

このチュートリアルでは、Workload Identity プールで CA Service を使用してサードパーティの ID を連携し、この ID を証明する証明書を取得する方法について説明します。

準備

始める前に、次のコンセプトを理解しておいてください。

  • Workload Identity プール: Workload Identity プールを使用すると、サードパーティの ID プロバイダを管理できます。詳細については、Workload Identity プールとプロバイダの管理をご覧ください。
  • Workload Identity 連携: Workload Identity 連携は、Workload Identity プールを利用して、サードパーティの ID に Google Cloud サービスへのアクセスを許可します。詳細については、Workload Identity 連携をご覧ください。
  • セキュリティ トークン サービス(STS): セキュリティ トークン サービスを使用すると、サードパーティ認証情報をファースト パーティ(Google Cloud)トークンと交換できます。詳細については、セキュリティ トークン サービスをご覧ください。
  • ID リフレクション: ID リフレクション機能を使用すると、証明書のリクエスト元の検証された ID が、リクエストされた証明書に進むことができます。詳細については、ID リフレクションをご覧ください。

次の IAM ロールがあることを確認してください。

  • 認証局(CA)と CA プールを管理し、証明書をリクエストするには、CA Service オペレーション マネージャー(privateca.caManager)のロールが必要です。CA Service の IAM ロールの詳細については、IAM を使用したアクセス制御をご覧ください。
  • Workload Identity プールとプロバイダを管理するには、Workload Identity プール管理者(iam.workloadIdentityPoolAdmin)のロールが必要です。
  • サービス アカウントを作成するには、サービス アカウント管理者(iam.serviceAccountAdmin)のロールが必要です。

IAM ロールの付与については、プロジェクト、フォルダ、組織へのアクセスを管理するをご覧ください。必要な IAM ロールを Google アカウント、サービス アカウント、Google グループ、Google Workspace アカウント、または Cloud Identity ドメインに付与できます。

Workload Identity プールとプロバイダを設定する

このチュートリアルでは、Google OpenID Connect(OIDC)プロバイダをサービス アカウントと組み合わせて使用し、サードパーティの ID として機能する方法について説明します。Google アカウントの OIDC プロバイダは、サードパーティ ID プロバイダ(IDP)として機能し、Google Cloud サービス アカウントは、この IDP で表明されたサンプルのサードパーティ ID です。

Workload Identity プールは、Microsoft Azure / オンプレミスの Active Directory、AWS、SAML ベースの ID プロバイダなど、さまざまな ID プロバイダをサポートしています。

Workload Identity プールとプロバイダを設定するには、以下の手順を行います。 1. 信頼できるフェデレーション ID のセットを表すには、Workload Identity プールを作成します。

```
gcloud iam workload-identity-pools create IDENTITY_POOL_ID --location global --display-name "tutorial-wip"
```

Replace the following:

- <var>IDENTITY_POOL_ID</var>: The unique identifier of the new workload
  identity pool.
  1. サードパーティ ID プロバイダの Workload Identity プール プロバイダを作成します。

    gcloud iam workload-identity-pools providers create-oidc PROVIDER_ID --location global --workload-identity-pool IDENTITY_POOL_ID --display-name "tutorial-oidc" --attribute-mapping "google.subject=assertion.sub" --issuer-uri="https://accounts.google.com"
    

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

    • PROVIDER_ID: Workload Identity プール内に作成する ID プロバイダの一意の識別子。

    ユースケースに合わせて次のフラグをカスタマイズできます。

    • attribute-mapping: このフラグは、サードパーティのクレームと Google のプリンシパル クレーム google.subject のマッピングを設定します。google.subject は、CEL 式を使用して任意のクレームまたはクレームの組み合わせに設定可能な必須のマッピングです。詳細については、属性のマッピングと条件を定義するをご覧ください。
    • issuer-uri: OIDC プロバイダの場合、このフラグは一般公開されるエンドポイントであり、サードパーティ トークンの検証用に Google から到達します。詳細については、外部 ID プロバイダの準備をご覧ください。

    Workload Identity プロバイダの設定の詳細については、Workload Identity 連携の構成をご覧ください。

CA プールと発行 CA を作成する

このセクションでは、CA プールを作成してルート CA を追加する方法について説明します。この CA プールを使用して、ID が反映された証明書を発行できます。既存の CA プールと CA を使用する場合は、このセクションをスキップできます。

ルート CA の代わりに下位 CA を作成することもできます。ルート CA を作成すると、手順を短縮できます。

  1. DevOps ティアで CA プールを作成します。

    gcloud privateca pools create CA_POOL_ID --location LOCATION --tier devops
    

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

    • CA_POOL_ID - 証明書を発行する CA Service の CA プールの ID。
    • LOCATION - CA プールのロケーション。

    CA プールの作成の詳細については、CA プールの作成をご覧ください。

  2. ルート CA を作成します。

    gcloud privateca roots create CA_ID --pool CA_POOL_ID  --location LOCATION --subject "CN=test,O=test-org"
    

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

    • CA_ID - 証明書を発行する認証局の ID。
    • CA_POOL_ID - 証明書を発行する CA Service の CA プールの ID。
    • LOCATION - CA プールのロケーション。

    ルート CA の作成の詳細については、ルート CA を作成するをご覧ください。

  3. Workload Identity プールの ID 連携を有効にして、CA プールから証明書を発行します。ID リフレクションには、CreateCertificate のリクエスト元に対する CA サービス ワークロード証明書リクエスト元(roles/privateca.workloadCertificateRequester)の IAM ロールが必要です。

    Workload Identity プール プリンシパルは、単一のサブジェクトからプロバイダ全体のプール内のすべての ID まで、さまざまな粒度で表現できます。詳細については、ユースケースに最適な利用可能なプリンシパルまたはプリンシパル セット(Google Cloud CLI タブを使用)をご覧ください。

    gcloud privateca pools add-iam-policy-binding CA_POOL_ID --location LOCATION --role roles/privateca.workloadCertificateRequester --member "principalSet://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/IDENTITY_POOL_ID/*"
    

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

    • PROJECT_NUMBER - Workload Identity プールを作成したプロジェクトのプロジェクト番号。

サードパーティの ID を表すサービス アカウントを作成する

次の手順では、サービス アカウントがサードパーティを表していることを前提としています。このセクションでは、GenerateIdToken IAM エンドポイントを使用して、OIDC トークンの形式でサードパーティ ID を取得する方法について説明します。ユースケースによっては、選択したサードパーティの ID トークンを取得するために必要な手順が異なる場合があります。

gcloud iam service-accounts create SERVICE_ACCOUNT

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

  • SERVICE_ACCOUNT - サードパーティの ID を表すサービス アカウントの ID。

サードパーティの ID を証明する証明書を発行する

開始する前に、サービス アカウント トークン作成者(roles/iam.serviceAccountTokenCreator)の IAM ロールがあることを確認してください。GenerateIdToken API を呼び出すには、この IAM ロールが必要です。

サードパーティの ID を証明する証明書を取得するには、次の手順を行います。

  1. サードパーティの ID プロバイダからサードパーティの ID トークンを取得します。

    curl

    export ID_TOKEN=`curl -d '{"audience":"//iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/IDENTITY_POOL_ID/providers/PROVIDER_ID"}' -H 'Content-Type: application/json' -H "Authorization: Bearer $(gcloud auth print-access-token)" https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/SERVICE_ACCOUNT@PROJECT_ID.iam.gserviceaccount.com:generateIdToken | python3 -c "import sys;import json;print(json.load(sys.stdin)['token'])"`
    

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

    • PROJECT_ID - リソースを作成するプロジェクトの Google Cloud プロジェクト ID。

    クライアント ライブラリ

    サードパーティ トークンにプログラムでアクセスするには、ファイル提供の認証情報または URL 提供の認証情報からトークンを取得します。詳細については、クライアント ライブラリ、gcloud CLI、または Terraform を使用した認証をご覧ください。このチュートリアルでは、ファイル提供の認証情報ワークフローに従います。

    証明書リクエスト元が読み取り可能なパスに認証情報を読み込みます。

    curl -d '{"audience":"//iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/IDENTITY_POOL_ID/providers/PROVIDER_ID"}' -H 'Content-Type: application/json' -H "Authorization: Bearer $(gcloud auth print-access-token)" https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/SERVICE_ACCOUNT@PROJECT_ID.iam.gserviceaccount.com:generateIdToken | python3 -c "import sys;import json;       print(json.load(sys.stdin)['token']) > /tmp/oidc_token.txt
    

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

    • PROJECT_ID: リソースを作成するプロジェクトのプロジェクト ID。
  2. STS token エンドポイントを使用して、サードパーティ トークンをフェデレーション OAuth トークンと交換します。

    curl

    export STS_TOKEN=`curl -L -X POST 'https://sts.googleapis.com/v1/token' -H 'Content-Type: application/json' \
    -d '{
        "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
        "audience": "//iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/IDENTITY_POOL_ID/providers/PROVIDER_ID",
        "requested_token_type": "urn:ietf:params:oauth:token-type:access_token",
        "scope": "https://www.googleapis.com/auth/cloud-platform",
        "subject_token": "'$ID_TOKEN'",
        "subject_token_type": "urn:ietf:params:oauth:token-type:jwt"
    }' | python3 -c "import sys;import json; print(json.load(sys.stdin)['access_token'])"`
    

    クライアント ライブラリ

    1. 証明書のリクエスト コードがトークン交換を実行するために読み取ることができる、oidc_token.txt という名前の認証情報構成ファイルを作成します。
    gcloud iam workload-identity-pools create-cred-config projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/IDENTITY_POOL_ID --output-file=/tmp/cred_config.json --credential-source-file=/tmp/oidc_token.txt
    
    1. oidc_token.txt ファイルを読み取って、クライアント ライブラリに承認メカニズムを設定します。

    python

    import json
    
    from google.auth import identity_pool
    
    with open('/tmp/cred_config.json', 'r') as f:
      json_config_info = json.loads(f.read())
    credentials = identity_pool.Credentials.from_info(json_config_info)
    scoped_credentials = credentials.with_scopes(
        ['https://www.googleapis.com/auth/cloud-platform'])
    
  3. REFLECTED_SPIFFE サブジェクト リクエスト モードで CA サービスへのリクエストを行います。

    curl

    1. 省略可: CSR がない場合は、次のコマンドを実行して CSR を作成します。

      export TUTORIAL_CSR=$(openssl req -newkey rsa:2048 -nodes -subj / -keyout tutorial_do_not_use.key)
      
    2. CSR、有効期間、反映されたサブジェクト リクエスト モードを使用して証明書をリクエストします。

      curl -H "Authorization: Bearer $(echo $STS_TOKEN)" https://privateca.googleapis.com/v1/projects/PROJECT_NUMBER/locations/LOCATION/caPools/CA_POOL_ID/certificates\?alt\=json  -X POST -H "Content-Type: application/json" -H 'Accept: application/json' --data '{"lifetime": "100s", "pemCsr": "'$TUTORIAL_CSR'", "subjectMode": "REFLECTED_SPIFFE"}'
      

    クライアント ライブラリ

    自社トークンを CA Service に転送するには、認証されたクライアントを作成する必要があります。この認証されたクライアントを使用して、証明書リクエストを行うことができます。

    1. 認証された CA Service クライアントを開始します。

      python

      caServiceClient = privateca_v1.CertificateAuthorityServiceClient(credentials=scoped_credentials)
      
    2. 証明書をリクエストする

      Python

      CA Service への認証を行うには、アプリケーションのデフォルト認証情報を設定します。 詳細については、ローカル開発環境の認証の設定をご覧ください。

      import google.cloud.security.privateca_v1 as privateca_v1
      from google.protobuf import duration_pb2
      
      def create_certificate(
          project_id: str,
          location: str,
          ca_pool_name: str,
          ca_name: str,
          certificate_name: str,
          common_name: str,
          domain_name: str,
          certificate_lifetime: int,
          public_key_bytes: bytes,
      ) -> None:
          """
          Create a Certificate which is issued by the Certificate Authority present in the CA Pool.
          The key used to sign the certificate is created by the Cloud KMS.
      
          Args:
              project_id: project ID or project number of the Cloud project you want to use.
              location: location you want to use. For a list of locations, see: https://cloud.google.com/certificate-authority-service/docs/locations.
              ca_pool_name: set a unique name for the CA pool.
              ca_name: the name of the certificate authority which issues the certificate.
              certificate_name: set a unique name for the certificate.
              common_name: a title for your certificate.
              domain_name: fully qualified domain name for your certificate.
              certificate_lifetime: the validity of the certificate in seconds.
              public_key_bytes: public key used in signing the certificates.
          """
      
          caServiceClient = privateca_v1.CertificateAuthorityServiceClient()
      
          # The public key used to sign the certificate can be generated using any crypto library/framework.
          # Also you can use Cloud KMS to retrieve an already created public key.
          # For more info, see: https://cloud.google.com/kms/docs/retrieve-public-key.
      
          # Set the Public Key and its format.
          public_key = privateca_v1.PublicKey(
              key=public_key_bytes,
              format_=privateca_v1.PublicKey.KeyFormat.PEM,
          )
      
          subject_config = privateca_v1.CertificateConfig.SubjectConfig(
              subject=privateca_v1.Subject(common_name=common_name),
              subject_alt_name=privateca_v1.SubjectAltNames(dns_names=[domain_name]),
          )
      
          # Set the X.509 fields required for the certificate.
          x509_parameters = privateca_v1.X509Parameters(
              key_usage=privateca_v1.KeyUsage(
                  base_key_usage=privateca_v1.KeyUsage.KeyUsageOptions(
                      digital_signature=True,
                      key_encipherment=True,
                  ),
                  extended_key_usage=privateca_v1.KeyUsage.ExtendedKeyUsageOptions(
                      server_auth=True,
                      client_auth=True,
                  ),
              ),
          )
      
          # Create certificate.
          certificate = privateca_v1.Certificate(
              config=privateca_v1.CertificateConfig(
                  public_key=public_key,
                  subject_config=subject_config,
                  x509_config=x509_parameters,
              ),
              lifetime=duration_pb2.Duration(seconds=certificate_lifetime),
          )
      
          # Create the Certificate Request.
          request = privateca_v1.CreateCertificateRequest(
              parent=caServiceClient.ca_pool_path(project_id, location, ca_pool_name),
              certificate_id=certificate_name,
              certificate=certificate,
              issuing_certificate_authority_id=ca_name,
          )
          result = caServiceClient.create_certificate(request=request)
      
          print("Certificate creation result:", result)
      
      

    3. 証明書を確認します。証明書のサブジェクトには、単一の URI SAN を含める必要があります。ID を証明する SAN の形式は次のとおりです。

      spiffe://IDENTITY_POOL_ID.PROJECT_NUMBER.global.workload.id.goog/subject/<oidc_subject_number>
      

      以下のように置き換えます。

      • IDENTITY_POOL_ID: Workload Identity プールの一意の識別子。
      • PROJECT_NUMBER - Workload Identity プールを作成したプロジェクトのプロジェクト番号。

クリーンアップ

このドキュメントに従って作成した CA Service リソースについて、Google Cloud アカウントに課金されないようにするには、Google Cloud CLI を使用して次の操作を行います。

  1. 作成した CA を削除します。

    1. CA を無効にします。

      gcloud privateca roots disable CA_ID --pool CA_POOL_ID  --location LOCATION
      

      以下のように置き換えます。

      • CA_ID: CA の一意の識別子。
      • CA_POOL_ID: CA プールの一意の識別子。
      • LOCATION: CA プールのロケーション。
    2. CA を削除します。

      gcloud privateca roots delete CA_ID --pool CA_POOL_ID  --location LOCATION --ignore-active-certificates
      

      以下のように置き換えます。

      • CA_ID: CA の一意の識別子。
      • CA_POOL_ID: CA プールの一意の識別子。
      • LOCATION: CA プールのロケーション。
  2. 作成した CA プールを削除します。

    gcloud privateca pools delete CA_POOL_ID --location LOCATION
    

    以下のように置き換えます。

    • CA_POOL_ID: CA プールの一意の識別子。
    • LOCATION: CA プールのロケーション。

    gcloud privateca pools delete コマンドの詳細については、gcloud privateca pools delete をご覧ください。

  3. 作成した Workload Identity プールを削除します。

    gcloud iam workload-identity-pools delete IDENTITY_POOL_ID --location global
    

    以下のように置き換えます。

    • IDENTITY_POOL_ID: Workload Identity プールの一意の識別子。
  4. 作成したサービス アカウントを削除します。

    gcloud iam service-accounts delete SERVICE_ACCOUNT@PROJECT_ID.iam.gserviceaccount.com
    

    以下のように置き換えます。

    • SERVICE_ACCOUNT: Workload Identity プールの一意の識別子。
    • PROJECT_ID: サービス アカウントを所有するプロジェクト。

CA Service ワークロード証明書リクエスト元(privateca.workloadCertificateRequester)の IAM ロールにより、発行される証明書のサブジェクトがリクエスト元の ID のみに制限されます。ID リフレクション機能を使用するユーザーまたはワークロードに、CA Service ワークロード証明書リクエスト元(privateca.workloadCertificateRequester)の IAM ロールのみが付与されていることを確認します。最小権限の原則に従うことで、CA Service 証明書リクエスト元(privateca.certificateRequester)の IAM ロールの付与を回避できます。

次のステップ