インスタンスの ID の確認

機密情報を仮想マシン インスタンスに送信する前に、アプリケーションは、Google によって署名されたインスタンス ID トークンを使用して、仮想マシンの ID を確認できます。各インスタンスには、インスタンスの詳細と Google の RS256 署名を含む一意の JSON ウェブトークン(JWT)が存在します。アプリケーションは、署名と Google の公開 OAuth2 証明書を照合して、接続を確立したインスタンスの ID を確認できます。

Compute Engine が署名付きインスタンス トークンを生成するのは、インスタンスがインスタンス メタデータからこのトークンをリクエストした場合のみです。インスタンスは、各自が持っている一意のトークンにのみアクセスでき、他のインスタンスのトークンにはアクセスできません。

インスタンスの ID 確認が必要になるのは、たとえば次のような状況です。

  • インスタンスを初めて起動した際、そのインスタンスに機密情報を送信する前に、接続しているインスタンスの ID の有効性確認がアプリケーションで必要になることがあります。
  • Compute Engine 環境の外部に認証情報を保存するようポリシーで定められていて、それらの認証情報を一時的に使用するために定期的にインスタンスに送信する場合。アプリケーションは、認証情報を送信する必要があるたびにインスタンスの ID を確認できます。

Google のインスタンス認証方法には次の利点があります。

  • Compute Engine は、インスタンスがトークンをリクエストするたびに一意のトークンを作成し、各トークンは 1 時間以内に期限切れになります。インスタンスの ID トークンを 1 回のみ受け入れるようにアプリケーションを構成すると、許可されていないシステムによってトークンが再利用されるリスクが減少します。
  • Google の秘密鍵と公開 OAuth2 証明書は毎日ローテーションされるため、これらの署名に対する潜在的な攻撃範囲が減少します。
  • 署名付きメタデータ トークンは、RFC 7519 のオープン業界標準と OpenID Connect 1.0 の ID レイヤを使用するため、既存のツールとライブラリは ID トークンとシームレスに連携します。

始める前に

インスタンスの ID の確認

アプリケーションが Compute Engine で実行されているインスタンスに機密データを送信する前に、そのインスタンスの ID を確認しなければならない場合があります。典型的な例は、システムが Compute Engine の外部で実行され(このシステムを「Host1」と呼びます)、Compute Engine にインスタンス(「VM1」と呼びます)が存在する場合です。VM1 は次のプロセスに沿って Host1 に接続し、そのインスタンスの ID を確認できます。

  1. VM1 は、HTTPS などの安全な接続プロトコルを使用して Host1 との安全な接続を確立します。

  2. VM1 は、メタデータ サーバーに一意の ID トークンをリクエストし、トークンのオーディエンスを指定します。この例では、オーディエンス値は Host1 の URI です。メタデータ サーバーへのリクエストにオーディエンスの URI を含めて、後でトークンを確認する間に Host1 が値を確認できるようにします。

  3. Google は、JWT 形式で新しい一意のインスタンス ID トークンを生成し、これを VM1 に提供します。トークンのペイロードには、インスタンスに関する詳細が含まれ、オーディエンスの URI も含まれます。トークンの内容の詳細については、トークンの内容をご覧ください。

  4. VM1 は、既存の安全な接続を介して Host1 に ID トークンを送信します。

  5. Host1 は ID トークンをデコードし、トークン ヘッダーとペイロードの値を取得します。

  6. Host1 はオーディエンス値を確認し、証明書の署名と公開 Google 証明書を照合することによって、トークンが Google によって署名されていることを確認します。

  7. トークンが有効な場合、Host1 は送信を開始し、送信が終了すると接続を閉じます。VM1 への後続の接続についても、Host1 と他のシステムはそのたびに新しいトークンをリクエストする必要があります。

インスタンス ID トークンの取得

仮想マシン インスタンスは、ID トークンを提供してほしいというリクエストを受け取ると、通常のインスタンス メタデータの取得プロセスに従ってメタデータ サーバーにトークンをリクエストします。たとえば、次のいずれかの方法を使用できます。

cURL

curl リクエストを作成し、audience パラメータに値を含めます。必要に応じて、format パラメータを含めて、ペイロードにプロジェクトとインスタンスの詳細を含めるかどうかを指定できます。full 形式を使用する場合は、licenses パラメータを含めて、ペイロードにライセンス コードを含めるかどうかを指定できます。

$ curl -H "Metadata-Flavor: Google" \
'http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience=[AUDIENCE]&format=[FORMAT]&licenses=[LICENSES]'

ここで

  • [AUDIENCE] は、インスタンスとインスタンスの ID を確認するシステムとの両方によって合意された一意の URI です。たとえば、2 つのシステムの接続に使われる URL などです。
  • [FORMAT] は、省略可能なパラメータで、プロジェクトとインスタンスの詳細をペイロードに含めるかどうかを指定します。この情報をペイロードに含める場合は full を指定し、含めない場合は standard を指定します。デフォルト値は standard です。トークン形式の詳細については、ID トークン形式をご覧ください。
  • [LICENSES] はオプションのパラメータで、このインスタンスに関連付けられたイメージのライセンス コードがペイロードに含まれるかどうかを指定します。この情報をペイロードに含める場合は TRUE を指定し、含めない場合は FALSE を指定します。デフォルト値は FALSE です。formatfull でない限り、効果はありません。

メタデータ サーバーは、RS256 アルゴリズムを使用して署名された JSON ウェブトークンでこのリクエストに応答します。このトークンは、Google の署名と追加情報をペイロードに含めます。このトークンを他のシステムやアプリケーションに送信すると、インスタンスのトークンを確認し ID を確認してもらうことができます。

Python

Python requests ライブラリのメソッドを使用して、インスタンスからメタデータ サーバーに単純なリクエストを送信できます。次の例では、インスタンス ID トークンをリクエストして出力します。このトークンは、このリクエストを行うインスタンスに固有です。

import requests

AUDIENCE_URL = 'http://www.example.com'
METADATA_HEADERS = {'Metadata-Flavor': 'Google'}
FORMAT = 'full'
LICENSES = 'TRUE'

# Construct a URL with the audience and format.
url = 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience={}&format={}&licenses={}'
url = url.format(AUDIENCE_URL, FORMAT, LICENSES)

# Request a token from the metadata server.
r = requests.get(url, headers=METADATA_HEADERS)

# Extract the token from the response.
token = r.text

print(token)

メタデータ サーバーは、RS256 アルゴリズムを使用して署名された JSON ウェブトークンでこのリクエストに応答します。このトークンは、Google の署名と追加情報をペイロードに含めます。このトークンを他のシステムやアプリケーションに送信すると、インスタンスのトークンを確認し ID を確認してもらうことができます。

トークンの確認

アプリケーションが Compute Engine インスタンスからインスタンス ID トークンを受信したら、次のプロセスを使用してトークンを確認できます。

  1. 仮想マシン インスタンスからトークンを受信し、RS256 JWT デコーダーを使用してトークンをデコードし、ヘッダーの内容を読み取って kid 値を取得します。

  2. トークンと公開 Google 証明書を照合して、トークンが署名されていることを確認します。各公開証明書には、トークン ヘッダーの kid 値に対応する kid 値が含まれています。

  3. トークンが有効な場合は、ペイロードの内容と期待値を比較します。トークン ペイロードにインスタンスとプロジェクトに関する詳細が含まれている場合、アプリケーションは instance_idproject_idzone の値を確認できます。これらの値はグローバルに一意のタプルで、アプリケーションが目的のプロジェクトの適切なインスタンスと通信していることを立証するものです。

トークンのデコードと確認には任意のツールを使用できますが、一般に使用言語のライブラリが使用されます。たとえば、Python 用の Google OAuth 2.0 ライブラリに含まれている verify_token メソッドを使用できます。verify_token メソッドは、kid 値を適切な証明書と照合し、署名を確認し、オーディエンス クレームを確認し、トークンからペイロードの内容を返します。

# Import libraries for token verification
import google.auth.transport.requests
from google.oauth2 import id_token

# Receive token from VM over an SSL connection
token = …
…

# Verify token signature and store the token payload
request = google.auth.transport.requests.Request()
payload = id_token.verify_token(token, request=request, audience=audience)
…

アプリケーションがトークンとその内容を確認したら、安全な接続を介してそのインスタンスと通信し、通信が終了したら接続を閉じることができます。後続の接続ごとにインスタンスに新しいトークンを再度リクエストし、インスタンスの ID を再確認します。

トークンの内容

インスタンス ID トークンは、3 つの主要部分で構成されます。

ヘッダーには、公開 OAuth2 証明書を識別するための kid 値が含まれています。署名の確認には、この値で識別された証明書を使用する必要があります。ヘッダーにはさらに、署名が RS256 アルゴリズムを使用して生成されたことを確認するための alg 値も含まれています。

{
  "alg": "RS256",
  "kid": "511a3e85d2452aee960ed557e2666a8c5cedd8ae",
}

ペイロード

ペイロードには、aud オーディエンス クレームが含まれています。インスタンスがトークンをリクエストしたときに format=full を指定した場合、ペイロードには、仮想マシン インスタンスとそのプロジェクトに関するクレームも含まれます。完全なフォーマットのトークンを要求するときに、licenses=TRUE と指定すると、インスタンスに関連付けられているライセンスに関するクレームも含まれます。

{
   "iss": "[TOKEN_ISSUER]",
   "iat": [ISSUED_TIME],
   "exp": [EXPIRED_TIME],
   "aud": "[AUDIENCE]",
   "sub": "[SUBJECT]",
   "azp": "[AUTHORIZED_PARTY]",
   "google": {
    "compute_engine": {
      "project_id": "[PROJECT_ID]",
      "project_number": [PROJECT_NUMBER],
      "zone": "[ZONE]",
      "instance_id": [INSTANCE_ID],
      "instance_name": "[INSTANCE_NAME]",
      "instance_creation_timestamp": [CREATION_TIMESTAMP],
      "license_id": [
        "[LICENSE_1]",
          ...
        "[LICENSE_N]"
      ]
    }
  }
}

ここで

  • [TOKEN_ISSUER] はトークンの発行者を識別する URL です。Compute Engine の場合、この値は https://accounts.google.com です。
  • [ISSUED_TIME] はトークンが発行された時刻を示す UNIX タイムスタンプです。この値は、インスタンスがメタデータ サーバーにトークンをリクエストするたびに更新されます。
  • [EXPIRED_TIME] はトークンの有効期限が切れる時刻を示す UNIX タイムスタンプです。
  • [AUDIENCE] は、インスタンスとインスタンスの ID を確認するシステムとの両方によって合意された一意の URI です。たとえば、2 つのシステムの接続に使われる URL などです。
  • [SUBJECT] はトークンの主体者で、インスタンスに関連付けられたサービス アカウントの一意の ID です。
  • [AUTHORIZED_PARTY] は ID トークン(インスタンスに関連付けられたサービス アカウントの一意の ID)の被発行者です。
  • [PROJECT_ID] はインスタンスが作成されたプロジェクトの ID です。
  • [PROJECT_NUMBER] はインスタンスが作成されたプロジェクトの一意の番号です。
  • [ZONE] はインスタンスが配置されているゾーンです。
  • [INSTANCE_ID] はこのトークンが属するインスタンスの一意の ID です。この ID は一意であり、再利用されることはありません。
  • [INSTANCE_NAME] はこのトークンが属するインスタンスの名前です。この名前は、長い期間複数のインスタンスで再利用される可能性があるため、instance_id 値を使用して一意のインスタンス ID を識別します。
  • [CREATION_TIMESTAMP] はインスタンスの作成時刻を示す UNIX タイムスタンプです。
  • [LICENSE_1] から [LICENSE_N] は、このインスタンスに関連付けられているイメージのライセンス コードです。

ペイロードは次の例のようになります。

{
  "iss": "https://accounts.google.com",
  "iat": 1496953245,
  "exp": 1496956845,
  "aud": "https://www.example.com",
  "sub": "107517467455664443765",
  "azp": "107517467455664443765",
  "google": {
    "compute_engine": {
      "project_id": "my-project",
      "project_number": 739419398126,
      "zone": "us-west1-a",
      "instance_id": "152986662232938449",
      "instance_name": "example",
      "instance_creation_timestamp": 1496952205,
      "license_id": [
        "1000204"
      ]
    }
  }
}

署名

Google は、ヘッダーとペイロードを base64url でエンコードし、これら 2 つの値を連結して署名を生成します。この値を公開 OAuth2 証明書と照合して、トークンを確認できます。

次のステップ

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

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

Compute Engine ドキュメント