Batch와 함께 Secret Manager를 사용하여 민감한 정보 보호

이 문서에서는 Secret Manager 보안 비밀을 사용하여 Batch 작업에 지정하려는 민감한 정보를 보호하는 방법을 설명합니다.

Secret Manager 보안 비밀은 암호화를 통해 민감한 정보를 보호합니다. Batch 작업에서는 기존 보안 비밀을 하나 이상 지정하여 포함된 민감한 정보를 안전하게 전달할 수 있으며 이를 통해 다음을 수행할 수 있습니다.

  • 민감한 정보가 포함된 커스텀 환경 변수를 안전하게 정의합니다.

  • 작업의 실행 가능 항목이 비공개 컨테이너 이미지에 액세스할 수 있도록 Docker 레지스트리의 로그인 사용자 인증 정보를 안전하게 지정합니다.

시작하기 전에

  1. Batch를 사용한 적이 없으면 Batch 시작하기를 검토하고 프로젝트 및 사용자 기본 요건을 완료하여 Batch를 사용 설정하세요.
  2. 보안 비밀을 만들거나 작업에 안전하게 지정할 민감한 정보의 보안 비밀을 식별합니다.
  3. 작업을 만드는 데 필요한 권한을 얻으려면 관리자에게 다음 IAM 역할을 부여해 달라고 요청하세요.

    역할 부여에 대한 자세한 내용은 프로젝트, 폴더, 조직에 대한 액세스 관리를 참조하세요.

    커스텀 역할이나 다른 사전 정의된 역할을 통해 필요한 권한을 얻을 수도 있습니다.

  4. 작업의 서비스 계정에 보안 비밀에 액세스하는 데 필요한 권한이 있는지 확인하려면 관리자에게 작업의 서비스 계정에 보안 비밀에 대한 Secret Manager 보안 비밀 접근자(roles/secretmanager.secretAccessor) IAM 역할을 부여해 달라고 요청하세요.

민감한 정보를 커스텀 환경 변수에 안전하게 전달

Secret Manager 보안 비밀에서 커스텀 환경 변수로 민감한 정보를 안전하게 전달하려면 환경의 보안 비밀 변수(secretVariables) 하위 필드에 각 환경 변수를 정의하고 각 값에 대해 보안 비밀을 지정해야 합니다. 작업에서 보안 비밀을 지정할 때마다 형식을 보안 비밀 버전의 경로(projects/PROJECT_ID/secrets/SECRET_ID/versions/VERSION)로 지정해야 합니다.

gcloud CLI, Batch API, Java, Node.js 또는 Python을 사용하여 보안 비밀 변수를 정의하는 작업을 만들 수 있습니다. 다음 예시에서는 모든 실행 가능 항목 환경(taskSpecenvironment 하위 필드)의 보안 비밀 변수를 정의하고 사용하는 작업을 만드는 방법을 설명합니다.

gcloud

  1. 작업 구성 세부정보를 지정하고 환경 하나 이상에 secretVariables 하위 필드를 포함하는 JSON 파일을 만듭니다.

    예를 들어 모든 실행 가능 항목 환경의 보안 비밀 변수를 사용하는 기본 스크립트 작업을 만들려면 다음 콘텐츠가 포함된 JSON 파일을 만듭니다.

    {
      "taskGroups": [
        {
          "taskSpec": {
            "runnables": [
              {
                "script": {
                  "text": "echo This is the secret: ${SECRET_VARIABLE_NAME}"
                }
              }
            ],
            "environment": {
              "secretVariables": {
                "{SECRET_VARIABLE_NAME}": "projects/PROJECT_ID/secrets/SECRET_NAME/versions/VERSION"
              }
            }
          }
        }
      ],
      "logsPolicy": {
        "destination": "CLOUD_LOGGING"
      }
    }
    

    다음을 바꿉니다.

    • SECRET_VARIABLE_NAME: 보안 비밀 변수의 이름. 규칙에 따라 환경 변수 이름은 대문자로 표시됩니다.

      변수의 Secret Manager 보안 비밀에서 민감한 정보에 안전하게 액세스하려면 작업의 실행 가능 항목에 이 변수 이름을 지정합니다. 보안 비밀 변수는 보안 비밀 변수를 정의하는 동일한 환경에 있는 모든 실행 가능 항목에 액세스할 수 있습니다.

    • PROJECT_ID: 프로젝트의 프로젝트 ID

    • SECRET_NAME: 기존 Secret Manager 보안 비밀의 이름

    • VERSION: 작업에 전달할 데이터가 포함된 지정된 보안 비밀의 버전. 버전 번호 또는 latest일 수 있습니다.

  2. 작업을 만들고 실행하려면 gcloud batch jobs submit 명령어를 사용합니다.

    gcloud batch jobs submit JOB_NAME \
      --location LOCATION \
      --config JSON_CONFIGURATION_FILE
    

    다음을 바꿉니다.

    • JOB_NAME: 작업의 이름

    • LOCATION: 작업의 위치

    • JSON_CONFIGURATION_FILE: 작업의 구성 세부정보가 포함된 JSON 파일의 경로

API

환경 하나 이상에 secretVariables 하위 필드를 지정하는 jobs.create 메서드POST 요청을 보냅니다.

예를 들어 모든 실행 가능 항목 환경에서 보안 비밀 변수를 사용하는 기본 스크립트 작업을 만들려면 다음 요청을 보냅니다.

POST https://batch.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/jobs?job_id=JOB_NAME
{
  "taskGroups": [
    {
      "taskSpec": {
        "runnables": [
          {
            "script": {
              "text": "echo This is the secret: ${SECRET_VARIABLE_NAME}"
            }
          }
        ],
        "environment": {
          "secretVariables": {
            "{SECRET_VARIABLE_NAME}": "projects/PROJECT_ID/secrets/SECRET_NAME/versions/VERSION"
          }
        }
      }
    }
  ],
  "logsPolicy": {
    "destination": "CLOUD_LOGGING"
  }
}

다음을 바꿉니다.

  • PROJECT_ID: 프로젝트의 프로젝트 ID

  • LOCATION: 작업의 위치

  • JOB_NAME: 작업의 이름

  • SECRET_VARIABLE_NAME: 보안 비밀 변수의 이름. 규칙에 따라 환경 변수 이름은 대문자로 표시됩니다.

    변수의 Secret Manager 보안 비밀에서 민감한 정보에 안전하게 액세스하려면 작업의 실행 가능 항목에 이 변수 이름을 지정합니다. 보안 비밀 변수는 보안 비밀 변수를 정의하는 동일한 환경에 있는 모든 실행 가능 항목에 액세스할 수 있습니다.

  • SECRET_NAME: 기존 Secret Manager 보안 비밀의 이름

  • VERSION: 작업에 전달할 데이터가 포함된 지정된 보안 비밀의 버전. 버전 번호 또는 latest일 수 있습니다.

Java


import com.google.cloud.batch.v1.BatchServiceClient;
import com.google.cloud.batch.v1.CreateJobRequest;
import com.google.cloud.batch.v1.Environment;
import com.google.cloud.batch.v1.Job;
import com.google.cloud.batch.v1.LogsPolicy;
import com.google.cloud.batch.v1.LogsPolicy.Destination;
import com.google.cloud.batch.v1.Runnable;
import com.google.cloud.batch.v1.Runnable.Script;
import com.google.cloud.batch.v1.TaskGroup;
import com.google.cloud.batch.v1.TaskSpec;
import com.google.protobuf.Duration;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class CreateBatchUsingSecretManager {

  public static void main(String[] args)
      throws IOException, ExecutionException, InterruptedException, TimeoutException {
    // TODO(developer): Replace these variables before running the sample.
    // Project ID or project number of the Google Cloud project you want to use.
    String projectId = "YOUR_PROJECT_ID";
    // Name of the region you want to use to run the job. Regions that are
    // available for Batch are listed on: https://cloud.google.com/batch/docs/get-started#locations
    String region = "europe-central2";
    // The name of the job that will be created.
    // It needs to be unique for each project and region pair.
    String jobName = "JOB_NAME";
    // The name of the secret variable.
    // This variable name is specified in this job's runnables
    // and is accessible to all of the runnables that are in the same environment.
    String secretVariableName = "VARIABLE_NAME";
    // The name of an existing Secret Manager secret.
    String secretName = "SECRET_NAME";
    // The version of the specified secret that contains the data you want to pass to the job.
    // This can be the version number or latest.
    String version = "VERSION";

    createBatchUsingSecretManager(projectId, region,
            jobName, secretVariableName, secretName, version);
  }

  // Create a basic script job to securely pass sensitive data.
  // The data is obtained from Secret Manager secrets
  // and set as custom environment variables in the job.
  public static Job createBatchUsingSecretManager(String projectId, String region,
                                                  String jobName, String secretVariableName,
                                                  String secretName, String version)
      throws IOException, ExecutionException, InterruptedException, TimeoutException {
    // Initialize client that will be used to send requests. This client only needs to be created
    // once, and can be reused for multiple requests.
    try (BatchServiceClient batchServiceClient = BatchServiceClient.create()) {
      // Define what will be done as part of the job.
      Runnable runnable =
          Runnable.newBuilder()
              .setScript(
                  Script.newBuilder()
                      .setText(
                          String.format("echo This is the secret: ${%s}.", secretVariableName))
                      // You can also run a script from a file. Just remember, that needs to be a
                      // script that's already on the VM that will be running the job.
                      // Using setText() and setPath() is mutually exclusive.
                      // .setPath("/tmp/test.sh")
                      .build())
              .build();

      // Construct the resource path to the secret's version.
      String secretValue = String
              .format("projects/%s/secrets/%s/versions/%s", projectId, secretName, version);

      // Set the secret as an environment variable.
      Environment.Builder environmentVariable = Environment.newBuilder()
          .putSecretVariables(secretVariableName, secretValue);

      TaskSpec task = TaskSpec.newBuilder()
          // Jobs can be divided into tasks. In this case, we have only one task.
          .addRunnables(runnable)
          .setEnvironment(environmentVariable)
          .setMaxRetryCount(2)
          .setMaxRunDuration(Duration.newBuilder().setSeconds(3600).build())
          .build();

      // Tasks are grouped inside a job using TaskGroups.
      // Currently, it's possible to have only one task group.
      TaskGroup taskGroup = TaskGroup.newBuilder()
          .setTaskSpec(task)
          .build();

      Job job =
          Job.newBuilder()
              .addTaskGroups(taskGroup)
              .putLabels("env", "testing")
              .putLabels("type", "script")
              // We use Cloud Logging as it's an out of the box available option.
              .setLogsPolicy(
                  LogsPolicy.newBuilder().setDestination(Destination.CLOUD_LOGGING))
              .build();

      CreateJobRequest createJobRequest =
          CreateJobRequest.newBuilder()
              // The job's parent is the region in which the job will run.
              .setParent(String.format("projects/%s/locations/%s", projectId, region))
              .setJob(job)
              .setJobId(jobName)
              .build();

      Job result =
          batchServiceClient
              .createJobCallable()
              .futureCall(createJobRequest)
              .get(5, TimeUnit.MINUTES);

      System.out.printf("Successfully created the job: %s", result.getName());

      return result;
    }
  }
}

Node.js

// Imports the Batch library
const batchLib = require('@google-cloud/batch');
const batch = batchLib.protos.google.cloud.batch.v1;

// Instantiates a client
const batchClient = new batchLib.v1.BatchServiceClient();

/**
 * TODO(developer): Update these variables before running the sample.
 */
// Project ID or project number of the Google Cloud project you want to use.
const projectId = await batchClient.getProjectId();
// Name of the region you want to use to run the job. Regions that are
// available for Batch are listed on: https://cloud.google.com/batch/docs/get-started#locations
const region = 'europe-central2';
// The name of the job that will be created.
// It needs to be unique for each project and region pair.
const jobName = 'batch-job-secret-manager';
// The name of the secret variable.
// This variable name is specified in this job's runnables
// and is accessible to all of the runnables that are in the same environment.
const secretVariableName = 'secretVariableName';
// The name of an existing Secret Manager secret.
const secretName = 'secretName';
// The version of the specified secret that contains the data you want to pass to the job.
// This can be the version number or latest.
const version = 'version';

// Define what will be done as part of the job.
const runnable = new batch.Runnable({
  script: new batch.Runnable.Script({
    commands: ['-c', `echo This is the secret: ${secretVariableName}`],
  }),
});

// Construct the resource path to the secret's version.
const secretValue = `projects/${projectId}/secrets/${secretName}/versions/${version}`;

// Set the secret as an environment variable.
const environment = new batch.Environment();
environment.secretVariables[secretVariableName] = secretValue;

const task = new batch.TaskSpec({
  runnables: [runnable],
  environment,
  maxRetryCount: 2,
  maxRunDuration: {seconds: 3600},
});

// Tasks are grouped inside a job using TaskGroups.
const group = new batch.TaskGroup({
  taskCount: 3,
  taskSpec: task,
});

const job = new batch.Job({
  name: jobName,
  taskGroups: [group],
  labels: {env: 'testing', type: 'script'},
  // We use Cloud Logging as it's an option available out of the box
  logsPolicy: new batch.LogsPolicy({
    destination: batch.LogsPolicy.Destination.CLOUD_LOGGING,
  }),
});

// The job's parent is the project and region in which the job will run
const parent = `projects/${projectId}/locations/${region}`;

async function callCreateUsingSecretManager() {
  // Construct request
  const request = {
    parent,
    jobId: jobName,
    job,
  };

  // Run request
  const [response] = await batchClient.createJob(request);
  console.log(JSON.stringify(response));
}

await callCreateUsingSecretManager();

Python

from typing import Dict, Optional

from google.cloud import batch_v1


def create_with_secret_manager(
    project_id: str,
    region: str,
    job_name: str,
    secrets: Dict[str, str],
    service_account_email: Optional[str] = None,
) -> batch_v1.Job:
    """
    This method shows how to create a sample Batch Job that will run
    a simple command on Cloud Compute instances with passing secrets from secret manager.
    Note: Job's service account should have the permissions to access secrets.
        - Secret Manager Secret Accessor (roles/secretmanager.secretAccessor) IAM role.

    Args:
        project_id: project ID or project number of the Cloud project you want to use.
        region: name of the region you want to use to run the job. Regions that are
            available for Batch are listed on: https://cloud.google.com/batch/docs/get-started#locations
        job_name: the name of the job that will be created.
            It needs to be unique for each project and region pair.
        secrets: secrets, which should be passed to the job. Environment variables should be capitalized
        by convention https://google.github.io/styleguide/shellguide.html#constants-and-environment-variable-names
            The format should look like:
                - {'SECRET_NAME': 'projects/{project_id}/secrets/{SECRET_NAME}/versions/{version}'}
            version can be set to 'latest'.
        service_account_email (optional): custom service account email

    Returns:
        A job object representing the job created.
    """
    client = batch_v1.BatchServiceClient()

    # Define what will be done as part of the job.
    task = batch_v1.TaskSpec()
    runnable = batch_v1.Runnable()
    runnable.script = batch_v1.Runnable.Script()
    runnable.script.text = (
        "echo Hello world! from task ${BATCH_TASK_INDEX}."
        + f" ${next(iter(secrets.keys()))} is the value of the secret."
    )
    task.runnables = [runnable]
    task.max_retry_count = 2
    task.max_run_duration = "3600s"

    envable = batch_v1.Environment()
    envable.secret_variables = secrets
    task.environment = envable

    # Tasks are grouped inside a job using TaskGroups.
    # Currently, it's possible to have only one task group.
    group = batch_v1.TaskGroup()
    group.task_count = 4
    group.task_spec = task

    # Policies are used to define on what kind of virtual machines the tasks will run on.
    # Read more about local disks here: https://cloud.google.com/compute/docs/disks/persistent-disks
    policy = batch_v1.AllocationPolicy.InstancePolicy()
    policy.machine_type = "e2-standard-4"
    instances = batch_v1.AllocationPolicy.InstancePolicyOrTemplate()
    instances.policy = policy
    allocation_policy = batch_v1.AllocationPolicy()
    allocation_policy.instances = [instances]

    service_account = batch_v1.ServiceAccount()
    service_account.email = service_account_email
    allocation_policy.service_account = service_account

    job = batch_v1.Job()
    job.task_groups = [group]
    job.allocation_policy = allocation_policy
    job.labels = {"env": "testing", "type": "script"}
    # We use Cloud Logging as it's an out of the box available option
    job.logs_policy = batch_v1.LogsPolicy()
    job.logs_policy.destination = batch_v1.LogsPolicy.Destination.CLOUD_LOGGING

    create_request = batch_v1.CreateJobRequest()
    create_request.job = job
    create_request.job_id = job_name
    # The job's parent is the region in which the job will run
    create_request.parent = f"projects/{project_id}/locations/{region}"

    return client.create_job(create_request)

Docker 레지스트리 사용자 인증 정보가 필요한 컨테이너 이미지에 안전하게 액세스

비공개 Docker 레지스트리의 컨테이너 이미지를 사용하려면 실행 가능 항목에서 해당 Docker 레지스트리에 액세스할 수 있는 로그인 사용자 인증 정보를 지정해야 합니다. 특히 비공개 Docker 레지스트리의 이미지로 설정된 이미지 URI(imageUri) 필드가 있는 컨테이너 실행 가능 항목의 경우 사용자 이름(username) 필드비밀번호(password) 필드를 사용하여 Docker 레지스트리에 액세스하는 데 필요한 사용자 인증 정보를 지정해야 합니다.

이러한 필드를 직접 정의하는 대신 정보가 포함된 기존 보안 비밀을 지정하여 Docker 레지스트리의 민감한 사용자 인증 정보를 보호할 수 있습니다. 작업에서 보안 비밀을 지정할 때마다 형식을 보안 비밀 버전의 경로(projects/PROJECT_ID/secrets/SECRET_ID/versions/VERSION)로 지정해야 합니다.

gcloud CLI 또는 Batch API를 사용하여 비공개 Docker 레지스트리에서 컨테이너 이미지를 사용하는 작업을 만들 수 있습니다. 다음 예시에서는 사용자 이름을 직접 지정하고 비밀번호를 보안 비밀로 지정하여 비공개 Docker 레지스트리의 컨테이너 이미지를 사용하는 작업을 만드는 방법을 설명합니다.

gcloud

  1. 작업의 구성 세부정보를 지정하는 JSON 파일을 만듭니다. 비공개 Docker 레지스트리의 이미지를 사용하는 컨테이너 실행 가능 항목의 경우 usernamepassword 필드에 액세스하는 데 필요한 사용자 인증 정보를 포함합니다.

    예를 들어 비공개 Docker 레지스트리의 이미지를 지정하는 기본 컨테이너 작업을 만들려면 다음 콘텐츠가 포함된 JSON 파일을 만듭니다.

    {
      "taskGroups": [
        {
          "taskSpec": {
            "runnables": [
              {
                "container": {
                  "imageUri": "PRIVATE_IMAGE_URI",
                  "commands": [
                    "-c",
                    "echo This runnable uses a private image."
                  ],
                  "username": "USERNAME",
                  "password": "PASSWORD"
                }
              }
            ],
          }
        }
      ],
      "logsPolicy": {
        "destination": "CLOUD_LOGGING"
      }
    }
    

    다음을 바꿉니다.

    • PRIVATE_IMAGE_URI: 비공개 Docker 레지스트리의 컨테이너 이미지에 대한 이미지 URI. 이 이미지에 다른 컨테이너 설정이 필요한 경우 이 컨테이너도 포함해야 합니다.

    • USERNAME: 보안 비밀로 또는 직접 지정할 수 있는 비공개 Docker 레지스트리의 사용자 이름

    • PASSWORD: 보안 비밀(권장)로 또는 직접 지정할 수 있는 비공개 Docker 레지스트리의 비밀번호.

      예를 들어 비밀번호를 보안 비밀로 지정하려면 PASSWORD를 다음으로 설정합니다.

      projects/PROJECT_ID/secrets/SECRET_ID/versions/VERSION
      

      다음을 바꿉니다.

  2. 작업을 만들고 실행하려면 gcloud batch jobs submit 명령어를 사용합니다.

    gcloud batch jobs submit JOB_NAME \
      --location LOCATION \
      --config JSON_CONFIGURATION_FILE
    

    다음을 바꿉니다.

    • JOB_NAME: 작업의 이름

    • LOCATION: 작업의 위치

    • JSON_CONFIGURATION_FILE: 작업의 구성 세부정보가 포함된 JSON 파일의 경로

API

jobs.create 메서드에 대해 POST 요청을 실행합니다. 비공개 Docker 레지스트리의 이미지를 사용하는 컨테이너 실행 가능 항목의 경우 usernamepassword 필드에 액세스하는 데 필요한 사용자 인증 정보를 포함합니다.

예를 들어 비공개 Docker 레지스트리의 이미지를 지정하는 기본 컨테이너 작업을 만들려면 다음 요청을 보냅니다.

POST https://batch.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/jobs?job_id=JOB_NAME
{
  "taskGroups": [
    {
      "taskSpec": {
        "runnables": [
          {
            "container": {
              "imageUri": "PRIVATE_IMAGE_URI",
                "commands": [
                  "-c",
                  "echo This runnable uses a private image."
                ],
                "username": "USERNAME",
                "password": "PASSWORD"
            }
          }
        ],
      }
    }
  ],
  "logsPolicy": {
    "destination": "CLOUD_LOGGING"
  }
}

다음을 바꿉니다.

  • PROJECT_ID: 프로젝트의 프로젝트 ID

  • LOCATION: 작업의 위치

  • JOB_NAME: 작업의 이름

  • PRIVATE_IMAGE_URI: 비공개 Docker 레지스트리의 컨테이너 이미지에 대한 이미지 URI. 이 이미지에 다른 컨테이너 설정이 필요한 경우 이 컨테이너도 포함해야 합니다.

  • USERNAME: 보안 비밀로 또는 직접 지정할 수 있는 비공개 Docker 레지스트리의 사용자 이름

  • PASSWORD: 보안 비밀(권장)로 또는 직접 지정할 수 있는 비공개 Docker 레지스트리의 비밀번호.

    예를 들어 비밀번호를 보안 비밀로 지정하려면 PASSWORD를 다음으로 설정합니다.

    projects/PROJECT_ID/secrets/SECRET_ID/versions/VERSION
    

    다음을 바꿉니다.

다음 단계