Agentes locais de IA e jobs de CI precisam de modelos de acesso diferentes
Agentes locais de IA e jobs de CI precisam de modelos de acesso diferentes porque a aprovação humana, a identidade do workload e a exposição de credenciais criam riscos distintos.

Uma estação de trabalho de uma pessoa desenvolvedora e um runner de compilação podem executar comandos shell, chamar APIs e enviar código. Tratá-los como o mesmo ambiente de segurança é uma forma de acabar colocando uma credencial de implantação em um lugar pelo qual nem uma pessoa nem um serviço conseguem responder adequadamente.
Agentes locais de IA funcionam em um ambiente acompanhado. Uma pessoa pode ver um aviso, revisar um diff, rejeitar uma solicitação incomum e revogar um processo que começou a se comportar mal. Jobs de CI funcionam porque ocorreu um evento: um push, pull request, tag, execução agendada ou disparo manual. O job precisa de uma autoridade verificável por máquina, vinculada a esse evento. Ele não pode esperar que uma pessoa desenvolvedora aprove cada chamada, nem deve herdar o acesso permanente dela apenas porque o workflow foi executado.
A diferença não é acadêmica. Ela define se o agente recebe um segredo, se um token existe por minutos ou meses, o que uma entrada de auditoria precisa registrar e se uma dependência comprometida pode se transformar em um incidente de produção.
A presença humana muda o significado de uma aprovação
Uma aprovação em uma estação de trabalho pode ser um controle de segurança relevante porque há uma pessoa presente para avaliar a ação imediata. A aprovação precisa descrever o chamador com detalhes suficientes para que essa avaliação seja útil. «Um agente quer fazer uma solicitação HTTP» é uma evidência fraca. «Um processo assinado por esta autoridade, iniciado nesta sessão, quer usar a credencial de implantação em produção» dá à pessoa operadora algo concreto para aceitar ou rejeitar.
Esse modelo tem um limite claro: a aprovação diz respeito a uma pessoa assumindo responsabilidade por um processo ativo. Ela não substitui a identidade. Se um processo malicioso consegue se passar pelo chamador confiável, ocultar o destino ou reutilizar uma aprovação depois que sua tarefa mudou, o aviso virou teatro.
Em uma máquina de desenvolvimento, quero três fatos separados:
- o armazenamento protegido continua trancado até que a pessoa local o desbloqueie;
- a primeira solicitação de um novo processo exige uma decisão de sessão;
- credenciais sensíveis podem exigir uma decisão a cada uso.
Esses controles respondem a perguntas diferentes. O bloqueio diz se alguma ação pode ocorrer. A autorização de sessão diz se esse processo pode agir durante essa execução. A aprovação por chamada diz se uma credencial específica é importante demais para ser reutilizada sem cuidado. As equipes costumam juntar os três em um único botão «permitir acesso do agente» e depois descobrem que o botão aprovou muito mais do que a pessoa desenvolvedora pretendia.
Um agente local também deve receber resultados, não o material da credencial. Se o agente precisa consultar uma API, um componente fora dele pode anexar a credencial, fazer a solicitação e devolver a resposta. Assim, uma injeção de prompt no agente não consegue simplesmente imprimir uma chave de API em um terminal, patch ou transcrição de conversa. Ocultar um segredo depois que ele entrou no contexto do agente não oferece a mesma proteção. O agente pode codificá-lo, enviá-lo a outro host ou usá-lo em uma solicitação antes que qualquer mecanismo de ocultação o veja.
O Sallyport usa esse modelo acompanhado no macOS: o cofre fica atrás de uma barreira absoluta e pode exigir autorização para uma sessão de processo ou para cada uso de uma credencial selecionada. Isso faz sentido quando há uma pessoa desenvolvedora presente. Seria o mecanismo errado para um runner sem supervisão.
Um job de CI precisa de uma identidade que resista a uma análise
Um job de CI não consegue fornecer intenção humana sob demanda. Ele precisa de uma identidade de workload, ou seja, uma identidade derivada de fatos verificáveis sobre o job, não de um segredo copiado para o ambiente dele.
Em um workflow de implantação, esses fatos normalmente incluem o emissor de CI, o repositório ou projeto, o commit ou ref, a identidade do workflow, o ambiente e o audience pretendido. O serviço de destino verifica a asserção assinada e a troca por uma credencial de curta duração. Essa é a parte útil da federação OIDC: o runner prova de onde veio o job sem carregar uma chave de nuvem reutilizável em seu armazenamento de segredos.
GitHub Actions documenta esse padrão por meio do endpoint de token OIDC e da permissão id-token: write. É fácil interpretar o nome da permissão de forma errada. Ela permite que o workflow solicite um token de identidade, mas não concede permissão de implantação por si só. A função na nuvem ou o serviço de destino ainda precisa rejeitar tokens cujo emissor, audience, subject ou outras declarações não correspondam ao workflow pretendido.
É nessa segunda parte que muitas configurações falham. Uma função que aceita qualquer token de um repositório delegou autoridade demais a todo workflow qualificado desse repositório. Uma prévia de documentação, um workflow de release e uma implantação em produção não devem se tornar equivalentes apenas porque compartilham o controle de código-fonte.
Use declarações para fazer a função descrever uma classe específica de job. A sintaxe exata depende do provedor de CI e da nuvem, mas a política deve responder a perguntas simples:
- Qual repositório pode solicitar essa função?
- Qual arquivo de workflow ou ambiente protegido pode solicitá-la?
- Qual branch, tag ou condição de release pode solicitá-la?
- Qual audience a asserção deve indicar?
- Por quanto tempo a credencial emitida pode continuar utilizável?
Não escreva condições de política que você não tenha inspecionado em um token real. Exiba as declarações em um ambiente de teste seguro, compare-as com a política de confiança e teste os casos de negação. As pessoas testam implantações bem-sucedidas e deixam o caminho perigoso, um pull request de uma branch não confiável, como mera suposição.
Segredos e identidades resolvem problemas diferentes
Um segredo prova posse. Uma asserção de identidade faz uma afirmação sobre o workload que solicitou acesso. Ambos podem terminar em um token bearer, mas criam caminhos de falha muito diferentes.
Um segredo de CI armazenado normalmente não guarda nenhuma informação sobre o motivo pelo qual o job o recebeu. Se um workflow consegue ler DEPLOY_TOKEN, um script alterado, uma action comprometida, um caminho malicioso de pull request ou um comando de log pode usar esse token onde quer que suas permissões permitam. A rotação limita por quanto tempo o segredo continua útil, mas não restringe o contexto de cada uso.
A federação de curta duração não torna o CI seguro por mágica. Um job comprometido ainda pode usar seu token válido durante o período de validade. O ganho é um raio de impacto menor: o invasor precisa executar um job elegível, cumprir as regras de emissor e declarações e agir antes que a credencial expire. Também é possível revogar ou alterar a função que aceita o token sem localizar cada segredo copiado.
Não confunda credenciais de curta duração com baixo privilégio. Um token válido por dez minutos que pode excluir todos os bancos de dados de produção continua sendo inaceitável. Limites de tempo reduzem a persistência; o escopo de autorização limita o dano. Você precisa dos dois.
O acesso de agentes locais tem a preocupação inversa. Uma pessoa desenvolvedora pode usar a mesma ferramenta local em muitos repositórios e tarefas, então uma única chave de API ampla pode se tornar um alvo atraente para um agente comprometido. O projeto local mais seguro mantém essa chave fora do agente e permite apenas a chamada de API ou o comando SSH específico aprovado pela pessoa usuária. Se o serviço externo oferece tokens com escopo granular, use-os também. Um cofre protegido não consegue melhorar um token poderoso demais depois que a solicitação deixa a máquina.
A conveniência perigosa é usar um token nos dois mundos
Usar o token pessoal de uma pessoa desenvolvedora no CI é popular porque desbloqueia rapidamente uma implantação parada. Também é uma das piores formas de apagar a responsabilização.
Um token pessoal costuma alcançar mais do que o pipeline precisa. Ele pode pertencer a alguém que muda de equipe, deixa a empresa, usa o token no laptop e tem acesso moldado por sua participação pessoal, não pelas responsabilidades de implantação. Quando aparece no CI, um log de auditoria pode mostrar que o token agiu, mas não pode dizer com honestidade se a ação veio da pessoa desenvolvedora ou de um job de release.
O erro inverso também acontece. As equipes expõem um segredo de implantação do CI a um agente local para que ele possa «testar a mesma coisa». Isso dá a um gerador de código interativo uma capacidade de produção sem supervisão, muitas vezes com menos verificações do que o workflow de release. Também torna a resposta a incidentes um pesadelo: o agente usou a credencial, um script shell a usou ou um valor copiado vazou para outra ferramenta?
Dê a cada ambiente seu próprio limite de autorização. Uma pessoa desenvolvedora local pode ter acesso interativo e revogável a um endpoint de desenvolvimento. Um job de release pode receber uma função federada limitada a um ambiente de produção protegido. Um job de pull request pode não ter nenhuma autoridade de escrita. Isso não são inconvenientes a serem suavizados. São as evidências que você usará depois para explicar por que uma ação foi permitida.
Uma regra útil de nomenclatura é fazer com que os nomes das credenciais revelem o ator e a finalidade. ci-release-prod-deploy informa muito mais a uma pessoa revisora do que deploy-token. Melhor ainda, o job de CI nunca armazena um token com esse nome. Ele solicita uma função vinculada à identidade cuja política de confiança codifica a mesma finalidade.
O estado do runner transforma acesso temporário em resíduo
Um job pode usar uma credencial temporária e ainda deixar um problema permanente. Os vazamentos mais comuns aparecem em logs, rastreamento do shell, diretórios pessoais em cache, camadas do Docker, artefatos do workspace e arquivos gravados por actions de terceiros.
Runners auto-hospedados merecem desconfiança especial porque podem reter estado entre jobs. Um job que baixa código não confiável pode instalar um executável modificado, alterar um cache compartilhado, inspecionar um workspace deixado para trás ou esperar que um workflow privilegiado reutilize o host. Isolar por nome de repositório não ajuda se os jobs compartilham a mesma conta do sistema operacional, o socket do contêiner ou o sistema de arquivos.
Runners efêmeros eliminam uma grande classe de resíduos porque desaparecem depois do job. Eles não eliminam a necessidade de controlar as entradas do workflow. Um job privilegiado que executa scripts de um pull request não confiável ainda está entregando autoridade a código não confiável, mesmo em uma máquina nova.
GitHub Actions alerta que pull_request_target é executado no contexto do repositório base e pode acessar segredos ou permissões de escrita. O evento tem uma finalidade legítima: pessoas mantenedoras podem precisar rotular ou comentar um pull request de um fork. O problema aparece quando um workflow disparado por esse evento baixa o commit head do pull request e executa seus scripts. O workflow combinou credenciais confiáveis com código controlado pelo invasor.
Mantenha o padrão simples:
- Execute código não confiável de pull requests sem autoridade de implantação.
- Reserve o acesso a ambientes protegidos para refs revisadas e caminhos de workflow controlados.
- Fixe actions de terceiros em referências de commit imutáveis quando o seu processo permitir.
- Mantenha o material secreto fora de caches, artefatos e saídas de diagnóstico.
- Destrua as instâncias sensíveis do runner depois do job.
O primeiro e o quarto itens evitam mais incidentes reais do que convenções elaboradas para nomes de tokens. Uma credencial perfeitamente delimitada ainda vaza se um comando shell a imprimir ou se um artefato incluir seu arquivo de configuração.
Uma identidade de implantação deve ser inspecionável em um arquivo
Um workflow deve deixar visível a solicitação de autoridade externa. Este exemplo do GitHub Actions solicita uma identidade OIDC apenas no job de implantação e declara um ambiente de produção. Ele não contém intencionalmente nenhuma credencial de nuvem armazenada.
name: deploy
on:
push:
tags:
- "v*"
permissions:
contents: read
id-token: write
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@<full-commit-sha>
- name: Request deployment identity
run: |
token=$(curl -sS \
-H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=deploy.example.internal")
test -n "$token"
- name: Deploy
run: ./scripts/deploy.sh
A troca real normalmente usa uma action ou uma CLI de nuvem, em vez de salvar a resposta em uma variável shell. O que importa aqui é o formato da solicitação: o GitHub fornece um token e uma URL de solicitação de uso único, o workflow pede um audience específico e o destino decide se essa identidade pode assumir uma função. O runner nunca deve tratar o token de identidade resultante como uma credencial de API genérica.
A política de confiança no lado da nuvem deve rejeitar um token a menos que suas declarações identifiquem esse contexto de release. Não copie um exemplo do provedor e pare por aí. Exemplos de provedores muitas vezes começam amplos porque precisam funcionar para muitos usuários. Restrinja o repositório, o ambiente, a branch ou tag e o audience antes que a função possa alterar a produção.
Se um destino não puder validar OIDC diretamente, coloque um pequeno broker de credenciais à frente dele. O broker valida a asserção de CI, mapeia as declarações para ações delimitadas, emite uma credencial temporária para o destino e registra o mapeamento. Não resolva essa limitação colocando um segredo permanente do destino em cada workflow que precisa do serviço.
SSH deixa essa diferença especialmente clara
Chaves SSH são uma base ruim para CI compartilhado. Uma chave privada copiada para um segredo de CI pode autenticar a partir de qualquer job autorizado a lê-la, e sua metade pública raramente informa ao destino qual revisão do repositório solicitou a conexão. Comandos forçados, restrições de origem e contas separadas podem limitar o dano, mas a identidade básica continua sendo uma chave privada reutilizável.
Para o trabalho local, o SSH pode precisar de uma barreira interativa porque uma pessoa desenvolvedora consegue ver que um agente quer se conectar a um host específico. A chave privada deve permanecer em armazenamento protegido, e o agente deve solicitar um comando específico em vez de obter acesso bruto à chave. O host ainda precisa impor suas próprias permissões de conta e restrições de comando. A aprovação local controla o ponto de partida; ela não torna seguro um comando remoto perigoso.
Para CI, prefira certificados SSH de curta duração quando a sua autoridade certificadora SSH e a frota de destinos oferecerem suporte. O job usa a federação de workload para solicitar um certificado com validade curta, um principal restrito e talvez um comando forçado. O destino verifica a autoridade certificadora em vez de aceitar uma chave privada copiada para sempre.
Se certificados não estiverem disponíveis, use uma conta de implantação distinta e uma chave privada dedicada para cada classe de implantação. Restrinja essa conta em authorized_keys e no servidor. Uma restrição mínima tem esta aparência:
command="/usr/local/bin/receive-release",no-port-forwarding,no-agent-forwarding,no-X11-forwarding ssh-ed25519 AAAA... ci-release
Essa linha impede que a chave abra um shell interativo arbitrário e força o servidor a executar um único programa receptor. Por si só, ela não identifica o repositório de origem. Combine-a com uma chave intermediada e de curta duração ou outro sinal de workload verificado se a decisão de release depender do contexto do repositório.
Não defina StrictHostKeyChecking=no em um script de CI para fazer o SSH funcionar. Isso desativa a verificação da identidade do servidor justamente onde os runners são lugares fáceis para redirecionar o tráfego. Forneça as chaves de hosts conhecidos por um mecanismo controlado, faça sua rotação de forma deliberada e faça o job falhar quando a verificação do host mudar inesperadamente.
Os registros de auditoria precisam responder a perguntas diferentes
Um log de ação que apenas diz «a implantação foi concluída» é uma saída operacional, não uma evidência de segurança suficiente. Você precisa reconstruir quem ou o que recebeu autoridade, qual solicitação fez e se o próprio registro foi alterado depois.
Para um agente local acompanhado, registre a identidade do processo local, o início da sessão, a decisão de aprovação, o rótulo da credencial ou a classe da ação, o destino, o horário da solicitação e o resultado. Não registre valores secretos nem payloads sensíveis completos. Uma assinatura de processo ou autoridade de assinatura de código é mais útil que um nome arbitrário de processo, porque nomes são fáceis de copiar.
Para CI, registre o provedor de CI, o ID da execução, o repositório, a referência do workflow, o SHA do commit, a pessoa que disparou a execução quando disponível, a classe do runner, o subject e o audience OIDC, a função de destino, os parâmetros de ação que seja seguro manter e o resultado. Isso permite que uma pessoa investigadora diferencie um release marcado por tag de um job executado manualmente de novo e um workflow confiável de uma concessão ampla acidental.
Mantenha os logs de autorização separados dos logs da aplicação. Logs de aplicação podem ser mutáveis, amostrados ou excluídos como parte das operações normais. Um histórico de autorização precisa ser somente de acréscimo e ter verificação independente. O encadeamento de hashes detecta alterações na sequência quando você mantém a cadeia e a compara com os registros esperados. Ele não impede que um invasor bloqueie registros futuros, portanto envie os registros para fora da máquina comprometida quando o projeto permitir.
O Sallyport projeta diários de sessão e de ações individuais a partir de um log de auditoria criptografado, encadeado por hashes e com gravação não reversível, e sp audit verify verifica a cadeia offline sem uma chave do cofre. Isso é uma evidência útil para ações de agentes acompanhados. O CI deve produzir um histórico equivalente, vinculado ao workload, nos sistemas que emitem e aceitam suas identidades.
A fadiga de aprovações é uma falha de design local
A aprovação por chamada pode proteger credenciais locais de grande impacto, mas um aviso a cada leitura inofensiva ensina as pessoas a aceitar avisos sem lê-los. Quando isso acontece, o controle vira um ritual e um invasor só precisa esperar pelo trabalho rotineiro.
Use aprovação por chamada para ações cujas consequências são difíceis de reverter: gravações em produção, alterações de DNS, administração de toda a organização, force push no controle de código-fonte ou comandos contra hosts sensíveis. Use autorização de sessão para ações repetitivas e de baixo risco que uma pessoa desenvolvedora possa delegar razoavelmente durante uma execução do agente. Mantenha a barreira do cofre separada, para que uma máquina trancada negue todas as ações mesmo quando existir uma aprovação de sessão antiga.
O CI tem sua própria versão da fadiga: barreiras de aprovação manual que aparecem em todos os jobs e são aprovadas porque o prazo do release virou a prioridade. Uma aprovação de ambiente protegido pode ser adequada para uma implantação em produção, mas deve aprovar um artefato de release e um destino claramente identificados. Ela não deve compensar um workflow que aceita código não confiável, uma função com declarações amplas ou um runner com estado desconhecido.
Uma revisão prática faz uma pergunta incômoda: se essa aprovação fosse clicada automaticamente durante um mês, o que poderia ser executado? Para agentes locais, reduza a superfície de ações aprovadas até que a resposta seja tolerável. Para CI, remova a aprovação de ações comuns de máquina e vincule a autoridade à identidade do job.
Separe os caminhos antes da próxima solicitação de credencial
Quando um agente ou pipeline solicita acesso, classifique o chamador antes de escolher um mecanismo de segredo. Há uma pessoa presente e capaz de inspecionar a ação? O chamador é um workload repetível com declarações que você pode validar? O chamador pode receber um resultado restrito em vez de uma credencial? A permissão do destino corresponde a uma tarefa nomeada?
Se o chamador for um agente local, proteja as credenciais fora do contexto dele, mantenha um caminho de aprovação significativo e registre cada ação em um formato que uma pessoa desenvolvedora possa revisar. Se o chamador for CI, use federação de curta duração, vincule as funções às declarações do workflow, isole o runner e registre os fatos do workload que autorizaram a chamada.
Não deixe que um token compartilhado apague essa fronteira porque isso é mais rápido hoje. O próximo incidente obrigará você a reconstruí-la enquanto tenta descobrir se quem agiu foi uma pessoa, um agente ou um runner.
FAQ
Por que agentes de programação com IA e pipelines de CI devem usar credenciais diferentes?
Um agente local funciona dentro da sessão de trabalho da pessoa desenvolvedora, que pode analisar uma solicitação e interromper o processo. Um job de CI funciona sem supervisão depois de um evento no controle de código-fonte, portanto suas permissões precisam vir de uma identidade de workload com escopo restrito e expirar junto com o job.
Posso usar o mesmo fluxo de aprovação para CI e agentes locais?
Não. Um aviso de aprovação humana prova apenas que alguém clicou em um determinado momento. Ele não descreve o repositório, o commit, o workflow ou a função de nuvem pretendida pelo job. Use aprovação para o trabalho local interativo e autorização vinculada ao workload para CI.
Chaves de API de longa duração são aceitáveis em CI?
Geralmente, é uma troca ruim. Um segredo de longa duração transforma qualquer runner comprometido, vazamento em logs, arquivo de cache ou dependência maliciosa em uma via de acesso duradoura. Prefira tokens de curta duração obtidos a partir de uma asserção OIDC com restrições de repositório, ref, workflow e audience.
O que é uma identidade de workload OIDC em CI?
Um token de workload OIDC é uma asserção do provedor de CI sobre o job em execução. Um serviço de nuvem ou de segredos valida essa asserção e emite uma credencial de curta duração para uma função específica, em vez de exigir que o job carregue um segredo de nuvem armazenado.
Um agente local de IA deve ter acesso a uma chave de API?
Se possível, o agente não deve receber credencial alguma. Dê ao agente local uma interface de ação que execute a solicitação fora do agente e retorne o resultado, mantendo o material de API ou SSH no armazenamento local protegido.
Como impedir que um job de CI acesse o ambiente errado?
Não. O workflow deve declarar cada destino externo de que precisa, e cada destino deve ter sua própria identidade e permissões. Um token de implantação genérico é conveniente até que uma compilação de documentação comprometida consiga implantar em produção.
Qual é o maior risco de segurança dos runners de CI auto-hospedados?
Trate o estado do runner como resíduo hostil. Use runners efêmeros para jobs sensíveis, fixe as dependências, evite restaurar diretórios que contenham segredos, remova segredos dos logs e presuma que um job privilegiado pode ser enganado por meio do código baixado.
Como auditar uma credencial de CI?
Primeiro, examine a origem da credencial. Depois, verifique o audience, a declaração do repositório ou projeto, a restrição de branch ou ambiente, o tempo de vida e as permissões do destino. Se você não consegue explicar cada campo, o token tem mais confiança do que o job merece.
O que um registro de auditoria deve guardar sobre ações de agentes?
Mantenha os registros separados. O registro de uma sessão interativa deve identificar o processo local e cada ação aprovada. Os registros de CI devem vincular cada ação ao ID da execução no provedor, à revisão do repositório, ao workflow, à classe do runner e à função emitida. Uma simples mensagem de sucesso não é um registro de auditoria.
O Sallyport pode rodar dentro de um runner de CI Linux?
Use uma identidade gerenciada na nuvem, um broker de implantação dedicado ou uma conta de serviço com escopo restrito atrás de uma federação de curta duração. O Sallyport foi projetado para o lado macOS com supervisão humana dessa divisão, não como um serviço de credenciais de CI sem interface.