Todos os artigos

// Knowledge.log — 技術記事

Deny na resource policy IAM: o Allow da identity que nunca chega a valer

Allow na identity policy não libera GetObject se a bucket policy tem Deny no mesmo ARN. Três JSON e o User Guide, sem conta.

O PR adiciona um statement à identity policy do usuário app-reader: Effect: Allow, s3:GetObject, arn:aws:s3:::amzn-s3-demo-bucket/*. O revisor lê, confere o ARN e aprova. A bucket policy não aparece no diff e quase sempre fica em outro repositório, então ninguém abre. Só que ela já tem um Deny para esse mesmo principal, essa mesma ação e esse mesmo ARN.

O merge "provou" que o GetObject estava liberado. O Deny continuou no bucket e não mudou nada.

A ideia aqui é sair com uma tabela de três linhas cruzando identity policy e bucket policy, e saber em qual delas o Allow da identity resolve o acesso e em quais ele não serve para nada. O material vem do IAM User Guide e do S3 User Guide, recuperados em 06/10/2026, e de três pares de JSON locais. Nada rodou contra uma conta AWS nem no Policy Simulator. Os resultados da tabela são o que a documentação manda esperar.

O texto sobre IfExists na política IAM já tratou de chave de contexto ausente dentro de uma única identity policy. Este é outro problema: o Effect de duas políticas diferentes, a da identidade e a do recurso, avaliadas no mesmo request. Não há Condition em nenhum JSON daqui.

A regra de avaliação, uma vez só

Toda requisição começa em implicit deny. A única exceção é o root user da conta. Daí em diante a ordem é curta:

  1. Se qualquer política aplicável tiver um Deny explícito que case com o request, a decisão final é Deny.
  2. Se não houver Deny, um Allow vindo da identity policy ou da resource policy basta na mesma conta, para a maioria dos recursos.
  3. Se não houver nem Deny nem Allow, vale o default Deny.

A página de lógica de avaliação descreve o resultado como a união das permissões dos dois tipos de política e fecha com a frase que importa aqui:

> An explicit deny in either of these policies overrides the allow.

O "para a maioria dos recursos" também está escrito. A página de deny e allow diz:

> For most resources, you only need an explicit Allow for the principal in either an identity-based policy or a resource-based policy to grant access. IAM role trust policies and KMS key policies are exceptions to this logic.

O S3 não aparece entre as exceções. A bucket policy é uma resource-based policy e entra no caso geral.

Todos os JSON usam "Version": "2012-10-17". Essa continua sendo a versão atual da linguagem de políticas, e a única alternativa documentada é a legada 2008-10-17.

Os três pares de JSON

Os placeholders são o usuário arn:aws:iam::111122223333:user/app-reader e o bucket amzn-s3-demo-bucket. A ação é s3:GetObject, e por isso o Resource aponta para os objetos (/*), não para o bucket.

Repare em uma diferença de forma que vale para todos os casos. A identity policy não tem Principal, porque o principal é implicitamente a identidade em que ela está anexada, e o elemento é proibido ali. A bucket policy é obrigada a declarar Principal. Quando você coloca os dois documentos lado a lado, é esse campo que mostra qual é qual.

Caso A: Allow na identity, Deny na bucket policy

Identity policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "IdentityAllowGetObject",
      "Effect": "Allow",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::amzn-s3-demo-bucket/*"
    }
  ]
}

Bucket policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ResourceDenyGetObject",
      "Effect": "Deny",
      "Principal": {
        "AWS": "arn:aws:iam::111122223333:user/app-reader"
      },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::amzn-s3-demo-bucket/*"
    }
  ]
}

Este é o cenário do PR do começo. O Allow da identity está correto e casa com o request. O Deny da bucket policy também casa, com o mesmo principal, a mesma ação e o mesmo ARN. Pela regra, o Deny explícito vence e o Allow nem entra na conta.

Caso B: Deny na identity, Allow na bucket policy

Identity policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "IdentityDenyGetObject",
      "Effect": "Deny",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::amzn-s3-demo-bucket/*"
    }
  ]
}

Bucket policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ResourceAllowGetObject",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::111122223333:user/app-reader"
      },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::amzn-s3-demo-bucket/*"
    }
  ]
}

É o espelho do caso A. Agora quem aprova sem olhar o outro lado é o dono do bucket: ele concede o acesso ao app-reader e não sabe que a identity policy do usuário já nega a mesma ação. O resultado é o mesmo, porque a regra não tem preferência por lado. Um Deny explícito em qualquer política aplicável basta.

Caso C: só Allow na identity

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "IdentityAllowGetObjectOnly",
      "Effect": "Allow",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::amzn-s3-demo-bucket/*"
    }
  ]
}

Aqui não existe bucket policy, ou existe uma sem nenhum statement que case com esse request. Não há Deny em lugar nenhum e há um Allow de um lado. Na mesma conta, isso basta. A página de fundamentos da avaliação diz isso diretamente:

> If either the identity-based policy or the resource-based policy within the same account allows the request and the other doesn't, the request is still allowed.

O caso C é o único em que a leitura apressada do revisor estaria certa. Só que ela estaria certa por sorte, porque ele também não abriu a bucket policy.

Resultado esperado na mesma conta

CasoIdentity policyBucket policyResultado esperado (mesma conta)
AAllowDeny no mesmo ARN e principalNegado: o Deny explícito vence
BDenyAllow no mesmo ARN e principalNegado: o Deny explícito vence
CAllownenhuma (ou nada que case)Permitido: Allow de um lado basta

Duas das três linhas negam, e em nenhuma das duas dá para chegar à resposta lendo só a identity policy.

O caso C muda quando o principal e o bucket estão em contas diferentes. Aí a AWS avalia a requisição nas duas contas, e a página de avaliação cross-account é explícita:

> The request is allowed only if both evaluations return a decision of Allow.

Ou seja, um Allow na identity da conta de origem, sem um Allow na bucket policy da conta dona do recurso, não concede o GetObject. Esse contraste vem só da documentação. Não há quarto fixture nem AssumeRole executado.

Onde a revisão escorrega

O erro principal é tratar o Allow da identity como suficiente. Ele é necessário em alguns cenários (cross-account) e suficiente só quando nenhuma outra política aplicável tem Deny. "Suficiente" é uma afirmação sobre todas as políticas do request, e o diff mostrou uma.

O segundo erro é mais sutil: confundir um statement que não se aplica com um Effect: Deny. Se a bucket policy tiver um Allow para outro prefixo ou outro principal, esse statement não casa com o request e, para este request, ele é só implicit deny. Ele não anula o Allow da identity. Um Deny que casa anula. A pergunta certa ao abrir a bucket policy não é "ela me concede?", e sim "algum statement com Effect: Deny casa com este principal, esta ação e este ARN?".

O terceiro erro é carregar a intuição de outros serviços. Key policy do KMS e trust policy de role do IAM são as exceções documentadas à regra de "Allow num lado basta", e não servem de modelo para pensar sobre S3. O caminho contrário também não funciona: o que vale para o bucket não explica o comportamento delas.

Limites desta tabela

A tabela cruza só o Effect da identity policy com o da bucket policy, na mesma conta, sem Condition. No acesso real entram outras camadas que podem negar ou restringir o que a tabela marca como permitido: SCP, RCP, permissions boundary, session policy, Block Public Access e ACLs (junto com a configuração de Object Ownership do bucket). Nenhuma delas foi modelada aqui. Os resultados também não foram confirmados numa conta nem no Policy Simulator. São a leitura do User Guide aplicada a três JSON.

O que fazer no review

A bucket policy é parte do diff, mesmo quando não aparece nele. Abra os dois documentos, a identity policy que o PR altera e a bucket policy que está de fato no bucket, não uma cópia antiga em outro repositório. Procure primeiro os statements com Effect: Deny que casem com o principal, a ação e o ARN do pedido. Só depois disso faz sentido avaliar os Allow.

Se existir um Deny assim, um PR que só anexa um Allow na identity não resolve o acesso, e a DevDojo recusaria o merge. A conversa precisa ir para quem é dono da bucket policy: ou o Deny está errado e é ali que se corrige, ou ele está certo e o pedido de acesso é que não deveria passar. Não existe Allow que contorne isso pelo outro lado.

A DevDojo não faria merge de uma mudança de acesso ao S3 depois de ler só a identity policy. A regra perde força se a mudança não depende de política nenhuma do bucket. Para saber disso, porém, você precisa ter aberto a bucket policy. E num cenário cross-account a exigência só aumenta, porque os dois lados precisam do Allow.

Próximo passo

Na próxima revisão de s3:GetObject, coloque a identity policy e a bucket policy lado a lado, uma ao lado da outra no mesmo editor, e procure Effect: Deny antes de ler qualquer Allow. Depois encaixe o par em uma das três linhas da tabela. Se ele não couber em nenhuma, a resposta está nas camadas que ficaram fora dela, e aí vale uma avaliação na conta.

awsiams3

// Continue.training — 次のステップ

Conhecimento só conta quando vira prática.

Volte ao artigo, execute os exemplos e compartilhe o que aprendeu.

Explorar mais artigos