Ativo · v0.7.0 · RDP + SSH + correlação temporal
PythonFastAPISQLAlchemyAlembicMariaDBUvicornsystemdNginxHTTPXRESTJSON

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

Arquitetura atual do sistema de monitoramento de sessões remotas
01 Agents Windows e Linux RDP Session Agent publica eventos e snapshots em /api/v1; SSH Session Agent utiliza /api/v2, sempre por HTTPS outbound.
02 Reverse proxy HTTPS Termina TLS e encaminha chamadas autenticadas para o serviço FastAPI exposto somente em loopback.
03 Remote Session API Autentica, normaliza v1/v2, persiste eventos, aplica state machine, reconcilia snapshots e expõe histórico global.
04A MariaDB / MySQL Eventos brutos, sessões consolidadas, credenciais por servidor, correlation jobs e evidência temporal congelada.
04B Correlation Worker Processa enrichment assíncrono sem bloquear ingestão e consulta o resolver temporal por source_ip + timestamp.
04C Temporal Network Resolver Retorna MATCHED, AMBIGUOUS ou UNRESOLVED com evidência de rede válida para o instante observado.
05 Portal, Grafana e consumidores read-only Consultam histórico, sessões ativas, timeline, alertas e métricas usando credencial de leitura separada.

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: o boot_id mudou 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, UNRESOLVED e AMBIGUOUS;
  • profundidade e idade da fila de enrichment;
  • last_seen dos 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:

  1. manter o contrato RDP existente;
  2. adicionar campos de origem opcionais;
  3. introduzir o domínio genérico v2;
  4. adicionar histórico global;
  5. introduzir correlação temporal assíncrona;
  6. integrar o Agent SSH;
  7. 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/v1 compatível para RDP;
  • /api/v2 gené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.