サードパーティの 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 連携をご覧ください。
  • Security Token Service(STS): Security Token Service を使用すると、サードパーティの認証情報をファーストパーティ(Google Cloud)トークンと交換できます。詳細については、Security Token Service をご覧ください。
  • 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 Service にリクエストを送信します。

    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 ロールの付与を回避できます。

次のステップ