Ativo · v0.3.1 · rollout Windows concluído
PowerShellWindows ServerWindows Event LogWTS APITask SchedulerDPAPIHTTPSJSONRDP

Problema

Monitorar RDP de forma centralizada exige coletar evidências que existem localmente em cada Windows Server sem transformar o monitoramento em outro serviço privilegiado exposto pela rede.

O Windows fornece boas fontes nativas, mas cada uma responde a uma pergunta diferente:

Event Log -> o que aconteceu e em qual ordem
WTS       -> quais sessões existem agora

A versão inicial do Agent já combinava essas duas fontes. A evolução posterior adicionou outro problema: preservar também a origem da conexão, sem tornar esse dado obrigatório e sem quebrar servidores antigos ou o contrato existente da API.

Solução

RDP Session Agent é o coletor Windows da Remote Session API.

Ele executa localmente como SYSTEM através do Task Scheduler e utiliza somente componentes disponíveis no próprio Windows:

  • Microsoft-Windows-TerminalServices-LocalSessionManager/Operational;
  • Windows Terminal Services API (wtsapi32.dll);
  • Windows PowerShell;
  • DPAPI;
  • Task Scheduler;
  • HTTPS de saída.

O Agent não abre listener próprio, não exige WinRM, SMB ou WMI remoto e não armazena a chave global de consulta da API.

Compatibilidade

O alvo formal atual é:

Windows Server 2012 -> Windows Server 2022
Windows PowerShell 3.0+

Manter compatibilidade com PowerShell antigo influenciou várias decisões de implementação, incluindo uso de APIs .NET disponíveis nessas versões e chamadas nativas WTS através de um helper C# carregado em runtime.

Arquitetura local

Arquitetura local do RDP Session Agent
01 Fontes nativas do Windows LocalSessionManager registra o lifecycle; WTS expõe o estado atual das sessões RDP e o endereço do cliente quando disponível.
02 RDP Session Agent Runtime PowerShell executado como SYSTEM pelo Task Scheduler, sem listener inbound ou serviço HTTP próprio.
03 Normalização + reconciliação Mapeia Event IDs 21/23/24/25, normaliza source_ip, prepara snapshots WTS e preserva checkpoints incrementais.
04A state.json Mantém checkpoint por EventRecordID e estado necessário para snapshots e retomada segura.
04B Remote Session API Recebe eventos e snapshots autenticados por HTTPS outbound e consolida o estado central das sessões.
04C credential.dat + spool + logs Secret por servidor protegido por DPAPI, batches duráveis pendentes, logs operacionais e cópias de runtime para rollback.
05 Task Scheduler Executa a cada minuto e dispara reconciliação WTS no intervalo configurado sem manter um serviço customizado residente.

A coleta permanece próxima da fonte, enquanto histórico, state machine, banco e correlação ficam centralizados na API.

Lifecycle coletado

O Agent lê o canal:

Microsoft-Windows-TerminalServices-LocalSessionManager/Operational

E normaliza os eventos principais:

Event IDEventoSignificado
21LOGONnova sessão RDP
23LOGOFFsessão encerrada
24DISCONNECTsessão continua aberta, porém desconectada
25RECONNECTsessão desconectada tornou-se ativa novamente

O EventRecordID é usado como checkpoint incremental. Depois da inicialização, o Agent busca somente registros posteriores ao último record ID confirmado.

Por que existe um snapshot WTS

Event Log preserva cronologia, mas uma sequência de eventos nunca deve ser assumida como perfeita.

A cada intervalo configurado, cinco minutos por padrão, o Agent enumera as sessões atuais através da WTS API.

São consideradas apenas sessões RDP relevantes em estado:

ACTIVE
DISCONNECTED

A API utiliza esse snapshot como mecanismo de reconciliação. Assim, uma sessão que ficou aberta no estado central, mas não existe mais no Windows, pode ser encerrada com semântica de RECONCILIATION.

Captura do IP de origem

A linha 0.3.x passou a transmitir source_ip quando o Windows fornece evidência utilizável.

Existem duas fontes.

Event Log

Nos eventos LOGON e RECONNECT, o LocalSessionManager pode incluir o campo Address.

O Agent aceita IPv4 e IPv6 válidos e converte para null valores como:

  • LOCAL;
  • loopback;
  • unspecified;
  • endereço inválido ou incompleto.

Um IP ausente não invalida o evento.

WTSClientAddress

Nos snapshots, o Agent consulta WTSClientAddress através de WTSQuerySessionInformation.

A falha dessa consulta também resulta apenas em source_ip=null; o snapshot continua válido.

Essa é uma decisão importante: origem é evidência adicional, não requisito para reconhecer que a sessão existe.

Origem não é identidade

O endereço fornecido pelo Windows pode representar o cliente RDP e não necessariamente o peer final visto em outra camada de rede.

NAT, VPN e Remote Desktop Gateway podem alterar essa interpretação.

Por isso o Agent não tenta concluir qual dispositivo originou a sessão. Ele envia a evidência disponível e deixa a correlação temporal para os componentes centrais.

A versão atual também não inventa source_port, pois as fontes usadas pelo collector não oferecem uma porta de origem confiável.

Spool antes do envio

O Agent foi desenhado considerando falha de rede como um estado normal possível.

Antes de transmitir um lote:

1. coletar eventos
2. persistir envelope no spool
3. enviar à API
4. receber acknowledgement
5. avançar checkpoint
6. remover spool confirmado

Se a chamada HTTP falhar, o arquivo permanece em disco e o checkpoint daquele lote não é avançado.

Na execução seguinte, o Agent tenta entregar spool pendente antes de buscar eventos mais recentes.

Idempotência e replay

O Agent não precisa resolver sozinho a ambiguidade de “a API recebeu, mas a resposta se perdeu?”.

Ele pode reenviar o lote.

A Remote Session API implementa ingestão idempotente e responde com contadores como:

accepted=3 duplicates=0

ou, em replay:

accepted=0 duplicates=3

O ponto importante é que replay não deve recriar a sessão.

Estado local

O runtime padrão fica em:

C:\ProgramData\RdpSessionAgent\

Principais artefatos:

  • config.json: URL da API, server_id e parâmetros operacionais;
  • credential.dat: Agent secret protegido por DPAPI;
  • state.json: checkpoint e último snapshot;
  • spool\: telemetria ainda não confirmada;
  • logs\: execução local;
  • rollback\: cópias de runtime para reversão de atualização;
  • src\: runtime efetivamente executado.

O checkout Git e o runtime instalado são deliberadamente separados.

Credencial por servidor

Cada Windows Server recebe seu próprio:

server_id
Agent secret

O secret é protegido com:

Windows DPAPI
DataProtectionScope.LocalMachine

O plaintext não fica em config.json.

Além disso, a ACL do diretório instalado é restrita a:

  • SYSTEM;
  • Administrators locais.

credential.dat é específico daquela máquina e não deve ser copiado para outro host.

Execução agendada

A instalação cria a Scheduled Task:

RDP Session Agent

Ela executa:

  • a cada minuto;
  • como SYSTEM;
  • com privilégios elevados;
  • diretamente a cópia instalada em C:\ProgramData\RdpSessionAgent.

A escolha por execuções curtas evita a necessidade de empacotar o Agent como Windows Service.

O problema de atualizar um Agent já instalado

Nas versões anteriores, atualizar o runtime significava reutilizar Install-Agent.ps1, que também recebe configuração e o secret do servidor.

Isso é inadequado para rollout rotineiro: a API armazena apenas o hash do secret e a credencial em plaintext não deveria precisar ser recuperada simplesmente para trocar código.

A versão 0.3.1 adicionou um caminho de atualização separado.

Update-Agent.ps1

O updater altera somente:

src\
VERSION

E preserva:

config.json
credential.dat
state.json
spool\
logs\

Antes da troca, ele cria uma cópia do runtime anterior em:

C:\ProgramData\RdpSessionAgent\rollback\

Durante o update, a Scheduled Task é temporariamente desabilitada/encerrada para evitar que o runtime seja trocado no meio de uma execução.

Depois da substituição, a task retorna ao estado anterior.

Se a troca falhar após ter começado, o script tenta restaurar automaticamente src e VERSION do backup.

Atualização sem secret

O fluxo normal passou a ser:

git switch main
git pull --ff-only
.\scripts\Update-Agent.ps1

O AgentSecret não participa dessa operação.

Install-Agent.ps1 fica reservado para:

  • primeira instalação;
  • rotação de credencial;
  • reparo de configuração/credencial.

Essa separação reduz risco operacional e torna rollout em lote muito mais previsível.

Rollout validado

A atualização 0.3.1 foi primeiro validada em canários com diferentes gerações de Windows Server e depois expandida gradualmente.

O gate verificou:

LOGON -> ACTIVE
DISCONNECT -> DISCONNECTED
RECONNECT -> ACTIVE
LOGOFF -> CLOSED

Além de:

  • Scheduled Task preservada;
  • autenticação existente continuando válida;
  • WTS funcionando após o update;
  • spool sem crescimento;
  • source_ip presente quando o Windows fornecia evidência;
  • ausência de duplicação de sessão;
  • rollback disponível sem copiar credenciais.

Depois dos canários, a versão foi distribuída para todos os servidores Windows monitorados sem regressões operacionais observadas.

Preflight e operação

Antes de uma instalação inicial, o projeto valida:

  • versão do PowerShell;
  • disponibilidade do LocalSessionManager;
  • enumeração WTS;
  • health HTTPS da API;
  • trust do certificado.

A operação diária pode ser inspecionada com:

$Root = 'C:\ProgramData\RdpSessionAgent'
Get-Content "$Root\VERSION"
schtasks.exe /Query /TN 'RDP Session Agent' /V /FO LIST
Get-Content "$Root\logs\agent-$(Get-Date -Format yyyyMMdd).log" -Tail 50
Get-ChildItem "$Root\spool" -File

Um Agent saudável deve apresentar task ativa, logs recentes, snapshots periódicos, spool estável e last_seen avançando na API.

Segurança e privacidade

O Agent coleta metadados de sessão, não conteúdo de usuário.

Ele não captura:

  • comandos;
  • clipboard;
  • senha;
  • conteúdo da sessão;
  • arquivos transferidos.

Também não exige um serviço inbound para coleta central.

O desenho mantém o host monitorado como produtor de telemetria outbound, em vez de transformar o servidor central em um operador remoto privilegiado de todos os Windows Servers.

Estado atual

A versão 0.3.1 reúne:

  • lifecycle RDP por Event Log;
  • reconciliação WTS;
  • origem IPv4/IPv6 opcional;
  • spool durável;
  • replay idempotente;
  • checkpoint local;
  • DPAPI por máquina;
  • Scheduled Task;
  • atualização segura sem plaintext secret;
  • runtime rollback;
  • suporte alvo a Windows Server 2012–2022.

O que o projeto demonstra

RDP Session Agent começou como um collector PowerShell pequeno, mas sua evolução mostra um problema relevante de engenharia de infraestrutura: coletar é apenas metade do trabalho; atualizar e recuperar o collector com segurança também faz parte do sistema.

O projeto demonstra uso de APIs nativas do Windows, processamento incremental de Event Log, reconciliação de estado, persistência orientada a falhas, gestão de segredo com DPAPI e rollout backward-compatible em servidores reais.