Como implantar com segurança no Cloud Run

1. Visão geral

Você vai modificar as etapas padrão para implantar um serviço no Cloud Run e melhorar a segurança. Em seguida, vai aprender a acessar o app implantado de maneira segura. O app é um "serviço de registro de parceiros". do aplicativo Cymbal Eats, que é usado por empresas que trabalham com a empresa para processar pedidos de comida.

O que você vai aprender

Ao fazer algumas pequenas alterações nas etapas mínimas padrão para implantar um app no Cloud Run, é possível aumentar significativamente a segurança dele. Você vai usar um app existente e as instruções de implantação, além de alterar as etapas da implantação para melhorar a segurança do app implantado.

Você vai aprender a autorizar o acesso ao app e fazer solicitações autorizadas.

Esta não é uma visão completa da segurança de implantação de aplicativos, mas uma análise das alterações que você pode fazer em todas as futuras implantações de aplicativos que vão melhorar a segurança com muito pouco esforço.

2. Configuração e requisitos

Configuração de ambiente personalizada

  1. Faça login no Console do Google Cloud e crie um novo projeto ou reutilize um existente. Crie uma conta do Gmail ou do Google Workspace, se ainda não tiver uma.

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • O Nome do projeto é o nome de exibição para os participantes do projeto. É uma string de caracteres não usada pelas APIs do Google Você pode atualizar a qualquer momento.
  • O ID do projeto precisa ser exclusivo em todos os projetos do Google Cloud e não pode ser mudado após a definição. O console do Cloud gera automaticamente uma string exclusiva. normalmente você não se importa com o que seja. Na maioria dos codelabs, é necessário fazer referência ao ID do projeto, que normalmente é identificado como PROJECT_ID. Se você não gostar do ID gerado, pode gerar outro ID aleatório. Como alternativa, você pode tentar o seu próprio e ver se ele está disponível. Ela não pode ser alterada após essa etapa e permanecerá durante a duração do projeto.
  • Para sua informação, há um terceiro valor, um Número de projeto, que algumas APIs usam. Saiba mais sobre esses três valores na documentação.
  1. Em seguida, ative o faturamento no console do Cloud para usar os recursos/APIs do Cloud. A execução deste codelab não será muito cara, se tiver algum custo. Para encerrar os recursos e não gerar faturamento além deste tutorial, exclua os recursos criados ou exclua o projeto inteiro. Novos usuários do Google Cloud estão qualificados para o programa de US$ 300 de avaliação sem custos.

Ativar o Cloud Shell

  1. No Console do Cloud, clique em Ativar o Cloud Shell853e55310c205094.png.

55efc1aaa7a4d3ad.png

Se você nunca iniciou o Cloud Shell antes, uma tela intermediária (abaixo da dobra) será exibida com a descrição dele. Se esse for o caso, clique em Continuar (e você não verá mais esse aviso). Esta é a aparência dessa tela única:

9c92662c6a846a5c.png

Leva apenas alguns instantes para provisionar e se conectar ao Cloud Shell.

9f0e51b578fecce5.png

Essa máquina virtual tem todas as ferramentas de desenvolvimento necessárias. Ela oferece um diretório principal persistente de 5 GB, além de ser executada no Google Cloud. Isso aprimora o desempenho e a autenticação da rede. Praticamente todo o seu trabalho neste codelab pode ser feito em um navegador ou no seu Chromebook.

Depois de se conectar ao Cloud Shell, você já estará autenticado e o projeto já estará configurado com seu ID do projeto.

  1. Execute o seguinte comando no Cloud Shell para confirmar que você está autenticado:
gcloud auth list

Resposta ao comando

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

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. Execute o seguinte comando no Cloud Shell para confirmar que o comando gcloud sabe sobre seu projeto:
gcloud config list project

Resposta ao comando

[core]
project = <PROJECT_ID>

Se o projeto não estiver configurado, configure-o usando este comando:

gcloud config set project <PROJECT_ID>

Resposta ao comando

Updated property [core/project].

Configuração do ambiente

Neste laboratório, você vai executar os comandos na linha de comando do Cloud Shell. Geralmente, é possível copiar os comandos e colá-los como estão, embora em alguns casos você precise alterar os valores de marcador para os corretos.

  1. Defina uma variável de ambiente como o ID do projeto para uso em comandos posteriores:
export PROJECT_ID=$(gcloud config get-value project)
export REGION=us-central1
export SERVICE_NAME=partner-registration-service
  1. Ative a API de serviço do Cloud Run que vai executar seu app, a API Firestore que vai fornecer armazenamento de dados NoSQL, a API Cloud Build que será usada pelo comando de implantação e o Artifact Registry que será usado para armazenar o contêiner do aplicativo quando criado:
gcloud services enable \
  run.googleapis.com \
  firestore.googleapis.com \
  cloudbuild.googleapis.com \
  artifactregistry.googleapis.com
  1. Inicializar o banco de dados do Firestore no modo Nativo. Esse comando usa a API App Engine, portanto, precisa ser ativado primeiro.

O comando precisa especificar uma região para o App Engine, que não vamos usar, mas que precisa ser criada por motivos históricos, e uma região para o banco de dados. Usaremos us-central para o App Engine e nam5 para o banco de dados. nam5 é o local multirregional dos Estados Unidos. Locais multirregionais maximizam a disponibilidade e a durabilidade do banco de dados.

gcloud services enable appengine.googleapis.com

gcloud app create --region=us-central
gcloud firestore databases create --region=nam5
  1. Clone o repositório do app de exemplo e navegue até o diretório
git clone https://github.com/GoogleCloudPlatform/cymbal-eats.git

cd cymbal-eats/partner-registration-service

3. Leia o README

Abra o editor e confira os arquivos do app. Consulte README.md, que descreve as etapas necessárias para implantar o app. Algumas dessas etapas podem envolver decisões de segurança implícitas ou explícitas a serem consideradas. Você mudará algumas dessas opções para melhorar a segurança do aplicativo implantado, conforme descrito aqui:

Etapa 3: executar npm install

É importante saber a procedência e a integridade de qualquer software de terceiros usado em um app. Gerenciar a segurança da cadeia de suprimentos de software é importante para a criação de qualquer software, não apenas de apps implantados no Cloud Run. Este laboratório focou na implantação, portanto, não aborda essa área, mas talvez seja melhor pesquisar o tópico separadamente.

Etapas 4 e 5: editar e executar deploy.sh

Estas etapas implantam o app no Cloud Run, deixando a maioria das opções no padrão. Você modificará essa etapa para tornar a implantação mais segura de duas maneiras principais:

  1. Não permita o acesso sem autenticação. Pode ser conveniente permitir que isso seja feito durante a exploração, mas esse é um serviço da Web para uso de parceiros comerciais e deve sempre autenticar seus usuários.
  2. Especifique que o aplicativo precisa usar uma conta de serviço dedicada e personalizada com apenas os privilégios necessários, em vez de uma conta padrão que provavelmente vai ter mais acesso a APIs e recursos do que o necessário. Isso é conhecido como princípio de privilégio mínimo e é um conceito fundamental da segurança de aplicativos.

Etapas 6 a 11: fazer exemplos de solicitações da Web para verificar o comportamento correto

Como a implantação do aplicativo agora requer autenticação, essas solicitações precisam incluir um comprovante da identidade do solicitante. Em vez de alterar esses arquivos, você fará solicitações diretamente na linha de comando.

4. Implante o serviço com segurança

Duas mudanças foram identificadas como necessárias no script deploy.sh: não permitir acesso não autenticado e usar uma conta de serviço dedicada com privilégios mínimos.

Primeiro, você criará uma nova conta de serviço e, em seguida, editará o script deploy.sh para fazer referência a essa conta de serviço e proibir o acesso não autenticado.Em seguida, implantará o serviço executando o script modificado antes de executar o script deploy.sh modificado.

Criar uma conta de serviço e conceder a ela o acesso necessário ao Firestore/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

Modifique o arquivo deploy.sh para proibir o acesso não autenticado(–no-allow-unauthenticated) e especificar a nova conta de serviço(–service-account) para o aplicativo implantado. Corrija "GOOGLE_PROJECT_ID" como o ID do seu projeto.

Você excluirá as duas primeiras linhas e alterará as outras três linhas, conforme mostrado abaixo.

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

Implante o serviço

Na linha de comando, execute o script deploy.sh:

./deploy.sh

Quando a implantação estiver concluída, a última linha da resposta ao comando vai mostrar o URL de serviço do novo app. Salve o URL em uma variável de ambiente:

export SERVICE_URL=<URL from last line of command output>

Agora tente buscar um pedido no app usando a ferramenta curl:

curl -i -X GET $SERVICE_URL/partners

A flag -i do comando curl instrui a inclusão de cabeçalhos de resposta na saída. A primeira linha da saída será:

HTTP/2 403

O aplicativo foi implantado com a opção de proibir solicitações não autenticadas. O comando curl não contém informações de autenticação, por isso foi recusado pelo Cloud Run. O aplicativo implantado não vai executar nem receber dados dessa solicitação.

5. Fazer solicitações autenticadas

O app implantado é invocado por meio de solicitações da Web, que agora precisam ser autenticadas pelo Cloud Run para permitir. As solicitações da Web são autenticadas com a inclusão de um cabeçalho Authorization no formato:

Authorization: Bearer identity-token

O token de identidade é uma string de curto prazo codificada, assinada criptograficamente e emitida por um provedor de autenticação confiável. Nesse caso, é necessário um token de identidade válido e emitido pelo Google.

Fazer uma solicitação como sua conta de usuário

A ferramenta CLI do Google Cloud pode fornecer um token para o usuário autenticado padrão. Execute este comando para receber um token de identidade para sua conta e salvá-lo na variável de ambiente ID_TOKEN:

export ID_TOKEN=$(gcloud auth print-identity-token)

Por padrão, os tokens de identidade emitidos pelo Google são válidos por uma hora. Execute o seguinte comando curl para fazer a solicitação que foi rejeitada anteriormente por não ter sido autorizada. Este comando vai incluir o cabeçalho necessário:

curl -i -X GET $SERVICE_URL/partners \
  -H "Authorization: Bearer $ID_TOKEN"

A resposta ao comando precisa começar com HTTP/2 200, indicando que a solicitação é aceitável e está sendo atendida. Se você aguardar uma hora e tentar essa solicitação novamente, ela falhará porque o token expirou. O corpo da resposta está no final da saída, depois de uma linha em branco:

{"status":"success","data":[]}

Ainda não há parceiros.

Registre parceiros usando os dados JSON de amostra no diretório com dois comandos curl:

curl -X POST \
  -H "Authorization: Bearer $ID_TOKEN" \
  -H "Content-Type: application/json" \
  -d "@example-partner.json" \
  $SERVICE_URL/partner

e

curl -X POST \
  -H "Authorization: Bearer $ID_TOKEN" \
  -H "Content-Type: application/json" \
  -d "@example-partner2.json" \
  $SERVICE_URL/partner

Repita a solicitação GET anterior para ver todos os parceiros registrados agora:

curl -i -X GET $SERVICE_URL/partners \
  -H "Authorization: Bearer $ID_TOKEN"

Você verá dados JSON com muito mais conteúdo, fornecendo informações sobre os dois parceiros registrados.

Fazer uma solicitação como uma conta não autorizada

A solicitação autenticada na última etapa foi bem-sucedida não apenas porque foi autenticada, mas também porque o usuário autenticado (sua conta) foi autorizado. Ou seja, a conta tinha permissão para invocar o app. Nem todas as contas autenticadas terão autorização para fazer isso.

A conta padrão usada na solicitação anterior foi autorizada por ter criado o projeto que contém o app e por padrão a permissão para invocar qualquer aplicativo do Cloud Run na conta. Essa permissão pode ser revogada, se necessário, o que é desejável em aplicativos de produção. Em vez de fazer isso agora, crie uma nova conta de serviço sem privilégios ou papéis atribuídos e a usará para tentar acessar o aplicativo implantado.

  1. Crie uma conta de serviço chamada tester.
gcloud iam service-accounts create tester
  1. Você receberá um token de identidade para essa nova conta da mesma forma que recebeu um token para sua conta padrão. No entanto, isso exige que sua conta padrão tenha permissão para representar contas de serviço. Conceda essa permissão à sua conta.
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
  1. Agora, execute o comando a seguir para salvar um token de identidade para essa nova conta na variável de ambiente TEST_IDENTITY. Se o comando mostrar uma mensagem de erro, aguarde um ou dois minutos e tente novamente.
export TEST_TOKEN=$( \
  gcloud auth print-identity-token \
    --impersonate-service-account \
    "tester@$PROJECT_ID.iam.gserviceaccount.com" \
)
  1. Faça a solicitação da Web autenticada como antes, mas usando este token de identidade:
curl -i -X GET $SERVICE_URL/partners \
  -H "Authorization: Bearer $TEST_TOKEN"

A resposta ao comando começará novamente com HTTP/2 403 porque a solicitação, embora autenticada, não está autorizada. A nova conta de serviço não tem permissão para invocar esse app.

Como autorizar uma conta

Uma conta de serviço ou usuário precisa ter o papel Chamador do Cloud Run em um serviço do Cloud Run para fazer solicitações a ele. Atribua esse papel à conta de serviço do testador com o 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}

Depois de aguardar um ou dois minutos para que o novo papel seja atualizado, repita a solicitação autenticada. Salve um novo TEST_TOKEN se já tiver passado uma hora ou mais desde que foi salvo pela primeira vez.

curl -i -X GET $SERVICE_URL/partners \
  -H "Authorization: Bearer $TEST_TOKEN"

A resposta ao comando agora começa com HTTP/1.1 200 OK e a última linha contém a resposta JSON. Essa solicitação foi aceita pelo Cloud Run e processada pelo app.

6. Diferenças entre autenticar programas e usuários

As solicitações autenticadas que você fez até agora usaram a ferramenta de linha de comando curl. Outras ferramentas e linguagens de programação poderiam ser usadas nesse lugar. No entanto, as solicitações autenticadas do Cloud Run não podem ser feitas usando um navegador da Web com páginas da Web simples. Se um usuário clicar em um link ou em um botão para enviar um formulário em uma página da Web, o navegador não vai adicionar o cabeçalho Authorization exigido pelo Cloud Run para solicitações autenticadas.

O mecanismo de autenticação integrado do Cloud Run é destinado ao uso por programas, não por usuários finais.

Observação:

O Cloud Run pode hospedar aplicativos da Web voltados para o usuário, mas esses tipos de aplicativos precisam configurar o Cloud Run para permitir solicitações não autenticadas dos usuários navegadores da Web. Se os aplicativos exigirem autenticação do usuário, o aplicativo vai precisar processar isso em vez de pedir para o Cloud Run fazer isso. O aplicativo pode fazer isso da mesma forma que os aplicativos da Web fora do Cloud Run. Este codelab não aborda como fazer isso.

Você deve ter notado que as respostas às solicitações de exemplo até agora foram objetos JSON, não páginas da web. Isso porque esse serviço de registro de parceiros é destinado a programas, e o JSON é um formulário conveniente para eles. Em seguida, você escreverá e executará um programa para consumir e usar esses dados.

Solicitações autenticadas de um programa Python

Um programa pode fazer solicitações autenticadas de um aplicativo seguro do Cloud Run usando solicitações HTTP padrão da Web, mas incluindo um cabeçalho Authorization. O único novo desafio para esses programas é ter um token de identidade válido e não expirado para colocar nesse cabeçalho. O token será validado pelo Cloud Run usando o Google Cloud Identity and Access Management (IAM). Por isso, ele precisa ser emitido e assinado por uma autoridade reconhecida pelo IAM. Há bibliotecas de cliente disponíveis em vários idiomas que os programas podem usar para solicitar a emissão desse token. A biblioteca de cliente que este exemplo usará é a google.auth do Python. Existem várias bibliotecas Python para fazer solicitações da Web em geral. este exemplo usa o conhecido módulo requests.

A primeira etapa é instalar as duas bibliotecas de cliente:

pip install google-auth
pip install requests

O código Python para solicitar um token de identidade para o usuário padrão é:

credentials, _ = google.auth.default()
credentials.refresh(google.auth.transport.requests.Request())
identity_token = credentials.id_token

Se você estiver usando um shell de comando, como o Cloud Shell, ou o shell padrão do terminal no seu computador, o usuário padrão será aquele autenticado dentro desse shell. No Cloud Shell, geralmente o usuário está conectado ao Google. Em outros casos, é qualquer usuário autenticado com gcloud auth login ou outro comando gcloud. Se o usuário nunca tiver feito login, não haverá um usuário padrão, e o código falhará.

Geralmente, para um programa que faz solicitações a outro programa, você geralmente não quer usar a identidade de uma pessoa, mas sim a identidade do programa solicitante. É para isso que as contas de serviço servem. Você implantou o serviço do Cloud Run com uma conta de serviço dedicada que fornece a identidade usada para fazer solicitações de API, como para o Cloud Firestore. Quando um programa é executado no Google Cloud Platform, as bibliotecas de cliente usam automaticamente a conta de serviço atribuída a ele como a identidade padrão. Assim, o mesmo código do programa funciona nas duas situações.

O código Python para fazer uma solicitação com um cabeçalho de autorização adicionado é:

auth_header = {"Authorization": "Bearer " + identity_token}
response = requests.get(url, headers=auth_header)

O programa Python completo a seguir faz uma solicitação autenticada ao serviço do Cloud Run para recuperar todos os parceiros registrados e, em seguida, imprimir os nomes e IDs atribuídos. Copie e execute o comando abaixo para salvar esse código no arquivo 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

Você executará este programa com um comando shell. Faça a autenticação como o usuário padrão para que o programa possa usar essas credenciais. Execute o comando gcloud auth abaixo:

gcloud auth application-default login

Siga as instruções para concluir o login. Em seguida, execute o programa na linha de comando:

python print_partners.py

A saída será semelhante a esta:

10102: Zippy food delivery
67292: Foodful

A solicitação do programa chegou ao serviço Cloud Run porque ele foi autenticado com sua identidade, e você é o proprietário do projeto e, portanto, tem autorização para executá-lo por padrão. Seria mais comum que esse programa fosse executado sob a identidade de uma conta de serviço. Quando executada na maioria dos produtos do Google Cloud, como Cloud Run ou App Engine, a identidade padrão é uma conta de serviço, em vez de uma conta pessoal.

7. Parabéns!

Parabéns, você concluiu o codelab.

O que vem em seguida:

Conheça outros codelabs da Cymbal Eats:

Limpar

Para evitar cobranças na sua conta do Google Cloud pelos recursos usados no tutorial, exclua o projeto ou mantenha o projeto e exclua cada um dos recursos.

Excluir o projeto

O jeito mais fácil de evitar cobranças é excluindo o projeto que você criou para este tutorial.