Como configurar o acesso baseado em recursos

Este tópico descreve como gerenciar o acesso a recursos específicos usando vinculações de papéis condicionais em suas políticas de gerenciamento de identidade e acesso (IAM, na sigla em inglês). Ao usar atributos de recursos em uma expressão de condição, é possível conceder um subescopo da vinculação de papel com base no nome do recurso, tipo de recurso e/ou serviço do Google Cloud.

Antes de começar

  • Leia a Visão geral das Condições do IAM para entender os princípios básicos das vinculações de papéis condicionais do IAM.
  • Consulte os atributos de recurso que podem ser usados em uma expressão de condição.
  • O atributo do nome do recurso pode controlar o acesso aos seguintes serviços do Google Cloud:
    • Compute Engine
    • Cloud Key Management Service
    • Cloud Storage
    • Cloud Spanner

Como conceder acesso a um grupo de recursos com base em prefixos de nomes de recursos

Uma vinculação de função condicional pode ser usada para conceder acesso a membros de recursos com nomes que correspondam a um prefixo, como instâncias de máquina virtual (VM) do Compute Engine com nomes que começam com uma determinada string. O prefixo do nome do recurso é normalmente usado para agrupar recursos destinados a determinadas funcionalidades ou que têm propriedades específicas.

Considere o seguinte exemplo: a empresa de software ExampleCo executa cargas de trabalho em determinadas instâncias de VM que podem operar em dados confidenciais de assistência médica. Outras cargas de trabalho não confidenciais precisam ser executadas no mesmo projeto, e a ExampleCo quer garantir que os desenvolvedores dela tenham acesso limitado a instâncias de VM que operam em dados confidenciais. Para atingir essa meta, as instâncias de VM com dados confidenciais são nomeadas com um prefixo sensitiveAccess e outras instâncias de VM são nomeadas com um prefixo devAccess. Em seguida, as vinculações de papel condicionais são usadas para garantir que os desenvolvedores possam continuar produtivos com instâncias de VM devAccess normais, mas sem conceder a eles acesso a instâncias de VM sensitiveAccess.

É possível usar só o atributo de condição resource.name para gerenciar o acesso, mas é comum usar os atributos resource.type e resource.service. Ao usar esses outros atributos, é menos provável que uma condição afete o acesso a diferentes tipos de recursos com nomes semelhantes. O exemplo nesta seção controla o acesso usando os atributos resource.name e resource.type.

Para conceder acesso com base em um prefixo de nome para discos e instâncias do Compute Engine em um projeto:

Console

  1. No Console do Cloud, acesse a página IAM.

    Acessar a página do IAM

  2. Na lista de membros, localize o membro desejado e clique no botão .

  3. No painel Editar permissões, localize o papel desejado para configurar uma condição. Em seguida, em Condição, clique em Adicionar condição.

  4. No painel Editar condição, insira um título e uma descrição opcional para a condição.

  5. É possível adicionar uma expressão de condição usando o Criador de condições ou o Editor da condição. O criador de condições fornece uma interface interativa para selecionar o tipo de condição desejado, o operador e outros detalhes aplicáveis sobre a expressão. O editor da condição fornece uma interface baseada em texto para inserir manualmente uma expressão usando a sintaxe CEL.

    Criador de condições:

    1. Clique na lista suspensa Adicionar e em Condições agrupadas.
    2. Na lista suspensa Tipo de condição, selecione Recurso > Tipo.
    3. Na lista suspensa Operador, selecione é.
    4. Na lista suspensa Tipo de recurso, selecione compute.googleapis.com/Disk.
    5. Clique no primeiro botão Adicionar logo abaixo da condição que você acabou de inserir para adicionar outra cláusula à expressão.
    6. Na lista suspensa Tipo de condição, selecione Recurso > Nome.
    7. Na lista suspensa Operador, selecione Começa com.
    8. No campo Valor, insira o nome do recurso no formato apropriado, como projects/project-123/zones/us-central1-a/disks/devAccess, para um disco com nome que começa com devAccess.
    9. À esquerda de cada tipo de condição, clique em E para garantir que as duas cláusulas sejam verdadeiras.
    10. Clique no botão Adicionar logo acima do botão Salvar para adicionar outro conjunto agrupado de condições.
    11. Na lista suspensa Tipo de condição, selecione Recurso > Tipo.
    12. Na lista suspensa Operador, selecione é.
    13. Na lista suspensa Tipo de recurso, selecione compute.googleapis.com/Instance.
    14. Clique no primeiro botão Adicionar logo abaixo da condição que você acabou de inserir e adicione outra cláusula à expressão.
    15. Na lista suspensa Tipo de condição, selecione Recurso > Nome.
    16. Na lista suspensa Operador, selecione Começa com.
    17. No campo Valor, insira o nome do recurso no formato apropriado, como projects/project-123/zones/us-central1-a/instances/devAccess, para uma instância com nome que começa com devAccess.
    18. À esquerda de cada tipo de condição, clique em E para garantir que as duas cláusulas sejam verdadeiras.
    19. Clique no botão Adicionar logo acima do botão Salvar para adicionar o terceiro conjunto agrupado de condições.
    20. Para garantir que essa condição não afete outros recursos, adicione também as seguintes cláusulas: na lista suspensa Tipo de condição, selecione Recurso > Tipo.
    21. Na lista suspensa Operador, selecione não é.
    22. Na lista suspensa Tipo de recurso, selecione compute.googleapis.com/Disk.
    23. Clique no primeiro botão Adicionar logo abaixo da condição que você acabou de inserir e adicione outra cláusula à expressão.
    24. Na lista suspensa Tipo de condição, selecione Recurso > Tipo.
    25. Na lista suspensa Operador, selecione não é.
    26. Na lista suspensa Tipo de recurso, selecione compute.googleapis.com/Instance.
    27. À esquerda de cada tipo de condição, clique em E para garantir que as duas cláusulas sejam verdadeiras.
    28. Ao terminar, o criador de condições será semelhante ao seguinte:

    29. Clique em Salvar para aplicar a condição.

    30. Quando o painel Editar condição for fechado, clique em Salvar novamente no painel Editar permissões para atualizar a política do IAM.

    Editor da condição:

    1. Clique na guia Editor da condição e digite a seguinte expressão:

      (resource.type == "compute.googleapis.com/Disk" &&
      resource.name.startsWith("projects/project-123/regions/us-central1/disks/devAccess")) ||
      (resource.type == "compute.googleapis.com/Instance" &&
      resource.name.startsWith("projects/project-123/zones/us-central1-a/instances/devAccess")) ||
      (resource.type != "compute.googleapis.com/Disk" &&
      resource.type != "compute.googleapis.com/Instance")
    2. Depois de inserir a expressão, é possível inspecionar a sintaxe CEL clicando em Executar linter acima da caixa de texto na parte superior direita.

    3. Clique em Salvar para aplicar a condição.

    4. Quando o painel Editar condição for fechado, clique em Salvar novamente no painel Editar permissões para atualizar a política do IAM.

Comando gcloud

As políticas do IAM são definidas usando o padrão read-modify-write.

Execute o comando gcloud projects get-iam-policy para descobrir a política atual do IAM para o projeto. No exemplo a seguir, a versão JSON da política é transferida por download para um caminho no disco.

Comando:

gcloud projects get-iam-policy project-id --format=json > filepath

É feito o download da política do IAM no formato JSON:

{
  "bindings": [
    {
      "members": [
        "user:project-owner@example.com"
      ],
      "role": "roles/owner"
    },
    {
      "members": [
        "group:devs@example.com"
      ],
      "role": "roles/compute.instanceAdmin"
    }
  ],
  "etag": "BwWKmjvelug=",
  "version": 1
}

Para configurar a política com uma condição de prefixo de nome de recurso, adicione a seguinte expressão de condição destacada. Se você não estiver usando a versão 263.0.0 ou posterior da ferramenta gcloud, verifique se você atualizou o valor version para 3. Se você estiver usando uma versão mais recente da ferramenta gcloud, o valor máximo da política será definido automaticamente para você:

{
  "bindings": [
    {
      "members": [
        "user:project-owner@example.com"
      ],
      "role": "roles/owner"
    },
    {
      "members": [
        "group:devs@example.com"
      ],
      "role": "roles/compute.instanceAdmin",
      "condition": {
          "title": "Dev_access_only",
          "description": "Only access to devAccess* VMs",
          "expression":
            "(resource.type == 'compute.googleapis.com/Disk' &&
            resource.name.startsWith('projects/project-123/regions/us-central1/disks/devAccess')) ||
            (resource.type == 'compute.googleapis.com/Instance' &&
            resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/devAccess')) ||
            (resource.type != 'compute.googleapis.com/Instance' &&
            resource.type != 'compute.googleapis.com/Disk')"
      }
    }
  ],
  "etag": "BwWKmjvelug=",
  "version": 3
}

Em seguida, execute o comando gcloud projects set-iam-policy para definir a nova política:

gcloud projects set-iam-policy project-id filepath

A nova vinculação de papel condicional concederá permissões devs@example.com da seguinte maneira:

  • Todas as permissões de disco e instância serão concedidas somente se o nome do recurso começar com devAccess.

  • Todas as outras permissões no papel Administrador de instâncias são concedidas para todos os outros tipos de recursos.

API REST

Use o padrão read-mod-write para permitir o acesso a recursos específicos.

Primeiro, leia a política do IAM para o projeto:

O método projects.getIamPolicy da API Resource Manager recebe a política do IAM de um projeto.

Antes de usar os dados da solicitação abaixo, faça as substituições a seguir:

Método HTTP e URL:

POST https://cloudresourcemanager.googleapis.com/v1/projects/project-id:getIamPolicy

Corpo JSON da solicitação:

{
  "options": {
    "requestedPolicyVersion": policy-version
  }
}

Para enviar a solicitação, expanda uma destas opções:

Você receberá uma resposta JSON semelhante a esta:

{
  "version": 1,
  "etag": "BwWKmjvelug=",
  "bindings": [
    {
      "role": "roles/owner",
      "members": [
        "user:project-owner@example.com"
      ]
    },
    {
      "members": [
        "group:devs@example.com"
      ],
      "role": "roles/compute.instanceAdmin"
    }
  ]
}

Em seguida, modifique a política para permitir o acesso a recursos específicos. Altere o campo version para o valor 3:

{
  "version": 3,
  "etag": "BwWKmjvelug=",
  "bindings": [
    {
      "role": "roles/owner",
      "members": [
        "user:project-owner@example.com"
      ]
    },
    {
      "role": "roles/compute.instanceAdmin",
      "members": [
        "group:devs@example.com"
      ],
      "condition": {
          "title": "Dev_access_only",
          "description": "Only access to devAccess* VMs",
          "expression":
            "(resource.type == 'compute.googleapis.com/Disk' &&
            resource.name.startsWith('projects/project-123/regions/us-central1/disks/devAccess')) ||
            (resource.type == 'compute.googleapis.com/Instance' &&
            resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/devAccess')) ||
            (resource.type != 'compute.googleapis.com/Instance' &&
            resource.type != 'compute.googleapis.com/Disk')"
      }
    }
  ]
}

Por fim, grave a política atualizada:

O método projects.setIamPolicy da API Resource Manager define a política na solicitação como a nova política do IAM do projeto.

Antes de usar os dados da solicitação abaixo, faça as substituições a seguir:

  • project-id: o ID do projeto do Google Cloud.

Método HTTP e URL:

POST https://cloudresourcemanager.googleapis.com/v1/projects/project-id:setIamPolicy

Corpo JSON da solicitação:

{
  "policy": {
    "version": 3,
    "etag": "BwWKmjvelug=",
    "bindings": [
      {
        "role": "roles/owner",
        "members": [
          "user:project-owner@example.com"
        ]
      },
      {
        "role": "roles/compute.instanceAdmin",
        "members": [
          "group:devs@example.com"
        ],
        "condition": {
          "title": "Dev_access_only",
          "description": "Only access to devAccess* VMs",
          "expression":
            "(resource.type == 'compute.googleapis.com/Disk' &&
            resource.name.startsWith('projects/project-123/regions/us-central1/disks/devAccess')) ||
            (resource.type == 'compute.googleapis.com/Instance' &&
            resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/devAccess')) ||
            (resource.type != 'compute.googleapis.com/Instance' &&
            resource.type != 'compute.googleapis.com/Disk')"
        }
      }
    ]
  }
}

Para enviar a solicitação, expanda uma destas opções:

A resposta contém a política atualizada.


Como extrair valores de nomes de recursos

Os exemplos anteriores mostram comparações booleanas entre o nome do recurso ou o início do nome do recurso e outro valor. Em alguns casos, talvez seja necessário comparar um valor com uma parte específica do nome do recurso que não esteja no início do nome.

É possível usar a função extract() e especificar um modelo de extração para extrair a parte relevante do nome do recurso como uma string. Se necessário, é possível converter a string extraída em outro tipo, como um carimbo de data/hora. Depois de extrair um valor do nome do recurso, é possível comparar esse valor com outros valores.

Os exemplos a seguir mostram expressões de condição que usam a função extract(). Para detalhes sobre a função extract(), consulte a referência do atributo Condições do IAM.

Exemplo: como fazer a correspondência de pedidos dos últimos 30 dias

Suponha que você armazene informações de pedidos em vários buckets do Cloud Storage e que os objetos em cada bucket estejam organizados por data. Um nome de objeto típico pode ser semelhante a este exemplo:

projects/_/buckets/acme-orders-aaa/data_lake/orders/order_date=2019-11-03/aef87g87ae0876

Você quer fazer a correspondência de qualquer pedido dos últimos 30 dias. A condição a seguir faz a correspondência desses pedidos. Ela usa as funções duration() e date() para subtrair 30 dias (2.592.000 segundos) do tempo de solicitação e comparar esse carimbo de data/hora com a data do pedido:

request.time - duration('2592000s') < date(resource.name.extract('/order_date={date_str}/'))

Para detalhes sobre as funções date() e duration(), consulte a referência do atributo de data/hora.

Exemplo: como fazer a correspondência de VMs do Compute Engine em qualquer local

Suponha que você precise fazer a correspondência de qualquer VM do Compute Engine em qualquer local com nome que começa com dev-. O nome do recurso para uma VM usa um formato semelhante a projects/project-id/zones/zone-id/instances/instance-name. A condição a seguir será avaliada como true se o nome da instância começar com a string dev-:

resource.name.extract('/instances/{name}/').startsWith('dev-')

O texto entre chaves identifica a parte do nome do recurso que é extraída para comparação. Neste exemplo, o modelo de extração retira os caracteres entre a primeira ocorrência da string /instances/ e a próxima ocorrência da string /.

Considerações importantes sobre o uso de condições baseadas em recursos

Ao adicionar uma condição baseada em recurso, é importante considerar como as permissões concedidas serão afetadas pela condição.

Papéis personalizados

Considere o exemplo a seguir, que envolve papéis personalizados. Um administrador quer criar um papel personalizado que concede acesso para criar instâncias de VM, mas só permite que o usuário crie instâncias de VM em um projeto com um nome de recurso que comece com o prefixo staging, usando os discos com o mesmo prefixo de nome.

Para atingir essa meta, verifique se o papel concedido contém as permissões necessárias para criar uma instância de VM, o que significa permissões em tipos de recurso de instância e disco. Em seguida, confirme se a expressão de condição verifica o nome do recurso para ambos: discos e instâncias. Além desses dois tipos, outras permissões no papel não são concedidas.

A seguinte expressão de condição resultará em um comportamento inesperado. As permissões para operar em VMs do Compute Engine estão bloqueadas:

"expression":
"resource.type == 'compute.googleapis.com/Disk' &&
 resource.name.startsWith('projects/project-123/regions/us-central1/disks/staging')"

A expressão de condição a seguir inclui discos e instâncias e controlará o acesso com base no nome do recurso para esses dois tipos:

"expression":
"(resource.type == 'compute.googleapis.com/Disk' &&
  resource.name.startsWith('projects/project-123/regions/us-central1/disks/staging')) ||
 (resource.type == 'compute.googleapis.com/Instance' &&
  resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/staging'))"

A expressão de condição a seguir inclui discos e instâncias e controlará o acesso com base no nome do recurso para esses dois tipos. Para qualquer outro tipo de recurso, a expressão de condição concederá o papel independentemente do nome do recurso:

"expression":
"(resource.type == 'compute.googleapis.com/Disk' &&
  resource.name.startsWith('projects/project-123/regions/us-central1/disks/staging')) ||
 (resource.type == 'compute.googleapis.com/Instance' &&
  resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/staging')) ||
 (resource.type != 'compute.googleapis.com/Disk' &&
  resource.type != 'compute.googleapis.com/Instance')"

Permissões somente para pai

Na hierarquia de recursos do Google Cloud, algumas das permissões em um papel que afetam um recurso filho precisam ser aplicadas somente no nível pai. Por exemplo, para listar as chaves de criptografia do Cloud KMS, o usuário precisa receber a permissão cloudkms.cryptokeys.list no keyring que contém as chaves criptográficas, não as chaves propriamente ditas. Esses tipos de permissões são chamados de permissões somente para pai e se aplicam apenas a operações list.

Para conceder acesso adequadamente às permissões *.*.list ao usar condições, a expressão de condição deve definir os atributos resource.service e resource.type de acordo com o tipo de recurso pai dos recursos de destino a serem listados.

Veja estes exemplos. Usando o exemplo do Compute Engine acima, a expressão a seguir impede o acesso às permissões compute.disks.list e compute.instances.list porque o recurso em que as permissões são verificadas tem o valor de atributo resource.type de cloudresourcemanager.googleapis.com/Project.

"expression":
"(resource.type == 'compute.googleapis.com/Disk' &&
  resource.name.startsWith('projects/project-123/regions/us-central1/disks/devAccess')) ||
 (resource.type == 'compute.googleapis.com/Instance' &&
  resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/devAccess'))"

É comum que essas permissões list sejam concedidas com outras permissões para operações regulares no recurso. Para aumentar o escopo da permissão nesse caso, estenda o escopo somente para o tipo cloudresourcemanager.googleapis.com/Project ou o escopo para todas as outras permissões que não sejam do tipo instância ou disco.

"expression":
"(resource.type == 'compute.googleapis.com/Disk' &&
  resource.name.startsWith('projects/project-123/regions/us-central1/disks/devAccess')) ||
 (resource.type == 'compute.googleapis.com/Instance' &&
  resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/devAccess')) ||
 resource.type == 'cloudresourcemanager.googleapis.com/Project'"

ou

"expression":
"(resource.type == 'compute.googleapis.com/Disk' &&
  resource.name.startsWith('projects/project-123/regions/us-central1/disks/devAccess')) ||
 (resource.type == 'compute.googleapis.com/Instance' &&
  resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/devAccess')) ||
 (resource.type != 'compute.googleapis.com/Disk' &&
  resource.type != 'compute.googleapis.com/Instance')"