서비스 간 인증

서비스 간 인증은 Cloud Run 서비스와 같은 한 서비스가 Cloud Run(완전 관리형) 서비스를 호출할 수 있는 기능입니다.

아키텍처에 여러 서비스가 사용되는 경우 이러한 서비스가 서로 통신할 수 있어야 합니다.

동기 또는 비동기 서비스 간 통신을 사용할 수 있습니다.

비동기 통신의 경우 다음을 사용합니다.

동기 통신의 경우에는 한 서비스가 엔드 포인트 URL을 사용하여 HTTP로 다른 서비스를 호출합니다. 이 사용 사례에서는 각 서비스가 특정 서비스에 대해서만 요청을 수행할 수 있도록 하는 것이 좋습니다. 예를 들어 login 서비스가 있으면 user-profiles 서비스에 액세스할 수 있지만 search 서비스에는 액세스할 수 없습니다.

먼저 수신 서비스가 호출 서비스의 요청을 수락하도록 구성해야 합니다.

  1. 수신 서비스의 호출 서비스 ID에 Cloud Run 호출자(roles/run.invoker) 역할을 부여합니다. 기본적으로 이 ID는 PROJECT_NUMBER-compute@developer.gserviceaccount.com입니다.

Console UI

  1. Google Cloud Console로 이동합니다.

    Google Cloud Console로 이동

  2. 수신 서비스를 선택합니다.

  3. 오른쪽 상단 모서리에 있는 정보 패널 표시를 클릭하여 권한 탭을 표시합니다.

  4. 구성원 추가 필드에 호출 서비스의 ID를 입력합니다.

  5. 역할 선택 드롭다운 메뉴에서 Cloud Run Invoker 역할을 선택합니다.

  6. 추가를 클릭합니다.

gcloud

gcloud run services add-iam-policy-binding 명령어를 사용합니다.

gcloud run services add-iam-policy-binding RECEIVING_SERVICE \
  --member='serviceAccount:CALLING_SERVICE_IDENTITY' \
  --role='roles/run.invoker'

여기서 RECEIVING_SERVICE는 수신 서비스의 이름이고 CALLING_SERVICE_IDENTITY는 서비스 계정의 이메일 주소입니다.

호출 서비스에서 다음을 수행해야 합니다.

  1. 대상(aud)이 수신 서비스의 URL로 설정된 Google이 서명한 OAuth ID 토큰을 만듭니다. 이 값은 스키마 프리픽스(http:// 또는 https://)를 포함해야 하며 커스텀 도메인은 현재까지 aud 값으로 지원되지 않습니다.

  2. ID 토큰을 서비스에 대한 요청의 Authorization: Bearer ID_TOKEN 헤더에 포함합니다.

Nodejs
// Make sure to `npm install --save request-promise` or add the dependency to your package.json
const request = require('request-promise');

const receivingServiceURL = ...

// Set up metadata server request
// See https://cloud.google.com/compute/docs/instances/verifying-instance-identity#request_signature
const metadataServerTokenURL = 'http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience=';
const tokenRequestOptions = {
    uri: metadataServerTokenURL + receivingServiceURL,
    headers: {
        'Metadata-Flavor': 'Google'
    }
};

// Fetch the token, then provide the token in the request to the receiving service
request(tokenRequestOptions)
  .then((token) => {
    return request(receivingServiceURL).auth(null, null, true, token)
  })
  .then((response) => {
    res.status(200).send(response);
  })
  .catch((error) => {
    res.status(400).send(error);
  });
    
Python
# Requests is already installed, no need to add it to requirements.txt
import requests

receiving_service_url = ...

# Set up metadata server request
# See https://cloud.google.com/compute/docs/instances/verifying-instance-identity#request_signature
metadata_server_token_url = 'http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience='

token_request_url = metadata_server_token_url + receiving_service_url
token_request_headers = {'Metadata-Flavor': 'Google'}

# Fetch the token
token_response = requests.get(token_request_url, headers=token_request_headers)
jwt = token_response.content.decode("utf-8")

# Provide the token in the request to the receiving service
receiving_service_headers = {'Authorization': f'bearer {jwt}'}
service_response = requests.get(receiving_service_url, headers=receiving_service_headers)

return service_response.content
    
Go
import (
	"fmt"
	"net/http"

	"cloud.google.com/go/compute/metadata"
)

// makeGetRequest makes a GET request to the specified Cloud Run endpoint in
// serviceURL (must be a complete URL) by authenticating with the ID token
// obtained from the Metadata API.
func makeGetRequest(serviceURL string) (*http.Response, error) {
	// query the id_token with ?audience as the serviceURL
	tokenURL := fmt.Sprintf("/instance/service-accounts/default/identity?audience=%s", serviceURL)
	idToken, err := metadata.Get(tokenURL)
	if err != nil {
		return nil, fmt.Errorf("metadata.Get: failed to query id_token: %+v", err)
	}
	req, err := http.NewRequest("GET", serviceURL, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", idToken))
	return http.DefaultClient.Do(req)
}
자바
이 샘플은 Google 인증 라이브러리를 사용하여 로컬 개발 또는 원격 Cloud Run 환경에서 애플리케이션 기본 사용자 인증 정보를 검색합니다. 이러한 사용자 인증 정보는 HTTP 요청에 추가할 ID 토큰을 생성하는 데 사용됩니다.
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.auth.oauth2.IdTokenCredentials;
import com.google.auth.oauth2.IdTokenProvider;
import java.io.IOException;

public class Authentication {

  // makeGetRequest makes a GET request to the specified Cloud Run endpoint,
  // serviceUrl (must be a complete URL), by authenticating with an Id token
  // retrieved from Application Default Credentials.
  public static HttpResponse makeGetRequest(String serviceUrl) throws IOException {
    GoogleCredentials credentials = GoogleCredentials.getApplicationDefault();
    if (!(credentials instanceof IdTokenProvider)) {
      throw new IllegalArgumentException("Credentials are not an instance of IdTokenProvider.");
    }
    IdTokenCredentials tokenCredential =
        IdTokenCredentials.newBuilder()
            .setIdTokenProvider((IdTokenProvider) credentials)
            .setTargetAudience(serviceUrl)
            .build();

    GenericUrl genericUrl = new GenericUrl(serviceUrl);
    HttpCredentialsAdapter adapter = new HttpCredentialsAdapter(tokenCredential);
    HttpTransport transport = new NetHttpTransport();
    HttpRequest request = transport.createRequestFactory(adapter).buildGetRequest(genericUrl);
    return request.execute();
  }
}

이 서비스 간 인증 기술을 사용하는 애플리케이션에 대한 엔드 투 엔드 연습을 위해서는 Cloud Run 서비스 보안 가이드를 참조하세요.

GCP 외부에서 호출

컴퓨팅 메타데이터에 액세스할 수 없는 컴퓨팅 인스턴스(예: 자체 서버)에서 서비스를 호출하는 경우 적절한 토큰을 수동으로 생성해야 합니다.

  1. target_audience 클레임이 수신 서비스의 URL로 설정된 서비스 계정 JWT에 자체 서명합니다.

  2. Google이 서명한 ID 토큰과 자체 서명한 JWT를 교환합니다. 이 토큰에는 위 URL로 설정된 aud 클레임이 있습니다.

  3. ID 토큰을 서비스에 대한 요청의 Authorization: Bearer ID_TOKEN 헤더에 포함합니다.

Cloud Run(완전 관리형)에 아직 IAP(Identity-Aware Proxy)가 지원되지는 않지만 위 단계의 코드 예시에 IAP(Identity-Aware Proxy) 샘플 코드를 검사할 수 있습니다.