feat(firewall): enhance firewall management with community selection and script handling
CI / changes (push) Successful in 10s
CI / commitlint (push) Has been skipped
CI / openapi (push) Has been skipped
CI / web (push) Successful in 57s
CI / go (push) Successful in 1m11s
CI / bird2 (push) Successful in 15s
CI / release (push) Successful in 4m16s
CI / changes (push) Successful in 10s
CI / commitlint (push) Has been skipped
CI / openapi (push) Has been skipped
CI / web (push) Successful in 57s
CI / go (push) Successful in 1m11s
CI / bird2 (push) Successful in 15s
CI / release (push) Successful in 4m16s
Added a community selection feature to the firewall rules management UI, allowing users to specify BGP communities for block/accept policies. Updated the backend to support reading firewall scripts from a specified directory, improving script management. Enhanced documentation to clarify the new community functionality and its implications for firewall rules. Additionally, introduced tests for the firewall script endpoints to ensure proper functionality.
This commit is contained in:
@@ -20,7 +20,10 @@ import {
|
||||
} from '@evobgp/ui/components/table'
|
||||
|
||||
import { PageHeader } from '@/components/page-header'
|
||||
import { CommunitySelect } from '@/components/modules/community-select'
|
||||
import { StatusBadge } from '@/components/status-badge'
|
||||
import { communityLabel } from '@/lib/modules/helpers'
|
||||
import { directoriesCommunitiesQueryOptions } from '@/queries/directories'
|
||||
import {
|
||||
firewallClientsQueryOptions,
|
||||
firewallInstallContextQueryOptions,
|
||||
@@ -29,7 +32,7 @@ import {
|
||||
useCreateFirewallRule,
|
||||
useDeleteFirewallRule,
|
||||
} from '@/queries/firewall'
|
||||
import type { FirewallClient } from '@/types/api'
|
||||
import type { BgpCommunity, FirewallClient } from '@/types/api'
|
||||
|
||||
export const Route = createFileRoute('/_auth/firewall')({
|
||||
component: FirewallPage,
|
||||
@@ -37,6 +40,7 @@ export const Route = createFileRoute('/_auth/firewall')({
|
||||
|
||||
function FirewallPage() {
|
||||
const installCtxQ = useQuery(firewallInstallContextQueryOptions())
|
||||
const communitiesQ = useQuery(directoriesCommunitiesQueryOptions())
|
||||
const clientsQ = useQuery(firewallClientsQueryOptions())
|
||||
const rulesQ = useQuery(firewallRulesQueryOptions('tenant'))
|
||||
const approve = useApproveFirewallClient()
|
||||
@@ -60,8 +64,11 @@ function FirewallPage() {
|
||||
}
|
||||
}, [installCtx?.bundle_seed, installCtx?.suggested_cp_url])
|
||||
const [ruleAction, setRuleAction] = useState<'block' | 'accept'>('block')
|
||||
const [ruleCommunityId, setRuleCommunityId] = useState<string | null>(null)
|
||||
const [ruleComment, setRuleComment] = useState('')
|
||||
|
||||
const communities = communitiesQ.data?.items ?? []
|
||||
|
||||
const clients = clientsQ.data?.items ?? []
|
||||
const pending = clients.filter((c) => c.status === 'pending')
|
||||
const rules = rulesQ.data?.items ?? []
|
||||
@@ -117,8 +124,9 @@ function FirewallPage() {
|
||||
<Info className="text-info" />
|
||||
<AlertTitle>Политика</AlertTitle>
|
||||
<AlertDescription>
|
||||
Только явный <strong>block</strong> добавляет IP в blocklist. Правила <strong>accept</strong> сами по себе
|
||||
не создают block all. Default — accept.
|
||||
Правила сопоставляются с <strong>BGP community</strong> префиксов опубликованной revision.{' '}
|
||||
<strong>block</strong> добавляет префиксы community в kernel; <strong>accept</strong> — не блокирует.
|
||||
Community «Все» — правило для любого community. Default без совпадений — accept.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
@@ -179,7 +187,7 @@ function FirewallPage() {
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="rules" className="mt-4 space-y-4">
|
||||
<div className="flex flex-wrap items-end gap-2">
|
||||
<div className="flex flex-wrap items-end gap-3">
|
||||
<div className="space-y-1">
|
||||
<Label>Действие</Label>
|
||||
<select
|
||||
@@ -191,18 +199,33 @@ function FirewallPage() {
|
||||
<option value="accept">accept</option>
|
||||
</select>
|
||||
</div>
|
||||
<Input
|
||||
className="max-w-xs"
|
||||
placeholder="Комментарий"
|
||||
value={ruleComment}
|
||||
onChange={(e) => setRuleComment(e.target.value)}
|
||||
<CommunitySelect
|
||||
id="fw-rule-community"
|
||||
label="Community"
|
||||
value={ruleCommunityId}
|
||||
onValueChange={setRuleCommunityId}
|
||||
communities={communities}
|
||||
nullable
|
||||
placeholder="Все communities"
|
||||
/>
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="fw-rule-comment">Комментарий</Label>
|
||||
<Input
|
||||
id="fw-rule-comment"
|
||||
className="max-w-xs"
|
||||
placeholder="Комментарий"
|
||||
value={ruleComment}
|
||||
onChange={(e) => setRuleComment(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
size="sm"
|
||||
className="mb-0.5"
|
||||
onClick={() =>
|
||||
createRule.mutate({
|
||||
scope: 'tenant',
|
||||
action: ruleAction,
|
||||
community_id: ruleCommunityId,
|
||||
comment: ruleComment,
|
||||
})
|
||||
}
|
||||
@@ -210,7 +233,11 @@ function FirewallPage() {
|
||||
Добавить правило
|
||||
</Button>
|
||||
</div>
|
||||
<RulesTable rules={rules} onDelete={(id) => deleteRule.mutate(id)} />
|
||||
<RulesTable
|
||||
rules={rules}
|
||||
communities={communities}
|
||||
onDelete={(id) => deleteRule.mutate(id)}
|
||||
/>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="requests" className="mt-4">
|
||||
@@ -279,9 +306,11 @@ function ClientsTable({
|
||||
|
||||
function RulesTable({
|
||||
rules,
|
||||
communities,
|
||||
onDelete,
|
||||
}: {
|
||||
rules: { id: string; priority: number; action: string; comment?: string }[]
|
||||
rules: { id: string; priority: number; action: string; community_id?: string | null; comment?: string }[]
|
||||
communities: BgpCommunity[]
|
||||
onDelete: (id: string) => void
|
||||
}) {
|
||||
if (rules.length === 0) {
|
||||
@@ -293,6 +322,7 @@ function RulesTable({
|
||||
<TableRow>
|
||||
<TableHead>#</TableHead>
|
||||
<TableHead>Действие</TableHead>
|
||||
<TableHead>Community</TableHead>
|
||||
<TableHead>Комментарий</TableHead>
|
||||
<TableHead />
|
||||
</TableRow>
|
||||
@@ -304,6 +334,9 @@ function RulesTable({
|
||||
<TableCell>
|
||||
<StatusBadge status={r.action} label={r.action} />
|
||||
</TableCell>
|
||||
<TableCell className="text-sm">
|
||||
{r.community_id ? communityLabel(r.community_id, communities) : 'Все'}
|
||||
</TableCell>
|
||||
<TableCell>{r.comment || '—'}</TableCell>
|
||||
<TableCell>
|
||||
<Button size="sm" variant="ghost" onClick={() => onDelete(r.id)}>
|
||||
|
||||
Reference in New Issue
Block a user