# Riscos do encaminhamento de agente SSH em desenvolvimento com IA

É fácil subestimar os riscos do encaminhamento de agente SSH porque a chave privada permanece na máquina do desenvolvedor. Isso é verdade, mas não basta. Um host remoto que recebe um socket de agente encaminhado pode pedir ao agente local que assine desafios de autenticação SSH. Para cada sistema que aceita essa identidade, o host remoto talvez consiga agir como você enquanto esse acesso durar.

O desenvolvimento com assistência de IA torna esse erro mais fácil de cometer e mais difícil de perceber. Um agente de programação pode abrir shells remotos, executar comandos de deploy, inspecionar repositórios e seguir uma configuração SSH escrita anos atrás para uma sessão interativa. Se o agente alcançar uma máquina que tenha seu socket encaminhado, o limite de segurança já mudou. Desative o encaminhamento por padrão e dê aos trabalhos remotos identidades próprias e restritas quando eles realmente precisarem passar por outro host.

## Um agente encaminhado pode autenticar-se além do primeiro host

O encaminhamento de agente SSH expõe um serviço de assinatura, não uma cópia da chave privada. Seu `ssh-agent` local mantém uma ou mais identidades privadas. Quando você se conecta com o encaminhamento ativado, o SSH cria um socket na máquina remota e retransmite as solicitações de assinatura pela conexão criptografada até o agente local.

Essa diferença costuma levar a decisões ruins sobre risco. As pessoas ouvem «a chave nunca sai do meu laptop» e concluem que a máquina remota não tem nada que valha a pena atacar. Os bytes privados podem continuar locais, mas a autenticação SSH não exige que a máquina remota possua esses bytes. Ela precisa de uma assinatura válida sobre uma solicitação de autenticação. O socket encaminhado oferece um meio de obtê-la.

O manual do OpenSSH `ssh_config(5)` descreve `ForwardAgent` de forma clara e alerta que usuários capazes de contornar as permissões de arquivo no host remoto podem acessar o agente encaminhado. Na prática, isso significa que a própria conta remota, um processo executado por essa conta, um administrador ou um invasor que obtenha controle suficiente pode usar o socket. O acesso root no host remoto encerra a discussão sobre permissões do socket.

A RFC 4252 descreve a autenticação SSH por chave pública como uma assinatura sobre dados específicos da conexão. O servidor valida essa assinatura comparando-a com uma chave pública autorizada. Um host remoto comprometido não pode transformar o agente em um serviço genérico de assinatura para documentos arbitrários, mas pode solicitar as assinaturas SSH necessárias para tentar logins SSH em outros lugares. Essa é exatamente a capacidade que um invasor deseja se sua identidade funcionar no controle de código-fonte, em hosts de produção ou em sistemas internos de administração.

O erro mais comum se parece com isto:

1. Um desenvolvedor se conecta a `build.example.net` com `ssh -A` porque essa máquina precisa acessar um repositório privado.
2. O agente local do desenvolvedor aparece no host de build por meio de `SSH_AUTH_SOCK`.
3. Um script de build, uma dependência comprometida ou outro usuário com acesso suficiente pede a esse socket uma assinatura para `git.example.net` ou para um host interno.
4. O destino aceita a chave pública do desenvolvedor e registra a nova conexão como se tivesse sido feita pelo desenvolvedor.

O servidor SSH do destino vê uma assinatura criptográfica legítima. Ele não consegue saber se uma pessoa iniciou a conexão em seu laptop ou se um processo na primeira máquina remota a solicitou por meio do encaminhamento. Os logs do servidor identificam a chave aceita e o endereço de origem, mas não recuperam essa distinção perdida.

## A herança de configuração cria exposição acidental

A maior parte dos encaminhamentos inseguros começa na configuração SSH, não em uma decisão consciente durante uma sessão arriscada. Uma única seção antiga, como `Host *` com `ForwardAgent yes`, pode aplicar-se a todos os hosts acessados por um desenvolvedor, uma ferramenta de terminal ou um agente de programação. Ela também se aplica quando a ferramenta chama `ssh` em um script, sem que ninguém veja um aviso.

A configuração do OpenSSH tem outro ponto delicado: para cada parâmetro, o cliente geralmente usa o primeiro valor que encontra. Uma regra ampla colocada antes de uma regra específica pode anular a exceção que você pensou ter escrito. Não confie em uma inspeção rápida de `~/.ssh/config`. Pergunte ao SSH qual configuração ele vai usar.

Execute isto na máquina local antes de conectar:

```sh
ssh -G deploy.internal.example | grep '^forwardagent '
```

Um resultado seguro é:

```text
forwardagent no
```

`ssh -G` mostra a configuração efetiva do cliente depois de processar os arquivos e as opções da linha de comando. Ele não abre uma conexão. Por isso, é útil em verificações de configuração e em um script de teste que leia uma lista de hosts sensíveis.

Defina uma regra explícita de negação por padrão perto do final do arquivo de configuração relevante. Coloque as exceções específicas antes dela, porque o OpenSSH usa o primeiro valor correspondente:

```sshconfig
Host docs-bastion.example
    ForwardAgent yes

Host *
    ForwardAgent no
```

Este exemplo ainda concede uma exceção perigosa, portanto ela precisa ter um motivo e um responsável. O objetivo é tornar essa exceção visível e limitá-la a um host, em vez de aplicá-la silenciosamente a todo ambiente novo.

Para uma conexão pontual, force a configuração segura mesmo que um arquivo de configuração diga o contrário:

```sh
ssh -o ForwardAgent=no developer@host.example
```

A forma mais curta, `ssh -a developer@host.example`, faz a mesma coisa. Use uma delas ao conectar-se a uma máquina que você ainda não analisou, a um host temporário de suporte, a um ambiente de treinamento ou a um sistema administrado por um fornecedor.

Não confunda `IdentitiesOnly yes` com um controle de encaminhamento. Essa opção afeta quais identidades o cliente SSH oferece ao autenticar-se em um servidor. Ela não impede que o SSH crie um socket de agente no lado remoto. Da mesma forma, remover `SSH_AUTH_SOCK` de um shell não é uma política completa. Um processo filho pode herdar outro ambiente, e uma configuração SSH pode reativar o encaminhamento na conexão seguinte.

## Os fluxos de IA multiplicam os caminhos que precisam ser revisados

Um agente de programação com IA não precisa ter intenção maliciosa para tornar o encaminhamento perigoso. Basta ter permissão para executar um comando que encontre seus hábitos SSH existentes. Os agentes seguem instruções do repositório, executam scripts de build, usam hosts de desenvolvimento remoto e repetem comandos com pequenas variações. Esses comportamentos são normais. Eles se tornam arriscados quando o ambiente lhes dá uma identidade de desenvolvedor reutilizável.

O caminho preocupante costuma ser indireto. Seu agente local inicia uma sessão SSH em uma máquina de desenvolvimento. Essa sessão encaminha seu agente por causa de uma regra de configuração global. O agente de programação executa um script do repositório na máquina de desenvolvimento. O script baixa uma dependência ou abre uma segunda conexão SSH. A segunda conexão pode usar o socket que a primeira sessão colocou ali.

Isso é pior do que uma pessoa digitando um único `git fetch`, porque um agente pode fazer muitas chamadas de ferramentas sem parar para questionar por que um shell remoto precisa acessar um destino sem relação aparente. O agente também pode encontrar instruções em um repositório que mandam usar determinado alias de host. Esse alias pode esconder um `ProxyCommand`, uma regra `Match` ou uma regra de encaminhamento herdada, fora da visão do operador.

Trate estas permissões separadamente:

- Permissão para um agente abrir um shell remoto.
- Permissão para esse shell remoto alcançar outro destino SSH.
- Permissão para o segundo destino aceitar a identidade do desenvolvedor.
- Permissão para o agente provocar a segunda conexão.

As equipes costumam juntar as quatro em «o agente precisa de SSH». Essa frase não descreve o limite de segurança. Um shell remoto e uma capacidade de assinatura reutilizável têm consequências diferentes e exigem decisões separadas.

Verifique como o processo do agente é iniciado. Se ele herdar `SSH_AUTH_SOCK` de um terminal interativo, talvez já tenha acesso às identidades do agente local para autenticação SSH direta. Se depois se conectar a um host com o encaminhamento ativado, esse host ganhará uma segunda rota até essas identidades. Um wrapper que limpe a variável de ambiente pode reduzir o uso acidental, mas não substitui uma configuração SSH segura nem uma identidade separada para automação.

Também inspecione as instruções do agente e os wrappers de automação em busca de `ssh -A`, `scp -A` ou de um alias que se expanda para essas opções. `scp` e `sftp` dependem das configurações do transporte SSH, portanto um hábito de encaminhamento pode se espalhar muito além do comando que o criou. Coloque a decisão de encaminhamento no lugar correto: na definição da conexão, com um padrão explícito de não.

## Um jump host não precisa do seu agente

Muitos desenvolvedores ativam o encaminhamento porque precisam atravessar um bastion antes de alcançar um sistema interno. Era uma solução comum quando o único modelo conveniente era «entrar no bastion e depois executar SSH novamente». Ela continua popular porque funciona rapidamente durante a configuração. Também transforma o bastion em uma máquina capaz de reutilizar sua identidade.

Use `ProxyJump` quando o primeiro host só precisar transportar o tráfego. O cliente local pode autenticar-se no host final enquanto cria um túnel pelo jump host, sem encaminhar um socket de agente para esse host.

```sshconfig
Host engineering-bastion
    HostName bastion.example
    User developer
    ForwardAgent no

Host release-host
    HostName release.internal.example
    User deploy
    ProxyJump engineering-bastion
    ForwardAgent no
```

Com essa configuração, o cliente SSH local estabelece a conexão SSH final por meio do bastion. O bastion transporta o tráfego criptografado. Ele não recebe um `SSH_AUTH_SOCK` remoto que possa usar para solicitar assinaturas ao seu agente.

Teste a rota, em vez de presumir que a configuração significa o que você espera:

```sh
ssh -vvv release-host
```

Na saída de depuração, procure a conexão do proxy jump e confirme que não há indicação de encaminhamento do agente. Depois de entrar no host final, inspecione o ambiente remoto:

```sh
printf 'SSH_AUTH_SOCK=%s\n' "${SSH_AUTH_SOCK:-}"
ssh-add -l
```

Uma variável de socket vazia é o resultado esperado quando você desativou intencionalmente o encaminhamento. Se `ssh-add -l` informar que não há conexão com um agente de autenticação, isso também é compatível com a ausência de um agente encaminhado. Não faça essas verificações somente no host final ao investigar. Faça-as em cada salto interativo onde alguém possa ter ativado o encaminhamento.

Há casos legítimos em que o jump host precisa autenticar-se em outro host, como em uma operação de release controlada. Isso significa que o jump host precisa de uma identidade própria de deploy ou de uma identidade de carga de trabalho de curta duração. Não significa que ele deva pegar emprestadas todas as identidades carregadas no agente do laptop de um desenvolvedor.

## A automação remota precisa de uma identidade própria

Uma máquina remota de build, um host de deploy ou uma ferramenta de IA deve autenticar-se como a carga de trabalho que recebeu, não como o desenvolvedor que por acaso iniciou a sessão. Essa mudança de arquitetura elimina a necessidade de encaminhamento, em vez de apenas torná-lo menos conveniente.

Uma identidade de carga de trabalho deve conceder acesso somente aos serviços que essa carga precisa usar. Para um repositório de código, use uma identidade de deploy limitada ao repositório quando o serviço oferecer esse recurso. Para um destino SSH, autorize uma chave pública dedicada para uma conta dedicada e limite os comandos ou as permissões dessa conta quando o servidor permitir. Em uma configuração de SSH baseada em certificados, emita certificados de curta duração com principals correspondentes à função da carga de trabalho.

Os certificados SSH podem restringir o acesso, mas não são mágicos. O principal de um certificado controla quais contas o aceitam somente se o servidor verificar os principals corretamente. Um período de validade curto limita por quanto tempo uma assinatura pode autenticar, mas um processo comprometido ainda pode usá-la durante essa janela. Revise a configuração do servidor que confia na autoridade certificadora e as regras de conta que usam esses principals.

Não reutilize a chave pública pessoal de um desenvolvedor como identidade «temporária» para um worker de CI ou agente remoto. Isso mantém o registro de auditoria ambíguo. Quando um registro de autenticação informa que a chave pessoal fez login, não é fácil determinar se foi o desenvolvedor, um job de build ou um shell remoto comprometido usando o encaminhamento.

Para ações HTTP e SSH controladas por agentes, outra opção é manter as credenciais em um gateway local de ações e devolver ao agente apenas o comando ou o resultado da API. O Sallyport usa esse modelo no macOS: seu cofre guarda a chave SSH e o helper `sp-ssh` executa a ação SSH sem entregar a credencial ao agente.

O limite útil é operacional, não apenas uma descrição. O agente solicita uma ação nomeada, o gateway aplica a credencial e o agente recebe a saída. O agente não recebe um socket de agente que possa passar a um processo remoto sem relação. Assim, as aprovações e os registros têm significado, porque correspondem a uma ação, não a uma capacidade aberta de pedir assinaturas futuras.

## Os pedidos de confirmação reduzem a exposição, mas não corrigem a confiança

Às vezes, uma tarefa curta de manutenção realmente exige encaminhamento e ainda não existe uma identidade dedicada. Nesse caso, encaminhe o mínimo possível de autoridade de assinatura e torne cada uso restante visível. Isso é um controle temporário, não uma arquitetura permanente.

Inicie um socket de agente separado, em vez de encaminhar o agente que contém sua coleção diária de identidades. Adicione apenas a identidade necessária para a tarefa de manutenção, com uma validade curta e exigência de confirmação:

```sh
ssh-agent -a "$HOME/.ssh/maintenance-agent.sock" > "$HOME/.ssh/maintenance-agent.env"
. "$HOME/.ssh/maintenance-agent.env"
ssh-add -c -t 900 ~/.ssh/maintenance_ed25519
ssh -o ForwardAgent=yes operator@maintenance.example
```

`ssh-add -c` pede confirmação antes de o agente assinar. `-t 900` remove a identidade depois de 15 minutos. Consulte o manual `ssh-add(1)` da versão do OpenSSH instalada, porque o comportamento da confirmação depende do agente local e da interface usada pelo usuário.

Use um shell separado para essa tarefa. Quando o trabalho terminar, remova a identidade e encerre o agente:

```sh
ssh-add -D
ssh-agent -k
```

Essa sequência impede solicitações posteriores por meio desse socket temporário. Ela não revoga assinaturas já emitidas, conexões já autenticadas nem cópias de dados que um processo remoto já tenha obtido. Feche a sessão remota e verifique os destinos aos quais essa identidade podia acessar.

O OpenSSH também oferece restrições de destino por meio de `ssh-add -h` nas versões que incluem esse recurso. Essas restrições podem limitar para quais caminhos de host um agente assina, com base nas chaves de host em `known_hosts`. Vale avaliá-las em um ambiente controlado, mas elas acrescentam dependências de configuração que as equipes frequentemente deixam de manter. Um arquivo `known_hosts` desatualizado ou incompleto pode transformar uma medida de segurança em uma interrupção justamente no pior momento. Teste com o caminho exato do salto e os aliases de host usados pela automação.

Não dependa do cansaço causado pelos avisos como limite de segurança. Um host comprometido pode pedir assinaturas repetidamente e indicar destinos parecidos com a infraestrutura normal. Se os operadores aprovarem os avisos rapidamente para encerrar um incidente, a confirmação protegerá menos do que imaginam. Uma identidade restrita e uma validade curta dão ao aviso um raio de impacto menor.

## Os logs precisam distinguir sessões de ações SSH

Os logs de autenticação SSH informam que uma chave se autenticou em um servidor. Raramente informam por que a assinatura foi solicitada ou se o encaminhamento de agente abriu o caminho. Se você permite ações remotas automatizadas, capture informações suficientes para reconstruir quem iniciou o agente, qual processo recebeu aprovação, qual destino ele contatou e qual ação solicitou.

Mantenha os registros de sessão separados dos registros de ação. Um registro de sessão responde qual processo do agente recebeu acesso e quando esse acesso terminou. Um registro de ação responde qual comando SSH ou chamada de API foi executado com esse acesso. Misturar tudo em um único log genérico torna as investigações lentas, porque o operador precisa deduzir uma cadeia de causa e efeito a partir de fragmentos.

Em um servidor SSH, revise os registros normais de autenticação depois de qualquer evento suspeito de encaminhamento. O local exato depende do sistema operacional e da configuração do serviço, mas lugares comuns incluem entradas do journal do sistema e o log de autenticação do daemon SSH. Procure a impressão digital da chave pública aceita, o nome da conta, o endereço de origem e o horário. Compare esses dados com o histórico da sessão no primeiro host remoto.

Não tire conclusões definitivas de um endereço IP. Uma conexão encaminhada pode vir de uma máquina de build, de um bastion, de um gateway de tradução de endereços de rede ou de uma rede overlay privada. O servidor consegue determinar de onde veio a conexão TCP, mas não quem iniciou a solicitação de assinatura na máquina do desenvolvedor.

Registros que evidenciem adulteração só ajudam se o sistema os gravar fora do controle do agente. Um processo capaz de editar seu próprio histórico de ações pode apagar o segundo salto suspeito antes que alguém o examine. O Sallyport registra sessões de agentes e chamadas individuais em um log de auditoria criptografado e encadeado por hash, e `sp audit verify` pode conferir essa cadeia offline sem uma chave do cofre.

Seja qual for a ferramenta escolhida, teste o registro com uma autorização recusada real e um comando SSH bem-sucedido real. Confirme que ele informa a execução do agente, o destino, a referência ou impressão digital da credencial, o resultado e o horário. Logs que apenas dizem «ferramenta concluída» não respondem a uma pergunta de incidente sobre reutilização de identidade.

## Trate uma exposição como abuso de autorização, não como roubo automático da chave

Se você descobrir que encaminhou um agente para um host em que não confia, responda como se esse host pudesse ter usado sua identidade enquanto a sessão existiu. Não espere provas de que ele extraiu uma chave privada. O risco relevante é a autenticação não autorizada, e a chave privada talvez nunca tenha saído da sua máquina.

Primeiro, impeça o uso futuro. Feche todas as sessões SSH nesse host, remova a identidade exposta do agente e desative a regra de encaminhamento específica do host. Se a identidade estava carregada em um agente compartilhado, não execute `ssh-add -D` automaticamente em um dia de trabalho e declare o problema resolvido. Isso pode interromper sessões legítimas e deixar a chave pública afetada autorizada em vários servidores.

Depois, remova ou revogue a autorização nos destinos que a identidade alcança. Em configurações comuns de chaves autorizadas, remova a chave pública das contas em que ela não deve mais funcionar e substitua-a por uma nova onde necessário. Para certificados SSH, revogue o certificado de acordo com os procedimentos da sua autoridade certificadora e do servidor, ou deixe um certificado de curta duração expirar se puder confirmar que a janela de exposição é aceitável. Para acesso a repositórios, revogue ou substitua a credencial de deploy ou de usuário correspondente pelos controles normais do serviço.

Investigue uma janela de tempo delimitada: desde quando o encaminhamento ficou disponível até o fim da sessão remota ou até o momento em que o agente local deixou de aceitar solicitações. Revise os registros de autenticação dos destinos, o histórico do shell remoto quando ele for confiável, os registros dos jobs e os logs de ações. Preserve os logs antes de limpar o host remoto se houver suspeita de comprometimento.

Por fim, corrija o caminho que permitiu o problema. Se um `ForwardAgent yes` global causou o evento, alterar apenas a entrada do host comprometido deixará o próximo host desconhecido exposto. Se um fluxo de IA herdou um socket pessoal, dê a esse fluxo uma configuração de conexão explícita e uma identidade de carga de trabalho dedicada. O reparo deve tornar o caminho inseguro impossível por padrão, não apenas lembrar as pessoas de usar uma opção.

## O padrão seguro deve funcionar mesmo sob pressão

O encaminhamento persiste porque reduz o atrito naquele momento. Um desenvolvedor precisa passar por mais um salto, um build precisa baixar algo privado ou um agente precisa concluir uma tarefa antes do prazo. Essas necessidades são reais. Elas não justificam colocar uma identidade de desenvolvedor amplamente confiável em todas as máquinas que por acaso estejam no caminho.

Defina `ForwardAgent no` globalmente. Use `ProxyJump` para bastions que servem apenas como transporte. Dê à automação remota uma identidade que indique o que ela pode fazer. Quando uma exceção temporária for inevitável, isole uma identidade, exija confirmação, defina uma expiração curta e remova-a quando a tarefa terminar.

Execute `ssh -G` contra os aliases de host usados pela equipe nesta semana. Essa pequena verificação encontra o erro silencioso de configuração antes que um processo remoto receba um serviço de assinatura que nunca deveria ter.
