Terminal e exec
Você pode abrir um shell interativo ou executar um comando avulso numa instância de aplicação em execução, pelo portal ou pela CLI. Não é preciso nenhum acesso ao Kubernetes: a plataforma abre a sessão por você, dentro do sandbox da instância, e grava e audita tudo.
Como abrir
No portal
Na página da aplicação, aba Terminal:
- Escolha a instância ou deixe Automática (primeira pronta).
- Clique em Abrir terminal. O aviso "Esta sessão é gravada" aparece antes da conexão.
- Digite
exitou clique em Encerrar para sair.
Na CLI
# shell interativo
coda shell --app api
# comando avulso, sem shell; a CLI sai com o código do processo remoto
coda exec --app api -- uname -a
coda exec --app api -- sh -c 'exit 3'; echo $? # 3
# outra instância e terminal remoto
coda exec --app api --instance <id> -t -- top
No modo de comando avulso, o argv é executado diretamente, sem interpretação por shell, com até 32 argumentos e 4 KB no total. A sessão termina quando o processo termina.
Quem pode
| Papel | Abrir sessões | Ver a lista de sessões | Reproduzir gravações |
|---|---|---|---|
Owner, Admin | Sim | Todas | Sim |
Developer | Sim, fora dos ambientes protegidos | Só as próprias | Não |
Viewer | Não | Não | Não |
Abrir sessões e ver a lista exigem, além disso, o papel Developer ou acima no projeto da aplicação, seja herdado da organização, seja por concessão direta ou de um time (um Member com concessão Developer no projeto também abre sessões). Se a concessão for revogada, as sessões abertas fecham com role_changed.
Com token de API, é preciso também o escopo exec:run.
Política da organização
Owner e Admin definem a política em Configurações › Terminal:
| Opção | Padrão | Efeito |
|---|---|---|
| Terminal habilitado na organização | Ligado | Desligado, ninguém abre sessão (403 exec.disabled) e as abertas são encerradas |
| Permitir shell interativo | Ligado | Desligado, só comandos avulsos são aceitos (403 exec.shell_disabled) |
| Ambientes protegidos | Nenhum | Nesses ambientes, só Owner e Admin abrem sessão (403 exec.protected_environment para Developer) |
A mesma página mostra, só para leitura, os limites do plano: se o terminal está incluído, as sessões simultâneas e a retenção das gravações. Cada mudança da política entra na auditoria.
Plano e limites
| Chave | Starter | Pro | Business | Enterprise | |
|---|---|---|---|---|---|
| Terminal incluído | exec.enabled | Sim | Sim | Sim | Sim |
| Sessões abertas ao mesmo tempo na organização | exec.maxConcurrentSessions | 1 | 10 | 30 | 100 |
| Retenção das gravações | exec.recordingRetentionDays | 7 dias | 30 dias | 90 dias | 365 dias |
- Sem
exec.enabled, o pedido responde 403plan.feature_unavailable. Se o plano deixar de incluir o terminal, as sessões abertas são encerradas em até 15 segundos; a política e as gravações ficam guardadas. - Acima de
exec.maxConcurrentSessions, o pedido responde 422quota.exceeded. Reduzir o limite não encerra sessões abertas. - O prazo de cada gravação é fixado quando a sessão termina. Uma mudança posterior da retenção vale só para as sessões encerradas depois.
Limites iguais para todos os planos, contra abuso:
| Limite | Valor |
|---|---|
| Sessões por usuário | 3 (429 com Retry-After na quarta) |
| Sessões por aplicação | 5 |
| Inatividade (sem entrada nem saída) | 15 minutos |
| Duração máxima | 1 hora |
| Entrada do teclado | 64 KB/s |
Onde o comando roda
- A sessão roda dentro do sandbox da instância (a mesma microVM), com o usuário da imagem, sem privilégios extras.
- Só o contêiner da aplicação é alcançável. Os contêineres auxiliares da plataforma, como o agente do cofre, ficam fora de alcance.
- A aplicação precisa ter instância em execução (409
exec.no_running_instance). Uma imagem sem/bin/sh(distroless, por exemplo) não abre shell (exec.no_shell); use o comando avulso com um binário da imagem. - Uma aplicação publicada antes desta funcionalidade pode pedir um novo deploy antes do primeiro terminal (
exec.redeploy_required).
Encerramento
A plataforma revalida as sessões abertas a cada 15 segundos e encerra a sessão quando algo muda. O motivo aparece no terminal e na aba Sessões:
| Motivo | Quando |
|---|---|
exited | O processo terminou |
client_closed | Você encerrou |
idle_timeout | 15 minutos sem entrada nem saída |
max_duration | 1 hora de sessão |
role_changed | Seu papel caiu abaixo de Developer, ou abaixo de Admin num ambiente protegido |
policy_disabled | O terminal foi desabilitado na organização |
plan_disabled | O plano deixou de incluir o terminal |
organization_suspended | A organização foi suspensa |
credential_revoked | O token usado foi revogado ou expirou, ou suas sessões foram revogadas |
instance_gone | A instância deixou de existir |
replica_lost | A conexão com o servidor da plataforma foi perdida; abra outra sessão |
killed | A plataforma encerrou a sessão |
no_shell | A imagem não tem shell |
ticket_expired | A conexão não foi concluída a tempo |
error | Erro inesperado |
Gravação
Toda sessão é gravada, com entrada e saída e os tempos relativos, no formato asciicast v2.
- A gravação fica fora do banco de dados, cifrada, com chave própria por sessão.
- O tamanho máximo é 10 MB por sessão. Acima disso, a sessão continua e a gravação é marcada como truncada.
- Só
OwnereAdminreproduzem gravações, na aba Sessões. Cada reprodução entra na auditoria. - Vencida a retenção, a gravação é apagada. A sessão e os eventos de auditoria continuam, com a indicação "gravação expirada".
Como a entrada também é gravada, evite digitar segredos no terminal. Use variáveis e segredos.
Auditoria
| Evento | Conteúdo |
|---|---|
workload.exec.opened | Instância, modo (shell ou command), terminal, canal e credencial |
workload.exec.command | O argv do comando avulso (até 4 KB) |
workload.exec.closed | Duração, bytes, código de saída, motivo e se a gravação foi truncada |
workload.exec.recording_viewed | Quem reproduziu a gravação |
A detecção de ameaças em tempo de execução vê o shell aberto dentro do contêiner. Durante uma sessão auditada, os alertas de shell e de processo interativo no mesmo pod aparecem ligados à sessão, como esperados e com severidade informativa. Outros alertas (como escrita em binário do sistema) mantêm a severidade, mesmo durante a sessão.
API
| Método e rota | O que faz |
|---|---|
POST /api/v1/orgs/{orgId}/projects/{projectId}/environments/{environmentId}/applications/{appId}/exec-sessions | Pede uma sessão (instance, mode, command, tty, cols, rows). Devolve um tíquete de uso único, válido por 30 segundos, e a URL wss://…/api/v1/exec/{tíquete} |
GET .../exec-sessions | Lista as sessões da aplicação |
GET .../exec-sessions/{id}/recording | Gravação em application/x-asciicast (Owner e Admin) |
GET, PUT /api/v1/orgs/{orgId}/settings/exec | Política da organização (Owner e Admin, só login interativo, If-Match) |
A conexão WebSocket só é aceita a partir do portal ou com o cabeçalho Authorization do mesmo usuário que pediu a sessão. Um tíquete sozinho não abre sessão.