Protéger les données sensibles à l'aide de Secret Manager avec Batch

Ce document explique comment protéger les données sensibles que vous souhaitez spécifier pour une tâche de traitement par lot à l'aide de secrets Secret Manager.

Les secrets Secret Manager protègent les données sensibles par chiffrement. Dans un job Batch, vous pouvez spécifier un ou plusieurs secrets existants pour transmettre en toute sécurité les données sensibles qu'ils contiennent, ce qui vous permet suivantes:

  • Définissez de manière sécurisée des variables d'environnement personnalisées contenant des données sensibles.

  • Spécifiez de manière sécurisée les identifiants de connexion pour un registre Docker afin de permettre aux exécutables d'une tâche d'accéder à ses images de conteneur privées.

Avant de commencer

  1. Si vous n'avez jamais utilisé Batch, consultez Premiers pas avec Batch et activez Batch en remplissant les conditions préalables pour les projets et les utilisateurs.
  2. Créer un secret ou identifier un secret des données sensibles que vous souhaitez spécifier de manière sécurisée pour un job.
  3. Pour obtenir les autorisations nécessaires pour créer un job, demandez à votre administrateur de vous accorder les rôles IAM suivants :

    Pour en savoir plus sur l'attribution de rôles, consultez la page Gérer l'accès aux projets, aux dossiers et aux organisations.

    Vous pouvez également obtenir les autorisations requises via des rôles personnalisés ou d'autres rôles prédéfinis.

  4. Pour vous assurer que le compte de service de la tâche dispose des autorisations nécessaires pour accéder aux secrets, demandez à votre administrateur d'accorder au compte de service de la tâche le rôle IAM Accesseur de secrets du gestionnaire de secrets (roles/secretmanager.secretAccessor) sur le secret.

Transmettre de façon sécurisée les données sensibles à des variables d'environnement personnalisées

Pour transférer de manière sécurisée les données sensibles des secrets Secret Manager vers des variables d'environnement, vous devez définir chaque variable d'environnement dans Sous-champ des variables de secret (secretVariables) pour un environnement et spécifier un secret pour chaque valeur. Chaque fois que vous spécifiez un secret dans une tâche, vous devez le mettre en forme sous la forme d'un chemin d'accès à une version de secret : projects/PROJECT_ID/secrets/SECRET_ID/versions/VERSION.

Vous pouvez créer un job qui définit des variables secrètes à l'aide de la Google Cloud CLI, de l'API Batch, de Java, de Node.js ou de Python. L'exemple suivant explique comment créer une tâche qui définit et utilise une variable secrète pour l'environnement de tous les exécutables (sous-champ environment de taskSpec).

.

gcloud

  1. Créez un fichier JSON qui spécifie les détails de configuration de la tâche et inclut le sous-champ secretVariables pour un ou plusieurs environnements.

    Par exemple, pour créer une tâche de script de base qui utilise une variable secrète dans l'environnement pour tous les exécutables, créez un fichier JSON avec le contenu suivant :

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

    Remplacez les éléments suivants :

    • SECRET_VARIABLE_NAME : nom de la variable secrète. Par convention, les noms de variables d'environnement sont mis en majuscules.

      Pour accéder de manière sécurisée aux données sensibles à partir du secret Secret Manager de la variable, spécifiez ce nom de variable dans les exécutables de cette tâche. La variable secrète est accessible à tous les exécutables qui se trouvent dans le même environnement que celui dans lequel vous définissez la variable secrète.

    • PROJECT_ID : ID de projet de votre projet.

    • SECRET_NAME: nom de un secret Secret Manager existant.

    • VERSION: le version du secret spécifié contenant les données que vous souhaitez à transmettre au job. Il peut s'agir du numéro de version ou de latest.

  2. Pour créer et exécuter le job, utilisez la Commande gcloud batch jobs submit:

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

    Remplacez les éléments suivants :

    • JOB_NAME : nom de la tâche.

    • LOCATION: emplacement du travail.

    • JSON_CONFIGURATION_FILE: chemin d'accès d'un fichier JSON contenant les détails de configuration du job.

API

Envoyez une requête POST à la méthode jobs.create qui spécifie le sous-champ secretVariables pour un ou plusieurs environnements.

Par exemple, pour créer une tâche de script de base qui utilise un code secret dans l'environnement pour tous les exécutables, envoyez la requête suivante:

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

Remplacez les éléments suivants :

  • PROJECT_ID : ID de projet de votre projet.

  • LOCATION : emplacement de la tâche.

  • JOB_NAME: nom de la tâche.

  • SECRET_VARIABLE_NAME : nom de la variable secrète. Par convention, les noms de variables d'environnement sont mis en majuscules.

    Pour accéder de façon sécurisée aux données sensibles à partir du Secret Manager, spécifiez ce nom de variable dans ce et les exécutables du job. La variable secrète est accessible à tous les exécutables qui se trouvent dans le même environnement que celui dans lequel vous définissez la variable secrète.

  • SECRET_NAME : nom d'un secret Secret Manager existant.

  • VERSION: le version du secret spécifié contenant les données que vous souhaitez à transmettre au job. Il peut s'agir du numéro de version ou de 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)

Accéder de manière sécurisée aux images de conteneur nécessitant des identifiants de registre Docker

Pour utiliser une image de conteneur provenant d'un registre Docker privé, un exécutable doit Spécifier des identifiants de connexion qui lui permettent d'accéder à ce registre Docker. Plus précisément, pour tout conteneur exécutable avec le champ URI de l'image (imageUri) défini sur une image provenant d'un dépôt Docker privé, vous devez spécifier toutes les identifiants requises pour accéder à ce dépôt Docker à l'aide des champs nom d'utilisateur (username) et mot de passe (password).

Vous pouvez protéger les identifiants sensibles d'un registre Docker en spécifiant des secrets existants contenant ces informations au lieu de les définir directement. Chaque fois que vous spécifiez un secret dans une tâche, vous devez le mettre en forme sous la forme d'un chemin d'accès à une version de secret: projects/PROJECT_ID/secrets/SECRET_ID/versions/VERSION

Vous pouvez créer une tâche qui utilise des images de conteneur à partir d'un dépôt Docker privé à l'aide de la CLI gcloud ou de l'API Batch. L'exemple suivant explique comment créer une tâche qui utilise une image de conteneur à partir d'un registre Docker privé en spécifiant directement le nom d'utilisateur et le mot de passe en tant que secret.

gcloud

  1. Créez un fichier JSON qui spécifie les détails de la configuration de la tâche. Pour tous les exécutables de conteneur qui utilisent des images d'un registre Docker privé, incluez tous les identifiants requis pour y accéder dans les champs username et password.

    Par exemple, pour créer un job de conteneur de base qui spécifie une image à partir d'un registre Docker privé, créez un fichier JSON avec le contenu suivant:

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

    Remplacez les éléments suivants :

    • PRIVATE_IMAGE_URI: URI de l'image pour d'un registre Docker privé. Si cette image nécessite d'autres paramètres de conteneur ; vous devez également les inclure.

    • USERNAME: le dans le registre Docker privé, qui peut être spécifié de manière confidentielle ou directe.

    • PASSWORD: le le mot de passe du registre Docker privé, qui peut être spécifié en tant que secret (recommandé) ou directement.

      Par exemple, pour spécifier le mot de passe en tant que secret, définissez PASSWORD comme suit :

      projects/PROJECT_ID/secrets/SECRET_ID/versions/VERSION
      

      Remplacez les éléments suivants :

      • PROJECT_ID: ID de votre projet.

      • SECRET_NAME: nom de un secret Secret Manager existant.

      • VERSION : version du secret spécifié qui contient les données que vous souhaitez transmettre à la tâche. Il peut s'agir du numéro de version ou de latest.

  2. Pour créer et exécuter la tâche, utilisez la commande gcloud batch jobs submit :

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

    Remplacez les éléments suivants :

    • JOB_NAME : nom de la tâche.

    • LOCATION : emplacement de la tâche.

    • JSON_CONFIGURATION_FILE: chemin d'accès d'un fichier JSON contenant les détails de configuration du job.

API

Envoyez une requête POST à la méthode jobs.create. Pour tous les exécutables de conteneur qui utilisent des images d'un registre Docker privé, incluez tous les identifiants requis pour y accéder dans les champs username et password.

Par exemple, pour créer un job de conteneur de base qui spécifie une image à partir d'un registre Docker privé, envoyez la requête suivante:

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

Remplacez les éléments suivants :

  • PROJECT_ID : ID de projet de votre projet.

  • LOCATION : emplacement de la tâche.

  • JOB_NAME : nom de la tâche.

  • PRIVATE_IMAGE_URI: URI de l'image pour d'un registre Docker privé. Si cette image nécessite d'autres paramètres de conteneur ; vous devez également les inclure.

  • USERNAME: le dans le registre Docker privé, qui peut être spécifié de manière confidentielle ou directe.

  • PASSWORD: le le mot de passe du registre Docker privé, qui peut être spécifié en tant que secret (recommandé) ou directement.

    Par exemple, pour spécifier le mot de passe en tant que secret, définissez PASSWORD comme suit :

    projects/PROJECT_ID/secrets/SECRET_ID/versions/VERSION
    

    Remplacez les éléments suivants :

    • PROJECT_ID: ID de votre projet.

    • SECRET_NAME : nom d'un secret Secret Manager existant.

    • VERSION: le version du secret spécifié contenant les données que vous souhaitez à transmettre au job. Il peut s'agir du numéro de version ou de latest.

Étape suivante