서비스 계정으로 인증

이 주제에서는 서비스 계정으로 애플리케이션을 인증하는 방법을 설명합니다. 일반적인 인증 시나리오 및 전략을 포함하여 Google Cloud API 인증에 대한 일반적인 정보는 인증 개요를 참조하세요. 서비스 계정에 대한 자세한 내용은 ID 및 액세스 관리 문서의 서비스 계정을 참조하세요.

사용자 인증 정보 자동으로 찾기

애플리케이션이 기본 서비스 계정이 있는 Google Cloud 환경 내에서 실행되는 경우 애플리케이션은 서비스 계정 사용자 인증 정보를 검색하여 Google Cloud API를 호출할 수 있습니다. 이러한 환경에는 Compute Engine, Google Kubernetes Engine, App Engine, Cloud Run, Cloud Functions가 포함됩니다. 이 전략은 사용자 인증 정보를 수동으로 전달하는 것보다 편리하고 안전하기 때문에 이 전략을 사용하는 것이 좋습니다.

또한 애플리케이션에 Google Cloud 클라이언트 라이브러리를 사용하는 것이 좋습니다. Google Cloud 클라이언트 라이브러리는 애플리케이션 기본 사용자 인증 정보(ADC)라는 라이브러리를 사용하여 서비스 계정 사용자 인증 정보를 자동으로 찾습니다. ADC는 다음과 같은 순서로 서비스 계정 사용자 인증 정보를 찾습니다.

  1. GOOGLE_APPLICATION_CREDENTIALS 환경 변수가 설정된 경우 ADC는 변수가 가리키는 서비스 계정 파일을 사용합니다.

  2. 환경 변수 GOOGLE_APPLICATION_CREDENTIALS가 설정되지 않은 경우 ADC는 Compute Engine, Google Kubernetes Engine, App Engine, Cloud Run, Cloud Functions에서 제공하는 기본 서비스 계정을 사용합니다.

  3. ADC에서 위 사용자 인증 정보 중 하나를 사용할 수 없으면 오류가 발생합니다.

다음 코드 예시는 애플리케이션 코드에서 ADC 라이브러리를 사용하는 방법을 보여줍니다.

C#

public object AuthImplicit(string projectId)
{
    // If you don't specify credentials when constructing the client, the
    // client library will look for credentials in the environment.
    var credential = GoogleCredential.GetApplicationDefault();
    var storage = StorageClient.Create(credential);
    // Make an authenticated API request.
    var buckets = storage.ListBuckets(projectId);
    foreach (var bucket in buckets)
    {
        Console.WriteLine(bucket.Name);
    }
    return null;
}

Go


// implicit uses Application Default Credentials to authenticate.
func implicit() {
	ctx := context.Background()

	// For API packages whose import path is starting with "cloud.google.com/go",
	// such as cloud.google.com/go/storage in this case, if there are no credentials
	// provided, the client library will look for credentials in the environment.
	storageClient, err := storage.NewClient(ctx)
	if err != nil {
		log.Fatal(err)
	}

	it := storageClient.Buckets(ctx, "project-id")
	for {
		bucketAttrs, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(bucketAttrs.Name)
	}

	// For packages whose import path is starting with "google.golang.org/api",
	// such as google.golang.org/api/cloudkms/v1, use NewService to create the client.
	kmsService, err := cloudkms.NewService(ctx)
	if err != nil {
		log.Fatal(err)
	}

	_ = kmsService
}

자바

static void authImplicit() {
  // If you don't specify credentials when constructing the client, the client library will
  // look for credentials via the environment variable GOOGLE_APPLICATION_CREDENTIALS.
  Storage storage = StorageOptions.getDefaultInstance().getService();

  System.out.println("Buckets:");
  Page<Bucket> buckets = storage.list();
  for (Bucket bucket : buckets.iterateAll()) {
    System.out.println(bucket.toString());
  }
}

Node.js

// Imports the Google Cloud client library.
const {Storage} = require('@google-cloud/storage');

// Instantiates a client. If you don't specify credentials when constructing
// the client, the client library will look for credentials in the
// environment.
const storage = new Storage();
// Makes an authenticated API request.
async function listBuckets() {
  try {
    const results = await storage.getBuckets();

    const [buckets] = results;

    console.log('Buckets:');
    buckets.forEach((bucket) => {
      console.log(bucket.name);
    });
  } catch (err) {
    console.error('ERROR:', err);
  }
}
listBuckets();

PHP

// Imports the Cloud Storage client library.
use Google\Cloud\Storage\StorageClient;

function auth_cloud_implicit($projectId)
{
    $config = [
        'projectId' => $projectId,
    ];

    # If you don't specify credentials when constructing the client, the
    # client library will look for credentials in the environment.
    $storage = new StorageClient($config);

    # Make an authenticated API request (listing storage buckets)
    foreach ($storage->buckets() as $bucket) {
        printf('Bucket: %s' . PHP_EOL, $bucket->name());
    }
}

Python

def implicit():
    from google.cloud import storage

    # If you don't specify credentials when constructing the client, the
    # client library will look for credentials in the environment.
    storage_client = storage.Client()

    # Make an authenticated API request
    buckets = list(storage_client.list_buckets())
    print(buckets)

Ruby

# project_id = "Your Google Cloud project ID"

require "google/cloud/storage"

# If you don't specify credentials when constructing the client, the client
# library will look for credentials in the environment.
storage = Google::Cloud::Storage.new project: project_id

# Make an authenticated API request
storage.buckets.each do |bucket|
  puts bucket.name
end

수동으로 사용자 인증 정보 전달

애플리케이션이 기본 서비스 계정을 제공하는 Google Cloud 환경 외부에서 실행되는 경우 서비스 계정을 수동으로 만들어야 합니다. 그런 다음 서비스 계정과 연결된 사용자 인증 정보인 서비스 계정 키를 하나 이상 만들 수 있습니다. 그러면 서비스 계정 키가 애플리케이션에 수동으로 전달될 수 있습니다.

서비스 계정 만들기

다음 단계에서는 서비스 계정이 없는 경우 이를 만드는 방법을 설명합니다.

Cloud Console

  1. Cloud Console에서 서비스 계정 키 만들기 페이지로 이동합니다.

    서비스 계정 키 만들기 페이지로 이동
  2. 서비스 계정 목록에서 새 서비스 계정을 선택합니다.
  3. 서비스 계정 이름 필드에 이름을 입력합니다.
  4. 역할 목록에서 프로젝트 > 소유자를 선택합니다.

    참고: 역할 필드가 리소스에 액세스할 수 있도록 서비스 계정을 승인합니다. 나중에 Cloud Console을 사용하여 이 필드를 보고 변경할 수 있습니다. 프로덕션 애플리케이션을 개발하는 경우 프로젝트 > 소유자보다 세부적인 권한을 지정합니다. 자세한 내용은 서비스 계정에 역할 부여를 참조하세요.
  5. 만들기를 클릭합니다. 키가 포함된 JSON 파일이 컴퓨터에 다운로드됩니다.

명령줄

로컬 머신 또는 Cloud Shell에서 Cloud SDK를 사용하여 다음 명령어를 실행할 수 있습니다.

  1. 서비스 계정을 만듭니다. [NAME]을 서비스 계정 이름으로 바꿉니다.

    gcloud iam service-accounts create [NAME]
  2. 서비스 계정에 권한을 부여합니다. [PROJECT_ID]를 프로젝트 ID로 바꿉니다.

    gcloud projects add-iam-policy-binding [PROJECT_ID] --member "serviceAccount:[NAME]@[PROJECT_ID].iam.gserviceaccount.com" --role "roles/owner"
    참고: 역할 필드가 리소스에 액세스할 수 있도록 서비스 계정을 승인합니다. 이 필드는 나중에 Cloud Console을 사용하여 보고 변경할 수 있습니다. 프로덕션 애플리케이션을 개발하는 경우 프로젝트 > 소유자보다 세부적인 권한을 지정합니다. 자세한 내용은 서비스 계정에 역할 부여를 참조하세요.
  3. 키 파일을 생성합니다. [FILE_NAME]을 키 파일 이름으로 바꿉니다.

    gcloud iam service-accounts keys create [FILE_NAME].json --iam-account [NAME]@[PROJECT_ID].iam.gserviceaccount.com

환경 변수를 통해 사용자 인증 정보 전달

GOOGLE_APPLICATION_CREDENTIALS 환경 변수를 설정하여 애플리케이션 코드에 사용자 인증 정보를 제공합니다. [PATH]를 서비스 계정 키가 포함된 JSON 파일의 파일 경로로 바꿉니다. 이 변수는 현재 셸 세션에만 적용되므로 새 세션을 연 경우 변수를 다시 설정합니다.

Linux 또는 macOS

export GOOGLE_APPLICATION_CREDENTIALS="[PATH]"

예를 들면 다음과 같습니다.

export GOOGLE_APPLICATION_CREDENTIALS="/home/user/Downloads/my-key.json"

Windows

PowerShell:

$env:GOOGLE_APPLICATION_CREDENTIALS="[PATH]"

예를 들면 다음과 같습니다.

$env:GOOGLE_APPLICATION_CREDENTIALS="C:\Users\username\Downloads\my-key.json"

명령어 프롬프트:

set GOOGLE_APPLICATION_CREDENTIALS=[PATH]

위의 단계를 완료하면 위 섹션에 설명된 대로 ADC가 사용자 인증 정보를 자동으로 찾을 수 있습니다. ADC를 사용하면 더 적은 코드를 요구하고 다양한 환경에서 코드를 이동할 수 있으므로 이를 사용하는 것이 좋습니다.

코드를 사용하여 사용자 인증 정보 전달

또는 다음 예시와 같이 코드에서 서비스 계정 파일을 명시적으로 가리킬 수 있습니다. 다음 예시를 실행하려면 Cloud Storage 클라이언트 라이브러리를 설치해야 합니다.

C#

        // Some APIs, like Storage, accept a credential in their Create()
        // method.
        public object AuthExplicit(string projectId, string jsonPath)
        {
            // Explicitly use service account credentials by specifying
            // the private key file.
            var credential = GoogleCredential.FromFile(jsonPath);
            var storage = StorageClient.Create(credential);
            // Make an authenticated API request.
            var buckets = storage.ListBuckets(projectId);
            foreach (var bucket in buckets)
            {
                Console.WriteLine(bucket.Name);
            }
            return null;
        }
        // Other APIs, like Language, accept a channel in their Create()
        // method.
        public object AuthExplicit(string projectId, string jsonPath)
        {
            LanguageServiceClientBuilder builder = new LanguageServiceClientBuilder
            {
                CredentialsPath = jsonPath
            };

            LanguageServiceClient client = builder.Build();
            AnalyzeSentiment(client);
            return 0;
        }

Go


// explicit reads credentials from the specified path.
func explicit(jsonPath, projectID string) {
	ctx := context.Background()
	client, err := storage.NewClient(ctx, option.WithCredentialsFile(jsonPath))
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Buckets:")
	it := client.Buckets(ctx, projectID)
	for {
		battrs, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(battrs.Name)
	}
}

자바

static void authExplicit(String jsonPath) throws IOException {
  // You can specify a credential file by providing a path to GoogleCredentials.
  // Otherwise credentials are read from the GOOGLE_APPLICATION_CREDENTIALS environment variable.
  GoogleCredentials credentials = GoogleCredentials.fromStream(new FileInputStream(jsonPath))
        .createScoped(Lists.newArrayList("https://www.googleapis.com/auth/cloud-platform"));
  Storage storage = StorageOptions.newBuilder().setCredentials(credentials).build().getService();

  System.out.println("Buckets:");
  Page<Bucket> buckets = storage.list();
  for (Bucket bucket : buckets.iterateAll()) {
    System.out.println(bucket.toString());
  }
}

Node.js

// Imports the Google Cloud client library.
const {Storage} = require('@google-cloud/storage');

// Instantiates a client. Explicitly use service account credentials by
// specifying the private key file. All clients in google-cloud-node have this
// helper, see https://github.com/GoogleCloudPlatform/google-cloud-node/blob/master/docs/authentication.md
// const projectId = 'project-id'
// const keyFilename = '/path/to/keyfile.json'
const storage = new Storage({projectId, keyFilename});

// Makes an authenticated API request.
async function listBuckets() {
  try {
    const [buckets] = await storage.getBuckets();

    console.log('Buckets:');
    buckets.forEach((bucket) => {
      console.log(bucket.name);
    });
  } catch (err) {
    console.error('ERROR:', err);
  }
}
listBuckets();

PHP

namespace Google\Cloud\Samples\Auth;

// Imports the Cloud Storage client library.
use Google\Cloud\Storage\StorageClient;

function auth_cloud_explicit($projectId, $serviceAccountPath)
{
    # Explicitly use service account credentials by specifying the private key
    # file.
    $config = [
        'keyFilePath' => $serviceAccountPath,
        'projectId' => $projectId,
    ];
    $storage = new StorageClient($config);

    # Make an authenticated API request (listing storage buckets)
    foreach ($storage->buckets() as $bucket) {
        printf('Bucket: %s' . PHP_EOL, $bucket->name());
    }
}

Python

def explicit():
    from google.cloud import storage

    # Explicitly use service account credentials by specifying the private key
    # file.
    storage_client = storage.Client.from_service_account_json(
        'service_account.json')

    # Make an authenticated API request
    buckets = list(storage_client.list_buckets())
    print(buckets)

Ruby

# project_id = "Your Google Cloud project ID"
# key_file   = "path/to/service-account.json"
require "google/cloud/storage"

# Explicitly use service account credentials by specifying the private key
# file.
storage = Google::Cloud::Storage.new project: project_id, keyfile: key_file

# Make an authenticated API request
storage.buckets.each do |bucket|
  puts bucket.name
end

사용자 인증 정보 관리 권장사항

사용자 인증 정보는 민감한 정보에 액세스할 수 있는 권한을 제공합니다. 다음 방법은 사용자 인증 정보에 대한 액세스를 보호하는 데 유용합니다.

  • API 키, OAuth 토큰, 서비스 계정 키와 같은 인증과 관련된 보안 비밀을 소스 코드에 삽입하지 마세요. Cloud Key Management Service와 같은 애플리케이션 소스 코드 외부의 사용자 인증 정보를 가리키는 환경 변수를 사용할 수 있습니다.

  • 테스트 환경 및 프로덕션 환경과 같이 각기 다른 컨텍스트에서 서로 다른 사용자 인증 정보를 만들고 사용하세요.

  • 제3자가 사용자 인증 정보를 가로채지 못하도록 HTTPS와 같은 보안 채널을 통해서만 사용자 인증 정보를 전송하세요. 일반 텍스트로 또는 URL의 일부로 전송하지 마세요.

  • 장시간 사용된 사용자 인증 정보를 클라이언트 측 애플리케이션에 삽입하지 마세요. 예를 들어 모바일 앱에 서비스 계정 키를 삽입하지 마세요. 제3자가 클라이언트 측 앱을 검사할 수 있으며 이 경우 손쉽게 사용자 인증 정보를 찾아 사용할 수 있습니다.

  • 더 이상 필요 없는 토큰을 취소합니다.

API 오류 문제 해결

Cloud APIs 오류에서 실패한 API 요청 문제를 해결하는 방법을 알아보세요.

다음 단계