Documentação técnica

Central de Documentação

Visão arquitetural, documentação da stack e guias do projeto renderizados dinamicamente.

README.md Mermaid refresh Atualizar

🛡️ Framework de Redes - Análise Didática Avançada

Versão Java/Quarkus (framework-net-java-quarkus) — Java 25 + Quarkus 3.37.

Migração do projeto original Python/Flask. Esta documentação é renderizada dentro da própria aplicação em /documentacao.

Framework de Redes — Análise Didática Avançada

Aplicação didática para análise de redes IPv4/IPv6, com foco em ensino, laboratório e revisão técnica. Originalmente escrita em Python/Flask, foi migrada para Java 25 + Quarkus e reorganizada como um monólito modular (*modular monolith*): um único artefato de implantação com domínios autocontidos, prontos para evoluir para microserviços. Reúne sete módulos:

  • Análise Didática — CIDR, máscara, wildcard, auto-CIDR, domínio (DNS), IPv6, comparador e GeoIP.
  • Calculadora de Sub-redes e VLANs — divisão de blocos (FLSM), plano de VLANs com script Cisco, sumarização de rotas e faixa de IPs para CIDR.
  • Portas — catálogo interativo de portas TCP/UDP.
  • Protocolos — catálogo de protocolos + troubleshooting de roteamento.
  • Resolução de Problemas (VLSM + WAN) — planejamento VLSM dinâmico, topologia WAN, CLI Cisco e exportação para laboratório.
  • Telemetria — dashboard de eventos e console ao vivo (server-side).
  • Documentação — este README renderizado.

Repositório: https://github.com/carmipa/framework-net-java-quarkus


📚 Sumário

#SeçãoO que responde
1Visão geralpara que serve e a quem atende
2Módulos e rotastabela completa de endpoints, métodos e proteção
3FuncionalidadesAnálise Didática · Calculadora de Sub-redes e VLANs · Portas/Protocolos · Resolução VLSM+WAN
4Arquitetura9 diagramas: fluxo geral, Calculadora, camadas de proteção, cache em 2 níveis, VLSM, telemetria, shared, exceções, deploy
5RequisitosJava, Gradle, Docker
6Execuçãolocal (quarkusDev) e VPS (deploy.sh)
7Configuraçãotodas as framework.* e as quarkus.* relevantes
8SegurançaCSRF, chave admin, rate limit, CSP/SRI, fail-closed de segredos
9Telemetriacorrelação por traceId, OTLP/JSON e dataset público sanitizado
10Estrutura de pastasonde mora cada coisa
11Testesincluindo os que guardam regras (arquitetura, cobertura de menu, CSP)
12Roadmapo que falta

🎯 Visão Geral

O framework cobre um fluxo didático completo para aula, laboratório e revisão técnica:

  • cálculo de rede/broadcast/hosts úteis;
  • decomposição binária e tabela AND por octeto;
  • conversão entre CIDR, máscara e wildcard;
  • resolução de domínio (DNS) com cache e timeout;
  • geolocalização de IP (GeoIP MaxMind, opcional);
  • classificação e contexto de risco/GRC;
  • geração automática de cenário de laboratório (VLSM, links WAN com prefixo configurável, CLI Cisco e exportação para laboratório ou entrega acadêmica em .txt/.zip).

🧭 Módulos e Rotas

MóduloRotaMétodoDescrição
Início/GETPágina inicial (landing) com atalhos para os módulos
Análise Didática/analiseGET/POSTCIDR, máscara, wildcard, auto-CIDR, domínio, IPv6, comparador, calculadora (parâmetro ?tab=)
Calculadora/calculadoraGETDivisão de blocos, plano de VLANs, sumarização e faixa→CIDR (parâmetro ?aba=)
Calculadora (API)/calculadora/api/dividirPOSTFragmento HTML: sub-redes de um bloco + matriz de capacidade
Calculadora (API)/calculadora/api/vlan, /calculadora/api/vlan-idPOSTFragmento HTML: plano de VLANs com CLI Cisco / parecer sobre um VLAN ID
Calculadora (API)/calculadora/api/sumarizar, /calculadora/api/comparar, /calculadora/api/faixaPOSTFragmento HTML: rota resumo, relação entre blocos, faixa em CIDR
Calculadora (export)/calculadora/export/divisao.csv, /calculadora/export/vlan.csvGETCSV do plano exibido
Localização/localizacaoGETLocalização por IP e por CEP no mapa
Localização (API)/localizacao/api/ip, /localizacao/api/cepGETJSON: geolocalização por IP / endereço por CEP (ViaCEP + OSM)
Tráfego/trafegoGETSub-abas: painel ao vivo (simulação), decodificador (hex), encapsulamento e handshake TCP
Tráfego (API)/trafego/api/decodificarPOSTJSON: camadas Ethernet/IP/TCP/UDP/ICMP decodificadas
Tráfego (API)/trafego/api/aovivoGETJSON: snapshot do painel ao vivo (simulação demo)
Simuladores (API)/simuladores/api/encapsularPOSTJSON: encapsulamento camada a camada (App→Enlace)
Simuladores (API)/simuladores/api/handshakeGETJSON: sequência do handshake TCP (?dados=&encerramento=)
GeoIP/informacoesGETPágina de geolocalização (?ip=)
GeoIP (API)/api/informacoes/geoGETJSON de geolocalização (?ip=)
Referência de máscaras/mascara-referenciaGETTabela JSON de máscaras/prefixos
Portas/portasGETCatálogo interativo TCP/UDP
Protocolos/protocolosGETCatálogo + troubleshooting de roteamento (aba Geral)
Protocolos — BGP/protocolos/bgpGETAprofundamento do BGP-4: atributos, seleção de melhor rota, sessão, proteções da borda
Protocolos — SSH/protocolos/sshGETAprofundamento do SSH: camadas, autenticação, chaves, túneis, hardening
Resolução VLSM/resolucao-problemasGET/POSTAba Projetar: cenários VLSM/WAN, demos e exportações
Resolução — reversa/resolucao-problemas?aba=reversaGET/POSTAba Engenharia reversa: interpreta configuração Cisco colada, audita, corrige e reconstrói o projeto
Páginas de erroqualquer rota que falhePágina única em paginaErros/erro.html servindo os 12 códigos (400…504)
Telemetria/telemetriaGETDashboard de eventos e console
Telemetria (API)/telemetria/api/*GET/POSTresumo, dashboard, console, console/limpar, exportar, pasta
Documentação/documentacaoGETEste README renderizado
Sobre/sobreGETO projeto, o autor e as tecnologias
Sonda de saúde/healthGETJSON {"status":"UP"} para o healthcheck do container; não registrada na telemetria
Histórico (API)/historyGETLista o histórico em JSON
Histórico catálogo/history/catalogPOSTRegistra consulta de portas/protocolos
Exportação análise/export/json, /export/pdfGET🔒 Protegido por chave administrativa
Login administrativo/admin/login, /admin/logoutGET/POSTAutenticação para rotas sensíveis

🚀 Funcionalidades

Módulo 1 — Análise Didática (/)

Selecionável via ?tab= (ou pelas abas da interface):

  • cidr — IP + /barra;
  • mask — decomposição por máscara decimal;
  • wildcard — engenharia reversa com base ACL/OSPF;
  • autoip — inferência didática de CIDR pelo IP;
  • dominio — hostname/URL → DNS → análise;
  • ipv6 — visão básica com resumo técnico;
  • comparador — comparação lado a lado entre dois prefixos;
  • calculadora — divisão rápida de um bloco em sub-redes, servida pelo módulo Calculadora via htmx.

Recursos de apoio: geolocalização (/informacoes), tabela de referência de máscaras (/mascara-referencia), histórico paginado e exportações (/export/json, /export/pdf).

Módulo 2 — Calculadora de Sub-redes e VLANs (/calculadora)

Ferramenta de bolso para plano de endereçamento — complementa o Módulo 4 (VLSM), que

resolve o cenário completo com WAN e roteamento. Aqui a resposta é imediata, em

sub-redes de tamanho fixo (FLSM). Quatro abas, selecionáveis via ?aba=:

dividir — divisão de blocos. Responde a pergunta clássica: *um /21, quantas

redes cabem?* Três critérios de entrada:

CritérioEntradaExemplo
Por prefixo alvoprefixo desejado192.168.0.0/21/24 = 8 sub-redes de 254 hosts
Por nº de sub-redesquantas você precisapedir 6 entrega 8 (potências de 2), com a sobra declarada
Por hostshosts por sub-rede500 hosts → /23 (reserva rede e broadcast: 502 endereços)

Acompanha a matriz de capacidade, que responde todos os prefixos de uma vez

(/22→2, /24→8, /26→32, /30→512), e exportação em CSV.

vlan — plano de VLANs. Gera o mapa VLAN ↔ sub-rede com gateway (SVI), faixa

DHCP e wildcard para ACL, mais o script Cisco pronto (vlan/name, portas de acesso,

tronco 802.1Q com switchport trunk allowed vlan, SVI e router-on-a-stick,

ip dhcp pool). Duas estratégias de mapeamento: sequencial (blocos consecutivos)

ou VLAN ID no 3º octeto (VLAN 10 → 192.168.10.0/24, a convenção de prova; exige

bloco base /16 e /24 por VLAN). Inclui validador de VLAN ID com as faixas do

padrão: 0 e 4095 reservados pelo 802.1Q, 1 default, 2–1001 normal range, 1002–1005

reservadas pela Cisco (FDDI/Token Ring) e 1006–4094 extended range.

agregar — sumarização e comparação. Calcula a rota resumo (menor prefixo que

cobre todas as redes informadas) com os comandos de area range (OSPF),

ip summary-address (EIGRP), rota estática e ACL — declarando quanto espaço extra a

agregação arrasta. E compara dois blocos: iguais, contido ou disjuntos, com os

endereços em comum.

faixa — faixa de IPs para CIDR. Converte "de 10.0.0.5 até 10.0.3.200" na

menor lista de blocos CIDR que cobre exatamente a faixa, com a ACL equivalente.

Tetos de renderização. Um /8 dividido em /30 são 4.194.304 sub-redes. A tela

lista no máximo framework.calculadora.max-linhas (padrão 512), mas o total

matemático real continua sendo exibido, com aviso explícito de truncamento — listagem

truncada nunca é apresentada como plano completo.

Módulo 3 — Portas (/portas) e Protocolos (/protocolos)

  • catálogo didático com filtros;
  • resumo IGP/EGP e bloco Troubleshooting rápido (roteamento) na página de protocolos;
  • registro opcional das consultas no histórico via /history/catalog.

Aprofundamentos por protocolo

O catálogo responde *quais protocolos existem e como se comparam*; ele não responde

*me explica este protocolo*. Para isso o módulo tem um segundo nível: uma página

dedicada por protocolo, alcançável pelo sub-menu (Geral · BGP · SSH) e pelo botão

Aprofundar na linha correspondente do DataGrid.

PáginaRotaConteúdo
BGP-4/protocolos/bgpAS/ASN, eBGP × iBGP, atributos de caminho, ordem completa de seleção de melhor rota, máquina de estados da sessão, mensagens e temporizadores, route reflector e confederação, filtros de prefixo, maximum-prefix, RPKI/ROV, GTSM, dampening, blackhole RFC 7999, laboratório Cisco e troubleshooting
SSH/protocolos/sshAutenticação de host × de usuário, TOFU e known_hosts, forward secrecy, as três camadas do SSH-2, métodos de autenticação, tipos de chave (incl. FIDO2), túneis -L/-R/-D/-J/-A, endurecimento do sshd_config, laboratório e troubleshooting

Como isso é montado:

  • AprofundamentoProtocolo (protocolos/domain) é o registro único: slug, rótulo,

ícone, template, CSS e os nomes que aquele aprofundamento cobre no catálogo. É ele que

gera o sub-menu, a rota e o botão "Aprofundar" — nenhum desses pontos é escrito à mão

no template.

  • Conteúdo em JSON (resources/protocolos/<slug>/conteudo.json), carregado uma vez

no boot e falha fechada: seção obrigatória vazia impede a aplicação de subir, porque

página pela metade em produção passa despercebida.

  • Uma página, um CSS: META-INF/resources/protocolos/<slug>/css/<slug>.css. O

sub-menu, comum às três páginas, fica em protocolos/css/subnav.css.

  • Rotas no mesmo resource (ProtocolosResource), de propósito: o projeto já viu dois

@Path sob o mesmo prefixo derrubarem uma rota para 404 no módulo de Tráfego.

  • Telemetria: cada visita emite aprofundamento_view no módulo protocolos, com o

campo protocolo, o que separa "abriu o catálogo" de "abriu o aprofundamento".

  • Guardas (AprofundamentoProtocoloTest): registro apontando para arquivo inexistente

reprova o build; página em disco fora do registro (órfã, no ar e sem link) reprova;

nome divergente do catálogo — que faria o botão "Aprofundar" sumir calado — reprova.

Para adicionar o próximo protocolo: um conteudo.json, um template, um CSS e uma linha

no registro.

Páginas de erro (org.framework.net.paginaErros)

Qualquer falha não tratada devolve a página do Framework no lugar da tela padrão do servidor:

mesmo desenho command-center, texto em português, trace_id real e atalhos de volta.

Um template serve os 12 códigos (400, 401, 403, 404, 405, 409, 422, 429, 500, 502, 503,

504). O estado vem da classe do <body> (err-404, err-500…), e o CSS deriva dela a cor de

acento, a aura e a cor da chuva Matrix — não há CSS duplicado por código. Código fora do

catálogo cai no representante da família (4xx → 400, resto → 500); nunca em tela branca.

PeçaOnde
Textos dos 12 estadospaginaErros/domain/CatalogoErros.java
Montagem + telemetriapaginaErros/application/PaginaErroService.java
InterceptaçãopaginaErros/presentation/PaginaErroMapper.java
Templatetemplates/paginaErros/erro.html
EstáticosMETA-INF/resources/paginaErros/{css,js}

Invariantes que o código sustenta:

  • HTML para gente, JSON para máquina. A página só sai quando o cliente pede text/html

e a rota não é de API. Devolver HTML num fetch() quebraria o response.json() do

frontend com um erro que não diz nada — é o tipo de defeito que só aparece em produção.

  • Resposta já formatada por outro componente é preservada (o 429 do RateLimitFilter,

por exemplo). O mapper não reescreve mensagem que outro componente escolheu dar.

  • Nada de interno na tela. Nome de classe, mensagem de exceção e stack não vão para a

página pública; o que liga o usuário ao diagnóstico é o trace_id, o mesmo registrado na

telemetria. Há teste que reprova hint contendo org.framework ou nome de exceção.

  • O trace_id é real ou é declarado indisponível — jamais gerado na hora só para

preencher o campo, o que simularia uma rastreabilidade inexistente.

  • A página de erro não pode gerar erro: falha ao renderizar o template cai para texto

simples com o mesmo status, em vez de recursão.

  • noindex no <head>, para que tela de erro não entre em buscador.
  • Animação Matrix respeita prefers-reduced-motion e lê a cor de --accent em tempo de

execução, acompanhando o estado sem uma linha de cor no JavaScript.

Módulo 4 — Resolução de Problemas (VLSM + WAN) (/resolucao-problemas)

O módulo tem duas abas, que são o mesmo problema em sentidos opostos:

AbaRotaVai dePara
Projetar/resolucao-problemasrequisitos (localidades e hosts)plano VLSM, WAN, roteamento, scripts
Engenharia reversa/resolucao-problemas?aba=reversaconfiguração Cisco prontaerros apontados e corrigidos + o mesmo plano reconstruído

Aba "Engenharia reversa"

O aluno cola a configuração que recebeu — normalmente com erro — e o botão Executar

devolve, na mesma tela: a auditoria linha a linha, os scripts corrigidos, as LANs, os

enlaces ponto a ponto, as tabelas por roteador e o desenho da topologia. O botão

Imprimir é separado do Executar: um faz o trabalho, o outro só manda a tela para o papel.

O algoritmo. As declarações neighbor <ip> remote-as <as> formam um sistema

sobredeterminado: cada roteador declara onde o *outro* está. Se SP diz que RJ mora em

.2, então RJ tem de possuir .2. Quando não possui, o erro não é apenas detectado — fica

localizado, e a correção sai do próprio texto colado. É isso que separa correção de

palpite.

Três classes de achado, e a classe decide o que a tela faz:

ClasseO que acontece
Erro corrigidocorrige e mostra *antes → depois* com a evidência que sustenta a correção
Erro sem correção automáticaaponta e para. Escolher sem evidência seria adivinhar
Avisonão impede o cenário (no auto-summary ausente, interface sem no shutdown)

O que o interpretador entende: hostname; interfaces (incl. abreviadas e loopback) com

ip address/máscara, no shutdown, clock rate (identifica o lado DCE), description,

encapsulation dot1Q; router bgp com neighbor/remote-as/network mask; router ospf

com network + wildcard + area; router eigrp; router rip; e ip route. A palavra

network é lida pelo contexto do bloco router, porque tem três gramáticas diferentes.

Invariantes que o código sustenta:

  • nenhuma linha é engolida — o que o interpretador não usou aparece numa lista com

número e motivo; parser silencioso desenha errado com cara de certo;

  • o texto do usuário nunca é sobrescrito — o script corrigido é material novo, ao lado;
  • cada linha alterada sai marcada com ! CORRIGIDO: dentro do próprio script, porque

configuração corrigida sem marca vira comando aplicado às cegas no equipamento;

  • o que falta vira pendência, não invenção — AS declarado como vizinho e não colado é

listado com a instrução de colar aquele script;

  • o resultado não depende da ordem em que os scripts foram colados (há teste de

regressão para isso: dois erros espelhados só se resolvem se a colisão for medida contra

o que os outros scripts *corroboram*, não contra o que um roteador meramente declarou).

Caso de referência (é o teste EngenhariaReversaServiceTest): três AS em eBGP com

ip adress escrito errado nove vezes, máscara de cinco octetos em três anúncios e duas

interfaces com o endereço trocado. A ferramenta corrige as catorze ocorrências, e a

confirmação de que a correção é a certa é que o clock rate passa a cair **exatamente uma

vez por enlace** — três sinais independentes convergindo.

  • entrada dinâmica com N localidades (nome + quantidade de hosts — o sistema calcula o CIDR pela fórmula 2^H ≥ N+2 → prefixo 32−H);
  • obrigatório: IP/rede base e localidades; opcional: CIDR da base (inferência classful se vazio), AS EIGRP (padrão 71), processo OSPF (padrão 1);
  • prefixo WAN configurável (padrão /30 para enlaces ponto a ponto, faixa 0–30);
  • topologias WAN: star, extended_star, mesh, ring e ring_redundant (aceita também os aliases estrela e estrela_estendida);
  • roteamento EIGRP/OSPF por distribuição (eigrp_only, ospf_only, metade/metade, auto);
  • acesso remoto VTY: Telnet (padrão, com transport input telnet), SSH ou ambos;
  • diagrama de topologia interativo no navegador;
  • demos na URL: ?demo=gs (Global Solution Mazola), ?demo=fiap, ?demo=8, ?demo=1;
  • exportações após calcular (recalculam o cenário e baixam o arquivo).

Exportações

Ação (action_type)ArquivoConteúdo
exportconfig_packet_tracer_consolidado.txtscripts IOS consolidados
export_ziplaboratorio_packet_tracer.zipguia de montagem, configs por roteador, README
export_entregadocumentacao_cenario_rede.txtrelatório completo para disciplina
export_class_zippacote_turma_packet_tracer.zippacote por aluno (por_aluno/<aluno>/)

Importar turma (Excel)

Cole na página de Resolução (TAB entre colunas):

Nome | Rede base | Hosts1 | Hosts2

🏗️ Arquitetura

Estilo arquitetural: monólito modular (*modular monolith*) em Java 25 + Quarkus, migrado do projeto original em Python/Flask. A aplicação é implantada como um único artefato (Quarkus fast-jar), mas o código é organizado por domínios autocontidos (*bounded contexts*) — cada módulo funciona como um "microserviço interno", com fronteiras claras e baixo acoplamento, pronto para ser extraído para um serviço independente caso o projeto evolua nesse sentido.

  • Runtime único: endpoints JAX-RS (quarkus-rest) e views em Qute (quarkus-rest-qute) sobre quarkus-vertx-http.
  • Módulos de domínio: analiseDidatica, calculadora, portas, protocolos, resolucaoProblemas, localizacao, analiseTrafego, ferramentasDiagnostico, segurancaRede, simuladores (encapsulamento e handshake TCP — computação pura, VPS-safe).
  • Módulos transversais: security (CSRF, rate limit, chave admin), telemetria (observabilidade), web (documentação, login, ícone) e shared (sanitização e utilitários de entrada).

Camadas por módulo (organização DDD-lite / hexagonal)

Cada domínio de negócio segue a mesma separação de responsabilidades:

CamadaResponsabilidadeExemplos
presentationEndpoints JAX-RS (@Path) e binding com templates QuteAnaliseDidaticaResource, ResolucaoProblemasResource
applicationCasos de uso / serviços de orquestraçãoHomeAnaliseService, VlsmService, export/, planning/, routing/
domainRegras de negócio puras (kernel + modelos)Ipv4Kernel, domain/model
infrastructureIntegrações externas e persistênciadns/, geo/ (MaxMind), historico/
support / config / exceptionApoio à UI, configuração e mapeamento de errosAnaliseDidaticaUiSupport, DnsConfig, *ExceptionMapper
flowchart LR U[Usuário] --> W[Browser] W --> HTTP[Quarkus HTTP / Vert.x] HTTP --> F1[TelemetriaRequestFilter] F1 --> F2[CsrfRequestFilter] F2 --> F3[AdminApiKeyFilter] F3 --> F4[RateLimitFilter] F4 --> R1[analiseDidatica · presentation] F4 --> R5[calculadora · presentation] F4 --> R2[resolucaoProblemas · presentation] F4 --> R3[portas · protocolos · presentation] F4 --> R4[telemetria · web · presentation] R1 --> A1[application HomeAnaliseService · ModoService] A1 --> D1[domain Ipv4Kernel] A1 --> I1[infrastructure DNS · GeoIP · Historico] R5 --> A5[application Divisao · Vlan · Agregacao] A5 --> D5[domain SubnetKernel] R2 --> A2[VlsmService] A2 --> A2a[normalization · planning · routing · export] R3 --> A3[PortasService · ProtocolosService] A3 --> D3[domain catálogos embarcados] R4 --> A4[TelemetriaStore · ReadmeLoader · AdminApiKeyService] I1 --> T[Templates Qute] A2 --> T A3 --> T A4 --> T T --> W

Ordem real dos filtros JAX-RS (prioridade): TelemetriaRequestFilterCsrfRequestFilterAdminApiKeyFilterRateLimitFilter → Resource.

Módulos em presentation:

PacoteResources principais
analiseDidatica/, /informacoes, /api/informacoes/geo, /mascara-referencia, /history, /export/*
resolucaoProblemas/resolucao-problemas
portas / protocolos/portas, /protocolos
telemetria/telemetria, /telemetria/api/*
web/documentacao, /admin/*, /icone.png

Fluxo do módulo VLSM/WAN

flowchart TD I[Entrada HTTP: rede base + localidades + topology_type + prefixo WAN] --> V[Validacao no Resource] V --> N[VlsmNormalizationService] N --> L[VlsmPlanningService: blocos LAN] L --> W[Links WAN com prefixo configuravel] W --> R[VlsmRoutingService: plano EIGRP/OSPF] R --> C[ExportTxtService: CLI Cisco + tabelas Packet Tracer] C --> M[Mermaid / topologyDetails] M --> O[Render HTML Qute] C --> E1[Export TXT lab] C --> E2[Export ZIP lab] C --> E3[Export TXT entrega] I --> E4[Export ZIP turma via BulkClassImportService]

Fluxo de telemetria (server-side)

flowchart LR Req[HTTP request] --> Flt[TelemetriaRequestFilter request] Flt --> Ctx[TelemetriaContext: request_id + trace_id no MDC] Ctx --> Res[JAX-RS Resource] Res --> Ev[TelemetriaLogger.logEvent / medir] Ev --> Store[TelemetriaStore + TelemetriaConsoleBuffer] Ev --> Log[Logger Quarkus: console + arquivo rotacionado] Flt --> Resp[TelemetriaRequestFilter response] Resp --> Acc[logHttpAccess + X-Request-Id / X-Trace-Id] Store --> Dash[Dashboard /telemetria/api/*] Log --> Obs[Docker logs / coletor / SIEM]

Utilitários transversais (shared)

Entrada do usuário passa por utilitários compartilhados antes dos services de domínio, reduzindo risco de injeção, IPs privados/reservados e formatos inválidos.

flowchart TD IN[Entrada HTTP form/query/JSON] --> R[Resource JAX-RS] R --> S1[UserInputSanitizer: labels e nomes] R --> S2[IpCidrInputNormalizer: split IP/CIDR] R --> S3[NetworkAddressGuard: hostname e IP publico] S1 --> APP[Application services] S2 --> APP S3 --> APP APP --> DOM[Domain kernel / modelos] S3 -.-> DNS[DnsResolver antes da resolucao externa]
ClasseUso principal
UserInputSanitizerNomes de localidade, roster da turma, labels Mermaid
IpCidrInputNormalizerSeparação e normalização de IP + CIDR na análise e resolução
NetworkAddressGuardBloqueio de hostnames reservados e endereços não públicos no DNS

Tratamento de exceções (ExceptionMapper)

Cada módulo de domínio possui um @Provider JAX-RS que converte exceções tipadas em respostas HTTP seguras e registra o evento na telemetria.

flowchart TD SVC[Service ou Resource] -->|lanca| EX[Excecao de dominio] EX --> M{ExceptionMapper do modulo} M --> A[AnaliseDidaticaExceptionMapper] M --> P[PortasExceptionMapper] M --> PR[ProtocolosExceptionMapper] M --> R[ResolucaoProblemasExceptionMapper] A --> T[TelemetriaExceptionSupport.registrar] P --> T PR --> T R --> T T --> TL[TelemetriaLogger] M --> HTTP[Resposta HTTP JSON com status adequado]
MapperExceçãoStatus típico
AnaliseDidaticaExceptionMapperEntradaInvalidaException400
AnaliseDidaticaExceptionMapperDnsResolucaoException500
PortasExceptionMapperPortasExceptionconforme tipo
ProtocolosExceptionMapperProtocolosExceptionconforme tipo
ResolucaoProblemasExceptionMapperEntradaInvalidaException / ResolucaoProblemasException400 / 500

Páginas HTML (Qute) tratam erros de validação inline no Resource (erro + invalidFields no template), sem passar pelo mapper — os mappers cobrem principalmente respostas JSON e falhas de domínio não capturadas.

Deploy Docker (build multi-stage → runtime)

flowchart LR subgraph build [Stage 1 — build] SRC[src/ + Gradle] --> GRAD[./gradlew build -x test] GRAD --> JAR[quarkus-app fast-jar perfil prod] end subgraph runtime [Stage 2 — UBI OpenJDK 25 runtime] JAR --> IMG[Imagem framework-net-java-quarkus] IMG --> RUN[quarkus-run.jar :8080] VOL[(Volume /deployments/data)] --> RUN end RUN --> LOGS[logs/] RUN --> GEO[geo/GeoLite2-City.mmdb opcional] RUN --> NPM[Nginx Proxy Manager via nginx-proxy-network]
EtapaDetalhe
Buildeclipse-temurin:25-jdk-noble./gradlew build -x test com -Dquarkus.package.jar.type=fast-jar -Dquarkus.profile=prod
Runtimeregistry.access.redhat.com/ubi9/openjdk-25-runtime — usuário 185, healthcheck em /health
Volumeframework-net-data:/deployments/data — logs, GeoIP e dados da aplicação
Env obrigatórias (prod)ADMIN_API_KEY, CSRF_SECRET, QUARKUS_PROFILE=prod
Rede (VPS)nginx-proxy-network (externa) + bind 127.0.0.1:${HTTP_PORT}:8080
Dev Dockerdocker-compose.dev.yml — dados em ./docker-data, porta padrão 8081

Calculadora de Sub-redes e VLANs — fluxo

Dois pontos de entrada, um serviço: a aba em /analise e a página /calculadora

chamam o mesmo endpoint e recebem o mesmo fragmento Qute renderizado no servidor.

flowchart LR ABA["/analise · aba Calculadora"] -->|hx-post| EP PAG["/calculadora · 4 abas"] -->|hx-post| EP EP[CalculadoraResource] --> DIV[DivisaoService] EP --> VLA[VlanService] EP --> AGR[AgregacaoService] EP --> EXP[CalculadoraExportService] DIV --> K[SubnetKernel · aritmetica 32 bits] VLA --> K AGR --> K DIV --> FR[fragmento Qute] VLA --> FR AGR --> FR EXP --> CSV[CSV do plano exibido] K -.erro de entrada.-> EXC[CalculadoraException] EXC --> MAP[CalculadoraExceptionMapper] MAP -->|HX-Request| ERRHTML[erro.html · 400] MAP -->|demais clientes| ERRTXT[texto puro · 400]

Tetos de renderização (framework.calculadora.*) ficam entre o serviço e o

fragmento: o total matemático é sempre calculado, mas a listagem é truncada e o

truncamento é declarado na tela.

Camadas de proteção da requisição

Cada filtro assume que o anterior falhou. A ordem importa: a telemetria abre a

correlação antes de tudo, e o rate limit fecha a fila antes de o recurso ser tocado.

flowchart TB REQ[Requisicao] --> H{rota = /health?} H -->|sim| SONDA[HealthResource · sem telemetria] H -->|nao| T[TelemetriaRequestFilter · abre traceId] T --> C{metodo mutante?} C -->|sim| CSRF[CsrfRequestFilter · HMAC double-submit] C -->|nao| ADM CSRF --> ADM{rota protegida?} ADM -->|/export · /telemetria| KEY[AdminApiKeyFilter] ADM -->|publica| RL KEY -->|sem chave| L401[401 ou redirect /admin/login] KEY -->|chave valida| RL[RateLimitFilter] RL -->|chave = remoteAddress| REC[Resource] RL -->|estourou| L429[429] REC --> SAN[shared · sanitizers e guards] SAN --> APP[application]

No boot, SegredosObrigatoriosVerificador recusa iniciar em produção sem

ADMIN_API_KEY/CSRF_SECRET — antes, a variável ausente desligava a proteção em

silêncio.

Cache de APIs externas em dois níveis

flowchart LR S[GeoLookupService · NominatimGeocoder] --> L1{L1 em memoria} L1 -->|hit| OK[resposta] L1 -->|miss| L2{L2 Redis · fnet:*} L2 -->|hit| REP[repovoa L1] --> OK L2 -->|miss ou indisponivel| ORI[API externa] ORI --> GRAVA[grava L1 + L2 com mesmo TTL] --> OK

O L2 existe porque o L1 morre a cada deploy e as origens têm limite —

ip-api.com corta em 45 req/min. Redis indisponível faz o L2 se comportar como

miss permanente: volta ao comportamento de antes, sem quebrar nada.


✅ Requisitos

  • JDK 25
  • Gradle (wrapper incluído — gradlew / gradlew.bat)
  • Docker (opcional, para deploy)

Dependências principais (build.gradle): quarkus-rest, quarkus-rest-jackson, quarkus-rest-qute, quarkus-qute, quarkus-cache, quarkus-vertx-http, com.github.seancfoley:ipaddress:5.5.1, com.maxmind.geoip2:geoip2:4.2.0.


▶️ Execução

Java / Quarkus local

Windows PowerShell:

.\gradlew.bat quarkusDev

Linux/macOS:

./gradlew quarkusDev

Aplicação em http://localhost:8080. Em modo dev, o navegador abre automaticamente quando %dev.framework.dev.open-browser=true.

Docker (VPS)

docker compose -f docker-compose.yml up -d --build

O perfil prod (QUARKUS_PROFILE=prod) habilita proxy reverso e grava dados persistentes em /deployments/data. É obrigatório definir ADMIN_API_KEY e CSRF_SECRET (ver .env.example / docker-compose.yml).

Parar:

docker compose -f docker-compose.yml down

⚙️ Variáveis de Configuração

As chaves são definidas em application.properties (dev) e application-prod.properties (deploy), podendo ser sobrescritas por variáveis de ambiente.

Aplicação

PropriedadePadrãoDescrição
quarkus.http.port8080Porta HTTP
quarkus.http.host0.0.0.0Host de bind
framework.app.max-history60Tamanho máximo do histórico
framework.calculadora.max-linhas512Sub-redes renderizadas por divisão. O total real continua sendo calculado e exibido — o teto evita travar o navegador (um /8 em /30 são 4.194.304)
framework.calculadora.max-vlans256VLANs geradas por plano
framework.calculadora.max-redes-agregacao64Redes aceitas por sumarização
framework.app.comparador-cidr-padrao-a20CIDR padrão do comparador (A)
framework.app.comparador-cidr-padrao-b24CIDR padrão do comparador (B)
framework.dns.cache-ttl-seconds180TTL do cache DNS
framework.dns.resolve-timeout-seconds3Timeout de resolução DNS
framework.geo.cache-ttl-seconds300TTL do cache GeoIP
framework.geo.database-pathgeo/GeoLite2-City.mmdbBase MaxMind (opcional)
framework.dev.open-browsertrue (dev)Abre navegador no quarkusDev

Telemetria

PropriedadePadrãoDescrição
framework.logs.base-dirlogsPasta de logs/exportações
framework.telemetry.enabledtrueHabilita coleta
framework.telemetry.dashboard-enabledtrueHabilita a página /telemetria
framework.telemetry.max-events5000Eventos em buffer
framework.telemetry.jsonl-max-bytes10485760Limite do JSONL ativo antes de manter uma única geração anterior

Segurança

PropriedadePadrão (dev)Descrição
framework.security.csrf-enabledtrueProteção CSRF
framework.security.csrf-secret*(dev)*Segredo do token CSRF — trocar em prod (CSRF_SECRET)
framework.security.sensitive-apis-enabledtrueHabilita APIs sensíveis
framework.security.rate-limit-enabledtrueHabilita rate limiting
framework.security.rate-limit-per-minute120Limite geral por minuto
framework.security.rate-limit-heavy-per-minute30Limite de rotas pesadas
framework.security.admin-api-key*(dev)*Chave admin — em prod via ADMIN_API_KEY
framework.security.admin-api-key-requiredtrueExige chave nas rotas protegidas

Variáveis de ambiente (deploy Docker)

  • HTTP_PORT (padrão 8080) — porta publicada no host;
  • ADMIN_API_KEYobrigatória em prod; gere uma vez e mantenha no .env da VPS;
  • CSRF_SECRETobrigatória em prod; gere uma vez e mantenha no .env da VPS;
  • GEO_DB_HOST_PATH — caminho do GeoLite2-City.mmdb no host (opcional);
  • QUARKUS_PROFILE=prod.

Comandos seguros de deploy na VPS:

cd /opt/framework-net-java-quarkus
git pull origin main
test -f .env || cp .env.example .env
docker compose up -d --build
docker compose ps
docker compose logs --tail=20 -f

Deploy automatizado:

cd /opt/framework-net-java-quarkus
chmod +x scripts/deploy.sh
scripts/deploy.sh

🔐 Segurança

  • Chave administrativa — rotas sob o prefixo /export são protegidas. O acesso é liberado por:
  • header X-Admin-Api-Key: <chave>, ou
  • cookie ADMIN_API_KEY obtido via login em /admin/login.
  • A exigência só é ativada quando admin-api-key-required=true e a chave está configurada; a comparação usa tempo constante.
  • CSRF — filtros de request/response emitem e validam token para operações sensíveis.
  • Rate limiting — limites por minuto configuráveis (geral e rotas pesadas).
  • Headers HTTP de segurançaX-Content-Type-Options: nosniff, X-Frame-Options: SAMEORIGIN, Referrer-Policy: strict-origin-when-cross-origin, Permissions-Policy restritiva.
  • Sanitização de entradaUserInputSanitizer, IpCidrInputNormalizer e NetworkAddressGuard no pacote shared.

📋 Telemetria e Observabilidade (Server-Side)

A telemetria é orientada a servidor e possui dashboard próprio em /telemetria.

Implementado:

  • request_id e trace_id por requisição (via TelemetriaRequestFilter + MDC);
  • eventos estruturados (TelemetriaLogger.logEvent / logException);
  • buffer em memória + arquivo compartilhado (TelemetriaStore);
  • console ao vivo e exportação JSON (/telemetria/api/exportar);
  • logs em console e arquivo com rotação (quarkus.log.file.*);
  • a sonda /health do container não é registrada — antes o healthcheck consultava

/ a cada 30 s (~2.880 acessos/dia) e era indistinguível de visitas reais.

Correlação dos eventos de negócio

Serviços de aplicação chamam TelemetriaLogger.medir() sem ter o

ContainerRequestContext do JAX-RS em mãos. A correlação é então resgatada do MDC

da própria thread da requisição (TelemetriaContext.contextoDoMdc()), de modo que todo

evento nascido dentro de um request carrega traceId e requestId.

Isso importa porque um único incidente rende mais de um evento — a operação de negócio

que falhou, a exceção mapeada e o acesso HTTP. Agrupe por traceId para contar

incidentes em vez de linhas. Eventos nascidos fora de requisição (app_start, tarefas

de fundo) seguem sem correlação, e isso é o valor honesto: correlação fabricada seria

pior que correlação ausente.

Formato de compartilhamento: OpenTelemetry OTLP/JSON

Os artefatos compartilháveis seguem o OpenTelemetry Logs Data Model serializado em OTLP/JSON — o padrão da indústria para interoperar telemetria com Grafana, Loki, Jaeger, Datadog, SIEMs, etc.

ArtefatoFormatoDescrição
logs/telemetria_compartilhada.jsonOTLP LogsDataDocumento canônico (resourceLogs → scopeLogs → logRecords)
logs/framework-net-eventos.jsonlNDJSON de LogRecord OTLPStream append (um LogRecord por linha)
GET /telemetria/api/exportarOTLP LogsDataDownload do documento canônico

Mapeamento (TelemetriaOtlpMapper): timeUnixNano/intValue como *string* (int64 conforme OTLP/JSON), severityNumber/severityText (INFO=9, WARN=13, ERROR=17), traceId/spanId em hexadecimal, e atributos com convenções semânticas (service.name, http.request.method, http.route, http.response.status_code, event.name) + atributos próprios em framework.*.

As APIs internas do dashboard (/telemetria/api/resumo e /telemetria/api/dashboard) permanecem no schema próprio de UI — o OTLP é usado nos artefatos de exportação/compartilhamento.

Coleta recomendada em produção: docker logs / compose logs e agregador central (ELK, Loki, Datadog, Splunk, SIEM) — o OTLP/JSON pode ser reenviado a um OpenTelemetry Collector.

Dataset público (sanitização)

A telemetria é artefato temporário destinado a virar dataset público. Entre o

arquivo cru e o repositório existe uma etapa obrigatória de sanitização, executada

na VPS por dois scripts sem dependências externas:

./scripts/exportar-dataset.sh            # extrai do container, sanitiza, gera dataset/AAAA-MM-DD/
ScriptPapel
scripts/exportar-dataset.shExtrai o NDJSON do container, garante o sal no .env e chama o sanitizador
scripts/sanitizar_telemetria.pySanitiza, audita e gera eventos.jsonl + README.md + schema.json + estatisticas.json (Python 3, só stdlib)

A distinção que sustenta as regras: identidade × conteúdo didático. O mesmo campo

framework.field.ip guarda tanto o endereço que o servidor observou (identidade)

quanto o que o usuário digitou no formulário de GeoIP (exercício). O nome do campo não

distingue — o valor sim: IPv4 roteável ou IPv6 é identidade e vira hash; faixa

privada, loopback, documentação e resolvedores públicos conhecidos (8.8.8.8,

1.1.1.1…) permanecem legíveis. Pseudonimizar conteúdo didático esvaziaria o dataset

sem proteger ninguém: baseNetwork=192.19.0.0/16 digitado num exercício de VLSM é

o dado que dá valor ao arquivo.

DadoTratamento
IP identificadorSHA-256(sal + valor) truncado em 12 hex — estável (conta visitantes únicos), irreversível sem o sal
lat / lonRemovidos — 6 casas decimais são ~10 cm; nem hash nem arredondamento tornam publicável
body (texto livre)Reconstruído a partir dos atributos já sanitizados, nunca filtrado por regex — o body repetia os valores (evento=geo_lookup status=ok ip=…)
traceId / spanId / request_idPreservados — aleatórios por requisição, não identificam, e são o que torna o dataset analisável
Estáticos, /q/*, /web/*, /telemetria/api*, /healthDescartados como ruído de infraestrutura

O sal vive no .env da VPS, é gerado uma vez e nunca entra no dataset; trocá-lo

quebra a continuidade dos pseudônimos entre datasets já publicados.

Ao final, uma auditoria bloqueante varre o arquivo gerado provando que nenhum valor

de identidade sobreviveu, em qualquer campo — se achar, apaga a saída e falha. IPv4

roteáveis que restaram (conteúdo de exercício) são listados para conferência humana.

O script não faz git push: publicação é irreversível assim que indexada, então é

decisão humana e não de cron.


🗂️ Estrutura de Pastas

framework-net-java-quarkus/
├── build.gradle · settings.gradle · gradle.properties · gradlew(.bat)
├── Dockerfile · docker-compose.yml · docker-compose.dev.yml · .env.example
├── logs/
├── src/main/java/org/framework/net/
│   ├── analiseDidatica/     # application, config, domain/kernel, infrastructure (dns/geo/historico), presentation, support
│   ├── calculadora/         # application, config, domain, exception, presentation
│   ├── portas/              # application, domain, exception, presentation
│   ├── protocolos/          # application, domain, exception, presentation
│   ├── resolucaoProblemas/  # application (export/importing/normalization/planning/routing), domain (kernel/model), presentation
│   ├── security/            # Admin API key, CSRF, rate limit, sensitive APIs
│   ├── shared/              # sanitizers, guards e normalizadores de entrada
│   ├── telemetria/          # store, dashboard, filter, presentation
│   └── web/                 # documentacao, admin login, ícone, filtros, support
├── src/main/resources/
│   ├── application.properties · application-prod.properties
│   ├── README.md            # esta documentação (renderizada em /documentacao)
│   ├── templates/           # Qute: home, analiseDidatica, portas, protocolos, resolucaoProblemas, telemetria, admin, documentacao, shared
│   └── META-INF/resources/  # estáticos: CSS por módulo (home/, portas/, protocolos/, telemetria/, resolucaoProblemas/, documentacao/) + web/ (design system compartilhado) + JS + ícone
└── src/test/java/org/framework/net/   # JUnit 5 + RestAssured

🧪 Testes

Suíte em JUnit 5 + RestAssured (quarkus-junit, rest-assured), em src/test/java.

.\gradlew.bat test

Cobertura por área:

ÁreaExemplos de testes
Análise DidáticaIpv4KernelTest, AnaliseDidaticaHttpTest, AnaliseExportHttpTest, HistoricoApiHttpTest, GeoLookupServiceTest, PdfSimplesServiceTest
CalculadoraDivisaoServiceTest, VlanServiceTest, AgregacaoServiceTest, CalculadoraHttpTest
SaúdeHealthResourceTest
Portas / ProtocolosPortasServiceTest, ProtocolosServiceTest
Resolução VLSM/WANVlsmServiceTest, VlsmPlanningServiceTest, ResolucaoProblemasHttpTest, BulkClassImportServiceTest
SegurançaAdminApiKeyServiceTest, AdminApiKeyHttpTest, CsrfTokenServiceTest
TelemetriaTelemetriaLoggerTest, TelemetriaHttpTest, TelemetriaConsoleBufferTest, TelemetryDisabledHttpTest, TelemetriaStoreRotationTest, ModuloDePathTest, CorrelacaoEventosTest
Menu e rotasMenuRotasHttpTest
ArquiteturaArquiteturaCamadasTest
Shared / WebUserInputSanitizerTest, IpCidrInputNormalizerTest, NetworkAddressGuardTest, WebIntegrationTest, DevBrowserLauncherTest

Testes que guardam regras, não só comportamento

Três suítes existem para impedir classes inteiras de regressão, e não para verificar

um caso de uso:

  • ArquiteturaCamadasTest — lê os imports dos fontes e reprova o build se

domain passar a conhecer HTTP/Qute/camadas externas, se application importar

presentation, se dois módulos de negócio se acoplarem sem registro explícito, ou

se um @Path aparecer fora de presentation.

  • MenuRotasHttpTest — abre as 13 rotas do menu, confere que cada uma se marca

como ativa e navega para as demais. Conta os itens no HTML renderizado: **item novo

no menu sem teste correspondente quebra o build**.

  • ModuloDePathTest — trava a tabela que atribui cada rota a um módulo no

dashboard. Existe porque o default do switch apontava para "Análise Didática", e

com isso /calculadora, /sobre, /admin e /simuladores eram silenciosamente

contabilizados no módulo errado — bug que não gera exceção, só número errado.

Ao criar um módulo novo, atualize também

  1. shared/main_menu.html e MenuRotasHttpTest (a contagem de itens é verificada);
  2. RateLimitFilter.HEAVY_PATHS — e o startsWith do subcaminho, se as APIs forem pesadas;
  3. TelemetriaDashboardService.moduloDePath — senão o tráfego é creditado a outro módulo;
  4. templates/home/index.html — o bloco do módulo na landing;
  5. este README.

🛣️ Roadmap

  • [x] Migração de Python/Flask para Java 25 + Quarkus 3.37
  • [x] VLSM dinâmico para N localidades
  • [x] Topologias WAN estrela / estrela estendida / malha / anel / anel redundante
  • [x] EIGRP + OSPF (distribuição por site, AS/processo opcionais)
  • [x] Telnet explícito nos scripts (transport input telnet)
  • [x] Demos (?demo=gs|fiap|8|1) e exportações lab/entrega/turma
  • [x] Prefixo WAN configurável e CIDR da base opcional com inferência
  • [x] Catálogos de portas e protocolos
  • [x] Módulo de telemetria com dashboard e console ao vivo
  • [x] Segurança: chave admin, CSRF, rate limiting e headers HTTP
  • [x] GeoIP MaxMind (opcional) e resolução DNS com cache
  • [ ] Persistência externa de logs operacionais (stack de observabilidade)
  • [ ] Filtros avançados de histórico por período e modo

👨‍💻 Autor

Paulo André Carminati | RM570877 | FIAP 2026 | Cyber Defense


📄 Licença

MIT.