인스턴스 ID 확인

애플리케이션은 민감한 정보를 가상 머신 인스턴스에 전송하기 전에 Google에서 서명한 인스턴스 ID 토큰을 사용하여 가상 머신의 ID를 인증할 수 있습니다. 각 인스턴스에는 인스턴스에 대한 세부정보와 Google의 RS256 서명이 포함된 고유한 JSON 웹 토큰(JWT)이 있습니다. 애플리케이션은 Google의 공개 Oauth2 인증서와 대조해 서명을 확인하여 연결을 설정한 인스턴스의 ID를 확인합니다.

인스턴스가 인스턴스 메타데이터에 요청하는 경우에만 Compute Engine은 서명된 인스턴스 토큰을 생성합니다. 인스턴스는 자체의 고유 토큰에만 액세스할 수 있으며 다른 인스턴스의 토큰에는 액세스할 수 없습니다.

다음과 같은 시나리오에서 인스턴스의 ID를 확인해야 할 수 있습니다.

  • 인스턴스를 처음 시작할 때 애플리케이션은 민감한 정보를 인스턴스에 전송하기 전에 애플리케이션에 연결된 인스턴스에 유효한 ID가 있는지 확인해야 할 수 있습니다.
  • 정책에 의거하여 Compute Engine 환경 외부에 사용자 인증 정보를 저장하고 정기적으로 해당 사용자 인증 정보를 임시 사용 용도로 인스턴스에 전송하는 경우 애플리케이션은 사용자 인증 정보를 전송해야 할 때마다 인스턴스의 ID를 확인할 수 있습니다.

Google의 인스턴스 인증 방법은 다음과 같은 이점이 있습니다.

  • Compute Engine은 인스턴스에서 요청할 때마다 고유한 토큰을 만들며, 각 토큰은 1시간 이내에 만료됩니다. 인스턴스의 ID 토큰을 한 번만 허용하도록 애플리케이션을 구성할 수 있으며, 이렇게 하면 승인받지 않은 시스템이 토큰을 재사용할 위험이 줄어듭니다.
  • Google의 비공개 키와 공개 Oauth2 인증서는 서명 공격에 노출될 수 있는 영역을 줄이기 위해 매일 순환됩니다.
  • 서명된 메타데이터 토큰은 개방형 업계 표준인 RFC 7519와 ID 레이어인 OpenID Connect 1.0을 사용하므로, 기존의 도구와 라이브러리가 ID 토큰과 원활하게 작동합니다.

시작하기 전에

인스턴스 ID 확인

일부 시나리오에서 애플리케이션은 Compute Engine에서 실행 중인 인스턴스에 민감한 정보를 전송하기 전에 해당 인스턴스의 ID를 확인해야 합니다. 한 가지 일반적인 예로, Compute Engine 외부에서 실행 중인 시스템('Host1') 하나와 Compute Engine 인스턴스('VM1') 하나가 있는 경우 다음 프로세스에 따라 VM1을 Host1에 연결하고 해당 인스턴스의 ID를 검증할 수 있습니다.

  1. 원하는 보안 연결 프로토콜(예: HTTPS)을 사용하여 VM1에서 Host1으로 보안 연결을 설정합니다.

  2. VM1이 메타데이터 서버에 고유한 ID 토큰을 요청하고 토큰의 대상을 지정합니다. 이 예시에서 대상 값은 Host1의 URI입니다. 메타데이터 서버 요청에는 이후 Host1이 토큰 검증 단계 도중에 값을 확인할 수 있도록 대상 URI가 포함됩니다.

  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입니다. 예를 들어 이 두 시스템 사이의 연결에 사용되는 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_id, project_id, zone 값을 확인할 수 있습니다. 이러한 값은 애플리케이션이 원하는 프로젝트 내의 적절한 인스턴스와 통신하는지 확인하는, 전역적으로 고유한 튜플입니다.

토큰을 디코딩하고 검증하는 데 임의의 도구를 사용할 수 있지만, 일반적인 방법은 원하는 언어의 라이브러리를 사용하는 것입니다. 예를 들어 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입니다. 예를 들어 이 두 시스템 사이의 연결에 사용되는 URL이 대상일 수 있습니다.
  • [SUBJECT]는 토큰의 제목으로, 인스턴스와 연결한 서비스 계정의 고유 ID입니다.
  • [AUTHORIZED_PARTY]는 ID 토큰을 발급받는 주체로, 인스턴스와 연결한 서비스 계정의 고유 ID입니다.
  • [PROJECT_ID]는 만든 인스턴스가 속한 프로젝트의 ID입니다.
  • [PROJECT_NUMBER]는 만든 인스턴스가 속한 프로젝트의 고유 번호입니다.
  • [ZONE]은 인스턴스가 있는 영역입니다.
  • [INSTANCE_ID]는 이 토큰이 속한 인스턴스의 고유 ID입니다. 이 ID는 고유하며 재사용되지 않습니다.
  • [INSTANCE_NAME]은 이 토큰이 속한 인스턴스의 이름입니다. 이 이름은 시간이 지남에 따라 다수의 인스턴스에 재사용할 수 있으므로, 고유한 인스턴스 ID를 식별하려면 instance_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로 인코딩하고 이 두 값을 연결하여 서명을 생성합니다. 이 값을 공개 Oauth2 인증서와 대조하여 토큰을 검증할 수 있습니다.

다음 단계

이 페이지가 도움이 되었나요? 평가를 부탁드립니다.

다음에 대한 의견 보내기...

Compute Engine 문서