De uma API RDP para um domínio comum de sessões remotas
A primeira versão deste projeto nasceu para resolver um problema específico: receber telemetria de sessões RDP de vários Windows Servers sem dar a dashboards ou integrações acesso administrativo aos hosts monitorados.
Com a evolução do sistema, o problema deixou de ser apenas RDP. A mesma infraestrutura passou a precisar representar sessões SSH, preservar a origem da conexão e permitir investigação histórica sem quebrar os consumidores Windows que já estavam em produção.
O repositório manteve o nome histórico RDP-Session-API, mas a aplicação evoluiu para uma Remote Session API capaz de representar RDP e SSH em um domínio comum.
A versão atual, 0.7.0, preserva o contrato /api/v1 para Windows/RDP e adiciona um contrato /api/v2 genérico para sessões remotas.
Problema
Monitorar sessões remotamente parece simples enquanto a pergunta é apenas “quem está conectado agora?”. O problema real aparece quando precisamos responder, com histórico consistente:
- quem abriu uma sessão;
- em qual servidor;
- por qual protocolo;
- quando conectou e encerrou;
- se houve disconnect/reconnect;
- qual era a origem da conexão;
- qual dispositivo estava associado àquele endereço naquele instante;
- e como recuperar o estado após falhas de rede, reinicializações ou eventos perdidos.
Consultar os servidores diretamente também cria um forte acoplamento operacional. Cada consumidor precisaria conhecer Windows Event Log, WTS, OpenSSH, logind, credenciais administrativas e a topologia de rede.
A solução foi centralizar persistência, normalização e consulta sem centralizar a coleta.
Arquitetura atual
O fluxo de ingestão é deliberadamente independente do enriquecimento de rede. Um evento é autenticado, persistido e consolidado sem depender de outro serviço estar disponível.
A correlação de origem ocorre depois, de forma assíncrona.
Compatibilidade entre /api/v1 e /api/v2
Uma decisão importante foi não migrar o Agent Windows à força apenas para introduzir SSH.
O /api/v1 continua sendo o contrato estável para RDP:
POST /api/v1/agent/events
POST /api/v1/agent/snapshot
GET /api/v1/servers
GET /api/v1/servers/{id}/summary
GET /api/v1/servers/{id}/sessions/active
GET /api/v1/servers/{id}/sessions/history
O /api/v2 representa sessões remotas de forma independente do provider:
POST /api/v2/agent/events
POST /api/v2/agent/snapshot
GET /api/v2/servers
GET /api/v2/sessions/active
GET /api/v2/sessions/history
GET /api/v2/sessions/{id}
GET /api/v2/sessions/{id}/timeline
GET /api/v2/correlation/metrics
A identidade genérica utiliza campos como:
platform;protocol;boot_id;provider_session_id;provider_event_id.
Assim, um SessionID do Windows e um logind:42 do Linux podem coexistir sem que a API imponha semântica específica de um sistema operacional ao outro.
Normalização interna
Payloads RDP v1 continuam sendo aceitos normalmente, mas são normalizados internamente para o mesmo domínio usado pelo v2.
Isso permitiu evoluir banco, histórico e consultas sem quebrar o Agent Windows existente.
É um padrão que considero importante em sistemas operacionais reais: compatibilidade na borda, modelo comum no núcleo.
Máquina de estados
A API mantém três estados consolidados:
ACTIVE
DISCONNECTED
CLOSED
Para RDP, o lifecycle normal é:
LOGON -> ACTIVE
DISCONNECT -> DISCONNECTED
RECONNECT -> ACTIVE
LOGOFF -> CLOSED
SSH possui um lifecycle menor, normalmente LOGON -> LOGOFF, mas utiliza a mesma entidade consolidada.
A API também consegue encerrar sessões por razões derivadas de reconciliação, como:
RECONCILIATION: a sessão ainda estava aberta no banco, mas desapareceu do snapshot atual;REBOOT: oboot_idmudou e as sessões pertenciam ao boot anterior.
Eventos e snapshots resolvem problemas diferentes
A arquitetura não trata um snapshot como substituto do histórico de eventos.
Eventos -> cronologia: o que aconteceu e quando
Snapshots -> verdade observada: o que existe agora
Essa combinação permite recuperar consistência quando um Agent, a API ou a rede ficam temporariamente indisponíveis.
Uma sessão pode ser criada ou atualizada a partir de eventos e depois corrigida por snapshot caso a visão consolidada tenha sofrido drift.
Ingestão idempotente
Agents utilizam spool local exatamente porque a rede pode falhar em qualquer momento.
Por isso a API precisa aceitar replay.
Cada evento recebe uma identidade/fingerprint determinística. Um lote pode ser enviado novamente sem recriar a sessão ou repetir uma transição que já foi persistida.
No v1, o algoritmo histórico foi preservado para manter compatibilidade com spools de Agents Windows existentes. No v2, a identidade combina o contexto genérico de provider, boot e sessão.
Idempotência não é apenas uma otimização neste projeto: ela é parte do protocolo de recuperação.
Autenticação por capacidade
O projeto separa credenciais de escrita e leitura.
Cada servidor monitorado recebe:
X-Server-ID: <server-id>
Authorization: Bearer <agent-secret>
O secret é individual por servidor e a API armazena somente seu hash.
Consultas utilizam outra credencial:
X-API-Key: <query-api-key>
Isso significa que comprometer a credencial de um Agent não concede automaticamente leitura do histórico global.
O worker de correlação possui ainda uma terceira credencial dedicada para comunicação service-to-service com o resolver de rede.
Origem da conexão
Os Agents podem enviar source_ip e, quando a fonte permite, source_port.
Esses dados são tratados como evidência, não como identidade permanente.
A ausência de IP não invalida a sessão. Da mesma forma, a API não transforma um endereço atual em uma conclusão histórica apenas porque aquele IP pertence a um equipamento agora.
Essa distinção tornou-se importante quando o sistema passou a correlacionar sessões com inventário.
Correlação temporal assíncrona
A pergunta correta não é:
quem possui este IP agora?
Mas sim:
qual evidência de rede relacionava este IP a um dispositivo no instante em que a sessão aconteceu?
A correlação utiliza source_ip + timestamp e é processada por um worker separado da API HTTP.
O resultado é determinístico:
MATCHED
AMBIGUOUS
UNRESOLVED
AMBIGUOUS e UNRESOLVED são resultados válidos. O sistema prefere não associar um dispositivo a produzir um falso MATCHED.
Erros transitórios de transporte podem ser reenfileirados, mas ausência ou ambiguidade de evidência não são tratadas como falhas que devam ser “corrigidas” por adivinhação.
Evidência congelada
Quando uma correlação é concluída, a API preserva a evidência retornada pelo resolver.
Isso evita um problema sutil: DHCP, inventário e endereçamento continuam mudando depois que a sessão ocorreu. Se uma consulta histórica dependesse sempre do estado atual, a interpretação de uma sessão antiga poderia mudar com o tempo.
A sessão passa a guardar o resultado da evidência temporal utilizada naquele momento.
Histórico global e timeline
O v2 adicionou consultas globais independentes de servidor e protocolo.
O histórico pode ser filtrado por atributos como:
- servidor;
- protocolo;
- estado;
- usuário;
- intervalo de tempo;
- origem;
- status de correlação.
Além da sessão consolidada, a API consegue reconstruir a timeline dos eventos relacionados.
Isso é especialmente importante porque identificadores de provider podem ser reutilizados. Um Session ID RDP ou um ID do logind isolado não é suficiente para identificar uma sessão para sempre.
Operação em Linux
A aplicação roda em Linux com Uvicorn em loopback atrás de reverse proxy HTTPS.
A operação é dividida em dois processos systemd independentes:
API HTTP
Correlation Worker
Essa separação permite, por exemplo, desabilitar temporariamente enrichment sem interromper a ingestão de telemetria.
Migrations são controladas por Alembic e as alterações de schema do projeto foram planejadas de forma aditiva para permitir rollout gradual entre API e Agents.
Observabilidade do próprio monitoramento
Além das sessões, o sistema acompanha a saúde da pipeline.
Métricas relevantes incluem:
- eventos aceitos e duplicados;
- cobertura de
source_ip; - taxa de correlação;
MATCHED,UNRESOLVEDeAMBIGUOUS;- profundidade e idade da fila de enrichment;
last_seendos servidores;- distribuição de versões dos Agents.
Isso evita o anti-pattern de construir um sistema de observabilidade que não consegue explicar a própria degradação.
Evolução validada sem big bang
A expansão foi executada de forma aditiva:
- manter o contrato RDP existente;
- adicionar campos de origem opcionais;
- introduzir o domínio genérico v2;
- adicionar histórico global;
- introduzir correlação temporal assíncrona;
- integrar o Agent SSH;
- ampliar o rollout sem exigir atualização simultânea de todos os coletores.
Esse caminho permitiu evoluir a arquitetura sem uma migração destrutiva dos servidores já monitorados.
Estado atual
A versão 0.7.0 já representa um sistema de sessões remotas, e não apenas uma API RDP.
O projeto mantém:
/api/v1compatível para RDP;/api/v2genérico para RDP e SSH;- ingestão idempotente;
- snapshots e reconciliação;
- histórico global e timeline;
- origem IPv4/IPv6 opcional;
- correlação temporal assíncrona;
- evidência histórica congelada;
- autenticação individual por servidor;
- query API separada;
- migrations e deployment Linux gerenciado.
O que o projeto demonstra
A evolução da Remote Session API demonstra um problema que aparece com frequência em infraestrutura: a primeira versão resolve um caso concreto, mas o valor real surge quando o domínio é abstraído sem perder compatibilidade com o que já funciona.
Tecnicamente, o projeto reúne versionamento de contratos, ingestão idempotente, state machines, reconciliação, processamento assíncrono, modelagem temporal de evidência, autenticação por capacidade e operação segura de serviços Python.
Mais importante, ele trata telemetria como evidência sujeita a falhas e ambiguidade — e não como uma sequência perfeita de fatos.
Pronto para ler este conteúdo em voz alta.