Pular para o conteúdo

Isolamento

Cada time, projeto e serviço roda isolado dos demais. Esta página diz o que você pode assumir (e o que não deve tentar). Ela não lista regras internas: o objetivo é você projetar o app certo, não conhecer a parede.

  • Um serviço = um container próprio, sem root. Se a imagem pede root, a plataforma o força a um usuário sem privilégio; se o app não funcionar assim, o deploy falha com image_requires_root e a ação é declarar build.user (Tabela de erros).
  • Sistema de arquivos somente leitura. Escreva em /tmp (memória, pequeno) ou em um volume. Dados fora do volume somem a cada deploy.
  • Sem capabilities extras e sem escalar privilégio: não há sudo, setuid útil, montar discos, mexer em rede ou em módulos do kernel. Portas abaixo de 1024 não são acessíveis ao app.
  • Sandbox de kernel. Os containers rodam sob um runtime com sandbox (gVisor), de modo que uma falha no app não chega ao kernel do host. Algumas chamadas de sistema exóticas não existem; apps comuns (web, workers, bancos embarcados) não notam.
  • Limites de recursos por serviço: memória, CPU e número de processos/threads. Estourar a memória reinicia o container (veja o log de execução); um fork bomb só derruba o próprio serviço. Os máximos dependem do plano (Limites e cotas, Escala e recursos).
  • Cada projeto tem a sua rede privada. Os serviços do mesmo projeto se encontram pelo name (Rede privada). Serviços de outros projetos e de outros times não são alcançáveis, nem pelo nome nem pelo IP.
  • Serviço private não tem rota pública; só public recebe URL e domínio.
  • O app não alcança o host da plataforma, a rede interna da infraestrutura nem os endpoints de metadados da nuvem. O tráfego de saída para a internet pública é permitido, com uma exceção prática: envio direto de e-mail por SMTP é bloqueado. Use um provedor de e-mail por API HTTPS.
  • Entrada: só o proxy da plataforma fala com o seu app, por HTTPS na borda. Confie em X-Forwarded-* apenas vindo dele.

Cada volume pertence a um serviço e só ele o monta. Há cota de tamanho. O volume é preservado entre deploys.

Cada banco é uma instância lógica só sua: usuário e credencial próprios, sem privilégios administrativos, conexão somente com TLS e somente a partir de origens autorizadas (os serviços do seu projeto). Uma credencial abre o seu banco e nenhum outro: nem os de outros projetos do mesmo time. Não há SUPERUSER, CREATE ROLE/DATABASE, leitura de arquivos do servidor nem execução de programas. Esperam-se erros de permissão se você tentar. Quando o banco atinge a cota (quota_mb) ele vira somente leitura até você reduzir os dados (Bancos).

Ao rotacionar a credencial, a antiga deixa de valer.

  • Todo recurso pertence a um time. Recurso de outro time responde 404, nunca 403: não é possível descobrir se um id existe. Não varie ids para “testar”.
  • O token carrega o time e (opcional) os projetos permitidos; o servidor ignora qualquer team_id/project_id enviado no corpo. Escopos finos por rota (Tokens e escopos).
  • Valores sensíveis de variáveis só saem com o escopo env:reveal (e, para criá-lo, confirmação forte de uma pessoa). Respostas de recusa nunca carregam segredo nem dados do recurso alheio.
  • Referências ${{svc.VAR}} só enxergam o próprio projeto; apontar para outro projeto ou time responde 422 unresolved_reference.
  • Parâmetros de template e valores de variável são tratados como texto literal: ${{...}} dentro de um parâmetro não vira referência.

A plataforma roda continuamente suítes que tentam atravessar essas paredes (containers que atacam vizinhos, credenciais de banco usadas fora do seu banco, tokens de um time em recursos de outro). Se você achar um caminho que atravessa, é uma falha de segurança: reporte pelo canal descrito em Reportar abuso (security no assunto) em vez de explorar.

  1. Escreva só em /tmp ou no volume; não dependa de root.
  2. Fale com outros serviços do projeto pelo nome; use private para o que não deve ter URL.
  3. Use as credenciais de banco entregues por variável (referências ${{db.…}}, veja Conexão e referências), nunca outras.
  4. 404 em recurso que “deveria existir” quase sempre é id ou time errado: liste com GET e use o id devolvido.