Pular para o conteúdo
Latchkey
Comece grátis
Menu da documentação

Arquitetura de segurança

Como o Latchkey isola cada job de CI: VMs dedicadas de uso único, nenhuma rede de entrada, credenciais efêmeras, criptografia em repouso e em trânsito, e as restrições sob as quais o agente de autorreparo é executado.

Esta página foi escrita para uma revisão de segurança. Ela descreve como a plataforma de runners é realmente construída - a fronteira de isolamento, o que sai da máquina, como as credenciais são tratadas e o que o agente de autorreparo tem e não tem permissão de fazer. Para o que o GitHub App pode ler e escrever nos seus repositórios, veja Segurança e permissões do GitHub.

1
job por máquina virtual
destruída depois que ele termina
0
regras de rede de entrada
runners não aceitam conexões
1
padrão de sudo permitido
para o agente de autorreparo
14d
retenção do cache de build
90 dias para logs de falha

Isolamento: um job, uma máquina#

O Latchkey não compartilha uma máquina entre jobs. Cada job roda em uma máquina virtual EC2 dedicada (classe m6a, Ubuntu 24.04 LTS) lançada para aquele job. Seu job não é um contêiner compartilhando um kernel com outro inquilino, e não é um processo em um host compartilhado de longa duração.

O uso único é imposto na camada de registro do GitHub, não apenas por convenção. Runners de cold start se registram com uma configuração just-in-time, que o GitHub aceita para exatamente um job. Runners de warm pool se registram com a flag --ephemeral, que faz o mesmo. Nos dois casos, a instância é configurada para terminar no desligamento, então não pode ser reutilizada mesmo que algo dê errado.

FronteiraComo ela é imposta
Um job por máquinaConfiguração just-in-time do GitHub (cold start) ou registro com --ephemeral (warm pool)
A máquina não pode ser reutilizadaO comportamento de desligamento iniciado pela instância é definido como terminar, tanto no launch template quanto no caminho de lançamento direto
O disco não sobreviveO volume EBS raiz é criptografado e marcado como delete-on-termination
Jobs descontrolados são recolhidosUm processo de limpeza termina instâncias que passam do limite de 4 horas por job (teto rígido de 8 horas)
Runners são vinculados a um único workspaceAs instâncias são marcadas com o ID da organização e registradas em um único grupo de runners da organização
01LançarUma instância EC2 dedicada inicializa para um job específico
02RegistrarConfiguração just-in-time, válida para exatamente um job
03Ler uma vezO runner busca sua credencial e a apaga imediatamente
04ExecutarSeu job roda sozinho na máquina
05TerminarA instância desliga; o comportamento de desligamento é terminar
06DestruirO volume raiz criptografado é excluído junto com a instância

Grupos de runners são criados com visibility: selected e uma lista explícita de repositórios, então um runner registrado para o seu workspace só recebe jobs dos repositórios que você habilitou.

O que roda como root, e por que isso está contido#

Dentro da sua própria VM, seu job tem controle total: o usuário runner tem sudo sem senha e acesso ao Docker. Isso é normal em CI - builds instalam pacotes e executam contêineres - e é seguro justamente porque o raio de alcance é uma única máquina de vida curta, sem rede de entrada, que é destruída ao final do job. Preferimos afirmar isso claramente a insinuar um sandbox que não existe.

Rede#

O que está bloqueado

  • Os runners são lançados em subnets privadas e sem endereço IP público
  • O security group do runner tem zero regras de entrada - nada na internet consegue abrir uma conexão com um runner
  • IMDSv2 é obrigatório, com o limite de hops definido como 1, para que um contêiner em rede bridge não consiga alcançar as credenciais da instância através do host
  • O tráfego interno da AWS usa gateway endpoints e interface endpoints da VPC em vez da internet pública
  • Um WAF (limitação de taxa, AWS Common Rule Set, Known Bad Inputs) fica na frente das aplicações web

O que o seu build ainda consegue alcançar

  • O tráfego de saída é aberto, porque a CI precisa alcançar npm, PyPI, Docker Hub, sua nuvem e seus próprios serviços
  • Trate qualquer segredo que você exponha a um job como alcançável por esse job - o mesmo modelo dos runners hospedados pelo GitHub

Credenciais e segredos#

As credenciais de runner são emitidas por job

Uma configuração de runner just-in-time é gerada para um único job e gravada no AWS Systems Manager Parameter Store como um SecureString criptografado, com escopo daquela instalação e daquele job.

O runner a lê uma vez e depois a apaga

Na inicialização, a instância busca o parâmetro e o apaga imediatamente, porque ele contém material de chave. O mesmo padrão vale para os tokens de registro de warm pool, que são mantidos em cache apenas durante sua vida útil de 55 minutos.

O acesso ao GitHub é de curta duração

O GitHub App assina um JWT com sua chave privada e o troca por um installation access token de curta duração a cada operação. Não existe nenhum token do GitHub de longa duração armazenado em um runner.

Os segredos da plataforma ficam fora do alcance dos runners

A chave privada do GitHub App fica no Parameter Store como um SecureString. O segredo que um runner pode ler é deliberadamente mínimo e separado do segredo de configuração da plataforma, de modo que um papel de instância comprometido não consegue ler credenciais da plataforma.

Os deploys se autenticam na AWS por assunção de papel via GitHub OIDC. Não há chaves de acesso AWS de longa duração na CI. As chaves de API da integração MCP são armazenadas apenas como um hash SHA-256 - a chave em texto puro é exibida uma vez na criação e nunca é persistida.

Criptografia#

DadoEm repousoEm trânsito
Disco raiz do runnerEBS criptografado (gp3), excluído na terminaçãon/a
Cache de build (S3)SSE-S3 (AES-256), expiração em 14 diasTLS
Banco de dados da aplicaçãoArmazenamento criptografado, sem acesso públicoTLS
Camada de cache / filaCriptografia em repouso e em trânsito habilitadaTLS
Configurações de runner e chaves do appSecureString do Parameter StoreTLS
Tráfego de APIn/aTLS 1.2 no mínimo no domínio da API

Isolamento do cache de build#

Os objetos de cache são armazenados no S3 sob um prefixo de chave derivado do ID da sua organização, e o ID do inquilino é injetado pelo control plane a partir da requisição do job - um job não pode se declarar outro inquilino definindo uma variável de ambiente. Os objetos expiram após 14 dias.

O que sai do runner#

Dois tipos de dados saem da máquina, e vale ser preciso sobre o segundo.

  • Metadados de execução e de job - nomes, tempos, status, labels e o YAML de workflow - que alimentam os recursos de análise e de otimização.
  • Saída do passo que falhou. Quando um passo falha, o pipeline de autorreparo captura a saída padrão e a saída de erro daquele passo (limitadas a 1 MiB cada) e as armazena para que a falha possa ser diagnosticada e, quando pertinente, transformada em uma correção proposta. A retenção é de 90 dias.

Como o agente de autorreparo é restringido#

O autorreparo é a parte em que um revisor deve pressionar mais forte, porque é o único componente que altera um job em execução. Veja exatamente o que ele pode fazer.

  • Ele roda como o usuário runner, não como root, na forma de um serviço systemd.
  • Ele escuta em um socket de domínio Unix com permissões restritas ao grupo. Não tem listener TCP nem superfície de rede.
  • Ele é invocado por um shim que intercepta apenas os scripts de passo do próprio agente do runner; todos os demais comandos passam direto, sem alteração.
  • Ele falha aberto: se o agente atinge o tempo limite, recusa ou dá erro, o comando roda exatamente como rodaria sem o Latchkey. Um reparador quebrado não pode quebrar o seu build.

As operações privilegiadas que ele pode executar são definidas por allowlist, não por filtragem:

ControleO que significa
Allowlist de sudoExatamente um padrão ancorado é permitido - um apt-get install não interativo de um único pacote. Qualquer outra coisa começando com sudo é rejeitada antes de o processo ser criado.
Allowlist de pacotesOs pacotes instaláveis vêm de uma lista definida em tempo de compilação, aplicada de forma independente em dois estágios, porque um dos estágios monta comandos a partir de saída de build não confiável.
Piso de segurança do ambienteDefinir LD_PRELOAD, LD_LIBRARY_PATH, PATH, BASH_ENV ou ENV é negado, assim como metacaracteres de shell e substituição de comandos.
Negação de caminhos sensíveisLeituras em /etc, /root, /proc, /sys, /var/log, ~/.ssh, ~/.aws e ~/.config/gh são negadas.
Sandbox das ferramentas de IAAs ferramentas do estágio 3 rodam dentro de um sandbox baseado em bubblewrap que começa negando tudo por padrão - sem rede, sem caminhos de escrita - e é ampliado por ferramenta até o conjunto mínimo necessário.
Nenhuma chave de IA residenteO passo de raciocínio se autentica no AWS Bedrock pelo papel da instância, então não há nenhuma chave de API de modelo de longa duração em uma máquina que executa o seu código.
Interruptor de desligamentoO autorreparo pode ser desativado por workspace.

O que o GitHub App pode acessar#

Cada permissão abaixo existe para viabilizar um recurso específico. Elas estão agrupadas pelo propósito, em vez de uma lista plana, para que você veja o motivo ao lado da concessão.

PropósitoAcessoPermissões
Executar seus jobs em runners do LatchkeyLeitura e escritaRunners auto-hospedados da organização, administração, administração da organização
Ver o que seus pipelines fizeramLeituraActions, metadados de artefatos, metadados
Reagir aos jobs conforme eles acontecemLeitura e escritaHooks da organização, hooks de repositório
Ler workflows e propor correções como PRsLeitura e escritaWorkflows, código, pull requests
Modelar seus custos do GitHub com precisãoLeituraPlano da organização
Verificar quem está na sua organizaçãoLeituraMembros
Exibir achados de segurança no seu painelLeituraAlertas do Dependabot, alertas de secret scanning, eventos de segurança, segredos do Dependabot
Construir imagens de runner adequadas à sua stackLeituraImagens personalizadas de runner da organização

Alterações nos seus repositórios#

O Latchkey nunca faz push na sua branch padrão. Toda alteração é um pull request que você revisa, e o caminho de escrita é restringido de três formas:

  1. O caminho do arquivo precisa ser resolvido como editável contra um manifesto por repositório. Caminhos sem correspondência são, por padrão, não editáveis - a verificação falha fechada.
  2. Pull requests originados de forks são rejeitados, e um sinal de fork ausente também é rejeitado, em vez de presumido seguro.
  3. Uma correção proposta precisa corresponder à sua assinatura de falha nos logs capturados, ou é suprimida.

Segurança operacional#

  • As imagens de runner são reconstruídas semanalmente, para que os patches de segurança do sistema operacional cheguem à frota em uma cadência definida.
  • O papel de instância do runner é de menor privilégio, incluindo uma negação explícita que restringe o acesso ao Parameter Store em toda a conta, concedido por uma política gerenciada pela AWS, apenas aos caminhos de runner do próprio Latchkey.
  • Toda ação administrativa que altera estado é gravada em um log de auditoria com operador, ação, alvo, parâmetros e resultado.
  • O acesso ao painel e à API é autenticado por um autorizador JWT no API gateway. Os papéis de workspace são Owner, Admin e Member, com os owners protegidos contra remoção ou rebaixamento.

Retenção de dados#

O que o Latchkey armazena, e por quanto tempo.

DadoRetenção
Seu código-fonte no runnerDestruído junto com a instância ao final do job
Objetos do cache de build14 dias a partir da última escrita
Saída do passo que falhou, capturada para diagnóstico90 dias
Registros de tentativas de autorreparo90 dias
Registros de correções propostas365 dias
Metadados de execução e de job que alimentam seus painéisRetidos enquanto o repositório permanecer habilitado

Desabilitar um repositório interrompe a coleta para ele. Excluir o seu workspace remove os dados dele. Se você precisa de uma janela de retenção específica para a sua organização, peça e conversaremos a respeito.

Reportando uma vulnerabilidade#

Envie um e-mail para security@latchkey.dev. Por favor, não abra uma issue pública no GitHub para um relato de segurança.

Meu código-fonte permanece no runner depois do job?

Não. A instância e seu volume raiz criptografado são destruídos quando o job termina. Nada do que você fez checkout sobrevive, além das entradas de cache de build que você habilitou explicitamente, que expiram após 14 dias.

O job de outro cliente consegue ver o meu?

Não. Cada job roda em sua própria máquina virtual, registrada para um único workspace e destruída após esse único job. Os runners não têm acesso de rede de entrada.

O agente de autorreparo pode executar comandos arbitrários no meu build?

Não. As ações privilegiadas são definidas por allowlist em vez de filtradas: um único padrão de sudo permitido, uma lista fixa de pacotes, negação de variáveis de ambiente que possibilitam injeção de código e um sandbox de negação por padrão para o estágio de ferramentas de IA. Se o agente falhar por qualquer motivo, o seu comando roda exatamente como rodaria sem ele.

Meus segredos de build são visíveis para o Latchkey?

Não. Seus segredos do GitHub Actions são injetados pelo GitHub diretamente no seu job em tempo de execução, exatamente como nos runners hospedados pelo GitHub, e o Latchkey nunca solicita nem armazena seus valores. O GitHub mascara os segredos registrados na saída de log antes que qualquer parte dela chegue até nós.

References