Runner self-hosted no notebook: o job ganha o poder desse computador
Use runner próprio para alcançar a rede interna sem entregar sua máquina a qualquer workflow: máquina dedicada, usuário sem privilégio, gatilhos restritos e segredos de escopo mínimo.
O teste de integração precisa do banco de homologação, que só existe na rede do escritório. O runner da nuvem não alcança. Alguém instala o runner do GitHub Actions no próprio notebook, como administrador, e o job finalmente passa. A partir daí, qualquer workflow que caia nesse runner executa comandos com o acesso desse notebook: as chaves SSH, a sessão do navegador, a VPN e a pasta de documentos.
A resposta curta: runner self-hosted mora numa máquina ou VM dedicada, sem dado pessoal, rodando com usuário sem privilégio de administrador e com acesso de rede só ao que o job precisa. Use apenas em repositório privado, não dispare em pull request de fork, mantenha o agente atualizado e, quando der, prefira runners efêmeros, que atendem um job e são descartados.
Por que o runner herda tudo
O runner é um programa que fica perguntando ao GitHub se há trabalho para ele. Quando aparece um job com os rótulos dele, o programa baixa o código e executa os passos do YAML como comandos do sistema operacional, com o usuário que o processo estiver usando. Não há sandbox mágica: um passo run: cat ~/.ssh/id_ed25519 funciona como funcionaria no seu terminal.
Quem controla o YAML controla a máquina. E o YAML vem do repositório, do branch que disparou o workflow. Se alguém consegue abrir um pull request que altera o workflow e esse pull request roda no seu runner, essa pessoa executa o que quiser no seu computador. É por isso que a documentação do GitHub recomenda usar runners self-hosted apenas com repositórios privados.
Há um segundo efeito, menos óbvio: runner self-hosted, por padrão, não é limpo entre jobs. O diretório de trabalho, arquivos temporários, caches e processos que ficaram rodando persistem. Um job malicioso pode deixar algo para trás que o próximo job, legítimo, vai executar ou entregar.
O caso: testes contra o banco de homologação
Uma equipe de software contábil tem o banco de homologação em 10.0.5.20, acessível só pela rede interna. O repositório é privado, mas tem colaboradores externos de uma consultoria. O objetivo é rodar os testes de integração a cada push em main sem expor o notebook de ninguém.
Como montar o runner do jeito certo
- Crie uma VM dedicada na rede interna. Nada de e-mail, navegador logado, chave pessoal ou pasta compartilhada nela.
- Restrinja a rede de saída: a VM precisa de HTTPS de saída para o GitHub e acesso à porta do banco de homologação. O runner não precisa de porta de entrada aberta, porque é ele que busca o trabalho.
- Crie um usuário próprio, sem sudo, só para o runner.
- Registre o runner com um rótulo específico, para que só os workflows que pedem esse rótulo caiam nele.
- Instale como serviço, rodando com o usuário criado, e não como root.
- Use credenciais de homologação, com usuário de banco que só enxerga o schema de teste.
- Coloque o runner num calendário de atualização, junto com o sistema operacional da VM.
Os comandos de download mudam a cada versão, então copie-os da página "New self-hosted runner", nas configurações de Actions do repositório. A parte que importa vem antes e depois:
# na VM dedicada, como administrador, uma única vez
sudo useradd --create-home --shell /bin/bash runner
# a partir daqui, como o usuário runner
sudo -iu runner
mkdir actions-runner
cd actions-runner
# baixe e extraia o pacote indicado na página do GitHub, depois:
./config.sh --url https://github.com/contabil-norte/sistema --token TOKEN_DE_REGISTRO --name homolog-01 --labels rede-interna
exit
# de volta como administrador, instala o serviço rodando com o usuário runner
cd /home/runner/actions-runner
sudo ./svc.sh install runner
sudo ./svc.sh start
O token de registro mostrado na página expira rápido e serve só para registrar. Ele não é o segredo que o job usa.
Como escrever o workflow que usa esse runner
O workflow deixa claro quando roda, com que permissões e em qual runner. Repare que não há gatilho pull_request: só push em main e disparo manual por quem tem permissão.
name: testes-integracao
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
jobs:
integracao:
runs-on: [self-hosted, linux, rede-interna]
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- name: Testes contra homologação
run: ./scripts/testes-integracao.sh
env:
DB_URL: ${{ secrets.DB_URL_HOMOLOG }}
O bloco permissions reduz o que o token automático do GitHub pode fazer durante o job. O timeout-minutes impede que um teste travado prenda o único runner da equipe por horas. E proteger o branch main com revisão obrigatória garante que ninguém troque o YAML sem outra pessoa olhar.
Runner efêmero: uma máquina por job
O registro aceita a opção --ephemeral, que faz o runner atender um único job e se desregistrar. Combinado com uma VM ou contêiner recriado a cada vez, cada job começa num ambiente limpo e nada sobrevive para o próximo. Dá mais trabalho de automação, mas resolve o problema da sujeira persistente. Para uma equipe pequena, uma VM com snapshot restaurado periodicamente já é um avanço grande sobre o notebook.
Segredo no runner é segredo daquela máquina
Tudo que o job recebe como variável de ambiente, ou que fica gravado na VM, pode ser lido por qualquer outro job que rode ali. Se o mesmo runner atende o repositório do site e o do sistema financeiro, o workflow do site enxerga o que sobrou do financeiro. Em organizações, use grupos de runners para limitar quais repositórios podem usar cada runner.
Prefira segredos de escopo mínimo: senha de homologação em vez de produção, token de leitura em vez de escrita. Se um desses segredos aparecer num log, a troca segue o processo de rotação em dois passos. E o raciocínio de não dar root ao processo é o mesmo de usuário não root no container.
Erros comuns
- Instalar o runner no notebook pessoal: qualquer job alcança chaves SSH, sessões de navegador, VPN e arquivos de cliente que estão na máquina.
- Runner self-hosted em repositório público: um desconhecido pode abrir pull request com um workflow alterado e executar código na sua rede interna.
- Rodar o serviço como administrador ou root: um passo malicioso instala o que quiser e persiste depois do job.
- Usar o mesmo runner para todos os repositórios: segredos e arquivos de um projeto ficam ao alcance dos workflows de outro.
- Esquecer o agente desatualizado: o runner deixa de receber correções e, eventualmente, para de aceitar jobs, quebrando o pipeline num dia qualquer.
- Dar ao job credencial de produção "porque era a que estava à mão": um teste mal escrito, ou um workflow alterado, mexe em dado real.
Checklist
- O runner roda numa VM ou máquina dedicada, sem dado ou sessão pessoal.
- O processo do runner usa um usuário sem privilégio de administrador.
- O repositório é privado e o workflow não roda em pull request de fork.
- O branch que dispara o workflow exige revisão antes do merge.
- O job declara
permissionsmínimas e temtimeout-minutes. - A rede da VM só alcança o GitHub e os serviços que o teste precisa.
- Os segredos usados são de homologação e de escopo mínimo.
- Existe uma data marcada para atualizar o agente e o sistema da VM.
Abra a lista de runners do seu repositório ou organização e veja em que máquina cada um está instalado. Quem consegue mudar o YAML que roda neles, e o que essa pessoa alcançaria se quisesse?
Referência: https://www.youtube.com/watch?v=G6nBM3NxBDc