Denylist de tools
O RootPilot pede leitura privilegiada da sua stack inteira, e para uma parte dela a resposta legítima é não. As operações que leem security group, ACL e regra de firewall descrevem a superfície de ataque da sua infraestrutura; há times que não vão autorizar isso, e não deveriam ter que desligar a AWS inteira — jogando fora custo, compute e RDS junto — para conseguir.
A denylist é como você diz “pode ler minhas métricas e meus logs, mas não os meus security groups”.
Promessa e garantia
Seção intitulada “Promessa e garantia”Uma denylist que vivesse só no nosso control-plane seria uma configuração nossa, que você teria que confiar que respeitamos. Aplicada na borda, ela é verificável por você: você lê o manifesto do próprio deployment. É o mesmo raciocínio que já rege as suas credenciais (nunca custodiadas fora), a redação de PII (na borda, antes de sair) e o gate de escrita.
Isso tem uma consequência que preferimos dizer na cara: a tela do control-plane é rascunho. Enquanto o artefato não estiver implantado, nada muda — nem a superfície de tools, nem o que o agente consegue ler. Esconder tools com base numa política que você compôs e nunca implantou produziria a sensação de proteção sem a proteção, e um controle de segurança que existe só na nossa interface não é um controle de segurança.
O que dá para negar
Seção intitulada “O que dá para negar”Quatro níveis, resolvidos na união — o mais fino vence sempre que se sobrepõem:
| Nível | Exemplo | Quando usar |
|---|---|---|
| Grupo nomeado | network-posture, iam |
O caminho principal. Recorte curado que acompanha o catálogo. |
| Capability | security, audit |
Quando a categoria inteira está fora de questão. |
| Connector | amplitude |
Quando a fonte inteira está fora de questão. |
| Operação | network.listSecurityGroups |
Ajuste fino, quando nenhum recorte acima serve. |
Por que grupo, e não capability
Seção intitulada “Por que grupo, e não capability”Porque capability é grosso demais para o caso que motiva o recurso. A capability network contém a
leitura de security group e a de VPC, subnet, load balancer, target group e transit gateway — negá-la
inteira derruba junto o mapa de recursos, o blast radius e a topologia de DNS, que provavelmente é o
oposto do que você quer.
O grupo network-posture isola exatamente as operações que descrevem postura de rede — regra de firewall,
ACL, security group — e deixa o resto de pé.
Por que não uma lista de operações
Seção intitulada “Por que não uma lista de operações”Você pode escrever uma, e para ajuste fino é o certo. Mas uma lista literal composta em março não cobre a operação de security group que entrou no catálogo em maio, e ninguém relê a denylist a cada release. O artefato guarda intenção, não a lista expandida: o grupo composto em março cobre a operação de maio.
O artefato
Seção intitulada “O artefato”Um JSON que você monta no deployment do runner — ConfigMap, arquivo montado, o que o seu GitOps usar:
{ "version": 1, "deny": { "groups": ["network-posture", "iam"], "capabilities": ["audit"], "connectors": ["amplitude"], "ops": ["query.runAthenaQuery"] }}Que ele seja um arquivo, e não um punhado de variáveis de ambiente, é deliberado: um JSON no seu repositório
de GitOps é diffável, entra numa pull request e é revisado pelo seu time de segurança. Variável de
ambiente enterrada num values.yaml não é revisada por ninguém.
O caminho do arquivo vai em RUNNER_DENY_POLICY_FILE. Para o caso simples, quatro variáveis fazem o
mesmo sem artefato — e as duas fontes se somam, se você usar as duas:
| Variável | Conteúdo |
|---|---|
RUNNER_DENY_POLICY_FILE |
Caminho do artefato JSON |
RUNNER_DENIED_GROUPS |
Grupos nomeados, separados por vírgula |
RUNNER_DENIED_CAPABILITIES |
Capabilities |
RUNNER_DENIED_CONNECTORS |
Connectors |
RUNNER_DENIED_OPS |
Operações |
Artefato ilegível, JSON inválido ou chave desconhecida dentro dele derrubam o boot, nunca degradam para “nada negado”: cair para política vazia quando você pediu negação é o pior default possível.
Os grupos que existem hoje
Seção intitulada “Os grupos que existem hoje”| Grupo | Cobre |
|---|---|
network-posture |
Security groups (resumo, anexos, os abertos ao mundo) e regras de firewall — AWS e GCP. Não cobre VPC, subnet, load balancer, rota nem DNS: negar postura não pode derrubar o mapa de recursos. |
iam |
Higiene de chave e credencial — access keys (inclusive por sufixo), chaves de service account GCP envelhecidas, API/app keys. |
audit-trail |
A trilha de quem fez o quê — CloudTrail (lookup e as consultas do Lake) e audit logs do GCP. Não cobre inventário de recurso: negar a trilha não pode derrubar a descoberta do que existe. |
end-user-activity |
O indivíduo — busca de usuário e atividade no Amplitude, e os session replays. Não cobre as métricas agregadas de produto (ativos, volume de evento, retenção, conversão): quem nega isto quer proteger a pessoa, não desligar o funil. |
A lista acompanha o catálogo: operação nova que descreva postura de rede entra em network-posture sem
que você mexa no seu artefato. É o motivo de o grupo existir.
Onde é aplicado
Seção intitulada “Onde é aplicado”Duas camadas, pela mesma estrutura do gate de escrita — uma no anúncio, outra na execução:
- Anúncio. No boot, o runner expande a sua política contra o próprio catálogo e subtrai as operações negadas do conjunto que ele declara servir. O control-plane não expõe ao agente uma tool cuja operação não é servida, então ela simplesmente não existe para o modelo.
- Execução. O
enforcement.ts— o mesmo ponto que já recusa qualquer operação fora do catálogo — recusa a invocação de uma operação negada, mesmo que ela chegue. Um control-plane com bug, uma versão antiga ou um catálogo dessincronizado não contornam a política.
A segunda camada existe justamente porque a primeira depende de nós. A negação não vale porque nós concordamos em escondê-la; vale porque o processo que roda na sua infra recusa executá-la.
Como você confere que está valendo
Seção intitulada “Como você confere que está valendo”O runner anuncia de volta o que a política de fato negou: quais operações, e por qual regra — se caiu por grupo, por capability, por connector ou nomeada diretamente. É a expansão aplicada, não a intenção que você escreveu.
Isso importa por dois motivos. O primeiro é que é assim que você descobre o que um rótulo expande: o
network-posture que você negou vira uma lista concreta de operações, dita pelo processo que as recusa. O
segundo é que ela se confronta com o rascunho: o Fleet mostra as duas lado a lado, e uma divergência
entre “o que eu compus” e “o que está valendo” é visível — tipicamente um artefato que ainda não foi
implantado, ou uma frota com runners em versões diferentes.
Suprimido não é ausente
Seção intitulada “Suprimido não é ausente”É a parte mais importante desta página, e o motivo de a denylist não ser só uma subtração.
Se uma tool simplesmente sumisse, três situações passariam a ter exatamente a mesma cara: o connector não está configurado, o connector está quebrado, e você decidiu proibir. Só na terceira a infraestrutura existe, está saudável, e o vazio é uma decisão. Perguntado sobre exposição de rede, o agente responderia “não encontrei security groups permissivos” — que é falso, e é o pior resultado possível, porque um erro faz alguém investigar e uma resposta tranquilizadora não.
Então a supressão é legível, em três lugares:
- A sessão sabe da política antes de precisar dela. A política é injetada no contexto da sessão, então o agente responde “isso está desabilitado pela política do seu tenant” em vez de “não encontrei nada”.
- A resposta carrega o motivo. A fonte negada é reportada como
denied_by_policy— que conta como não consegui ler, nunca como não existe. “A política me proibiu” não é prova de ausência. - O Fleet distingue as três situações de fora: não conectou · conectou e quebrou · conectou e restringiu.
O que acontece com as análises compostas
Seção intitulada “O que acontece com as análises compostas”Uma análise que dependia de uma operação negada roda com o que sobrou e diz o que faltou, em vez de falhar inteira — composta multi-sinal costuma tocar muita coisa além da dimensão negada, e desligá-la faria a capacidade sumir sem explicação.
O que não sai é o veredito derivado. Uma nota de segurança calculada por penalidade ficaria mais alta justamente por não ter conseguido ler a dimensão negada — leitura que não aconteceu melhorando o número —, então a nota não é emitida. Você recebe o que foi observado, marcado como parcial, e nenhuma conclusão que dependa do que a política escondeu.
O que a política não apaga
Seção intitulada “O que a política não apaga”Parar de aprender é automático: o conhecimento derivado (topologia, mapeamentos) é extraído do resultado das operações, e uma operação negada não produz resultado.
O que foi aprendido antes de a política entrar em vigor continua onde está. Isso é deliberado: o artefato vive num runner descartável, e um erro de digitação num arquivo não pode destruir meses de topologia aprendida em silêncio. A purga é uma ação separada e auditada — o Fleet mostra que existe estado anterior à política e oferece apagá-lo.
Quando o boot falha de propósito
Seção intitulada “Quando o boot falha de propósito”Se o artefato citar uma operação que o runner não conhece — renomeada no catálogo, ou composta contra uma versão mais nova —, o runner recusa subir e nomeia a entrada.
Parece severo, e é a escolha certa: uma diretiva de negação que não tem efeito é invisível até virar incidente. Runner é gado, boot que falha é barulhento, e o seu supervisor mostra o erro na hora — enquanto uma política silenciosamente parcial só aparece no dia em que alguém lê o que não deveria.
Uma entrada pode também nomear algo que o catálogo teve e removeu de propósito. A recusa é a mesma — a
regra não protege mais nada, e mantê-la é acreditar numa proteção que não existe —, mas a mensagem é outra:
ela diz em que versão o alvo saiu e o que usar no lugar, em vez de mandar procurar uma operação renomeada,
que aqui seria a causa errada. É o caso da capability profiling e do connector witness, que saíram na
versão 0.50.0 do catálogo: se a sua política ainda os cita, veja o Profiler.