Skip to content

Tutorial: credenciais de nuvem

Objetivo

Ao final você verá o agente rodar a AWS CLI com uma credencial que o OpenBao cria para aquela execução e apaga quando o comando termina, e rodar o gcloud com um token de acesso que ele nunca vê.

Cada comando e saída abaixo vem de uma execução real, gravada em demo/tutorials/cloud/expected/ e conferida pelo run.sh --check.

O que é real e o que é emulado. O OpenBao e o plugin de segredos da AWS são os de verdade. A AWS em si é o moto, um emulador local das APIs de IAM e STS, então a execução não precisa de conta AWS. O plugin do Google Cloud precisa de um projeto Google real, então o caso do gcloud guarda um token falso no KV e mostra o mecanismo de arquivo que as credenciais gcp: usam.

Rodar

pip install "moto[server]"     # mais as CLIs aws e gcloud
export KH_AWS_PLUGIN=/caminho/para/openbao-plugin-secrets-aws   # da release do plugin no GitHub
bash demo/tutorials/cloud/run.sh all
bash demo/tutorials/cloud/run.sh --check

Script: demo/tutorials/cloud/run.sh. A sessão usa duas políticas: policy-project-read.hcl e policy-cloud.hcl. KEYHOLDER_DYNAMIC=aws/creds/deploy.

1. O humano habilita o engine da AWS

Fora do chat. O role cria um usuário IAM por credencial, que só pode chamar sts:GetCallerIdentity.

# The human enables the AWS secrets engine and a role (outside the chat)

$ bao secrets enable -path=aws openbao-plugin-secrets-aws
Success! Enabled the openbao-plugin-secrets-aws secrets engine at: aws/
exit_code=0

$ bao write aws/config/root access_key=moto-root secret_key=moto-root-secret region=us-east-1 iam_endpoint=http://127.0.0.1:15000 sts_endpoint=http://127.0.0.1:15000
Success! Data written to: aws/config/root
exit_code=0

$ bao write aws/roles/deploy credential_type=iam_user policy_document={"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"sts:GetCallerIdentity","Resource":"*"}]}
Success! Data written to: aws/roles/deploy
exit_code=0

The emulator's root keys are fake. With real AWS they are an IAM user that may create users; the agent never gets them.

2. O agente vê o caminho, não uma credencial

O keyholder_list mostra o caminho dinâmico permitido e como usá-lo. A listagem nunca o lê: uma leitura criaria uma credencial.

# The agent sees the dynamic path and how to use it, not a credential

$ BAO_WRAPPING_TOKEN=<wrapped token> khcall keyholder_list
{
  "dynamic": [
    {
      "path": "aws/creds/deploy",
      "use": "credentials: [\"aws:aws/creds/deploy\"]"
    }
  ],
  "entries": [
    {
      "ref": "vault://my-project/gcp/ACCESS_TOKEN",
      "version": 1
    }
  ],
  "note": "Use {{vault://path/KEY}} in the args, env or files of keyholder_run. Dynamic paths are read when the command runs and revoked after it; use them through credentials or {{bao://path#field}}.",
  "scope": "my-project"
}
exit_code=0

3. Uma credencial por execução

O agente pede credentials: ["aws:aws/creds/deploy"]. O Keyholder lê o caminho uma vez, define AWS_ACCESS_KEY_ID e AWS_SECRET_ACCESS_KEY no filho, roda o aws e revoga o lease quando ele termina. O ARN mostra o usuário que o OpenBao criou para essa execução; depois dela não sobra nenhum.

# The agent runs the AWS CLI with a credential made for this run

$ BAO_WRAPPING_TOKEN=<wrapped token> khcall keyholder_run '{"command":"aws","args":["--endpoint-url","http://127.0.0.1:15000","sts","get-caller-identity","--query","Arn","--output","text"],"env":{"AWS_DEFAULT_REGION":"us-east-1"},"credentials":["aws:aws/creds/deploy"]}'
{
  "exit_code": 0,
  "notes": [
    "revoked 1 dynamic credential lease(s) after the run"
  ],
  "refs_used": [
    "bao://aws/creds/deploy#access_key",
    "bao://aws/creds/deploy#secret_key"
  ],
  "stderr": "",
  "stdout": "arn:aws:iam::123456789012:user/vault-token-deploy-<id>\n"
}
--- stderr
<timestamp> audit cmd="aws" refs=[bao://aws/creds/deploy#access_key bao://aws/creds/deploy#secret_key] exit=0 dur=<duration> status=ok
exit_code=0

IAM users left in AWS after the run (checked by the human): 0

Keyholder revoked the lease when aws exited, and OpenBao deleted the user it had created for the run.

4. Só caminhos listados

Um caminho fora de KEYHOLDER_DYNAMIC é recusado antes de qualquer leitura, mesmo que a política permitisse.

# A dynamic path outside KEYHOLDER_DYNAMIC is refused before any read

$ BAO_WRAPPING_TOKEN=<wrapped token> khcall keyholder_run '{"command":"aws","args":["sts","get-caller-identity"],"credentials":["aws:aws/creds/admin"]}'
error: bao://aws/creds/admin#access_key: dynamic path not allowed: it must be listed in KEYHOLDER_DYNAMIC (aws/creds/admin)
--- stderr
<timestamp> audit cmd="aws" refs=[] exit=0 dur=<duration> status=error: bao://aws/creds/admin#access_key: dynamic path not allowed: it must be listed in KEYHOLDER_DYNAMIC (aws/creds/admin)
exit_code=1

5. gcloud com arquivo de token

O files entrega ao gcloud o caminho de um arquivo temporário com o token (CLOUDSDK_AUTH_ACCESS_TOKEN_FILE), apagado depois da execução. O gcloud imprime o token que recebeu; o agente o lê mascarado. Com o plugin do Google Cloud, credentials: ["gcp:gcp/roleset/<nome>/token"] faz o mesmo com um token criado pelo plugin.

# gcloud reads an access token from a file it gets for this run only

$ BAO_WRAPPING_TOKEN=<wrapped token> khcall keyholder_run '{"command":"gcloud","args":["auth","print-access-token"],"env":{"CLOUDSDK_CONFIG":"/tmp/keyholder-tutorial-gcloud","CLOUDSDK_CORE_DISABLE_PROMPTS":"1"},"files":{"CLOUDSDK_AUTH_ACCESS_TOKEN_FILE":"{{vault://my-project/gcp/ACCESS_TOKEN}}"}}'
{
  "exit_code": 0,
  "refs_used": [
    "vault://my-project/gcp/ACCESS_TOKEN"
  ],
  "stderr": "",
  "stdout": "[hidden:vault://my-project/gcp/ACCESS_TOKEN]\n"
}
--- stderr
<timestamp> audit cmd="gcloud" refs=[vault://my-project/gcp/ACCESS_TOKEN] exit=0 dur=<duration> status=ok
exit_code=0

O que isso mostra

  • Uma credencial dinâmica existe só durante o comando: lida na execução, revogada depois dela.
  • O agente vê o caminho e o resultado, nunca as chaves nem o token.
  • A revogação depende do tipo de credencial: usuários IAM são apagados; credenciais STS e tokens de acesso do GCP só expiram. Veja Configuração → Credenciais dinâmicas.