diff --git a/src/elastic/eventrepo/mod.rs b/src/elastic/eventrepo/mod.rs index c6f84543..542f8708 100644 --- a/src/elastic/eventrepo/mod.rs +++ b/src/elastic/eventrepo/mod.rs @@ -123,9 +123,11 @@ impl ElasticEventRepo { "ssh.client.software_version" => "ssh.client.software_version.keyword", "ssh.server.software_version" => "ssh.server.software_version.keyword", "quic.sni" => "quic.sni.keyword", + "quic.ja4" => "quic.ja4.keyword", "tls.issuerdn" => "tls.issuerdn.keyword", "tls.sni" => "tls.sni.keyword", "tls.subject" => "tls.subject.keyword", + "tls.ja4" => "tls.ja4.keyword", "traffic.id" => "traffic.id.keyword", "traffic.label" => "traffic.label.keyword", _ => name, diff --git a/webapp/src/App.tsx b/webapp/src/App.tsx index 16fb680c..daf4a877 100644 --- a/webapp/src/App.tsx +++ b/webapp/src/App.tsx @@ -28,6 +28,7 @@ import { AlertsReport } from "./reports/AlertsReport"; import { OverviewReport } from "./reports/OverviewReport"; import { DhcpReport } from "./reports/DhcpReport"; import { IS_AUTHENTICATED } from "./global"; +import { Ja4Report } from "./pages/ja4"; export function AppRouter() { return ( @@ -57,6 +58,7 @@ export function AppRouter() { + diff --git a/webapp/src/EventView.tsx b/webapp/src/EventView.tsx index 8e086212..a4b1f490 100644 --- a/webapp/src/EventView.tsx +++ b/webapp/src/EventView.tsx @@ -835,6 +835,19 @@ export function EventView() { {e.val} + + + {e.val} + + diff --git a/webapp/src/pages/ja4.tsx b/webapp/src/pages/ja4.tsx new file mode 100644 index 00000000..5792a980 --- /dev/null +++ b/webapp/src/pages/ja4.tsx @@ -0,0 +1,159 @@ +// SPDX-FileCopyrightText: (C) 2024 Jason Ish +// SPDX-License-Identifier: MIT + +import { useParams } from "@solidjs/router"; +import { TIME_RANGE, Top } from "../Top"; +import * as api from "../api"; +import { createResource } from "solid-js"; +import { CountValueDataTable } from "../components/CountValueDataTable"; +import { SearchLink } from "../common/SearchLink"; + +function getPrefix(ja4: string) { + if (ja4.startsWith("t")) { + return "tls"; + } else if (ja4.startsWith("q")) { + return "quic"; + } else { + // TODO: Throw an error? + return ""; + } +} + +export function Ja4Report() { + const params = useParams<{ ja4: string }>(); + const prefix = getPrefix(params.ja4); + const q = `${prefix}.ja4:${params.ja4}`; + + const [topSnis] = createResource(TIME_RANGE, async () => { + let snis = await api.fetchAgg({ + field: `${prefix}.sni`, + q: q, + time_range: TIME_RANGE(), + }); + return snis.rows; + }); + + const [leastSnis] = createResource(TIME_RANGE, async () => { + let snis = await api.fetchAgg({ + field: `${prefix}.sni`, + q: q, + order: "asc", + time_range: TIME_RANGE(), + }); + return snis.rows; + }); + + const [topSourceIps] = createResource(TIME_RANGE, async () => { + let agg = await api.fetchAgg({ + field: `src_ip`, + q: q, + time_range: TIME_RANGE(), + }); + return agg.rows; + }); + + const [leastSourceIps] = createResource(TIME_RANGE, async () => { + let agg = await api.fetchAgg({ + field: "src_ip", + q: q, + order: "asc", + time_range: TIME_RANGE(), + }); + return agg.rows; + }); + + const [topDestIps] = createResource(TIME_RANGE, async () => { + let agg = await api.fetchAgg({ + field: "dest_ip", + q: q, + time_range: TIME_RANGE(), + }); + return agg.rows; + }); + + const [leastDestIps] = createResource(TIME_RANGE, async () => { + let agg = await api.fetchAgg({ + field: "dest_ip", + q: q, + order: "asc", + time_range: TIME_RANGE(), + }); + return agg.rows; + }); + + return ( + <> + + +
+
+
+

+ JA4: + + {params.ja4} + +

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ + ); +}