Proteggere i dati sensibili utilizzando Secret Manager con Batch

Questo documento descrive come proteggere i dati sensibili da specificare per un job batch utilizzando i secret di Secret Manager.

I segreti di Secret Manager proteggono i dati sensibili tramite la crittografia. In un job batch, puoi specificare uno o più secret esistenti per trasmettere in modo sicuro i dati sensibili in essi contenuti, che puoi utilizzare per:

  • Definisci in modo sicuro variabili di ambiente personalizzate che contengono dati sensibili.

  • Specifica in modo sicuro le credenziali di accesso per un Docker Registry per consentire ai file eseguibili di un job di accedere alle relative immagini container private.

Prima di iniziare

  1. Se non hai mai utilizzato Batch, consulta la guida introduttiva all'utilizzo di Batch e attivalo completando i prerequisiti per progetti e utenti.
  2. Crea un segreto o identifica un segreto per i dati sensibili che vuoi specificare in modo sicuro per un job.
  3. Per ottenere le autorizzazioni necessarie per creare un job, chiedi all'amministratore di concederti i seguenti ruoli IAM:

    Per saperne di più sulla concessione dei ruoli, consulta Gestire l'accesso a progetti, cartelle e organizzazioni.

    Potresti anche riuscire a ottenere le autorizzazioni richieste tramite i ruoli personalizzati o altri ruoli predefiniti.

  4. Per assicurarti che l'account di servizio del job disponga delle autorizzazioni necessarie per accedere ai secret, chiedi all'amministratore di concedere all'account di servizio del job il ruolo IAM Accesso ai secret di Secret Manager (roles/secretmanager.secretAccessor) sul secret.

Trasferisci in modo sicuro i dati sensibili alle variabili di ambiente personalizzate

Per trasmettere in modo sicuro i dati sensibili dai secret di Secret Manager alle variabili di ambiente personalizzate, devi definire ogni variabile di ambiente nel sottocampo delle variabili di secret (secretVariables) per un ambiente e specificare un secret per ogni valore. Ogni volta che specifichi un secret in un job, devi formattarlo come percorso a una versione del secret: projects/PROJECT_ID/secrets/SECRET_ID/versions/VERSION.

Puoi creare un job che definisce le variabili di secret utilizzando l'interfaccia a riga di comando gcloud, l'API Batch, Java, Node.js o Python. L'esempio seguente spiega come creare un job che definisce e utilizza una variabile di secret per l'ambiente di tutti i file eseguibili (campo secondario environment di taskSpec).

gcloud

  1. Crea un file JSON che specifichi i dettagli di configurazione del job e includa il sottocampo secretVariables per uno o più ambienti.

    Ad esempio, per creare un job di script di base che utilizza una variabile segreta nell'ambiente per tutti gli elementi eseguibili, crea un file JSON con i seguenti contenuti:

    {
      "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"
      }
    }
    

    Sostituisci quanto segue:

    • SECRET_VARIABLE_NAME: il nome della variabile secret. Per convenzione, i nomi delle variabili di ambiente sono scritti in maiuscolo.

      Per accedere in sicurezza ai dati sensibili dal segreto Secret Manager della variabile, specifica il nome della variabile negli elementi eseguibili del job. La variabile secret è accessibile a tutti i programmabili nello stesso ambiente in cui la definisci.

    • PROJECT_ID: l'ID progetto del progetto.

    • SECRET_NAME: il nome di un secret di Secret Manager esistente.

    • VERSION: la versione del secret specificato contenente i dati da passare al job. Può essere il numero di versione o latest.

  2. Per creare ed eseguire il job, utilizza il comando gcloud batch jobs submit:

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

    Sostituisci quanto segue:

    • JOB_NAME: il nome del job.

    • LOCATION: la località del lavoro.

    • JSON_CONFIGURATION_FILE: il percorso di un file JSON con i dettagli di configurazione del job.

API

Invia una richiesta POST al metodo jobs.create che specifica il sottocampo secretVariables per uno o più ambienti.

Ad esempio, per creare un job di script di base che utilizza una variabile segreta nell'ambiente per tutti i file eseguibili, effettua la seguente richiesta:

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"
  }
}

Sostituisci quanto segue:

  • PROJECT_ID: l'ID progetto del progetto.

  • LOCATION: la località del lavoro.

  • JOB_NAME: il nome del job.

  • SECRET_VARIABLE_NAME: il nome della variabile secret. Per convenzione, i nomi delle variabili di ambiente sono scritti in maiuscolo.

    Per accedere in sicurezza ai dati sensibili dal segreto Secret Manager della variabile, specifica il nome della variabile negli elementi eseguibili del job. La variabile secret è accessibile a tutti i programmabili nello stesso ambiente in cui la definisci.

  • SECRET_NAME: il nome di un secret di Secret Manager esistente.

  • VERSION: la versione del secret specificato contenente i dati da passare al job. Può essere il numero di versione o 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)

Accedere in modo sicuro alle immagini dei container che richiedono le credenziali del registry Docker

Per utilizzare un'immagine container da un registry Docker privato, un file eseguibile deve specificare le credenziali di accesso che consentono di accedere al registry Docker. Nello specifico, per qualsiasi container eseguibile con il campo URI immagine (imageUri) impostato su un'immagine di un registry Docker privato, devi specificare le eventuali credenziali necessarie per accedere a quel registry Docker utilizzando il campo nome utente (username) e il campo password (password).

Puoi proteggere le credenziali sensibili di un registry Docker specificando i secret esistenti che contengono le informazioni anziché definire direttamente questi campi. Ogni volta che specifichi un secret in un job, devi formattarlo come percorso a una versione del secret: projects/PROJECT_ID/secrets/SECRET_ID/versions/VERSION.

Puoi creare un job che utilizza immagini container da un registry Docker privato utilizzando la CLI gcloud o l'API Batch. L'esempio seguente spiega come creare un job che utilizza un'immagine container da un registry Docker privato specificando direttamente il nome utente e la password come secret.

gcloud

  1. Crea un file JSON che specifichi i dettagli di configurazione del job. Per tutti i container eseguibili che utilizzano immagini di un registro Docker privato, includi le credenziali necessarie per accedervi nei campi username e password.

    Ad esempio, per creare un job di container di base che specifica un'immagine da un registry Docker privato, crea un file JSON con i seguenti contenuti:

    {
      "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"
      }
    }
    

    Sostituisci quanto segue:

    • PRIVATE_IMAGE_URI: l'URI dell'immagine per un'immagine container da un registry Docker privato. Se questa immagine richiede altre impostazioni del contenitore, devi includerle.

    • USERNAME: il nome utente per il registry Docker privato, che può essere specificato come secret o direttamente.

    • PASSWORD: la password per il registry Docker privato, che può essere specificata come secret (opzione consigliata) o direttamente.

      Ad esempio, per specificare la password come segreto, imposta PASSWORD su quanto segue:

      projects/PROJECT_ID/secrets/SECRET_ID/versions/VERSION
      

      Sostituisci quanto segue:

  2. Per creare ed eseguire il job, utilizza il comando gcloud batch jobs submit:

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

    Sostituisci quanto segue:

    • JOB_NAME: il nome del job.

    • LOCATION: la località del lavoro.

    • JSON_CONFIGURATION_FILE: il percorso di un file JSON con i dettagli di configurazione del job.

API

Invia una richiesta POST al metodo jobs.create. Per tutti i container eseguibili che utilizzano immagini di un registro Docker privato, includi le credenziali necessarie per accedervi nei campi username e password.

Ad esempio, per creare un job di container di base che specifica un'immagine da un registry Docker privato, effettua la seguente richiesta:

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"
  }
}

Sostituisci quanto segue:

  • PROJECT_ID: l'ID progetto del progetto.

  • LOCATION: la località del lavoro.

  • JOB_NAME: il nome del job.

  • PRIVATE_IMAGE_URI: l'URI dell'immagine per un'immagine container da un registry Docker privato. Se questa immagine richiede altre impostazioni del contenitore, devi includerle.

  • USERNAME: il nome utente per il registry Docker privato, che può essere specificato come secret o direttamente.

  • PASSWORD: la password per il registry Docker privato, che può essere specificata come secret (opzione consigliata) o direttamente.

    Ad esempio, per specificare la password come segreto, imposta PASSWORD su quanto segue:

    projects/PROJECT_ID/secrets/SECRET_ID/versions/VERSION
    

    Sostituisci quanto segue:

Passaggi successivi