8 min de leitura

Segurança do logout no macOS: encerre a autoridade do agente local

A segurança do logout do macOS deve revogar o acesso ao cofre, as aprovações do agente e as ações na fila antes que um desenvolvedor ausente possa iniciar outra chamada.

Segurança do logout no macOS: encerre a autoridade do agente local

O logout de um desenvolvedor precisa encerrar a autoridade do agente local, mesmo quando um processo do agente, uma solicitação de rede ou um aplicativo na barra de menus ainda não percebeu o que aconteceu. Tratar o logout como um pedido educado para fazer a limpeza cria uma janela em que uma máquina sem supervisão ainda pode agir usando as credenciais de uma pessoa.

Isso é especialmente importante para agentes de programação, porque o trabalho útil deles atravessa um limite. Eles chamam APIs, abrem sessões SSH, criam tickets, publicam pacotes ou alteram a infraestrutura. Se a autoridade veio da pessoa diante do teclado, ela expira quando essa pessoa sai da sessão do macOS. O sistema deve preservar as evidências do que aconteceu, mas não pode preservar a capacidade de fazer mais.

O erro que vejo repetidamente é misturar três coisas diferentes: um segredo armazenado localmente, uma aprovação concedida a um processo e uma ação que já está em andamento. Cada uma precisa de um comportamento de encerramento diferente. Um único handler quit é tarde demais e também é vago demais.

O logout é um limite de autoridade, não um evento do aplicativo

A segurança do logout no macOS significa que a ausência de uma sessão interativa de usuário precisa negar futuras ações que usem credenciais, independentemente de todos os aplicativos saírem de forma organizada. Um aplicativo de desktop pode receber notificações de encerramento durante um logout normal, mas a segurança não pode depender de essas notificações chegarem, terminarem ou serem executadas na ordem esperada.

O usuário pode fechar o laptop, trocar de usuário, forçar o encerramento de um aplicativo, perder energia ou iniciar o logout enquanto um agente espera por um endpoint lento. O sistema operacional também pode encerrar processos em uma ordem que o seu código não previa. Um projeto que diz «vamos revogar o acesso em applicationWillTerminate» já aceitou incerteza demais.

A documentação de launchd da Apple faz a distinção de escopo que importa aqui. Ela separa os domínios do sistema, do usuário e do login gráfico. Um job em um domínio gráfico de usuário pertence a uma determinada sessão conectada, enquanto um job do sistema tem outro ciclo de vida e outro modelo de autoridade. Não interprete «o processo ainda está executando» como prova de que ele ainda tem permissão para agir em nome do usuário que saiu.

Construa a verificação de autorização em torno de um fato atual da sessão que o gateway de ações possa verificar no momento da chamada. Toda ação deve perguntar, nesta ordem:

  1. O cofre está aberto neste momento para a sessão conectada deste usuário?
  2. Esta solicitação pertence a um processo de agente ativo e autorizado nessa mesma sessão?
  3. Esta credencial exige aprovação para este uso específico?
  4. Um evento de logout ou revogação da sessão já avançou a geração da sessão?

A quarta verificação impede uma condição de corrida sutil. Uma solicitação pode passar pelas três primeiras verificações, ficar em uma fila e chegar ao executor depois que o logout começar. Um número de geração, verificado novamente imediatamente antes da injeção da credencial ou da execução do SSH, faz essa solicitação antiga falhar.

Não faça o comportamento do logout depender de o agente concordar em parar. Na fronteira, o agente é um chamador não confiável. Ele pode estar confuso, ocupado, comprometido ou simplesmente ter desaparecido.

Um cofre bloqueado deve negar o acesso antes do início da limpeza

A barreira do cofre deve fechar primeiro, de forma síncrona do ponto de vista do executor. Depois que a barreira fecha, nenhuma nova injeção de credencial HTTP e nenhuma nova autenticação SSH pode começar. A limpeza pode vir depois, mas não pode ser o mecanismo que torna o sistema seguro.

A ordem parece óbvia até que o aplicativo mantenha uma fila de solicitações. Uma falha típica acontece assim: um agente envia cinco chamadas de implantação, a interface começa a limpeza do logout, o aplicativo limpa o cartão visível da sessão e uma thread de trabalho retira da fila a quarta chamada com uma referência de credencial que havia resolvido antes. O aplicativo parece ter saído da sessão, mas a ação ainda chega ao serviço externo.

Mantenha um único objeto de autoridade no processo que controla o uso dos segredos. Ele deve conter um identificador opaco da sessão, um contador de geração e um estado de habilitação. Os workers nunca recebem os bytes dos segredos. Eles recebem uma solicitação de ação e precisam obter uma concessão de autorização nova imediatamente antes de o cofre executar a ação.

Uma estrutura pequena é suficiente:

AuthorityState {
  sessionID: 6C17...
  generation: 41
  vaultOpen: true
  logoutStarted: false
}

execute(request):
  lease = authority.issueLease(request, generation: 41)
  vault.perform(request, lease)

beginLogout():
  authority.logoutStarted = true
  authority.vaultOpen = false
  authority.generation = 42
  cancelPendingRequests()

vault.perform deve rejeitar a concessão se a geração já não corresponder. Essa segunda comparação deve ficar o mais perto possível do ponto em que o cofre fornece um cabeçalho HTTP, inicia um helper SSH ou assina uma solicitação. Verificar apenas quando a solicitação entra na fila deixa uma condição de corrida grande o bastante para importar.

Em um aplicativo do macOS que protege o cofre com Secure Enclave e Touch ID, o acesso controlado por hardware é útil porque dá à barreira um responsável local claro. Isso não elimina a necessidade do estado da sessão. Um prompt biométrico aceito antes do logout não pode autorizar uma chamada depois do logout.

A barreira do cofre do Sallyport segue esta regra: enquanto está bloqueado, toda ação é negada. Esse limite fixo é melhor que uma lista de exceções de desligamento, porque as listas crescem até que ninguém consiga explicar qual chamada ainda escapa delas.

Uma aprovação ativa pertence a um processo e a uma sessão

A aprovação do usuário deve estar vinculada a um processo de agente específico, à autoridade de assinatura do código desse processo e à sessão de login atual do macOS. Ela nunca deve significar «esta conta aprovou esta ferramenta algum dia».

A identidade do processo é mais do que um ID de processo. IDs de processo são reutilizados. Um chamador que registra apenas o PID 4812 pode aprovar por engano um processo sem relação depois de alguma rotatividade. Registre a hora de início do processo e uma identidade de assinatura do código junto com o PID. Se o processo for filho de um terminal ou de uma integração com editor, mantenha informações suficientes sobre o processo pai para explicar a rota no registro de aprovação, mas não faça da ancestralidade o único sinal de confiança. Wrappers de shell e supervisores de processos mudam essa estrutura constantemente.

O cartão de aprovação deve começar pela autoridade de assinatura, porque isso dá ao desenvolvedor uma pergunta significativa: «Quero que este processo de agente assinado atue nesta sessão?» O nome de um pacote ou uma string arbitrária fornecida pelo MCP não responde a essa pergunta.

Quando o logout começa, descarte todas as aprovações ativas daquela sessão. Não as suspenda. Não as serialize para o próximo login. Não as recrie porque o mesmo binário voltou depois de uma reinicialização. O desenvolvedor precisa aprovar a nova execução como uma nova execução.

Isso também vale para diálogos de aprovação que já estejam na tela. Eles devem desaparecer ou ficar inativos quando a sessão mudar. Um clique atrasado em um cartão antigo não pode reativar uma autorização que já morreu. Dê a cada prompt de aprovação uma expiração vinculada à mesma geração da sessão que protege as solicitações.

Há uma distinção útil que muitas implementações confundem:

  • O desbloqueio do cofre permite que o gateway local considere ações.
  • A autorização da sessão permite que um processo de agente identificado envie ações.
  • A aprovação por uso permite um uso específico de uma credencial.

O logout invalida os três, mas eles não carregam as mesmas evidências nem têm o mesmo momento de validade. O cofre fecha imediatamente. As aprovações da sessão tornam-se inválidas em grupo. Os prompts de uso individual falham porque a geração da sessão mudou. Juntar tudo em um único booleano dificulta a auditoria do motivo pelo qual uma chamada foi aprovada ou negada.

Ferramentas em execução precisam de cancelamento e de incerteza honesta

O logout deve interromper o trabalho que ainda não atravessou o limite externo e tentar interromper o trabalho que já atravessou. Ele não pode desfazer uma operação que um serviço remoto já aceitou.

Separe uma ação em estados com significado operacional:

queued -> authorized -> dispatched -> response received
                    \-> cancelled

Uma ação na fila ainda não saiu do Mac. Remova-a da fila e informe cancelled_before_dispatch. Uma ação autorizada pode manter apenas uma concessão interna de curta duração. Invalide a concessão antes do envio e informe revoked_before_dispatch se o worker chegar tarde demais.

Uma ação enviada é diferente. O sistema remoto pode tê-la recebido mesmo que o processo local nunca receba uma resposta. Não a informe como cancelada apenas porque você fechou um socket ou matou um helper. Registre logout_during_dispatch, capture o identificador da solicitação se o protocolo remoto fornecer um e informe ao usuário que o resultado é desconhecido até que ele verifique o sistema remoto.

As solicitações HTTP exigem atenção especial. Fechar uma conexão do cliente pode interromper um upload antes que o servidor o leia ou pode acontecer depois que o servidor confirmou uma alteração. Tokens de idempotência reduzem o dano quando o usuário tenta novamente, mas não transformam uma solicitação incerta em uma solicitação cancelada. Para operações que criam um recurso externo, envie um identificador de idempotência que a API remota realmente aceite e registre esse identificador sem armazenar a credencial.

O SSH é ainda menos previsível. Enviar um sinal para um helper local pode matar o processo local enquanto o comando remoto continua executando em seu próprio grupo de processos. Quando você controla o ambiente remoto, execute trabalhos longos sob um supervisor de jobs remoto, com um identificador explícito e um caminho de cancelamento. Quando não controla o ambiente, diga isso no registro da atividade. Fingir que você cancelou uma migração remota porque o terminal local fechou é uma forma de piorar um incidente ruim.

Não deixe o logout esperar indefinidamente pela limpeza. Feche a autoridade primeiro, peça aos workers que cancelem, dê ao aplicativo um intervalo curto e limitado para a limpeza e deixe o sistema operacional concluir o logout. A propriedade de segurança é negar usos futuros. Uma saída organizada é apenas uma conveniência de melhor esforço.

A persistência em segundo plano muda o modelo de ameaça

Não carregue aprovações adiante
A autorização da sessão termina quando a execução do agente acaba, em vez de acompanhar a conta.

Um aplicativo por usuário que continua agindo depois que o usuário sai da sessão deixou de ser um assistente de desktop e se tornou um serviço sem supervisão. Isso pode ser adequado para uma conta de serviço criada deliberadamente. Não é adequado como efeito acidental de um aplicativo na barra de menus.

Evite instalar um helper privilegiado ou um job de inicialização no domínio do sistema apenas para manter um agente vivo durante o logout. Essa solução é popular porque faz os jobs longos parecerem confiáveis. Ela também separa o caminho da ação da pessoa que o aprovou e frequentemente amplia o acesso para além da sessão original do usuário.

Se uma equipe realmente precisa que o trabalho continue depois que um desenvolvedor sair, dê a esse trabalho uma estrutura própria. Use uma identidade de serviço explícita, credenciais com escopo remoto restrito e expiração, responsabilidade definida, trilha de auditoria e um procedimento de cancelamento que outro operador possa usar. Torne a transferência visível. Um agente local não deve assumir esse papel silenciosamente.

A Troca Rápida de Usuário expõe o mesmo problema. O usuário A pode deixar uma sessão gráfica aberta enquanto o usuário B faz login. O usuário B não pode aprovar nem observar a autoridade do agente do usuário A. Vincule cada instância do gateway, registro de aprovação e verificação de acesso ao cofre ao usuário e à sessão gráfica corretos. Um daemon que atende os dois usuários de forma indiscriminada exige um isolamento muito cuidadoso. A maioria das ferramentas de desktop deve evitar essa arquitetura.

Suspensão não é logout. Um Mac em suspensão pode retomar a mesma sessão do usuário, portanto as equipes precisam de uma decisão separada para suspensão e bloqueio da tela. Para credenciais sensíveis, fechar o cofre ao bloquear a tela costuma ser uma medida sensata. Para trabalhos locais menos sensíveis, o gateway pode manter o estado do cofre, mas exigir uma nova aprovação depois que o computador despertar. Qualquer que seja a política escolhida, não a descreva como comportamento de logout. Usuários e pessoas que analisam incidentes precisam de termos precisos.

Os logs devem sobreviver à autoridade sem se tornar um segundo cofre de segredos

Você precisa de um registro durável da autoridade que terminou, especialmente quando uma ação de rede coincidiu com o logout. Não precisa de um segundo banco de dados cheio de tokens, corpos de solicitações ou material privado de SSH.

Registre eventos do ciclo de vida como fatos: sessão aberta, processo de agente aprovado, solicitação enviada, uso da credencial autorizado, envio iniciado, logout observado, concessão revogada, encerramento do helper solicitado e resultado final. Inclua identificadores estáveis que permitam ao operador relacionar os eventos, mas minimize o conteúdo do usuário. Um registro de atividade pode dizer que uma chamada HTTP para um endpoint configurado foi bem-sucedida sem guardar um cabeçalho de autorização nem um corpo de resposta sensível.

Um log de auditoria encadeado por hash acrescenta uma propriedade que o logging comum de aplicativos não tem: um verificador offline pode detectar registros removidos ou alterados. Isso é útil depois de uma implantação contestada ou de uma suspeita de comprometimento local, mas não torna um log verdadeiro por mágica. O log prova a continuidade entre os registros que contém. Ele não prova que um processo malicioso nunca parou de registrar eventos antes de agir.

Por esse motivo, escreva o evento de revogação antes de qualquer limpeza de processos feita como melhor esforço. Se o aplicativo falhar ao matar um helper, o registro ainda deve mostrar que a autoridade local terminou e que o resultado da ação pode ser incerto. Uma trilha de auditoria que registra apenas conclusões bem-sucedidas ensina a lição errada aos operadores.

O Sallyport projeta seus diários de sessão e de atividade a partir de um único log de auditoria criptografado, encadeado por hash e sem possibilidade de escrita, e sp audit verify pode verificar essa cadeia offline sobre o texto cifrado. Assim, a equipe consegue conferir a continuidade sem abrir o cofre apenas para descobrir se o logout revogou uma execução.

A pergunta útil de auditoria é específica: «Qual processo tinha autoridade, qual caminho de credencial ele solicitou e o que o gateway sabia quando o usuário saiu?» Um log de depuração enorme raramente responde a isso.

Teste as condições de corrida em vez de testar uma saída organizada

Rastreie o limite do logout
Os dois diários vêm de um único log de auditoria criptografado e encadeado por hash, em vez de registros mutáveis separados.

Um teste de logout precisa fazer uma ação coincidir com o limite. Testes que chamam um método de encerramento do aplicativo depois que todos os workers terminam verificam apenas se a limpeza normal funciona.

Comece com um endpoint sob seu controle que aceite uma solicitação, registre o recebimento e atrase a resposta. Envie uma ação pelo gateway do agente e inicie o logout depois que o gateway marcar a ação como enviada, mas antes de a resposta retornar. No login seguinte, compare o estado da auditoria local com o registro do endpoint. O resultado esperado nem sempre é «cancelada». É um estado preciso, como logout_during_dispatch, acompanhado de um identificador de solicitação que possa ser investigado.

Use um teste separado para o trabalho na fila. Pause um worker depois que ele retirar um item da fila, mas antes de pedir ao cofre uma concessão para a ação. Inicie o logout, libere o worker e confirme que ele recebe um resultado de revogação, em vez de enviar qualquer coisa. Esse teste detecta o erro comum de verificar a autorização somente quando as solicitações entram na fila.

Depois, exercite os casos desconfortáveis:

  • Troque para outro usuário enquanto a sessão original continua aberta.
  • Bloqueie a tela, desperte o computador e teste a política escolhida para essa transição.
  • Force o encerramento do agente enquanto uma solicitação aguarda aprovação e depois inicie um novo processo com um PID reutilizado, se o seu ambiente de teste conseguir fazer isso.
  • Interrompa o gateway durante o desligamento e verifique se o registro de auditoria ainda identifica o trabalho não resolvido.
  • Envie um comando SSH que inicie trabalho remoto e confirme o comportamento no lado remoto, em vez de confiar no status de saída do helper local.

No macOS, inspecione os domínios de inicialização durante a configuração do teste para saber o que realmente foi iniciado:

uid="$(id -u)"
launchctl print "gui/$uid" | grep -E "(agent-gateway|your-test-label)"

A saída varia conforme o job instalado e a versão do macOS, mas deve mostrar o job correspondente dentro do domínio gui/<uid> atual. Se o job de teste aparecer em um domínio do sistema, o resultado do logout dirá pouco sobre o comportamento da sessão de um aplicativo de desktop comum.

Não automatize primeiro um logout real na conta principal de um desenvolvedor. Use uma conta local descartável, uma credencial de API descartável e um endpoint em que você possa inspecionar cada solicitação. Testes de logout podem destruir trabalho não salvo e deixar o estado remoto parcialmente alterado. Isso não é motivo para ignorá-los. É motivo para deixar de tratá-los como um teste unitário.

As credenciais remotas devem limitar o impacto de uma chamada tardia

Injete credenciais HTTP localmente
O Sallyport executa chamadas HTTP com bearer, autenticação básica e cabeçalhos personalizados sem entregar a credencial ao agente.

A revogação local não consegue voltar no tempo e revogar um bearer token que um serviço remoto já aceitou. O projeto das credenciais remotas limita o dano caso o gateway descubra uma solicitação tarde demais, um aplicativo falhe ou uma máquina seja comprometida antes do logout.

Prefira credenciais remotas com permissões restritas e curta duração sempre que o destino oferecer esse recurso. Use credenciais separadas para ambientes diferentes. Um agente que pode atualizar uma implantação de staging não deve receber uma credencial capaz de apagar dados de produção apenas porque os dois endpoints usam o mesmo fornecedor de API.

Para SSH, use uma conta remota dedicada e restrinja o que ela pode fazer. Se um comando precisar iniciar uma tarefa longa, torne a responsabilidade e o cancelamento da tarefa visíveis no lado remoto. Uma identidade SSH pessoal com acesso amplo ao shell é conveniente até que você precise explicar por que um job continuou depois que seu processo pai local desapareceu.

Não passe credenciais ao agente como variáveis de ambiente, texto de configuração, substituições de placeholders ou argumentos de shell. Depois que um segredo entra no chamador, o logout pode interromper futuras ações locais, mas não consegue fazer essa cópia desaparecer da memória do processo, do histórico do shell, de relatórios de falha ou de uma transcrição. Um gateway deve injetar a credencial somente na ação que executa e devolver o resultado ao agente.

Esse projeto dá ao logout uma tarefa clara: fechar o cofre, invalidar concessões ativas, rejeitar concessões antigas, interromper o trabalho pendente e registrar a incerteza do trabalho que já foi enviado. Ele não pode prometer desfazer a internet. Pode impedir que a próxima chamada pegue emprestada a autoridade de uma pessoa que já não está ali.

A condição de saída deve ser fácil de explicar

Escreva a regra em uma frase que um desenvolvedor cansado consiga usar durante um incidente: quando a sessão de usuário do macOS termina, nenhum processo de agente local pode iniciar outra ação que use credenciais sob a autoridade desse usuário.

Todo o resto deriva dessa regra. A barreira do cofre fecha antes da limpeza. As aprovações morrem junto com o processo e a sessão que as conquistaram. Uma solicitação na fila perde sua concessão. Uma solicitação já enviada recebe um estado desconhecido honesto até que o sistema remoto confirme o resultado. Os logs continuam disponíveis para análise, enquanto os segredos e a autoridade executável não.

Se um produto precisa de autoridade depois do logout, crie um serviço autônomo explícito, com identidade própria. Não esconda essa decisão no caminho de desligamento de um aplicativo de desktop.

FAQ

Bloquear a tela de um Mac é o mesmo que sair da sessão para a segurança do agente?

Não. Bloquear a tela protege o console contra o uso casual, mas a sessão do usuário e seus processos podem continuar ativos. Trate o bloqueio da tela como um motivo para restringir ações ou exigir uma nova aprovação. O logout deve encerrar a autoridade local e invalidar completamente a sessão.

O que deve acontecer se um agente fizer uma solicitação durante o logout?

Ele deve negar imediatamente. O bloqueio do cofre precisa acontecer antes de qualquer trabalho de desligamento atrasado, solicitação de limpeza ou animação da interface. Se uma ferramenta já iniciou uma operação externa, registre o estado conhecido e impeça novas chamadas depois do logout.

Um agente aprovado deve continuar aprovado depois que o usuário fizer login novamente?

Não. Uma aprovação pertence a um processo de agente específico dentro de uma sessão de usuário específica, não à conta em geral. Um novo login precisa de uma nova verificação da identidade do processo e de uma nova decisão de autorização.

Os logs de auditoria devem sobreviver ao logout do macOS?

Um sistema seguro deve preservar evidências, não autoridade. Mantenha um registro de auditoria criptografado com a sessão, as aprovações, as chamadas negadas e qualquer revogação causada pelo logout, mas não restaure credenciais executáveis nem o estado de aprovação a partir desse registro.

O logout consegue interromper com segurança um comando SSH ou uma solicitação HTTP já em andamento?

Tente interrompê-la, mas não finja que a interrupção prova que a ação externa parou. Registre o identificador do processo, seu processo pai, o comando e o estado da ação antes da revogação. Para trabalho remoto, use credenciais com escopo restrito e cancelamento ou expiração no lado remoto, quando o serviço oferecer esse recurso.

E se um processo do macOS por usuário sobreviver ao logout?

Ele deve falhar de forma segura. O aplicativo precisa tratar a ausência de uma sessão interativa de login como uma falha de autoridade, mesmo que um processo em segundo plano continue ativo por algum tempo. Um login posterior pode iniciar uma nova instância do serviço, mas não deve herdar as concessões da instância anterior.

O trabalho autônomo do agente deve continuar depois que o desenvolvedor sair da sessão?

Somente se o usuário tiver criado deliberadamente uma autoridade de serviço separada, com credenciais, responsáveis, registros de auditoria e regras de encerramento próprios. Um gateway de agente para desktop que usa a autoridade local do desenvolvedor não deve se transformar silenciosamente em um servidor só porque um comando demora para terminar.

Como um agente pode usar credenciais de API ou SSH sem mantê-las depois do logout?

Sim. Se o aplicativo roda no mesmo Mac e controla as credenciais, ele pode fazer o logout revogar a autoridade sem colocar os segredos no processo do agente. O Sallyport mantém os segredos em seu cofre criptografado e executa a ação HTTP ou SSH por conta própria. Assim, o agente recebe o resultado, não um material de credencial reutilizável.

O que um log de auditoria deve registrar quando a autoridade local do agente termina?

No mínimo, registre a hora, a identidade do usuário, o identificador da sessão, a identidade do processo do agente, a autoridade aprovada, o identificador da ação, o canal da ação, o resultado e o motivo da revogação. Não coloque corpos brutos de solicitações ou respostas, tokens nem material privado de SSH em um log amplamente acessível.

O logout do macOS é adequado para trabalhos de agentes em produção que duram muito tempo?

Não use uma sessão de usuário como um lugar improvisado para executar automações de produção sem supervisão. Coloque o trabalho autônomo atrás de uma conta de serviço, credenciais remotas de curta duração, responsabilidade explícita e um fluxo de implantação que possa ser revisado. O logout de um desenvolvedor não deve deixar um desktop pessoal com autoridade sobre produção.

Sallyport

O Sallyport executa chamadas de API e comandos SSH pelo seu agente de IA. As chaves ficam em um cofre local no seu Mac; você aprova cada execução e toda ação vai para um registro selado.

© 2026 Sallyport · Código aberto sob Apache-2.0 · Oleg Sotnikov