1. Descripción general
Modificarás los pasos predeterminados para implementar un servicio en Cloud Run para mejorar la seguridad y, luego, verás cómo acceder a la app implementada de forma segura. La app es un "servicio de registro de socios" de la aplicación Cymbal Eats, que usan las empresas que trabajan con Cymbal Eats para procesar pedidos de comida.
Qué aprenderás
Si realizas algunos pequeños cambios en los pasos mínimos predeterminados para implementar una app en Cloud Run, puedes aumentar significativamente su seguridad. Tomarás una app existente y las instrucciones de implementación, y cambiarás los pasos de implementación para mejorar la seguridad de la app implementada.
Luego, verás cómo autorizar el acceso a la app y realizar solicitudes autorizadas.
Esta no es una descripción exhaustiva de la seguridad de la implementación de aplicaciones, sino una descripción de los cambios que puedes realizar en todas tus implementaciones futuras de apps que mejorarán su seguridad con muy poco esfuerzo.
2. Configuración y requisitos
Cómo configurar el entorno a tu propio ritmo
- 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.
- 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 es ú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 importa cuál sea. En la mayoría de los codelabs, deberás hacer referencia al ID del proyecto (suele identificarse como
PROJECT_ID
). Si no te gusta el ID que se generó, podrías generar otro aleatorio. También puedes probar uno propio y ver si está disponible. No se puede cambiar después de este paso y se mantendrá durante todo el proyecto. - Recuerda que 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.
- 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 generen cobros 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.
Activar Cloud Shell
- En la consola de Cloud, haz clic en Activar Cloud Shell.
Si nunca has iniciado Cloud Shell, aparecerá una pantalla intermedia (mitad inferior de la página) en la que se describirá qué es. Si ese es el caso, haz clic en Continuar (y no volverás a verla). Así es como se ve la pantalla única:
El aprovisionamiento y la conexión a Cloud Shell solo tomará unos minutos.
Esta máquina virtual está cargada con todas las herramientas de desarrollo que necesitarás. 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.
- 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`
- En Cloud Shell, ejecuta el siguiente comando para confirmar que el comando 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].
Configuración del entorno
En este lab, ejecutarás comandos en la línea de comandos de Cloud Shell. Por lo general, puedes copiar los comandos y pegarlos tal como están, aunque en algunos casos deberás cambiar los valores de los marcadores de posición por los correctos.
- Establece una variable de entorno en el ID del proyecto para usarla en comandos posteriores:
export PROJECT_ID=$(gcloud config get-value project)
export REGION=us-central1
export SERVICE_NAME=partner-registration-service
- Habilita la API del servicio de Cloud Run que ejecutará tu app, la API de Firestore que proporcionará el almacenamiento de datos NoSQL, la API de Cloud Build que usará el comando de implementación y el Registro de artefactos que se usará para contener el contenedor de la aplicación cuando se compile:
gcloud services enable \
run.googleapis.com \
firestore.googleapis.com \
cloudbuild.googleapis.com \
artifactregistry.googleapis.com
- Inicializa la base de datos de Firestore en modo nativo. Ese comando usa la API de App Engine, por lo que primero se debe habilitar.
El comando debe especificar una región para App Engine, que no usaremos, pero que debemos crear por motivos históricos, y una región para la base de datos. Usaremos us-central para App Engine y nam5 para la base de datos. nam5 es la ubicación multirregional de Estados Unidos. Las ubicaciones multirregionales maximizan la disponibilidad y durabilidad de la base de datos.
gcloud services enable appengine.googleapis.com
gcloud app create --region=us-central
gcloud firestore databases create --region=nam5
- Clona el repositorio de la app de ejemplo y navega al directorio
git clone https://github.com/GoogleCloudPlatform/cymbal-eats.git
cd cymbal-eats/partner-registration-service
3. Revisa el archivo README
Abre el editor y observa los archivos que componen la app. Consulta el archivo README.md, en el que se describen los pasos necesarios para implementar esta app. Algunos de estos pasos pueden implicar decisiones de seguridad implícitas o explícitas que debes tener en cuenta. Cambiarás algunas de estas opciones para mejorar la seguridad de la app implementada, como se describe a continuación:
Paso 3: Ejecuta npm install
Es importante conocer la procedencia y la integridad de cualquier software de terceros que se use en una app. Administrar la seguridad de la cadena de suministro de software es relevante para compilar cualquier software, no solo las apps que se implementan en Cloud Run. Este lab se enfoca en la implementación, por lo que no aborda esta área, pero te recomendamos que investigues el tema por separado.
Pasos 4 y 5: Edita y ejecuta deploy.sh
Estos pasos implementan la app en Cloud Run y dejan la mayoría de las opciones en sus valores predeterminados. Modificarás este paso para que la implementación sea más segura de dos maneras clave:
- No permitas el acceso no autenticado. Puede ser conveniente permitirlo para probar funciones durante la exploración, pero este es un servicio web para que lo usen los socios comerciales y siempre debe autenticar a sus usuarios.
- Especifica que la aplicación debe usar una cuenta de servicio dedicada con solo los privilegios necesarios, en lugar de una predeterminada que probablemente tenga más acceso a la API y a los recursos de lo necesario. Esto se conoce como el principio de privilegio mínimo y es un concepto fundamental de la seguridad de las aplicaciones.
Pasos del 6 al 11: Realiza solicitudes web de muestra para verificar el comportamiento correcto
Dado que la implementación de la aplicación ahora requiere autenticación, estas solicitudes deben incluir un comprobante de la identidad del solicitante. En lugar de alterar estos archivos, realizarás solicitudes directamente desde la línea de comandos.
4. Implementa el servicio de forma segura
Se identificaron dos cambios necesarios en la secuencia de comandos deploy.sh
: no permitir el acceso no autenticado y usar una cuenta de servicio dedicada con privilegios mínimos.
Primero, crearás una cuenta de servicio nueva, luego, editarás la secuencia de comandos deploy.sh
para hacer referencia a esa cuenta de servicio y no permitir el acceso no autenticado.Luego, ejecutarás la secuencia de comandos modificada para implementar el servicio antes de que podamos ejecutar la secuencia de comandos deploy.sh modificada.
Crea una cuenta de servicio y bríndale el acceso necesario a Firestore o Datastore
gcloud iam service-accounts create partner-sa
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:partner-sa@${PROJECT_ID}.iam.gserviceaccount.com" \
--role=roles/datastore.user
Editar deploy.sh
Modifica el archivo deploy.sh
para no permitir el acceso no autenticado(–no-allow-unauthenticated) y especificar la nueva cuenta de servicio(–service-account) para la app implementada. Corrige el valor de GOOGLE_PROJECT_ID para que sea el ID de tu propio proyecto.
Borrarás las dos primeras líneas y cambiarás otras tres, como se muestra a continuación.
gcloud run deploy $SERVICE_NAME \
--source . \
--platform managed \
--region ${REGION} \
--no-allow-unauthenticated \
--project=$PROJECT_ID \
--service-account=partner-sa@${PROJECT_ID}.iam.gserviceaccount.com
Implemente el servicio
Desde la línea de comandos, ejecuta la secuencia de comandos deploy.sh
:
./deploy.sh
Cuando se complete la implementación, la última línea del resultado del comando mostrará la URL del servicio de la app nueva. Guarda la URL en una variable de entorno:
export SERVICE_URL=<URL from last line of command output>
Ahora, intenta recuperar un pedido de la app con la herramienta curl
:
curl -i -X GET $SERVICE_URL/partners
La marca -i
del comando curl
le indica que incluya encabezados de respuesta en el resultado. La primera línea del resultado debería ser la siguiente:
HTTP/2 403
La app se implementó con la opción de no permitir solicitudes no autenticadas. Este comando curl no contiene información de autenticación, por lo que Cloud Run lo rechaza. La aplicación implementada real ni siquiera se ejecuta ni recibe datos de esta solicitud.
5. Realiza solicitudes autenticadas
La app implementada se invoca mediante solicitudes web, que ahora deben autenticarse para que Cloud Run las permita. Para autenticar las solicitudes web, se incluye un encabezado Authorization
del siguiente formato:
Authorization: Bearer
identity-token
El token de identidad es una cadena codificada a corto plazo, firmada de forma criptográfica y emitida por un proveedor de autenticación de confianza. En este caso, se requiere un token de identidad válido y sin vencer emitido por Google.
Realiza una solicitud con tu cuenta de usuario
La herramienta Google Cloud CLI puede proporcionar un token para el usuario autenticado predeterminado. Ejecuta este comando para obtener un token de identidad para tu propia cuenta y guárdalo en la variable de entorno ID_TOKEN:
export ID_TOKEN=$(gcloud auth print-identity-token)
De forma predeterminada, los tokens de identidad emitidos por Google son válidos durante una hora. Ejecuta el siguiente comando de curl para realizar la solicitud que se rechazó antes porque no estaba autorizada. Este comando incluirá el encabezado necesario:
curl -i -X GET $SERVICE_URL/partners \
-H "Authorization: Bearer $ID_TOKEN"
El resultado del comando debe comenzar con HTTP/2 200
, lo que indica que la solicitud es aceptable y se está cumpliendo. (Si esperas una hora y vuelves a intentar esta solicitud, fallará porque el token habrá vencido). El cuerpo de la respuesta se encuentra al final del resultado, después de una línea en blanco:
{"status":"success","data":[]}
Aún no hay socios.
Registra a los socios con los datos de JSON de muestra en el directorio con dos comandos curl
:
curl -X POST \
-H "Authorization: Bearer $ID_TOKEN" \
-H "Content-Type: application/json" \
-d "@example-partner.json" \
$SERVICE_URL/partner
y
curl -X POST \
-H "Authorization: Bearer $ID_TOKEN" \
-H "Content-Type: application/json" \
-d "@example-partner2.json" \
$SERVICE_URL/partner
Repite la solicitud GET anterior para ver todos los socios registrados ahora:
curl -i -X GET $SERVICE_URL/partners \
-H "Authorization: Bearer $ID_TOKEN"
Deberías ver datos JSON con mucho más contenido que proporcionan información sobre los dos socios registrados.
Realiza una solicitud como una cuenta no autorizada
La solicitud autenticada que se realizó en el último paso se realizó correctamente no solo porque se autenticó, sino también porque el usuario autenticado (tu cuenta) estaba autorizado. Es decir, la cuenta tenía permiso para invocar la app. No todas las cuentas autenticadas tendrán autorización para hacerlo.
La cuenta predeterminada que se usó en la solicitud anterior se autorizó porque es la cuenta que creó el proyecto que contiene la app y, de forma predeterminada, le otorgó permiso para invocar cualquier aplicación de Cloud Run en la cuenta. Ese permiso se puede revocar si es necesario, lo que sería conveniente en una aplicación de producción. En lugar de hacerlo ahora, crearás una cuenta de servicio nueva sin privilegios ni roles asignados y la usarás para intentar acceder a la app implementada.
- Crea una cuenta de servicio llamada
tester
.
gcloud iam service-accounts create tester
- Recibirás un token de identidad para esta cuenta nueva de la misma manera que recibiste uno para tu cuenta predeterminada antes. Sin embargo, esto requiere que tu cuenta predeterminada tenga permiso para usar la identidad de cuentas de servicio. Otorga este permiso a tu cuenta.
export USER_EMAIL=$(gcloud config list account --format "value(core.account)")
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="user:$USER_EMAIL" \
--role=roles/iam.serviceAccountTokenCreator
- Ahora, ejecuta el siguiente comando para guardar un token de identidad para esta cuenta nueva en la variable de entorno TEST_IDENTITY. Si el comando muestra un mensaje de error, espera uno o dos minutos y vuelve a intentarlo.
export TEST_TOKEN=$( \
gcloud auth print-identity-token \
--impersonate-service-account \
"tester@$PROJECT_ID.iam.gserviceaccount.com" \
)
- Realiza la solicitud web autenticada como antes, pero con este token de identidad:
curl -i -X GET $SERVICE_URL/partners \
-H "Authorization: Bearer $TEST_TOKEN"
El resultado del comando volverá a comenzar con HTTP/2 403
porque la solicitud, aunque está autenticada, no está autorizada. La cuenta de servicio nueva no tiene permiso para invocar esta app.
Cómo autorizar una cuenta
Un usuario o una cuenta de servicio deben tener el rol de invocador de Cloud Run en un servicio de Cloud Run para poder realizar solicitudes a él. Otorga ese rol a la cuenta de servicio del verificador con el siguiente comando:
export REGION=us-central1
gcloud run services add-iam-policy-binding ${SERVICE_NAME} \
--member="serviceAccount:tester@$PROJECT_ID.iam.gserviceaccount.com" \
--role=roles/run.invoker \
--region=${REGION}
Después de esperar uno o dos minutos para que se actualice el nuevo rol, repite la solicitud autenticada. Guarda un TEST_TOKEN nuevo si transcurrió una hora o más desde que se guardó por primera vez.
curl -i -X GET $SERVICE_URL/partners \
-H "Authorization: Bearer $TEST_TOKEN"
El resultado del comando ahora comienza con HTTP/1.1 200 OK
y la última línea contiene la respuesta JSON. Cloud Run aceptó esta solicitud y la app la procesó.
6. Autenticación de programas en comparación con la autenticación de usuarios
Las solicitudes autenticadas que realizaste hasta ahora usaron la herramienta de línea de comandos curl
. Existen otras herramientas y lenguajes de programación que se podrían haber usado en su lugar. Sin embargo, las solicitudes autenticadas de Cloud Run no se pueden realizar con un navegador web que tenga páginas web simples. Si un usuario hace clic en un vínculo o en un botón para enviar un formulario en una página web, el navegador no agregará el encabezado Authorization
que requiere Cloud Run para las solicitudes autenticadas.
El mecanismo de autenticación integrado de Cloud Run está diseñado para que lo usen los programas, no los usuarios finales.
Nota:
Cloud Run puede alojar aplicaciones web para usuarios, pero ese tipo de aplicaciones deben configurar Cloud Run para permitir solicitudes sin autenticar de los navegadores web de los usuarios. Si las aplicaciones requieren autenticación del usuario, la aplicación debe controlarlo en lugar de pedirle a Cloud Run que lo haga. La aplicación puede hacerlo de la misma manera que las aplicaciones web fuera de Cloud Run. La forma en que se hace está fuera del alcance de este codelab.
Es posible que hayas notado que las respuestas a las solicitudes de ejemplo hasta ahora han sido objetos JSON, no páginas web. Esto se debe a que este servicio de registro de socios está diseñado para que lo usen los programas, y JSON es una forma conveniente para que lo consuman. A continuación, escribirás y ejecutarás un programa para consumir y usar estos datos.
Solicitudes autenticadas desde un programa de Python
Un programa puede realizar solicitudes autenticadas de una aplicación segura de Cloud Run a través de solicitudes web HTTP estándar, pero con un encabezado Authorization
. El único desafío nuevo para esos programas es obtener un token de identidad válido y sin vencer para colocarlo en ese encabezado. Cloud Run validará ese token con Google Cloud Identity and Access Management (IAM), por lo que el token debe ser emitido y firmado por una autoridad reconocida por IAM. Hay bibliotecas cliente disponibles en muchos lenguajes que los programas pueden usar para solicitar que se emita un token de este tipo. La biblioteca cliente que usará este ejemplo es la de google.auth
de Python. Existen varias bibliotecas de Python para realizar solicitudes web en general. En este ejemplo, se usa el popular módulo requests
.
El primer paso es instalar las dos bibliotecas cliente:
pip install google-auth
pip install requests
El código de Python para solicitar un token de identidad para el usuario predeterminado es el siguiente:
credentials, _ = google.auth.default()
credentials.refresh(google.auth.transport.requests.Request())
identity_token = credentials.id_token
Si usas una shell de comandos, como Cloud Shell o la shell de terminal estándar en tu propia computadora, el usuario predeterminado es el que se autenticó dentro de esa shell. En Cloud Shell, por lo general, es el usuario que accedió a Google. En otros casos, es cualquier usuario autenticado con gcloud auth login
o algún otro comando gcloud
. Si el usuario nunca accedió, no habrá un usuario predeterminado y este código fallará.
En el caso de un programa que realiza solicitudes a otro, por lo general, no es conveniente usar la identidad de una persona, sino la identidad del programa solicitante. Para eso se usan las cuentas de servicio. Implementaste el servicio de Cloud Run con una cuenta de servicio dedicada que proporciona la identidad que usa cuando realiza solicitudes a la API, como a Cloud Firestore. Cuando un programa se ejecuta en una plataforma de Google Cloud, las bibliotecas cliente usarán automáticamente la cuenta de servicio que se le asignó como identidad predeterminada, de modo que el mismo código de programa funcione en ambas situaciones.
El código de Python para realizar una solicitud con un encabezado de Authorization agregado es el siguiente:
auth_header = {"Authorization": "Bearer " + identity_token}
response = requests.get(url, headers=auth_header)
El siguiente programa completo de Python realizará una solicitud autenticada al servicio de Cloud Run para recuperar todos los socios registrados y, luego, imprimir sus nombres y los IDs asignados. Copia y ejecuta el siguiente comando para guardar este código en el archivo print_partners.py
.
cat > ./print_partners.py << EOF
def print_partners():
import google.auth
import google.auth.transport.requests
import requests
credentials, _ = google.auth.default()
credentials.refresh(google.auth.transport.requests.Request())
identity_token = credentials.id_token
auth_header = {"Authorization": "Bearer " + identity_token}
response = requests.get("${SERVICE_URL}/partners", headers=auth_header)
parsed_response = response.json()
partners = parsed_response["data"]
for partner in partners:
print(f"{partner['partnerId']}: {partner['name']}")
print_partners()
EOF
Ejecutarás este programa con un comando de shell. Primero, deberás autenticarte como el usuario predeterminado para que el programa pueda usar esas credenciales. Ejecuta el siguiente comando de gcloud auth:
gcloud auth application-default login
Sigue las instrucciones para completar el acceso. Luego, ejecuta el programa desde la línea de comandos:
python print_partners.py
El resultado se verá de la siguiente manera:
10102: Zippy food delivery
67292: Foodful
La solicitud del programa llegó al servicio de Cloud Run porque se autenticó con tu identidad, y tú eres el propietario de este proyecto y, por lo tanto, tienes autorización para ejecutarlo de forma predeterminada. Lo más común es que este programa se ejecute con la identidad de una cuenta de servicio. Cuando se ejecuta en la mayoría de los productos de Google Cloud, como Cloud Run o App Engine, la identidad predeterminada es una cuenta de servicio y se usa en lugar de una cuenta personal.
7. ¡Felicitaciones!
¡Felicitaciones! Completaste el codelab.
¿Qué sigue?
Explora otros codelabs de Cymbal Eats:
- Cómo activar flujos de trabajo de Cloud con Eventarc
- Cómo activar el procesamiento de eventos desde Cloud Storage
- Cómo conectarse a CloudSQL privado desde Cloud Run
- Cómo conectarse a bases de datos completamente administradas desde Cloud Run
- Cómo proteger una aplicación sin servidor con Identity-Aware Proxy (IAP)
- Cómo activar trabajos de Cloud Run con Cloud Scheduler
- Protege el tráfico de entrada de Cloud Run
- Cómo conectarse a AlloyDB privado desde GKE Autopilot
Limpia
Para evitar que se apliquen cargos a tu cuenta de Google Cloud por los recursos usados en este instructivo, borra el proyecto que contiene los recursos o conserva el proyecto y borra los recursos individuales.
Borra el proyecto
La manera más fácil de eliminar la facturación es borrar el proyecto que creaste para el instructivo.