feat(api, web): enhance MikroTik integration and IP hit tracking
- Updated the `evofw-firewall.sh` script to improve the handling of NFT sets, ensuring compatibility with kernel limitations on counters and enhancing logging for better diagnostics. - Introduced a new presence mode for MikroTik, allowing for real-time tracking of IP hits with updated last seen timestamps and packet counts. - Enhanced the API to support the new presence mode, updating the database interactions to reflect the changes in how IP hits are recorded. - Updated the agent detail view to display sync windows for MikroTik, providing clearer insights into blocked IPs and their activity. - Improved documentation to reflect the new features and changes in the MikroTik handling process, ensuring clarity for users and developers. These changes significantly enhance the monitoring capabilities and user experience for agents, particularly those using MikroTik devices.
This commit is contained in:
@@ -5,7 +5,7 @@ import {
|
||||
useReactTable,
|
||||
type ColumnDef,
|
||||
} from '@tanstack/react-table'
|
||||
import { BanIcon, RouterIcon } from 'lucide-react'
|
||||
import { BanIcon } from 'lucide-react'
|
||||
import {
|
||||
Frame,
|
||||
FrameDescription,
|
||||
@@ -21,10 +21,9 @@ import { agentBlockedIpsQueryOptions } from '@/queries'
|
||||
import { Skeleton } from '@evofw/ui/components/skeleton'
|
||||
|
||||
/**
|
||||
* Per-IP/CIDR drop counters from Linux nft/ipset.
|
||||
* Per-IP blocked stats — Linux nft/ipset counters or MikroTik EVOFW_HITS.
|
||||
* Preview: https://reui.io/preview/base/data-grid-filtering-2
|
||||
* · https://reui.io/preview/base/empty-state-12
|
||||
* MikroTik: address-list has no per-entry counters — empty hint only.
|
||||
*/
|
||||
|
||||
export type BlockedIpRow = {
|
||||
@@ -57,10 +56,12 @@ function formatSeen(iso: string): string {
|
||||
|
||||
export function AgentBlockedIps({ agentId, platform }: AgentBlockedIpsProps) {
|
||||
const isMikrotik = platform === 'mikrotik'
|
||||
const q = useQuery({
|
||||
...agentBlockedIpsQueryOptions(agentId),
|
||||
enabled: !isMikrotik,
|
||||
})
|
||||
const q = useQuery(agentBlockedIpsQueryOptions(agentId))
|
||||
|
||||
const packetsTitle = isMikrotik ? 'Sync windows' : 'Packets'
|
||||
const description = isMikrotik
|
||||
? 'Src /32 из EVOFW_HITS (add-src при deny, timeout 1h). Sync windows — сколько минут IP был в hits.'
|
||||
: 'Drop-пакеты по записям deny (nft/ipset). Top по накопленным packets.'
|
||||
|
||||
const columns = useMemo<ColumnDef<BlockedIpRow>[]>(
|
||||
() => [
|
||||
@@ -79,14 +80,14 @@ export function AgentBlockedIps({ agentId, platform }: AgentBlockedIpsProps) {
|
||||
accessorKey: 'packets',
|
||||
id: 'packets',
|
||||
header: ({ column }) => (
|
||||
<DataGridColumnHeader column={column} title="Packets" />
|
||||
<DataGridColumnHeader column={column} title={packetsTitle} />
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<span className="tabular-nums">
|
||||
{packetFmt.format(row.original.packets)}
|
||||
</span>
|
||||
),
|
||||
meta: { headerTitle: 'Packets' },
|
||||
meta: { headerTitle: packetsTitle },
|
||||
},
|
||||
{
|
||||
accessorKey: 'last_seen_at',
|
||||
@@ -102,7 +103,7 @@ export function AgentBlockedIps({ agentId, platform }: AgentBlockedIpsProps) {
|
||||
meta: { headerTitle: 'Last seen' },
|
||||
},
|
||||
],
|
||||
[],
|
||||
[packetsTitle],
|
||||
)
|
||||
|
||||
const data = q.data?.items ?? []
|
||||
@@ -117,20 +118,10 @@ export function AgentBlockedIps({ agentId, platform }: AgentBlockedIpsProps) {
|
||||
<Frame dense spacing="sm">
|
||||
<FrameHeader>
|
||||
<FrameTitle>Blocked IPs</FrameTitle>
|
||||
<FrameDescription>
|
||||
Drop-пакеты по записям deny (nft/ipset). Top по накопленным packets.
|
||||
</FrameDescription>
|
||||
<FrameDescription>{description}</FrameDescription>
|
||||
</FrameHeader>
|
||||
<FramePanel className="p-0">
|
||||
{isMikrotik ? (
|
||||
<EmptyState
|
||||
icon={RouterIcon}
|
||||
title="Per-IP недоступен на MikroTik"
|
||||
description="У address-list в RouterOS нет counters по записи. Доступны только суммарные Traffic ↓/↑ с filter-правил."
|
||||
centered={false}
|
||||
className="py-8"
|
||||
/>
|
||||
) : q.isLoading ? (
|
||||
{q.isLoading ? (
|
||||
<div className="flex flex-col gap-2 p-4">
|
||||
<Skeleton className="h-8 w-full" />
|
||||
<Skeleton className="h-8 w-full" />
|
||||
@@ -148,7 +139,11 @@ export function AgentBlockedIps({ agentId, platform }: AgentBlockedIpsProps) {
|
||||
<EmptyState
|
||||
icon={BanIcon}
|
||||
title="Пока нет hit’ов"
|
||||
description="Когда deny-префиксы начнут дропать пакеты, здесь появятся IP/CIDR с counters."
|
||||
description={
|
||||
isMikrotik
|
||||
? 'Когда трафик попадёт в EVOFW_DENY, src появится в EVOFW_HITS и здесь после sync.'
|
||||
: 'Когда deny-префиксы начнут дропать пакеты, здесь появятся IP/CIDR с counters.'
|
||||
}
|
||||
centered={false}
|
||||
className="py-8"
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user