Pular para o conteúdo
● ONLINE

Guardrails para agentes de IA em servidores: comparação entre allowlist, sandbox e broker de aprovação

Guardrails para agentes de IA em servidores

O agente pode apagar produção? Pode — se receber um shell irrestrito com credenciais de operador. O controle começa antes do prompt: a arquitetura define quais ações chegam ao host, com que identidade e sob qual política. Eu não entregaria bash de produção diretamente a um modelo, mesmo com instruções para “ter cuidado”. Instruções não são controle de acesso.

Uma resposta errada não é o único risco. O agente pode interpretar mal uma saída, compor comandos inesperados ou ser induzido por conteúdo malicioso em logs e arquivos. Trate cada solicitação como uma operação automatizada não confiável: valide a intenção, autorize uma ação estreita, execute em contexto limitado e registre o resultado.

Três pontos de controle

A allowlist restringe os comandos disponíveis; o sandbox limita o ambiente de execução; o broker acrescenta autorização explícita e controle de fluxo. São controles compatíveis entre si. Para produção, prefiro combiná-los a depender de uma única barreira.

Wrapper com allowlist de operações

O wrapper expõe operações nomeadas, não um terminal genérico. Em vez de deixar o agente executar systemctl restart nginx, a API recebe uma ação estruturada, como service_status para o serviço nginx. O código valida o nome, monta os argumentos sem interpolar texto arbitrário e chama um executável conhecido.

É simples de auditar e barato de operar. Funciona bem para tarefas previsíveis: consultar o estado de uma unit, verificar espaço em disco ou ler uma parte delimitada de um log. Mas cada operação nova exige implementação, teste e atualização da política. Não há shell livre para “resolver o caso diferente”. Isso é uma virtude — até surgir uma tarefa que o wrapper não cobre.

Defina ações, não comandos

O contrato deve usar um esquema pequeno e tipado. Se a meta é impedir execução arbitrária, não aceite campos como command, shell ou script. Uma política declarativa pode ser assim:

version: 1
actions:
 service_status:
 executable: /usr/bin/systemctl
 args:
 - is-active
 - --no-pager
 - "{{ service }}"
 allowed_services:
 - nginx
 - ssh
 timeout_seconds: 5
 max_output_bytes: 4096
 disk_usage:
 executable: /usr/bin/df
 args:
 - -P
 - /
 timeout_seconds: 3
 max_output_bytes: 2048

O template descreve a intenção; não passe a string resultante para sh -c. O executor deve chamar o processo com uma lista de argumentos, usando, por exemplo, execve ou subprocess.run([...], shell=False). Valide service contra a lista permitida, imponha timeout e corte a saída. Para saber se o Nginx está ativo, o agente não precisa de argumentos arbitrários de systemctl.

A chamada pode ser um endpoint interno ou uma ferramenta do agente com JSON estrito:

{
 "action": "service_status",
 "service": "nginx",
 "ticket": "INC-4821"
}

ticket não concede autorização. O backend valida o formato e consulta o sistema de identidade ou de incidentes, se essa integração fizer parte do fluxo. Tudo que vem do modelo é dado não confiável, mesmo quando parece um identificador legítimo.

Privilégios mínimos no host

Não rode o wrapper como root só porque uma ação exige privilégio. Crie uma conta de serviço sem shell interativo e autorize operações específicas via sudoers. Evite curingas em argumentos: eles ampliam a política de um jeito fácil de não perceber.

# /etc/sudoers.d/ai-ops
Defaults:aiops !requiretty
aiops ALL=(root) NOPASSWD: /usr/bin/systemctl is-active --no-pager nginx
aiops ALL=(root) NOPASSWD: /usr/bin/systemctl is-active --no-pager ssh

Confira a sintaxe antes de instalar:

sudo visudo -cf /etc/sudoers.d/ai-ops
sudo -l -U aiops

O sudoers acima é ilustrativo. Valide como a versão da distribuição trata argumentos e caminhos absolutos. Para ações de escrita, prefiro uma unidade systemd dedicada ou um helper pequeno, com parâmetros validados, a permissões amplas em systemctl.

Faz sentido para: times que automatizam verificações ou operações bem definidas em hosts Linux. Não basta como única defesa se as tarefas executam código dinâmico ou consultam arquivos sensíveis do sistema.

Sandbox descartável para ferramentas variadas

Quando o agente precisa de ferramentas variadas, tire a execução do host de produção. Um job efêmero em container recebe apenas os dados necessários, roda dentro de limites de kernel e desaparece ao terminar. Para tarefas de leitura, bloqueie a rede, torne o root filesystem somente leitura, remova capabilities e impeça elevação de privilégio.

Este exemplo com Docker serve para laboratório ou para um executor dedicado. Não monte o socket do daemon no container: quem controla /var/run/docker.sock costuma controlar o host.

docker run --rm 
 --network none 
 --read-only 
 --user 10001:10001 
 --cap-drop ALL 
 --security-opt no-new-privileges:true 
 --pids-limit 64 
 --memory 256m 
 --cpus 0.5 
 --tmpfs /tmp:rw,noexec,nosuid,size=16m 
 --mount type=bind,src=/srv/diagnostics,dst=/input,readonly 
 executor-tools:1.4 
 /usr/local/bin/check-disk /input

Esses limites reduzem o impacto, mas container não é máquina virtual: o kernel continua compartilhado. Para workloads realmente hostis, use uma VM curta por job ou um runtime de isolamento mais forte, com atualização e avaliação próprias. E --network none não impede todo vazamento; o agente ainda pode inferir dados pela saída devolvida ao modelo.

Imagem imutável, ferramentas fixadas

Inclua apenas os executáveis necessários na imagem. Construa-a em pipeline e identifique-a por digest no ambiente de execução. Instalar pacotes no início de cada job prejudica a reprodutibilidade e abre uma janela para alterações de dependências.

FROM debian:12-slim

RUN apt-get update 
 && apt-get install -y --no-install-recommends coreutils procps 
 && rm -rf /var/lib/apt/lists/*

RUN useradd --system --uid 10001 --create-home runner
COPY --chown=root:root check-disk /usr/local/bin/check-disk
RUN chmod 0555 /usr/local/bin/check-disk

USER 10001:10001
ENTRYPOINT ["/usr/local/bin/check-disk"]

No Kubernetes, aplique limites também no Pod. O manifesto abaixo ilustra um executor sem rede e sem escalada de privilégios. Adapte o perfil seccomp e as permissões ao runtime e à distribuição usados no cluster.

apiVersion: v1
kind: Pod
metadata:
 name: agent-job
spec:
 restartPolicy: Never
 automountServiceAccountToken: false
 securityContext:
 runAsNonRoot: true
 runAsUser: 10001
 seccompProfile:
 type: RuntimeDefault
 containers:
 - name: executor
 image: registry.internal/ops/executor@sha256:REPLACE_WITH_DIGEST
 command: ["/usr/local/bin/check-disk", "/input"]
 securityContext:
 allowPrivilegeEscalation: false
 readOnlyRootFilesystem: true
 capabilities:
 drop: ["ALL"]
 resources:
 requests:
 cpu: 100m
 memory: 64Mi
 limits:
 cpu: 500m
 memory: 256Mi

O manifesto, sozinho, não cria uma política de rede. No Kubernetes, aplique uma NetworkPolicy que selecione esse namespace ou os Pods do executor e negue egress; confira também se o CNI implementa as regras. Não monte o diretório raiz do host, credenciais SSH, o socket do container runtime nem o token da ServiceAccount. Se o job precisa de um arquivo, monte apenas aquele volume como somente leitura.

Faz sentido para: equipes de plataforma que já operam containers ou jobs Kubernetes e precisam executar ferramentas com dependências variadas. O custo: pipeline de imagens, isolamento de rede e gestão de runtime. Também é preciso limitar os dados devolvidos ao agente.

Broker para aprovar e mediar a execução

O broker fica entre o agente e o executor. Recebe a ação estruturada, identifica usuário, host e ambiente, avalia a política e decide se executa, rejeita ou pede aprovação humana. O agente não recebe credenciais SSH nem conversa diretamente com o host. Para operações de escrita em produção, essa separação costuma ser a opção mais sólida.

Um fluxo razoável: o modelo propõe uma ação; o broker valida esquema e identidade; a política calcula risco e escopo; uma aprovação é solicitada quando necessário; um executor de curta duração realiza a operação e publica saída limitada. A auditoria guarda a proposta original, a decisão, a identidade, o alvo, o resultado e um identificador de correlação. Não registre tokens ou segredos junto com o prompt.

Política explícita por ambiente

Não use uma única regra de “agente autorizado” para todos os hosts. Separe leitura e escrita, liste os alvos permitidos e defina uma duração máxima. Por exemplo:

policy:
 default: deny
 environments:
 staging:
 read:
 require_approval: false
 actions: [service_status, disk_usage]
 write:
 require_approval: true
 actions: [restart_service]
 allowed_services: [nginx]
 production:
 read:
 require_approval: false
 actions: [service_status, disk_usage]
 write:
 require_approval: true
 actions: [restart_service]
 max_approval_age_seconds: 300
 allowed_services: [nginx]

Essa configuração não substitui a autorização no serviço. O broker deve rejeitar ações desconhecidas, campos extras, ambiente ausente e alvos fora da lista. A aprovação precisa estar vinculada ao payload exato. Se alguém altera host, serviço ou argumento depois da confirmação, ela deixa de valer. “Pode corrigir o incidente” é uma aprovação genérica e uma lacuna de controle.

Para evitar que um pedido de restart execute duas vezes, use uma chave de idempotência associada ao identificador da ação. O broker também impõe timeout e limites de tamanho para stdout e stderr; o cancelamento precisa alcançar o processo filho. Em operações longas, exponha estado e logs por uma API controlada. Não mantenha uma sessão de shell aberta para o modelo.

Auditoria útil na investigação

Um evento JSON pode registrar metadados sem armazenar conteúdo sensível:

{
 "event": "execution.completed",
 "request_id": "req-7f1c2",
 "principal": "agent:incident-triage",
 "environment": "production",
 "action": "service_status",
 "target": "web-03",
 "decision": "allow",
 "exit_code": 0,
 "duration_ms": 83,
 "output_bytes": 7
}

Envie os eventos para um destino central com controle de acesso e retenção definida. No host Linux, journald ajuda com logs locais, mas não substitui uma cópia remota resistente a alterações. Correlacione request_id com o registro do sistema de aprovação e com o job do executor. Sem essa ligação, “comando executado” não informa quem autorizou a operação nem qual payload foi validado.

Faz sentido para: organizações que permitem alterações em staging ou produção, exigem trilha de auditoria e já têm identidade corporativa ou fluxo de mudança. O custo: mais componentes, manutenção de políticas e latência humana quando há aprovação. Para leitura restrita, o broker ainda pode executar automaticamente.

O que não deve cruzar a fronteira

Alguns atalhos parecem convenientes, mas entregam acesso remoto com poder excessivo:

  • Não exponha uma ferramenta genérica que aceite uma string e a execute com bash -c.
  • Não forneça ao agente chave SSH de operador, credencial de cloud ampla ou acesso ao socket do Docker.
  • Não trate texto em logs, tickets ou arquivos como instruções confiáveis. Pode conter prompt injection.
  • Não devolva arquivos inteiros de configuração se a tarefa exige apenas o estado de uma unit.
  • Não permita que o agente altere allowlists, regras de sudo ou políticas de rede.

Limite também o que volta ao contexto do modelo. Remova segredos conhecidos, corte saídas extensas e normalize erros. Um journalctl sem janela de tempo pode despejar milhares de linhas. Prefira uma consulta restrita, como journalctl -u nginx --since "-5 min" -n 100 --no-pager, executada por uma ação dedicada que imponha o limite de bytes.

Escolha o controle pelo tipo de tarefa

Para diagnóstico com comandos previsíveis, comece pelo wrapper allowlist. Se ferramentas variadas processam dados não confiáveis, rode jobs descartáveis com isolamento. Para qualquer operação que altere produção, acrescente broker com identidade, aprovação vinculada ao payload e auditoria centralizada.

Minha preferência é autorização explícita na camada de controle e sandbox na camada de execução. Uma não deve compensar a ausência da outra.

Implante em etapas. Primeiro, grave as solicitações em modo de observação, sem executar nada, e compare as ações propostas com uma política revisada por quem administra os hosts. Depois libere apenas ações de leitura em um grupo de máquinas de teste. Teste entradas malformadas, serviço fora da allowlist, timeout, saída acima do limite e falha do broker. Inclua um log com instruções maliciosas: o agente deve tratá-las como dado, não como autorização.

Antes de habilitar escrita, faça um exercício de revogação. Desative a identidade do agente, encerre jobs em andamento e confirme que o executor não conserva credenciais reutilizáveis. No Linux, verifique também usuário efetivo, capabilities e processos filhos dentro do container. Este comando ajuda na validação inicial:

id
grep -E '^(CapEff|NoNewPrivs):' /proc/self/status
cat /proc/self/cgroup
ps -o pid,ppid,user,comm -ax

Essas saídas não provam isolamento completo. Revelam, porém, erros básicos, como execução acidental como root ou ausência de NoNewPrivs. Registre os resultados no pipeline de homologação e bloqueie o deploy se a imagem ou o manifesto fugir do perfil aprovado.

Se o agente precisa de sudo sem restrição para corrigir um serviço, ainda não há guardrail: há uma conta privilegiada com interface em linguagem natural. O limite correto é a menor ação verificável, executada por uma identidade curta e revogável, em um ambiente cujo acesso ao host foi deliberadamente removido.

↑