Segredos
Cinco segredos determinam a segurança de um deployment. Gere cada um com openssl rand -hex 32.
| Segredo | Protege | Rotacionável |
|---|---|---|
BETTER_AUTH_SECRET | Tokens de sessão | Sim — invalida todas as sessões |
ENCRYPTION_KEY | Variáveis de ambiente do workspace, chaves de provedor armazenadas, credenciais OAuth do MCP, segredos de deployment/chat | Não — veja abaixo |
API_ENCRYPTION_KEY | Cópia reversível armazenada das chaves de API geradas por pessoas | Não — as chaves existentes continuam autenticando, mas a cópia armazenada não pode mais ser exibida |
INTERNAL_API_SECRET | Chamadas entre serviços | Sim — rotacione app e realtime juntos |
CRON_SECRET | Endpoints de jobs em background | Sim — rotacione app e cron juntos |
ENCRYPTION_KEY não pode ser rotacionada sem re-encriptar os dados que ela protege, e não pode ser recuperada se for perdida. Trocá-la torna todos esses dados permanentemente ilegíveis. Faça backup dela separadamente do banco de dados.
BETTER_AUTH_SECRET precisa ser idêntica nos serviços app e realtime — eles compartilham sessões pelo banco de dados, e uma divergência faz o realtime rejeitar todo socket autenticado.
Onde guardá-los
Em ordem crescente de prontidão para produção:
--setna linha de comando — só para desenvolvimento. Os valores aparecem na saída dehelm get valuese no histórico do shell.- Um Secret do Kubernetes criado previamente — defina
app.secrets.existingSecret.enabled: truee o nome do secret. Funciona com Sealed Secrets e SOPS. O secret é consumido por completo e precisa usar os nomes de chave padrão. - External Secrets Operator — sincronize a partir de Vault, AWS Secrets Manager, Azure Key Vault ou GCP Secret Manager. Recomendado.
Nos modos padrão e External Secrets, o chart grava todas as chaves sob app.env e realtime.env em um Secret gerenciado pelo chart e montado via envFrom, então nenhum valor fica embutido em uma spec de pod. (No modo existingSecret, o Secret pré-criado é a fonte da verdade e quaisquer valores de app.env que você ainda passar são renderizados inline — nesse modo, forneça tudo pelo Secret.) De qualquer forma, um segredo comitado no values.yaml é um segredo no seu histórico do git.
Fronteiras de rede
Ingress
Exponha apenas o app (3000) e o realtime (3002). Todo o resto — Postgres, Redis, o serviço de PII, os endpoints de cron — deve ser acessível somente de dentro do deployment.
Os endpoints de jobs em background sob /api/cron/*, /api/webhooks/poll/* e /api/schedules/execute são autenticados pelo CRON_SECRET, mas não há motivo para expô-los publicamente. Aponte o cron para o Service interno ao cluster.
NetworkPolicy
O chart traz uma policy opcional que isola o tráfego leste-oeste e bloqueia os endpoints de metadados da nuvem (169.254.169.254/32, 169.254.170.2/32) na saída — vale ativar, porque esses endpoints são o alvo padrão de escalonamento de SSRF.
networkPolicy:
enabled: truenetworkPolicy.ingressFrom tem como padrão [{}] — um seletor de peer vazio que permite ingress de qualquer pod do cluster. Em um cluster compartilhado ou multi-tenant, restrinja-o ao seu ingress controller:
networkPolicy:
ingressFrom:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginxA policy já permite saída HTTPS (443) para tudo, exceto os CIDRs de metadados, o que cobre APIs de provedores de modelos, APIs de integrações e endpoints de armazenamento de objetos na nuvem. Ela também permite o Postgres e o Redis empacotados, por seletor de pod.
O que ela não cobre é qualquer datastore que você rode fora do chart — um Postgres ou Redis gerenciado em uma porta diferente de 443. Adicione uma regra para cada:
networkPolicy:
enabled: true
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/16 # your VPC / managed-service subnet
ports:
- protocol: TCP
port: 6379 # managed Redis
- protocol: TCP
port: 5432 # managed PostgresIsso vale mesmo quando REDIS_URL chega ao pod por um Secret em vez do values.yaml — o chart não consegue ver o host, então não consegue gerar a regra. Um deployment que aceita a URL mas não tem uma regra de egress correspondente não vai conseguir alcançar o Redis com networkPolicy.enabled: true.
Se manter listas de CIDR não valer o esforço, remova a restrição de portas:
networkPolicy:
enabled: true
allowExternalEgress: trueOs endpoints de metadados da nuvem continuam bloqueados nos dois casos. O padrão é false porque o chart do Studio é deliberadamente mais rígido que o padrão comum de charts, que permite saída sem restrições.
Pod Security Standards
Todas as workloads definem runAsNonRoot, removem todas as capabilities do Linux, desabilitam escalonamento de privilégios e usam seccompProfile: RuntimeDefault — os quatro controles exigidos pelo perfil restricted. Rotule o namespace para aplicá-lo:
kubectl label namespace studio pod-security.kubernetes.io/enforce=restrictedreadOnlyRootFilesystem não é definido por padrão: Postgres e Ollama precisam de um root gravável, e o container do app escreve no .next/cache do Next.js. É viável nos serviços genuinamente sem estado (realtime, pii, copilot) — defina <component>.securityContext.readOnlyRootFilesystem: true e monte um emptyDir em /tmp via extraVolumes / extraVolumeMounts.
Onde o código das pessoas roda
Workflows podem executar JavaScript e Python escritos por pessoas. Saiba qual sandbox você está usando antes de expor o Studio a autores não confiáveis.
| Modo | Configuração | Isolamento |
|---|---|---|
| isolated-vm (padrão) | nenhuma | Isolate V8 no mesmo processo, dentro do container do app. Sem namespace de rede nem separação de filesystem em relação ao processo do app — o isolamento é no nível do engine JS. Só JavaScript. |
| E2B | E2B_ENABLED=true, E2B_API_KEY | Sandbox remoto por execução. Isolamento mais forte; exige acesso de saída ao E2B. |
| Daytona | SANDBOX_PROVIDER=daytona, DAYTONA_API_KEY | Sandbox remoto por execução. |
Python, Shell, JavaScript com imports externos e blocks dependentes de
ferramentas exigem um provedor de sandbox remoto. JavaScript sem import ou
require continua rodando no isolate em processo quando nenhum provedor remoto
está configurado.
Com o sandbox em processo padrão, trate qualquer pessoa que possa criar um workflow como alguém executando código no contexto de segurança do container do app. Se a sua instância do Studio está aberta a um público amplo ou parcialmente confiável, use um provedor de sandbox remoto e ative as restrições de egress da NetworkPolicy.
Tetos de recursos para o caminho em processo:
| Variável | Controla |
|---|---|
IVM_MAX_EXECUTIONS_PER_WORKER | Execuções antes de um worker ser reciclado |
IVM_MAX_BROKERS_PER_EXECUTION | Brokers de chamadas ao host por execução |
IVM_MAX_BROKER_ARGS_JSON_CHARS | Tamanho máximo do payload de argumentos |
IVM_MAX_BROKER_RESULT_JSON_CHARS | Tamanho máximo do payload de resultado |
A fronteira de SSRF
O Studio bloqueia requisições de saída de ferramentas de banco de dados e de conectores para endereços privados, reservados e de loopback. Isso impede que um workflow seja usado para escanear a sua rede interna.
Deployments auto-hospedados muitas vezes precisam, legitimamente, alcançar um banco interno pelo nome do serviço. Isso é opt-in:
ALLOW_PRIVATE_DATABASE_HOSTS=trueIsso afrouxa a fronteira de SSRF para toda pessoa que cria workflows na instância. Ative apenas em uma rede privada confiável, e de preferência combine com uma NetworkPolicy que restrinja o que o app pode realmente alcançar.
IP do cliente e headers encaminhados
Atrás de um load balancer, X-Forwarded-For é controlável pelo cliente. Defina AUTH_TRUSTED_PROXIES com os endereços reais dos seus proxies para que o Better Auth resolva o IP real do cliente, e TRUSTED_ORIGINS se as pessoas acessam o Studio por mais de uma origem. Os dois estão cobertos em Autenticação.
Restringindo quem pode usar a instância
Listas de permissão e de bloqueio para cadastro, controles de login social, SSO e a saída de emergência DISABLE_AUTH estão todos cobertos em Autenticação. O resumo relevante para segurança: restrinja o cadastro antes de expor a instância, e nunca defina DISABLE_AUTH=true atrás de um ingress voltado para a internet.
Redação de PII
O serviço opcional baseado em Presidio dá suporte ao block Guardrails PII e, quando ativado, à redação automática de PII dos logs de workflow:
pii:
enabled: true
app:
env:
PII_REDACTION: "true"
INTERNAL_API_BASE_URL: "http://studio-app.studio.svc.cluster.local:3000"INTERNAL_API_BASE_URL precisa ser a URL do Service interno ao cluster. O caminho de redação chama a própria API do app, e uma URL pública de ingress normalmente não é alcançável por hairpin de dentro do cluster. Sem um valor alcançável, o caminho falha de forma fechada — os campos afetados são substituídos por [REDACTION_FAILED] em vez de vazarem, mas a redação não roda de fato.
O serviço embute cerca de 2,2 GB de modelos spaCy, então a primeira inicialização leva por volta de três minutos e ele precisa de pelo menos 4 GB de memória.
Checklist pré-lançamento
- Todos os cinco segredos gerados do zero, guardados em um gerenciador de segredos e com a
ENCRYPTION_KEYem backup separado -
BETTER_AUTH_SECRETidêntica no app e no realtime - Imagens fixadas em uma tag ou digest explícito no app, no realtime e nas migrations
- TLS terminando no ingress; HTTP redirecionado ou desabilitado
-
NEXT_PUBLIC_APP_URLeBETTER_AUTH_URLapontando para a origem pública real -
AUTH_TRUSTED_PROXIESdefinido se estiver atrás de um load balancer - Cadastro restrito (
DISABLE_REGISTRATIONouALLOWED_LOGIN_DOMAINS) -
DISABLE_AUTHnão definido - NetworkPolicy ativada e
ingressFromrestrito ao ingress controller - Namespace rotulado com
pod-security.kubernetes.io/enforce=restricted - Buckets de armazenamento de objetos privados, com CORS limitado à sua origem do Studio
- Banco de dados acessível apenas de dentro do deployment; TLS obrigatório (
sslMode: require) - Backups configurados e uma restauração ensaiada
- Estratégia de sandbox definida para código escrito por pessoas