DEV Community

César Augusto de Fázio
César Augusto de Fázio

Posted on AI-assisted

Policies no OCI: Condicionais

No artigo anterior, vimos como as Policies do OCI funcionam. Agora vamos falar de condicionais, que permitem um controle de acesso granular. Em miúdos, elas definem regras de acesso para determinados tipos de recursos sob condições específicas.

Com elas, podemos criar policies que exijam um endereço de IP específico do solicitante, ou que deem controle total apenas sobre uma única instância ou sobre recursos etiquetados.

Revisando a Sintaxe das Policies do OCI

Lembre-se: a sintaxe serve para oferecer a alguém acesso a uma ação sobre um recurso em algum lugar sob determinadas condições.

allow Subjects to Actions in Placement where CONDITIONS

  1. Subjects: Grupos, Usuários, Dynamic-Groups
  2. Actions: Verbo + Recurso (ou permissões granulares baseadas em chamadas de API)
  3. Placement: Compartment ou Tenancy
  4. Conditions: o assunto deste artigo

Controle de Acesso Granular

Toda condição é avaliada e resulta em um de três valores: True, False ou Not Applicable. A policy só concede o acesso quando a condição resulta em True. Nos outros casos, o acesso não é concedido.

Prefixos das Condições: Request e Target

Toda condição começa com um destes prefixos:

  • Request: avalia a requisição, ou seja, os atributos que a pessoa precisa ter para a policy autorizar o acesso.
  • Target: avalia o recurso-alvo, ou seja, os atributos do recurso que é o objeto de interesse da policy.

Em resumo: request fala de quem pede, e target fala de o que é pedido.

Exemplos:

  • request.permission = 'OBJECT_CREATE': requer a permissão de criar objetos em buckets para que a policy entre em vigor para o Subject
  • request.permission = 'VOLUME_INSPECT': requer do Subject a permissão de inspecionar volumes para que a Policy entre em vigor

Any e All

Quando há mais de uma condição, você escolhe como combiná-las:

  • Any: atende ao menos uma das condições.
  • All: atende a todas as condições.

Exemplo:

  • where any { request.permission = 'OBJECT_CREATE', request.permission = ’VOLUME_INSPECT’ }: requer que você tenha ou permissão de criar objetos, ou permissão de inspecionar volumes.
  • where any { request.permission = 'OBJECT_CREATE', request.permission = ’VOLUME_INSPECT’ }: requer que você tenha tanto a permissão de criar objetos, quanto a permissão de inspecionar volumes.

Combinando Any e All, Request e Target

É possível ser ainda mais granular. Imagine um compartment chamado Production que contém dois compartments chamados Neo e Nova.

Tenancy (raiz)  
└── Compartment Production  
    ├── Compartment Neo  
    └── Compartment Nova  
        └── Compartment NovaDBs
Enter fullscreen mode Exit fullscreen mode

Exemplo 1:

allow group NeoAdmins, NovaAdmins to manage virtual-network-family in compartment Production
 where any {
   all { target.compartment.id = '<ocid.do.compartment.Neo>', request.group.id = '<ocid.do.grupo.NeoAdmins>' },
  all { target.compartment.id = '<ocid.do.compartment.Nova>', request.group.id = '<ocid.do.grupo.NovaAdmins>' }  
 }
Enter fullscreen mode Exit fullscreen mode

Mas que diabos é isso? Por que não preferir uma versão mais simples?

Exemplo 2:

allow group NeoAdmins to manage virtual-network-family in compartment Production:Neo  
allow group NovaAdmins to manage virtual-network-family in compartment Production:Nova
Enter fullscreen mode Exit fullscreen mode

“É a mesma coisa, César, só que bem mais legível!”

Não. Existem algumas diferenças.

  1. Quebra de Herança: No exemplo 1, NovaAdmins terá acesso a todos os recursos de rede somente no compartment Nova, a herança não se aplica; então NovaAdmins não poderão administrar redes no compartment NovaDBs.
  2. Manutenção: No exemplo 1, é 1 policy com matriz atendendo a vários casos. Lembre-se que existe um limite de 500 policies por tenancy. No exemplo 2, para cada permissão extra, é preciso criar mais uma policy.
  3. Filtro por grupo: No exemplo 2, temos um allow group fixo, enquanto no exemplo 1, podemos juntar vários grupos e distribuir os acessos em uma única policy.
  4. Escopo: No exemplo 1, é possível usar as conditions para ter um acesso a um recurso exato: um compartment, uma instância, um dynamic-group, uma VCN, uma Subnet, etc. No exemplo 2, o escopo do grupo é o compartment já definido e os compartments herdeiros.
  5. Permissão Ampla vs Restrita: manage é um verbo amplo, que oferece todas as permissões possíveis sobre o recurso. O exemplo 1 permite mirar direto no recurso, o exemplo 2 oferece mais privilégios e herança.

Então o exemplo 1 é o certo e o exemplo 2 está errado? Não. Depende da regra de negócio.

Às vezes você precisa mesmo da herança e da legibilidade, especialmente se seu grupo usando a nuvem for pequeno.

Contudo, lembre-se que para cada requisição da API da OCI, a API vai conferir todas as políticas. Ter uma única policy declarada ao invés de 4 ou 5 gera melhor performance e também inclui mais espaço para outras policies. Não bata no teto das 500 policies.

Se sua empresa for do nível enterprise, você vai preferir algo similar ao exemplo 1.

Padrões com Coringa/Wildcard

Para comparar nomes, podemos usar padrões entre barras:

  • /exadata*/: começa com "exadata"
  • /*exadata/: termina com "exadata"
  • /*exadata*/: contém a palavra "exadata"

Controle de Acesso Baseado em Tags (TBAC)

Tags no OCI são recursos combinados em três elementos: Namespace + Key + Value.

Com o TBAC, a decisão de acesso passa a depender das tags aplicadas aos recursos, e não apenas do nome ou do compartment.

where target|request.<seu-alvo>.tag.{tagNamespace}.{tagKeyDefinition} = '<value>'
Enter fullscreen mode Exit fullscreen mode
  • <seu-alvo>: group, compartment, resource ou dynamic-group
  • {tagNamespace}: É o contexto da tag. Uma categoria que agrupa chaves de tags relacionadas (por exemplo, Operations, HumanResources, Finances).
  • {tagKeyDefinition}: É o molde que define as regras da tag. Define o nome da chave e como o valor será preenchido no futuro (String ou Enum).
  • <value>: Valor da tag, como pré-definido pela Key.
Tipo de Recurso TagNamespace tagKeyDefinition value Exemplo de Tag Completa
Resource Operations Environment Production target.resource.tag.Operations.Environment = "Production"
Dynamic-Group HumanResources Project Development target.principal.group.tag.HumanResources.Project = "Development"
Group Operations Environment Alpha target.principal.group.tag.Operations.Environment = "Alpha"
Compartment HumanResources CostCenter 42 target.principal.compartment.tag.HumanResources.CostCenter = "42"

Observe que tanto grupos quanto dynamic-groups usam group.

Criando Suas Tags

Governance & Administration → Tag Namespaces → Create Tag Namespace

Network Sources (controle de acesso checando IP e origem de rede)

Com as Network Sources, restringimos o acesso a partir da origem da requisição.

Criando uma Network Source:

Identity & Security → Network Source → Create Network Source

Você vai informar:

  • Nome
  • Descrição
  • VCN ou endereço IP
  • CIDR blocks

Sintaxe:

allow group NetworkAdmin to manage subnets in compartment XPTO where request.networkSource.name = "MinhaNetworkSource"
Enter fullscreen mode Exit fullscreen mode

Se um usuário do grupo NetworkAdmin tentar alterar uma sub-rede fora dos IPs cadastrados na fonte corporativa, a chamada de API será rejeitada pelo IAM.

OCI IAM para Objetos: Controle de Acesso Granular dentro de um Bucket

A granularidade chega até o nível dos objetos dentro de um bucket. Para isso, usamos:

  • target.object.name: o nome do objeto, onde podemos usar os padrões com curinga vistos acima.
  • request.permission: a permissão sendo solicitada na requisição.

Com essas duas variáveis, é possível, por exemplo, liberar a leitura apenas de objetos que sigam um determinado padrão de nome dentro de um bucket, mantendo o restante protegido.

Exemplos:

allow group QA to manage objects in tenancy where all { target.bucket.name = 'test-bucket', target.object.name = 'tests/*' }
Enter fullscreen mode Exit fullscreen mode

Permite ao grupo QA gerenciar objetos na tenancy… desde que o nome do bucket seja test-bucket e esteja dentro da pasta tests/! Ou seja, o grupo QA tem acesso limitado aos objetos da tenancy, sendo eles somente de uma pasta dentro de um bucket específico (lembrando que buckets possuem nomes únicos).

E se quiséssemos dar acesso de apenas leitura (Read-only) para o time de QA ao invés de gerenciamento total?

allow group QA to manage objects in tenancy where all { target.bucket.name = 'test-bucket', target.object.name = 'tests/*', any { request.permission = 'OBJECT_INSPECT', request.permission = 'OBJECT_READ' } }
Enter fullscreen mode Exit fullscreen mode

"Ah, mas eu poderia usar apenas o verbo read."

Poderia, mas caso você queira inserir outras permissões de forma granular, basta adicionar um request.permission. Lembre-se que manage é um verbo muito potente. Vamos supor que você queira dar somente permissão de leitura e escrita.

allow group QA to manage objects in tenancy where all { target.bucket.name = 'test-bucket', target.object.name = 'tests/*', any { request.permission = 'OBJECT_INSPECT', request.permission = 'OBJECT_READ', request.permission = 'OBJECT_OVERWRITE' } }
Enter fullscreen mode Exit fullscreen mode

A documentação usa OBJECT_CREATE nesses casos, mas aqui estou assumindo que a permissão de escrita é edição sobre objetos que já existem e não a criação de novos. Nada impede você de entregar mais uma permissão com OBJECT_CREATE e deixar o resto intocado.

Outra forma de escrever essas permissões de leitura e escrita:

allow group QA { OBJECT_INSPECT, OBJECT_READ, OBJECT_OVERWRITE } in tenancy where all { target.bucket.name = 'test-bucket', target.object.name = 'tests/*' }
Enter fullscreen mode Exit fullscreen mode

Mais enxuta e legível, fica a gosto.

Conclusão

As condicionais transformam policies amplas em regras precisas: quem pede, o que é pedido, de onde vem a requisição e até qual objeto dentro de um bucket. Combinando request, target, any, all, tags e Network Sources, você aplica o princípio do menor privilégio com muito mais controle sobre o seu ambiente.

FAQ

O que acontece se uma condicional do OCI avaliar como "Not Applicable"?

A requisição é tratada como não autorizada pela política em questão. O OCI adota negação padrão: para que o acesso seja liberado, pelo menos uma política deve avaliar todas as suas condições explicitamente como True.

Qual é a diferença prática entre target.compartment.id e a cláusula "in compartment"?

A cláusula in compartment define o escopo de herança hierárquica, cobrindo o compartimento listado e todas as suas subpastas. Já target.compartment.id = '<ocid>' valida apenas o compartimento específico indicado pelo identificador, bloqueando a herança em níveis inferiores.

É possível combinar checagem de IP e Tags na mesma condicional de policy?

Sim. Você pode estruturar um bloco com o operador all, exigindo que request.networkSource.name coincida com a rede autorizada e que target.resource.tag.<Namespace>.<Key> corresponda à tag esperada do recurso.

Existe limite para o número de condicionais dentro de uma policy no OCI?

O OCI não impõe um limite estrito de condicionais por linha, mas cada política tem um limite total de declarações/statements. Recomenda-se consolidar matrizes de acesso de forma legível para evitar falhas de manutenção e respeitar a cota máxima de 500 políticas por tenancy, 100 objetos de policy por tenancy e 50 statements por policy. Este é um limite fixo que não pode ser aumentado.

Top comments (0)