1. Visão geral
Você vai modificar as etapas padrão de implantação de um serviço no Cloud Run para melhorar a segurança e depois vai aprender a acessar o app implantado de forma segura. O app é um "serviço de registro de parceiros" do aplicativo Cymbal Eats, que é usado por empresas que trabalham com a Cymbal Eats para processar pedidos de comida.
O que você vai aprender
Com algumas pequenas mudanças nas etapas mínimas padrão para implantar um app no Cloud Run, é possível aumentar significativamente a segurança. Você vai usar um app e instruções de implantação existentes e mudar as etapas de implantação para melhorar a segurança do app implantado.
Você vai aprender a autorizar o acesso ao app e fazer solicitações autorizadas.
Este não é um olhar exaustivo sobre a segurança de implantação de aplicativos, mas sim as mudanças que você pode fazer em todas as implantações futuras de apps para melhorar a segurança com muito pouco esforço.
2. Configuração e requisitos
Configuração de ambiente personalizada
- 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.
- 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 É possível atualizar o local 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. Em geral, não importa o que seja. Na maioria dos codelabs, é necessário fazer referência ao ID do projeto, normalmente identificado como
PROJECT_ID
. Se você não gostar do ID gerado, crie outro aleatório. Se preferir, teste o seu e confira se ele está disponível. Ele não pode ser mudado após essa etapa e permanece durante o projeto. - Para sua informação, há um terceiro valor, um Número do projeto, que algumas APIs usam. Saiba mais sobre esses três valores na documentação.
- 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 evitar cobranças 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
- No Console do Cloud, clique em Ativar o Cloud Shell.
Se você nunca iniciou o Cloud Shell, vai aparecer uma tela intermediária (abaixo da dobra) com a descrição dele. Se esse for o caso, clique em Continuar e você não vai encontrar essa tela novamente. Esta é a aparência dessa tela única:
Leva apenas alguns instantes para provisionar e se conectar ao Cloud Shell.
Essa máquina virtual contém 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.
- 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`
- Execute o comando a seguir no Cloud Shell para confirmar se 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
Você vai executar comandos na linha de comando do Cloud Shell para este laboratório. Geralmente, é possível copiar os comandos e colá-los como estão, mas, em alguns casos, é necessário mudar os valores de marcador de posição para os corretos.
- Defina uma variável de ambiente para 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
- Ative a API do 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 manter o contêiner do aplicativo quando ele for criado:
gcloud services enable \
run.googleapis.com \
firestore.googleapis.com \
cloudbuild.googleapis.com \
artifactregistry.googleapis.com
- Inicialize o banco de dados do Firestore no modo nativo. Esse comando usa a API App Engine, então ele precisa ser ativado primeiro.
O comando precisa especificar uma região para o App Engine, que não será usada, mas 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 é a localização multirregião dos Estados Unidos. Os 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
- 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 analise os arquivos que compõem o app. Confira o 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. Você vai mudar algumas dessas opções para melhorar a segurança do app 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 é relevante para criar qualquer software, não apenas apps implantados no Cloud Run. Este laboratório se concentra na implantação, portanto não aborda essa área, mas você pode pesquisar o assunto separadamente.
Etapas 4 e 5: editar e executar deploy.sh
Estas etapas implantam o app no Cloud Run, deixando a maioria das opções como padrão. Você vai modificar essa etapa para tornar a implantação mais segura de duas maneiras:
- Não permita o acesso não autenticado. Pode ser conveniente permitir isso para testar coisas durante a exploração, mas este é um serviço da Web para uso por parceiros comerciais e sempre precisa autenticar os usuários.
- Especifique que o aplicativo precisa usar uma conta de serviço dedicada adaptada com apenas os privilégios necessários, em vez de uma padrão que provavelmente terá mais acesso a APIs e recursos do que o necessário. Isso é conhecido como princípio do menor privilégio e é um conceito fundamental da segurança de aplicativos.
Etapas de 6 a 11: fazer solicitações da Web de exemplo para verificar o comportamento correto
Como a implantação do aplicativo agora exige autenticação, essas solicitações precisam incluir um comprovante da identidade do solicitante. Em vez de alterar esses arquivos, você vai fazer solicitações diretamente na linha de comando.
4. Implantar o serviço com segurança
Foram identificadas duas mudanças 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ê vai criar uma nova conta de serviço e, em seguida, editar o script deploy.sh
para fazer referência a essa conta e impedir o acesso não autenticado.Depois, implante o serviço executando o script modificado antes de executar o script deploy.sh modificado.
Crie uma conta de serviço e conceda 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 app implantado. Corrija o GOOGLE_PROJECT_ID para ser o ID do seu projeto.
Você vai excluir as duas primeiras linhas e mudar três outras, 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 for concluída, a última linha da saída do 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 do app usando a ferramenta curl
:
curl -i -X GET $SERVICE_URL/partners
A flag -i
do comando curl
indica que ele deve incluir cabeçalhos de resposta na saída. A primeira linha da saída precisa ser:
HTTP/2 403
O app foi implantado com a opção de proibir solicitações não autenticadas. Esse comando curl não contém informações de autenticação, portanto, é recusado pelo Cloud Run. O aplicativo implantado não executa nem recebe dados dessa solicitação.
5. Fazer solicitações autenticadas
O app implantado é invocado fazendo solicitações da Web, que agora precisam ser autenticadas para que o Cloud Run as permita. As solicitações da Web são autenticadas incluindo um cabeçalho Authorization
do formulário:
Authorization: Bearer
identity-token
O token de identidade é uma string codificada e assinada criptograficamente de curto prazo emitida por um provedor de autenticação confiável. Nesse caso, é necessário um token de identidade válido e não expirado 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 salvar 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 comando curl a seguir para fazer a solicitação que foi rejeitada antes porque não estava autorizada. Esse comando vai incluir o cabeçalho necessário:
curl -i -X GET $SERVICE_URL/partners \
-H "Authorization: Bearer $ID_TOKEN"
A saída do comando deve começar com HTTP/2 200
, indicando que a solicitação é aceitável e está sendo atendida. Se você esperar uma hora e tentar essa solicitação novamente, ela vai falhar porque o token terá expirado. O corpo da resposta está no final da saída, após uma linha em branco:
{"status":"success","data":[]}
Ainda não há parceiros.
Registre parceiros usando os dados JSON de exemplo 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 conferir todos os parceiros registrados:
curl -i -X GET $SERVICE_URL/partners \
-H "Authorization: Bearer $ID_TOKEN"
Você vai encontrar dados JSON com muito mais conteúdo, com informações sobre os dois parceiros registrados.
Fazer uma solicitação como uma conta não autorizada
A solicitação autenticada feita na última etapa teve êxito 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 são autorizadas a fazer isso.
A conta padrão usada na solicitação anterior foi autorizada porque foi a conta que criou o projeto que contém o app e, por padrão, concedeu permissão para invocar qualquer aplicativo do Cloud Run na conta. Essa permissão pode ser revogada, se necessário, o que seria desejável em um aplicativo de produção. Em vez disso, você vai criar uma nova conta de serviço sem privilégios ou funções atribuídas e usar essa conta para tentar acessar o app implantado.
- Crie uma conta de serviço chamada
tester
.
gcloud iam service-accounts create tester
- Você vai receber um token de identidade para essa nova conta da mesma forma que recebeu um para sua conta padrão anteriormente. No entanto, isso exige que sua conta padrão tenha permissão para falsificar 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
- Agora execute o comando abaixo 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" \
)
- 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 saída do comando vai começar novamente com HTTP/2 403
porque a solicitação, embora autenticada, não é autorizada. A nova conta de serviço não tem permissão para invocar esse app.
Autorizar uma conta
Uma conta de usuário ou serviço precisa ter a função de invocação 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}
Após aguardar um ou dois minutos para que a nova função seja atualizada, repita a solicitação autenticada. Salve um novo TEST_TOKEN se já faz uma hora ou mais desde a primeira vez que ele foi salvo.
curl -i -X GET $SERVICE_URL/partners \
-H "Authorization: Bearer $TEST_TOKEN"
A saída do 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. Autenticar programas em vez de usuários
As solicitações autenticadas que você criou até agora usaram a ferramenta de linha de comando curl
. Você poderia usar outras ferramentas e linguagens de programação. 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 ao usuário, mas esses tipos de aplicativos precisam definir o Cloud Run para permitir solicitações não autenticadas dos navegadores da Web dos usuários. Se os aplicativos exigirem autenticação do usuário, eles precisarão processá-la em vez de pedir ao Cloud Run para fazer isso. O aplicativo pode fazer isso da mesma forma que os aplicativos da Web fora do Cloud Run. Como isso é feito está fora do escopo deste codelab.
Você pode ter notado que as respostas às solicitações de exemplo até agora foram objetos JSON, não páginas da Web. Isso ocorre porque esse serviço de registro de parceiros é destinado ao uso de programas, e o JSON é uma forma conveniente de consumo. Em seguida, você vai 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 por meio de solicitações da Web HTTP padrão, mas incluindo um cabeçalho Authorization
. O único desafio para esses programas é conseguir um token de identidade válido e não expirado para colocar nesse cabeçalho. Esse token será validado pelo Cloud Run usando o Google Cloud Identity and Access Management (IAM). Portanto, ele precisa ser emitido e assinado por uma autoridade reconhecida pelo IAM. Há bibliotecas de cliente disponíveis em muitas linguagens que os programas podem usar para solicitar a emissão de um token. A biblioteca de cliente usada neste exemplo é a google.auth
do Python. Existem várias bibliotecas do Python para fazer solicitações da Web em geral. Este exemplo usa o 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 de terminal padrão no seu computador, o usuário padrão será aquele que tiver feito a autenticação nesse shell. No Cloud Shell, geralmente é o usuário 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 esse código vai falhar.
Para um programa que faz solicitações de outro programa, geralmente não é necessário 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 ao fazer solicitações de API, como para o Cloud Firestore. Quando um programa é executado em uma plataforma do Google Cloud, as bibliotecas de cliente usam automaticamente a conta de serviço atribuída a ele como identidade padrão, para que o mesmo código de programa funcione 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 fará uma solicitação autenticada ao serviço do Cloud Run para recuperar todos os parceiros registrados e imprimir os nomes e os IDs atribuídos. Copie e execute o comando abaixo para salvar o 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ê vai executar esse programa com um comando de shell. Primeiro, você precisa se autenticar 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 do Cloud Run porque foi autenticada com sua identidade. Como você é o proprietário do projeto, está autorizado a executá-la por padrão. Seria mais comum que esse programa fosse executado com a identidade de uma conta de serviço. Quando executado na maioria dos produtos do Google Cloud, como o Cloud Run ou o App Engine, a identidade padrão é uma conta de serviço e é usada 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:
- Como acionar fluxos de trabalho do Cloud com o Eventarc
- Como acionar o processamento de eventos do Cloud Storage
- Como se conectar ao CloudSQL privado pelo Cloud Run
- Como se conectar a bancos de dados totalmente gerenciados do Cloud Run
- Proteger aplicativos sem servidor com o Identity Aware Proxy (IAP)
- Como acionar jobs do Cloud Run com o Cloud Scheduler
- Como proteger o tráfego de entrada do Cloud Run
- Como se conectar ao AlloyDB particular do GKE Autopilot
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.