Usa Secret Manager con Python

1. Descripción general

En este codelab, te enfocarás en el uso de Secret Manager en Python.

Secret Manager te permite almacenar, administrar y acceder a los secretos como BLOB binarios o cadenas de texto. Con los permisos adecuados, puedes ver el contenido del secreto.

Secret Manager funciona bien para almacenar información de configuración, como contraseñas de bases de datos, claves de API o certificados TLS que necesita una aplicación en el entorno de ejecución.

Qué aprenderás

  • Cómo usar Cloud Shell
  • Cómo instalar la biblioteca cliente de Secret Manager para Python
  • Cómo crear secretos y acceder a ellos con la biblioteca cliente de Python
  • Cómo acceder a los secretos en Cloud Functions con la biblioteca cliente de Python

Requisitos

  • Un proyecto de Google Cloud
  • Un navegador, como Chrome o Firefox
  • Conocimientos de Python 3

Encuesta

¿Cómo usarás este instructivo?

Leer Leer y completar los ejercicios

¿Cómo calificarías tu experiencia en Python?

Principiante Intermedio Avanzado

¿Cómo calificarías tu experiencia en el uso de los servicios de Google Cloud?

Principiante Intermedio Avanzado .
.

2. Configuración y requisitos

Configuración del entorno de autoaprendizaje

  1. Accede a Google Cloud Console y crea un proyecto nuevo o reutiliza uno existente. Si aún no tienes una cuenta de Gmail o de Google Workspace, debes crear una.

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • El Nombre del proyecto es el nombre visible de los participantes de este proyecto. Es una cadena de caracteres que no se utiliza en las APIs de Google. Puedes actualizarla en cualquier momento.
  • El ID del proyecto debe ser único en todos los proyectos de Google Cloud y es inmutable (no se puede cambiar después de configurarlo). La consola de Cloud genera automáticamente una cadena única. por lo general, no te importa qué es. En la mayoría de los codelabs, deberás hacer referencia al ID del proyecto (por lo general, se identifica como PROJECT_ID). Si no te gusta el ID generado, puedes generar otro aleatorio. También puedes probar el tuyo propio y ver si está disponible. No se puede cambiar después de este paso y se mantendrá mientras dure el proyecto.
  • Para tu información, hay un tercer valor, un número de proyecto que usan algunas APIs. Obtén más información sobre estos tres valores en la documentación.
  1. A continuación, deberás habilitar la facturación en la consola de Cloud para usar las APIs o los recursos de Cloud. Ejecutar este codelab no debería costar mucho, tal vez nada. Para cerrar recursos y evitar que se te facture más allá de este instructivo, puedes borrar los recursos que creaste o borrar todo el proyecto. Los usuarios nuevos de Google Cloud son aptos para participar en el programa Prueba gratuita de USD 300.

Inicia Cloud Shell

Si bien Google Cloud y Spanner se pueden operar de manera remota desde tu laptop, en este codelab usarás Google Cloud Shell, un entorno de línea de comandos que se ejecuta en la nube.

Activar Cloud Shell

  1. En la consola de Cloud, haz clic en Activar Cloud Shell853e55310c205094.png.

55efc1aaa7a4d3ad.png

Si nunca iniciaste Cloud Shell, aparecerá una pantalla intermedia (mitad inferior de la página) que describe en qué consiste. Si ese es el caso, haz clic en Continuar (y no volverás a verlo). Así es como se ve la pantalla única:

9c92662c6a846a5c.png

El aprovisionamiento y la conexión a Cloud Shell solo tomará unos minutos.

9f0e51b578fecce5.png

Esta máquina virtual está cargada con todas las herramientas de desarrollo que necesitas. Ofrece un directorio principal persistente de 5 GB y se ejecuta en Google Cloud, lo que permite mejorar considerablemente el rendimiento de la red y la autenticación. Gran parte de tu trabajo en este codelab, si no todo, se puede hacer simplemente con un navegador o tu Chromebook.

Una vez conectado a Cloud Shell, debería ver que ya se autenticó y que el proyecto ya se configuró con tu ID del proyecto.

  1. En Cloud Shell, ejecuta el siguiente comando para confirmar que está autenticado:
gcloud auth list

Resultado del comando

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. Ejecuta el siguiente comando en Cloud Shell para confirmar que el comando de gcloud conoce tu proyecto:
gcloud config list project

Resultado del comando

[core]
project = <PROJECT_ID>

De lo contrario, puedes configurarlo con el siguiente comando:

gcloud config set project <PROJECT_ID>

Resultado del comando

Updated property [core/project].

3. Habilita la API de Secret Manager

Antes de comenzar a usar la API de Secret Manager, debes habilitarla. En Cloud Shell, puedes habilitar la API con el siguiente comando:

gcloud services enable secretmanager.googleapis.com

Debería ver un resultado como este:

Operation "operations/acf.cc11852d-40af-47ad-9d59-477a12847c9e" finished successfully.

4. Instala la biblioteca cliente de Secret Manager para Python

Instala la biblioteca cliente de Secret Manager:

pip3 install --user google-cloud-secret-manager==2.10.0

5. Iniciar Python interactivo

Como parte de este instructivo, usarás un intérprete de Python interactivo llamado IPython, que está preinstalado en Cloud Shell. Para iniciar una sesión, ejecuta ipython en Cloud Shell:

ipython

Debería ver algo como esto:

Python 3.9.2 (default, Feb 28 2021, 17:03:44)
Type 'copyright', 'credits' or 'license' for more information
IPython 8.3.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]:

6. Crea secretos

Un secreto contiene una o más versiones de secretos. Se pueden crear con la línea de comandos de gcloud, pero también con Python.

Para usar un secreto, primero debes crearlo con el nombre del secreto y, luego, agregarle una versión, que será su valor.

Establece el ID del proyecto en IPython:

PROJECT_ID = "<PROJECT_ID>"

Crea un secreto

Copia el siguiente código en tu sesión de IPython:

from google.cloud import secretmanager

def create_secret(secret_id):
    # Create the Secret Manager client.
    client = secretmanager.SecretManagerServiceClient()

    # Build the resource name of the parent project.
    parent = f"projects/{PROJECT_ID}"

    # Build a dict of settings for the secret
    secret = {'replication': {'automatic': {}}}

    # Create the secret
    response = client.create_secret(secret_id=secret_id, parent=parent, secret=secret)

    # Print the new secret name.
    print(f'Created secret: {response.name}')   

Llama a la función para crear un secreto nuevo llamado my_secret_value:

create_secret("my_secret_value")

Deberías ver el siguiente resultado:

Created secret: projects/<PROJECT_NUM>/secrets/my_secret_value

Agrega una versión del Secret

Ahora que el secreto existe, puedes asignarle un valor creando una versión.

Copia el siguiente código en tu sesión de IPython:

def add_secret_version(secret_id, payload):
    # Create the Secret Manager client.
    client = secretmanager.SecretManagerServiceClient()

    # Build the resource name of the parent secret.
    parent = f"projects/{PROJECT_ID}/secrets/{secret_id}"

    # Convert the string payload into a bytes. This step can be omitted if you
    # pass in bytes instead of a str for the payload argument.
    payload = payload.encode('UTF-8')

    # Add the secret version.
    response = client.add_secret_version(parent=parent, payload={'data': payload})

    # Print the new secret version name.
    print(f'Added secret version: {response.name}')   

Llama a la función para crear una nueva versión del secreto:

add_secret_version("my_secret_value", "Hello Secret Manager")

Deberías ver el siguiente resultado:

Added secret version: projects/<PROJECT_NUM>/secrets/my_secret_value/versions/1

Los Secrets pueden tener varias versiones. Vuelve a llamar a la función con un valor diferente:

add_secret_version("my_secret_value", "Hello Again, Secret Manager")

Deberías ver el siguiente resultado:

Added secret version: projects/<PROJECT_NUM>/secrets/my_secret_value/versions/2

Observa que la nueva versión de nuestro secreto es significativamente más larga que la original. Más adelante, se hará referencia a este atributo.

7. Acceso a Secrets

El acceso a una versión del secreto muestra su contenido, así como metadatos adicionales sobre la versión del secreto. Cuando accedes a una versión del secreto, puedes especificar una versión específica o solo solicitar la última versión especificando “más reciente”.

Los secretos deben ser secretos. Almacenar las credenciales de la base de datos como Secrets y, luego, usarlas para autenticar, o almacenar las certificaciones y usarlas pero no imprimas directamente tus secretos, ya que esto va en contra del objetivo de mantenerlos en secreto.

Realizarás operaciones en nuestros secretos y evaluarás su valor sin imprimirlos directamente. En su lugar, imprimirás un hash del valor del secreto.

Copia el siguiente código en tu sesión de IPython:

def access_secret_version(secret_id, version_id="latest"):
    # Create the Secret Manager client.
    client = secretmanager.SecretManagerServiceClient()

    # Build the resource name of the secret version.
    name = f"projects/{PROJECT_ID}/secrets/{secret_id}/versions/{version_id}"

    # Access the secret version.
    response = client.access_secret_version(name=name)

    # Return the decoded payload.
    return response.payload.data.decode('UTF-8')
    
import hashlib

def secret_hash(secret_value): 
  # return the sha224 hash of the secret value
  return hashlib.sha224(bytes(secret_value, "utf-8")).hexdigest()

Llama a la función para recuperar el secreto como un hash de su valor:

secret_hash(access_secret_version("my_secret_value"))

Deberías ver un resultado similar a un hash (es posible que el valor exacto no coincida con este resultado):

83f8a4edb555cde4271029354395c9f4b7d79706ffa90c746e021d11

Como no especificaste una versión, se recuperó el valor más reciente.

Llama a la función y agrega el número de versión esperado para confirmar:

secret_hash(access_secret_version("my_secret_value", version_id=2))

Deberías ver el mismo resultado que el último comando:

83f8a4edb555cde4271029354395c9f4b7d79706ffa90c746e021d11

Vuelve a llamar a la función, pero esta vez especifica la primera versión:

secret_hash(access_secret_version("my_secret_value", version_id=1))

Esta vez, deberías ver un hash diferente, lo que indica un resultado diferente:

9a3fc8b809ddc611c82aee950c636c7557e220893560ec2c1eeeb177

8. Usa Secret Manager con Cloud Functions

Puedes usar Secrets en muchas partes de Google Cloud. En esta sección, te enfocarás en Cloud Functions, la oferta de computación sin servidores controlada por eventos de Google.

Si te interesa usar Python en Cloud Functions, puedes seguir el Codelab de HTTP de Google Cloud Functions en Python.

Cierra IPython; para ello, llama a la función exit:

exit

Deberías regresar a Cloud Shell:

yourname@cloudshell:~ (<PROJECT_ID>)$

Antes de comenzar a usar la API de Cloud Functions, debes habilitarla. En Cloud Shell, puedes habilitar la API con el siguiente comando:

gcloud services enable cloudfunctions.googleapis.com cloudbuild.googleapis.com

Crea una carpeta nueva para compilar la función y crea archivos vacíos en los que escribir:

mkdir secret-manager-api-demo
cd secret-manager-api-demo
touch main.py
touch requirements.txt

Abre el editor de código desde la parte superior derecha de Cloud Shell:

7651a97c51e11a24.png

Navega al archivo main.py dentro de la carpeta secret-manager-api-demo. Aquí es donde colocarás todo tu código.

9. Escribe una Cloud Function para acceder a los secretos

Si bien almacenar y recuperar valores de secretos desde la línea de comandos o la terminal de IPython es útil, lo es mucho más poder acceder a estos secretos dentro de una función.

Con la función access_secret_version que creaste antes, puedes usarla como base para tu Cloud Function.

Copia el siguiente código en el archivo main.py:

main.py

import os

from google.cloud import secretmanager

project_id = os.environ["PROJECT_ID"]

client = secretmanager.SecretManagerServiceClient()
name = f"projects/{project_id}/secrets/my_secret_value/versions/latest"
response = client.access_secret_version(name=name)
my_secret_value = response.payload.data.decode("UTF-8")


def secret_hello(request):
    if "Again" in my_secret_value:
        return "We meet again!\n"

    return "Hello there.\n"

Antes de que puedas implementar tu función, debes finalizar la configuración del entorno. Esto requiere que configures tu dependencia de función.

Crea un archivo nuevo llamado requirements.txt y agrégale el paquete google-cloud-secret-manager:

requirements.txt

google-cloud-secret-manager==2.10.0

Ahora, deberías tener una carpeta que contenga solo un main.py y un requirements.txt.

Cómo permitir el acceso a tu secreto

Antes de que puedas implementar tu función, debes permitir que Cloud Functions acceda a tu secreto.

Vuelve a la terminal:

c5b686edf94b5222.png

Otorga acceso a la cuenta de servicio de Cloud Functions para acceder a tu secreto:

export PROJECT_ID=$(gcloud config get-value core/project)

gcloud secrets add-iam-policy-binding my_secret_value \
    --role roles/secretmanager.secretAccessor \
    --member serviceAccount:${PROJECT_ID}@appspot.gserviceaccount.com

Deberías ver el siguiente resultado:

Updated IAM policy for secret [my_secret_value].
bindings:
- members:
  - serviceAccount:<PROJECT_ID>@appspot.gserviceaccount.com
  role: roles/secretmanager.secretAccessor
etag: BwWiRUt2oB4=
version: 1

10. Implementa tu Cloud Function

Dada la configuración que realizaste en las secciones anteriores, ahora puedes implementar y probar tu Cloud Function.

Dentro de la carpeta que contiene solo los dos archivos que creaste, implementa la función:

gcloud functions deploy secret_hello \
    --runtime python39 \
    --set-env-vars PROJECT_ID=${PROJECT_ID} \
    --trigger-http \
    --allow-unauthenticated

Deberías ver el siguiente resultado (truncado):

Deploying function (may take a while - up to 2 minutes)...done.

...

entryPoint: secret_hello
httpsTrigger:
  url: https://<REGION>-<PROJECT_ID>.cloudfunctions.net/secret_hello
...
status: ACTIVE
...

Recupera la URL de tu función (metadatos httpsTrigger.url) con el siguiente comando:

FUNCTION_URL=$(gcloud functions describe secret_hello --format 'value(httpsTrigger.url)')

Ahora, llama a tu función para probar que se pueda acceder a la función con el valor de retorno esperado:

curl $FUNCTION_URL

Deberías ver el siguiente resultado:

We meet again!

Esta función hace referencia a la versión más reciente del secreto, que se configuró para contener la cadena "Vuelve a", por lo que la función está funcionando según lo esperado.

11. ¡Felicitaciones!

Aprendiste a usar la API de Secret Manager con Python.

Limpia

Para evitar que se generen cargos en tu cuenta de Google Cloud por los recursos que usaste en este instructivo, sigue estos pasos:

  • En la consola de Cloud, ve a la página Administrar recursos.
  • En la lista de proyectos, selecciona el tuyo y haz clic en Borrar.
  • En el diálogo, escribe el ID del proyecto y, luego, haz clic en Cerrar para borrarlo.

Más información

Licencia

Este trabajo cuenta con una licencia Atribución 2.0 Genérica de Creative Commons.