Azure · Key Vault · Segurança
Azure Managed HSM – AccessDenied ao criar uma Role Assignment
Recentemente precisei permitir que equipes de desenvolvimento gerenciassem o acesso às suas próprias chaves em um Azure Key Vault Managed HSM, sem conceder permissões no HSM inteiro.
Em resumo, a ideia era utilizar a função Managed HSM Policy Administrator no scope de uma chave. Desta forma, cada equipe poderia criar e remover Role Assignments apenas na chave que estivesse sob sua responsabilidade.
Foi quando recebi um AccessDenied utilizando o Azure CLI, mas, ao verificar a chave, notei que a permissão havia sido criada mesmo assim.
Vou mostrar o que aconteceu.
Cenário
O usuário que executava o comando tinha a função Managed HSM Policy Administrator no scope /keys/my-key.
De acordo com a documentação, essa função permite criar e remover Role Assignments. Quando ela é atribuída em /keys/<nome-da-chave>, a permissão fica limitada àquela chave.
Então, executei o seguinte comando para conceder Managed HSM Crypto User a uma Managed Identity:
az keyvault role assignment create \
--hsm-name <hsm-name> \
--assignee-object-id <object-id> \
--assignee-principal-type ServicePrincipal \
--role "Managed HSM Crypto User" \
--scope "/keys/my-key"
Erro
Mesmo com a permissão no scope da chave, recebi o seguinte erro:
ERROR: (AccessDenied) Not authorized to access
Microsoft.KeyVault/managedHsm/roleAssignments/read/action on /
Olhando para a mensagem, notei que o acesso negado aconteceu em /, que é o scope raiz do HSM, e não em /keys/my-key, que foi o scope utilizado no comando.
Até aí, imaginei que a Role Assignment não tivesse sido criada.
Mas, ao consultar as permissões da chave, a nova Role Assignment estava lá.
Ou seja, o Azure CLI mostrou um erro, mas a operação foi concluída.
Verificar se a Role Assignment foi criada
Para confirmar, você pode listar as permissões utilizando o mesmo scope da chave:
az keyvault role assignment list \
--hsm-name <hsm-name> \
--scope "/keys/my-key" \
--query "[?principalId=='<object-id>'].{role:roleName, scope:scope, principalId:principalId}" \
--output table
Se a identidade, a função e o scope aparecerem no resultado, a permissão foi criada, mesmo que o comando anterior tenha retornado AccessDenied.
O que aconteceu
Ao analisar o código do Azure CLI, encontrei o motivo do erro.
Durante a execução, o comando:
- procura a definição da função;
- cria a Role Assignment no scope informado;
- consulta novamente as definições de função para montar a resposta que será exibida.
O problema acontece no terceiro passo.
Depois de criar a permissão no scope correto, o código chama list_role_definitions sem informar o scope. Com isso, a consulta é feita em /.
Como o usuário tinha Managed HSM Policy Administrator apenas em /keys/my-key, ele podia criar a permissão naquela chave, mas não podia consultar as definições no HSM inteiro.
Então, a criação funcionou e a consulta realizada depois falhou.
Cuidado com scripts e pipelines
Esse comportamento pode causar um problema em scripts e pipelines, pois o comando será interpretado como uma falha e poderá ser executado novamente.
Antes de fazer uma nova tentativa:
- consulte as Role Assignments utilizando
--scope "/keys/<nome-da-chave>"; - confirme se a identidade, a função e o scope estão corretos;
- execute o comando novamente apenas se a permissão realmente não existir.
Cada execução pode criar uma nova Role Assignment para a mesma identidade, função e scope. Por isso, não basta confiar apenas no erro apresentado pelo Azure CLI.
Também seria possível conceder ao usuário permissão no scope raiz, mas isso daria acesso além do necessário e eliminaria a separação que eu queria manter entre as equipes.
O que aprendi
Neste caso, o AccessDenied não significa que a criação da Role Assignment falhou.
A permissão é criada no scope da chave e o erro acontece depois, quando o Azure CLI tenta fazer uma consulta em /.
Então, se você encontrar o mesmo comportamento, verifique primeiro as permissões diretamente em /keys/<nome-da-chave> antes de executar o comando novamente.
Abri uma issue no repositório do Azure CLI e, no momento em que escrevo este artigo, ela ainda está aberta.