push サブスクリプションの使用

Cloud Pub/Sub は push と pull の両方のメッセージ配信をサポートします。pull サブスクリプションと push サブスクリプションの概要と比較については、サブスクライバーの概要をご覧ください。このドキュメントでは、push 配信について説明します。pull 配信の説明については、pull サブスクライバー ガイドをご覧ください。

Cloud Pub/Sub サブスクリプションでは、すべてのメッセージを Webhook(push エンドポイント)の URL に対する HTTP POST リクエストとして送信するように構成できます。一般に、push エンドポイントは、一般公開された HTTPS サーバーにします。これは、認証局が署名した有効な SSL 証明書を提示し、DNS によるルーティングが可能なサーバーである必要があります。また、push エンドポイントのドメインまたは URL パスに対する、所有権または同等のアクセスレベルを検証する必要もあります。

さらに、エンドポイントでリクエストを承認する際に認証ヘッダーを使用するように push サブスクリプションを構成できます。サブスクリプションと同じプロジェクトでホストされている App Engine スタンダード環境と Cloud Functions エンドポイントでは、さらに簡単な認証および承認メカニズムを使用できます。

push メッセージの受信

Cloud Pub/Sub の push リクエストは以下の例のようになります。message.data フィールドは base64 でエンコードされています。

    POST https://www.example.com/my-push-endpoint 

   {
     "message": {
       "attributes": {
         "key": "value"
       },
       "data": "SGVsbG8gQ2xvdWQgUHViL1N1YiEgSGVyZSBpcyBteSBtZXNzYWdlIQ==",
       "messageId": "136969346945"
     },
     "subscription": "projects/myproject/subscriptions/mysubscription"
   }
push エンドポイントは受信メッセージを処理し、処理の成否を示す HTTP ステータス コードを返す必要があります。success レスポンスはメッセージの確認応答と同等です。Cloud Pub/Sub システムでメッセージの確認応答として解釈されるステータス コードは、200201202204102 です。成功した場合のレスポンスは次のようになります。

204 No Content

Webhook によって成功コードが返されない場合、サブスクリプションのメッセージ保持期間が終了し、メッセージの確認応答期限が切れるまで、Cloud Pub/Sub は配信を再試行します。push サブスクリプションのデフォルトの確認応答期限は構成可能です。ただし pull サブスクリプションとは異なり、個々のメッセージの期限は延長できません。事実上、期限はエンドポイントが push リクエストに応答する必要がある時間です。

認証と承認

JSON Web Token(JWT)の使用

サービス アカウント ID と push リクエストを関連付けるように push サブスクリプションを構成することで、push エンドポイントによる認証が可能になります。push サブスクリプションで認証を有効にした場合、そのサブスクリプションからの push リクエストでは、認証ヘッダーに署名付きの OpenIDConnect JWT が含まれます。push エンドポイントではトークンを使用して、サブスクリプションに関連付けられたサービス アカウントに対してリクエストが発行されていることを検証し、承認を決定します。

OpenIDConnect JWT は、一連の 3 つのピリオド区切り Base64 エンコード文字列(ヘッダー、クレームセット、署名)です。認証ヘッダーの例を次に示します。

"Authorization" : "Bearer
eyJhbGciOiJSUzI1NiIsImtpZCI6IjdkNjgwZDhjNzBkNDRlOTQ3MTMzY2JkNDk5ZWJjMWE2MWMzZDVh
YmMiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJodHRwczovL2V4YW1wbGUuY29tIiwiYXpwIjoiMTEzNzc0M
jY0NDYzMDM4MzIxOTY0IiwiZW1haWwiOiJnYWUtZ2NwQGFwcHNwb3QuZ3NlcnZpY2VhY2NvdW50LmNvb
SIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJleHAiOjE1NTAxODU5MzUsImlhdCI6MTU1MDE4MjMzNSwia
XNzIjoiaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tIiwic3ViIjoiMTEzNzc0MjY0NDYzMDM4MzIxO
TY0In0.QVjyqpmadTyDZmlX2u3jWd1kJ68YkdwsRZDo-QxSPbxjug4ucLBwAs2QePrcgZ6hhkvdc4UHY
4YF3fz9g7XHULNVIzX5xh02qXEH8dK6PgGndIWcZQzjSYfgO-q-R2oo2hNM5HBBsQN4ARtGK_acG-NGG
WM3CQfahbEjZPAJe_B8M7HfIu_G5jOLZCw2EUcGo8BvEwGcLWB2WqEgRM0-xt5-UPzoa3-FpSPG7DHk7
z9zRUeq6eB__ldb-2o4RciJmjVwHgnYqn3VvlX9oVKEgXpNFhKuYA-mWh5o7BCwhujSMmFoBOh6mbIXF
cyf5UiVqKjpqEbqPGo_AvKvIQ9VTQ" 

ヘッダーとクレームセットは JSON 文字列です。デコードされると次の形式になります。

{"alg":"RS256","kid":"7d680d8c70d44e947133cbd499ebc1a61c3d5abc","typ":"JWT"}

{
   "aud":"https://example.com",
   "azp":"113774264463038321964",
   "email":"gae-gcp@appspot.gserviceaccount.com",
   "sub":"113774264463038321964",
   "email_verified":true,
   "exp":1550185935,
   "iat":1550182335,
   "iss":"https://accounts.google.com"
  }

トークンの有効期間は 1 時間です。

App Engine スタンダード環境と Cloud Functions の URL の認証

サブスクリプションと同じプロジェクト内にある、App Engine スタンダード環境のアプリまたは Cloud Functions である Webhook については、push URL を保護するシンプルな代替メカニズムを使用できます。

App Engine アプリの場合は、次のパターンを使用して、URL に対する push リクエストについて管理者ログインを必須にできます。/_ah/push-handlers/.*

これを行うには、login: admin オプションを app.yamlPython 2 の場合)または <security-constraint>Java の場合)に追加する必要があります。ただし login: admin は Python 3 アプリケーションでは動作しません。アプリケーション コードに認証ロジックを実装する必要があります。詳しくは、アクセス制御についてをご覧ください。

Cloud Functions の場合もメカニズムは同様ですが、特定のパスは必要とされません。

Cloud Pub/Sub での push 認証の設定

サブスクリプションの認証構成は、次の 2 つのパラメータで構成されます。

  • サービス アカウント: push サブスクリプションに関連付けられている GCP サービス アカウント
  • トークン オーディエンス(省略可): Webhook で特定のトークンの対象ユーザーの検証に使用する、大文字と小文字が区別されない単一の文字列

これらのフィールドの構成に加えて、サービス アカウント用にトークンを作成する権限を Cloud Pub/Sub に付与する必要があります。Cloud Pub/Sub では、プロジェクト専用のサービス アカウント service-PROJECT_NUMBER@gcp-sa-pubsub.iam.gserviceaccount.com. が作成、維持されます。このサービス アカウントは、サービス アカウント トークン作成者の役割を必要とします。Cloud Console を使用して push 認証用のサブスクリプションを設定する場合は、この役割が自動的に付与されます。その他の場合は、アカウントに対する役割を明示的に付与する必要があります。

段階的にロールアウトされるこの機能が古いプロジェクトに反映されるまでは時間がかかるため、機能のリリース直後はプロジェクトに Cloud Pub/Sub サービス アカウントが存在しない可能性があります。一方、新規に作成したプロジェクトでは、すぐにサービス アカウントを使用できます。この機能を特定のプロジェクトですぐに有効にする必要がある場合は、cloud-pubsub@google.com までお問い合わせください。

コマンドライン

# grant Cloud Pub/Sub the permission to create tokens
PUBSUB_SERVICE_ACCOUNT="service-${PROJECT_NUMBER}@gcp-sa-pubsub.iam.gserviceaccount.com"
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
 --member="serviceAccount:${PUBSUB_SERVICE_ACCOUNT}"\
 --role='roles/iam.serviceAccountTokenCreator'

# configure the subscription push identity
gcloud beta pubsub subscriptions (create|update|modify-push-config) ${SUBSCRIPTION} \
 --topic=${TOPIC} \
 --push-endpoint=${PUSH_ENDPOINT_URI} \
 --push-auth-service-account=${SERVICE_ACCOUNT_EMAIL} \
 --push-auth-token-audience=${OPTIONAL_AUDIENCE_OVERRIDE}

Console

  1. Cloud Pub/Sub の [トピック] ページに移動します。

    [トピック] ページに移動

  2. トピック名をクリックします。

  3. サブスクリプションを作成または更新します。

  4. ID を入力し、任意でオーディエンスを入力します。

push エンドポイントによる認証と承認

クレーム

JWT を使用して、クレーム(email クレームと aud クレームを含む)が Google によって署名されていることを検証できます。認証と承認の両方で Google の OAuth 2.0 API を使用する方法の詳細については、OpenID Connect をご覧ください。

こうしたクレームを有用にするメカニズムが 2 つあります。最初に、Cloud Pub/Sub では、サービス アカウント ID と push サブスクリプションを関連付けるユーザー アカウントまたはサービス アカウントに、プロジェクトまたはサービス アカウント用のサービス アカウント ユーザーの役割を付与する必要があります。

次に、トークンの署名に使用する証明書に対するアクセス権を厳密に管理する必要があります。トークンを作成するには、Cloud Pub/Sub が個別の署名サービス アカウント ID を使用して、Google の内部サービスを呼び出す必要があります。申請されたサービス アカウント、またはアカウントを含むプロジェクト用のトークンを作成するには、署名サービス アカウントを承認する必要があります。それには、iam.serviceAccounts.getOpenIdToken 権限またはサービス アカウント トークン作成者の役割を使用します。

この役割または権限は、どのアカウントにも付与できます。Cloud IAM サービスを使用して、この権限を Cloud Pub/Sub 署名アカウントに付与するように指定することもできます。具体的には、Cloud Pub/Sub では次のようなサービス アカウントを使用します。

service-{project_number}@gcp-sa-pubsub.iam.gserviceaccount.com
  • {project_number}: サブスクリプションを含む GCP プロジェクト。
  • gcp-sa-pubsub: 署名サービス アカウントを含む、Google 所有のプロジェクト。

トークンの検証

次の例は、App Engine アプリケーションに対する push リクエストを認証する方法を示しています。

プロトコル

リクエスト:

GET https://oauth2.googleapis.com/tokeninfo?id_token={BEARER_TOKEN}

レスポンス:

200 OK
{
    "alg": "RS256",
    "aud": "example.com",
    "azp": "104176025330667568672",
    "email": "{SERVICE_ACCOUNT_NAME}@{YOUR_PROJECT_NAME}.iam.gserviceaccount.com",
    "email_verified": "true",
    "exp": "1555463097",
    "iat": "1555459497",
    "iss": "https://accounts.google.com",
    "kid": "3782d3f0bc89008d9d2c01730f765cfb19d3b70e",
    "sub": "104176025330667568672",
    "typ": "JWT"
}

Python

@app.route('/_ah/push-handlers/receive_messages', methods=['POST'])
def receive_messages_handler():
    # Verify that the request originates from the application.
    if (request.args.get('token', '') !=
            current_app.config['PUBSUB_VERIFICATION_TOKEN']):
        return 'Invalid request', 400

    # Verify that the push request originates from Cloud Pub/Sub.
    try:
        # Get the Cloud Pub/Sub-generated JWT in the "Authorization" header.
        bearer_token = request.headers.get('Authorization')
        token = bearer_token.split(' ')[1]
        TOKENS.append(token)

        # Verify and decode the JWT. `verify_oauth2_token` verifies
        # the JWT signature, the `aud` claim, and the `exp` claim.
        claim = id_token.verify_oauth2_token(token, requests.Request(),
                                             audience='example.com')
        # Must also verify the `iss` claim.
        if claim['iss'] not in [
            'accounts.google.com',
            'https://accounts.google.com'
        ]:
            raise ValueError('Wrong issuer.')
        CLAIMS.append(claim)
    except Exception as e:
        return 'Invalid token: {}\n'.format(e), 400

    envelope = json.loads(request.data.decode('utf-8'))
    payload = base64.b64decode(envelope['message']['data'])
    MESSAGES.append(payload)
    # Returning any 2xx status indicates successful receipt of the message.
    return 'OK', 200

署名なし JWT の検証方法の例は、ウェブサイト用の Google ログインガイドで確認できます。OpenID トークンの概要については、OpenID Connect ガイドをご覧ください。

Cloud Run

Cloud Run サービスは HTTP 呼び出しを自動的に認証します。ユーザーについて必要な構成は、呼び出し元アカウントに付与される必須の IAM 役割だけです。たとえば、アカウントの特定の Cloud Run エンドポイントを呼び出す権限の承認や取り消しができます。

ドメイン所有権の検証

Cloud Pub/Sub では、望ましくないトラフィックを防止するために、push エンドポイントの所有権を検証する必要があります。次のものを除き、エンドポイントでは後続の手順を実行する必要があります。

  • Cloud Functions
  • Cloud Run
  • サブスクリプションと同じプロジェクト内にある App Engine スタンダード環境のアプリ

A. ドメインに対する管理者権限があることを確認する

Search Console を使用して、サイト確認プロセスを完了します。サイトの URL の https:// バージョンを必ず登録します。詳細については、サイトの所有権に関するドキュメントをご覧ください。

B. サブスクリプションを含むプロジェクトへのアクセスを許可する

GCP プロジェクトによってドメインまたはエンドポイントの URL へのトラフィックを生成するには、サブスクリプションを含む GCP プロジェクトへのドメイン アクセス権を付与する必要もあります。

  1. [API とサービス] の [認証情報] コンソール ページに移動します。
    [API とサービス] の [認証情報] コンソール ページに移動
  2. 必要に応じてプロジェクトを選択します。
  3. [ドメインの確認] タブを選択します。
  4. [ドメインを追加] を選択します。
  5. ドメインを入力して [ドメインを追加] を選択します。

GCP Console では、Search Console で確認したドメインに照らしてドメインがチェックされます。ドメインが適切に検証されると、ページが更新され、許可されるドメインの新しいリストが表示されるようになります。

これで、これらのドメインを使用して push メッセージを受信できるようになります。これを行うには、Cloud Pub/Sub サブスクリプションを作成するときにエンドポイントとして構成する必要があります。詳しくは、サブスクリプションの構成をご覧ください。

配信の停止と再開

Cloud Pub/Sub で push エンドポイントに対するリクエストの送信を一時的に停止するには、サブスクリプションを pull に変更します。この変更が適用されるまで、数分かかることがあります。

push 配信を再開するには、URL を有効なエンドポイントにもう一度設定します。配信を完全に停止するには、サブスクリプションを削除します。

割り当て、制限、配信比率

push サブスクリプションには割り当てリソース制限があります。さらに push 配信比率は、push エンドポイントの負荷が過大にならない範囲で配信比率が最大になるように自動的に調整されます。これは Slow Start アルゴリズムを使用することで可能になります。

  • 最初は、システムが一度に送信するメッセージは 1 つです。
  • 配信が成功するたびに、システムが同時に送信するメッセージの数は 2 倍になります。
  • システムが同時にメッセージを配信する数は、配信が失敗するか、またはシステムが割り当てまたはリソース制限に達するまで倍増し続けます。
  • 配信が失敗するたびに、エンドポイントへの同時リクエスト数は半減し続け、最終的には一度に 1 つのリクエストという下限に達します。

このアルゴリズムでは、最適なスループットを維持できるだけの十分な数のパブリッシュ済みメッセージがキュー内にあることが前提となります。最終的に push 配信比率は、メッセージが公開されるペースによって制約されます。

このページは役立ちましたか?評価をお願いいたします。

フィードバックを送信...

Cloud Pub/Sub ドキュメント