feat(firewall): add packet statistics tracking for firewall clients
CI / changes (push) Successful in 11s
CI / commitlint (push) Has been skipped
CI / openapi (push) Successful in 29s
CI / web (push) Successful in 1m6s
CI / go (push) Successful in 1m23s
CI / bird2 (push) Successful in 17s
CI / release (push) Successful in 4m42s
CI / changes (push) Successful in 11s
CI / commitlint (push) Has been skipped
CI / openapi (push) Successful in 29s
CI / web (push) Successful in 1m6s
CI / go (push) Successful in 1m23s
CI / bird2 (push) Successful in 17s
CI / release (push) Successful in 4m42s
Enhanced the firewall client functionality by introducing packet statistics tracking, including the cumulative count of packets dropped and accepted. Updated the API to support these new fields and modified the database schema accordingly. Improved the firewall scripts to collect and report packet statistics, ensuring better visibility into client performance. Adjusted the UI components to display packet counts in the clients table, enhancing user experience and monitoring capabilities.
This commit is contained in:
@@ -279,6 +279,13 @@ function FirewallPage() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatPacketCount(value?: number | null): string | null {
|
||||||
|
if (value == null || value <= 0) return null
|
||||||
|
if (value >= 1_000_000) return `${(value / 1_000_000).toFixed(1)}M`
|
||||||
|
if (value >= 1_000) return `${(value / 1_000).toFixed(1)}k`
|
||||||
|
return String(value)
|
||||||
|
}
|
||||||
|
|
||||||
function ClientsTable({
|
function ClientsTable({
|
||||||
clients,
|
clients,
|
||||||
onApprove,
|
onApprove,
|
||||||
@@ -305,6 +312,7 @@ function ClientsTable({
|
|||||||
<TableHead>Статус</TableHead>
|
<TableHead>Статус</TableHead>
|
||||||
<TableHead>Last seen</TableHead>
|
<TableHead>Last seen</TableHead>
|
||||||
<TableHead>Apply</TableHead>
|
<TableHead>Apply</TableHead>
|
||||||
|
<TableHead>Пакеты</TableHead>
|
||||||
<TableHead />
|
<TableHead />
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
@@ -323,6 +331,23 @@ function ClientsTable({
|
|||||||
{c.last_apply_status ?? '—'}
|
{c.last_apply_status ?? '—'}
|
||||||
{c.last_apply_prefix_count != null ? ` (${c.last_apply_prefix_count})` : ''}
|
{c.last_apply_prefix_count != null ? ` (${c.last_apply_prefix_count})` : ''}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
<TableCell className="text-muted-foreground text-xs">
|
||||||
|
{formatPacketCount(c.last_apply_packets_dropped) || formatPacketCount(c.last_apply_packets_accepted) ? (
|
||||||
|
<>
|
||||||
|
{formatPacketCount(c.last_apply_packets_dropped) ? (
|
||||||
|
<span className="text-destructive">↓{formatPacketCount(c.last_apply_packets_dropped)}</span>
|
||||||
|
) : null}
|
||||||
|
{formatPacketCount(c.last_apply_packets_dropped) && formatPacketCount(c.last_apply_packets_accepted)
|
||||||
|
? ' · '
|
||||||
|
: null}
|
||||||
|
{formatPacketCount(c.last_apply_packets_accepted) ? (
|
||||||
|
<span className="text-success">↑{formatPacketCount(c.last_apply_packets_accepted)}</span>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
'—'
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<div className="flex justify-end gap-2">
|
<div className="flex justify-end gap-2">
|
||||||
{c.status === 'pending' ? (
|
{c.status === 'pending' ? (
|
||||||
|
|||||||
@@ -356,6 +356,8 @@ export type FirewallClient = {
|
|||||||
last_apply_at?: string | null
|
last_apply_at?: string | null
|
||||||
last_apply_status?: string
|
last_apply_status?: string
|
||||||
last_apply_prefix_count?: number
|
last_apply_prefix_count?: number
|
||||||
|
last_apply_packets_dropped?: number
|
||||||
|
last_apply_packets_accepted?: number
|
||||||
last_apply_source?: string
|
last_apply_source?: string
|
||||||
client_version?: string
|
client_version?: string
|
||||||
created_at: string
|
created_at: string
|
||||||
|
|||||||
@@ -1605,6 +1605,14 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
last_apply_prefix_count:
|
last_apply_prefix_count:
|
||||||
type: integer
|
type: integer
|
||||||
|
last_apply_packets_dropped:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
description: Cumulative packets dropped by blocklist rule (from client kernel counter).
|
||||||
|
last_apply_packets_accepted:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
description: Cumulative packets accepted past blocklist chain (nft counter accept rule).
|
||||||
client_version:
|
client_version:
|
||||||
type: string
|
type: string
|
||||||
|
|
||||||
@@ -4547,6 +4555,31 @@ paths:
|
|||||||
tags: [Firewall]
|
tags: [Firewall]
|
||||||
summary: Report last apply status
|
summary: Report last apply status
|
||||||
operationId: firewallApplyReport
|
operationId: firewallApplyReport
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
error:
|
||||||
|
type: string
|
||||||
|
prefix_count:
|
||||||
|
type: integer
|
||||||
|
ip_count:
|
||||||
|
type: integer
|
||||||
|
packets_dropped:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
packets_accepted:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
kernel_method:
|
||||||
|
type: string
|
||||||
|
source:
|
||||||
|
type: string
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: OK
|
description: OK
|
||||||
|
|||||||
@@ -147,8 +147,119 @@ if [[ -z "${TOTAL// }" ]]; then
|
|||||||
TOTAL=${#PREFIXES[@]}
|
TOTAL=${#PREFIXES[@]}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
PACKETS_DROPPED=0
|
||||||
|
PACKETS_ACCEPTED=0
|
||||||
|
KERNEL_METHOD=""
|
||||||
|
APPLIED_V4=0
|
||||||
|
|
||||||
|
count_ipv4_prefixes() {
|
||||||
|
local n=0 p
|
||||||
|
for p in "${PREFIXES[@]}"; do
|
||||||
|
[[ "$p" == *:* ]] && continue
|
||||||
|
n=$((n + 1))
|
||||||
|
done
|
||||||
|
APPLIED_V4=$n
|
||||||
|
}
|
||||||
|
|
||||||
|
nft_rule_packets() {
|
||||||
|
local line=$1
|
||||||
|
if [[ "$line" =~ counter[[:space:]]+packets[[:space:]]+([0-9]+) ]]; then
|
||||||
|
echo "${BASH_REMATCH[1]}"
|
||||||
|
else
|
||||||
|
echo 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_nft_counters() {
|
||||||
|
local table=inet name=evobgp_blocklist
|
||||||
|
nft list chain "$table" "$name" input >/dev/null 2>&1 || return 0
|
||||||
|
local drop_line
|
||||||
|
drop_line=$(nft -a list chain "$table" "$name" input 2>/dev/null | grep 'ip saddr @v4' | grep drop | head -1 || true)
|
||||||
|
if [[ -n "$drop_line" && "$drop_line" != *counter* ]]; then
|
||||||
|
local handle
|
||||||
|
handle=$(echo "$drop_line" | sed -n 's/.*# handle \([0-9]\+\).*/\1/p')
|
||||||
|
if [[ -n "$handle" ]]; then
|
||||||
|
nft delete rule "$table" "$name" input handle "$handle" 2>>"$LOG_FILE" || true
|
||||||
|
drop_line=""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [[ -z "$drop_line" ]]; then
|
||||||
|
nft add rule "$table" "$name" input ip saddr @v4 counter drop
|
||||||
|
fi
|
||||||
|
if ! nft list chain "$table" "$name" input 2>/dev/null | grep -qE '[[:space:]]counter[[:space:]]+accept'; then
|
||||||
|
nft add rule "$table" "$name" input counter accept
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
collect_nft_packet_stats() {
|
||||||
|
PACKETS_DROPPED=0
|
||||||
|
PACKETS_ACCEPTED=0
|
||||||
|
local line pkts
|
||||||
|
while IFS= read -r line; do
|
||||||
|
if [[ "$line" == *"ip saddr @v4"* && "$line" == *drop* ]]; then
|
||||||
|
pkts=$(nft_rule_packets "$line")
|
||||||
|
[[ -n "$pkts" ]] && PACKETS_DROPPED=$pkts
|
||||||
|
elif [[ "$line" == *counter* && "$line" == *accept* && "$line" != *@v4* ]]; then
|
||||||
|
pkts=$(nft_rule_packets "$line")
|
||||||
|
[[ -n "$pkts" ]] && PACKETS_ACCEPTED=$pkts
|
||||||
|
fi
|
||||||
|
done < <(nft list chain inet evobgp_blocklist input 2>/dev/null || true)
|
||||||
|
}
|
||||||
|
|
||||||
|
collect_ipset_packet_stats() {
|
||||||
|
PACKETS_DROPPED=0
|
||||||
|
PACKETS_ACCEPTED=0
|
||||||
|
local pkts
|
||||||
|
pkts=$(iptables -L INPUT -v -n -x 2>/dev/null | awk '/match-set evobgp_blocklist_v4/ {print $1; exit}')
|
||||||
|
[[ "$pkts" =~ ^[0-9]+$ ]] && PACKETS_DROPPED=$pkts
|
||||||
|
}
|
||||||
|
|
||||||
|
collect_packet_stats() {
|
||||||
|
case "${KERNEL_METHOD:-$BACKEND}" in
|
||||||
|
nft)
|
||||||
|
ensure_nft_counters
|
||||||
|
collect_nft_packet_stats
|
||||||
|
;;
|
||||||
|
ipset)
|
||||||
|
collect_ipset_packet_stats
|
||||||
|
;;
|
||||||
|
iptables)
|
||||||
|
PACKETS_DROPPED=$(iptables -L INPUT -v -n -x 2>/dev/null | awk '/DROP/ {s+=$1} END {print s+0}')
|
||||||
|
PACKETS_ACCEPTED=0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if command -v nft >/dev/null 2>&1 && nft list chain inet evobgp_blocklist input >/dev/null 2>&1; then
|
||||||
|
KERNEL_METHOD=nft
|
||||||
|
ensure_nft_counters
|
||||||
|
collect_nft_packet_stats
|
||||||
|
elif iptables -L INPUT -v -n -x 2>/dev/null | grep -q 'evobgp_blocklist_v4'; then
|
||||||
|
KERNEL_METHOD=ipset
|
||||||
|
collect_ipset_packet_stats
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
send_client_reports() {
|
||||||
|
collect_packet_stats
|
||||||
|
local km="${KERNEL_METHOD:-$BACKEND}"
|
||||||
|
local report
|
||||||
|
report=$(printf '{"status":"ok","prefix_count":%s,"ip_count":%s,"packets_dropped":%s,"packets_accepted":%s,"source":"cp","kernel_method":"%s"}' \
|
||||||
|
"${TOTAL:-0}" "${APPLIED_V4:-0}" "${PACKETS_DROPPED:-0}" "${PACKETS_ACCEPTED:-0}" "$km")
|
||||||
|
curl -fsS -X POST "${EVOBGP_CP_URL%/}/v1/firewall/apply-report" \
|
||||||
|
-H "Authorization: Bearer ${CLIENT_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "$report" >/dev/null 2>&1 || true
|
||||||
|
curl -fsS -X POST "${EVOBGP_CP_URL%/}/v1/firewall/heartbeat" \
|
||||||
|
-H "Authorization: Bearer ${CLIENT_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"source":"cp"}' >/dev/null 2>&1 || true
|
||||||
|
}
|
||||||
|
|
||||||
if [[ -f "$HASH_FILE" && "$(tr -d '\r\n' <"$HASH_FILE")" == "$HASH" && -n "$HASH" ]]; then
|
if [[ -f "$HASH_FILE" && "$(tr -d '\r\n' <"$HASH_FILE")" == "$HASH" && -n "$HASH" ]]; then
|
||||||
log "unchanged hash $HASH — skip kernel apply"
|
count_ipv4_prefixes
|
||||||
|
log "unchanged hash $HASH — skip kernel apply (ipv4=${APPLIED_V4})"
|
||||||
|
send_client_reports
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -184,8 +295,11 @@ apply_nft() {
|
|||||||
|
|
||||||
nft list chain "$table" "$name" input >/dev/null 2>&1 || {
|
nft list chain "$table" "$name" input >/dev/null 2>&1 || {
|
||||||
nft add chain "$table" "$name" input '{ type filter hook input priority 0; policy accept; }'
|
nft add chain "$table" "$name" input '{ type filter hook input priority 0; policy accept; }'
|
||||||
nft add rule "$table" "$name" input ip saddr @v4 drop
|
nft add rule "$table" "$name" input ip saddr @v4 counter drop
|
||||||
|
nft add rule "$table" "$name" input counter accept
|
||||||
}
|
}
|
||||||
|
ensure_nft_counters
|
||||||
|
KERNEL_METHOD=nft
|
||||||
APPLIED_V4=${#v4[@]}
|
APPLIED_V4=${#v4[@]}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,6 +316,7 @@ apply_ipset() {
|
|||||||
done
|
done
|
||||||
iptables -C INPUT -m set --match-set "$set" src -j DROP 2>/dev/null || \
|
iptables -C INPUT -m set --match-set "$set" src -j DROP 2>/dev/null || \
|
||||||
iptables -I INPUT -m set --match-set "$set" src -j DROP
|
iptables -I INPUT -m set --match-set "$set" src -j DROP
|
||||||
|
KERNEL_METHOD=ipset
|
||||||
APPLIED_V4=$n
|
APPLIED_V4=$n
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,6 +329,7 @@ apply_iptables_only() {
|
|||||||
iptables -C INPUT -s "$p" -j DROP 2>/dev/null || iptables -A INPUT -s "$p" -j DROP
|
iptables -C INPUT -s "$p" -j DROP 2>/dev/null || iptables -A INPUT -s "$p" -j DROP
|
||||||
n=$((n + 1))
|
n=$((n + 1))
|
||||||
done
|
done
|
||||||
|
KERNEL_METHOD=iptables
|
||||||
APPLIED_V4=$n
|
APPLIED_V4=$n
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,9 +343,11 @@ clear_block() {
|
|||||||
iptables) iptables -S INPUT | grep -i evobgp | sed 's/^-A /-D /' | while read -r line; do iptables $line 2>/dev/null || true; done ;;
|
iptables) iptables -S INPUT | grep -i evobgp | sed 's/^-A /-D /' | while read -r line; do iptables $line 2>/dev/null || true; done ;;
|
||||||
esac
|
esac
|
||||||
APPLIED_V4=0
|
APPLIED_V4=0
|
||||||
|
KERNEL_METHOD="${BACKEND:-auto}"
|
||||||
}
|
}
|
||||||
|
|
||||||
APPLIED_V4=0
|
APPLIED_V4=0
|
||||||
|
KERNEL_METHOD=""
|
||||||
if [[ "${TOTAL:-0}" == "0" || ${#PREFIXES[@]} -eq 0 ]]; then
|
if [[ "${TOTAL:-0}" == "0" || ${#PREFIXES[@]} -eq 0 ]]; then
|
||||||
clear_block
|
clear_block
|
||||||
log "cleared blocklist (api total=${TOTAL:-0}) backend=$BACKEND"
|
log "cleared blocklist (api total=${TOTAL:-0}) backend=$BACKEND"
|
||||||
@@ -244,14 +362,4 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo "$HASH" >"$HASH_FILE"
|
echo "$HASH" >"$HASH_FILE"
|
||||||
|
send_client_reports
|
||||||
REPORT=$(printf '{"status":"ok","prefix_count":%s,"ip_count":%s,"source":"cp"}' "${TOTAL:-0}" "${APPLIED_V4:-0}")
|
|
||||||
curl -fsS -X POST "${EVOBGP_CP_URL%/}/v1/firewall/apply-report" \
|
|
||||||
-H "Authorization: Bearer ${CLIENT_TOKEN}" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d "$REPORT" >/dev/null 2>&1 || true
|
|
||||||
|
|
||||||
curl -fsS -X POST "${EVOBGP_CP_URL%/}/v1/firewall/heartbeat" \
|
|
||||||
-H "Authorization: Bearer ${CLIENT_TOKEN}" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"source":"cp"}' >/dev/null 2>&1 || true
|
|
||||||
|
|||||||
@@ -450,13 +450,15 @@ func (s *Server) handleFirewallApplyReport(w http.ResponseWriter, r *http.Reques
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
var body struct {
|
var body struct {
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
Error string `json:"error"`
|
Error string `json:"error"`
|
||||||
PrefixCount int `json:"prefix_count"`
|
PrefixCount int `json:"prefix_count"`
|
||||||
IPCount int `json:"ip_count"`
|
IPCount int `json:"ip_count"`
|
||||||
Version string `json:"version"`
|
PacketsDropped int64 `json:"packets_dropped"`
|
||||||
KernelMethod string `json:"kernel_method"`
|
PacketsAccepted int64 `json:"packets_accepted"`
|
||||||
Source string `json:"source"`
|
Version string `json:"version"`
|
||||||
|
KernelMethod string `json:"kernel_method"`
|
||||||
|
Source string `json:"source"`
|
||||||
}
|
}
|
||||||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||||
writeProblem(w, http.StatusBadRequest, "Bad Request", "invalid JSON body")
|
writeProblem(w, http.StatusBadRequest, "Bad Request", "invalid JSON body")
|
||||||
@@ -466,7 +468,10 @@ func (s *Server) handleFirewallApplyReport(w http.ResponseWriter, r *http.Reques
|
|||||||
if src == "" {
|
if src == "" {
|
||||||
src = "cp"
|
src = "cp"
|
||||||
}
|
}
|
||||||
_ = s.store.TouchFirewallClientLastApply(a.APIKeyID, src, body.Status, body.Error, body.PrefixCount, body.IPCount)
|
_ = s.store.TouchFirewallClientLastApply(
|
||||||
|
a.APIKeyID, src, body.Status, body.Error,
|
||||||
|
body.PrefixCount, body.IPCount, body.PacketsDropped, body.PacketsAccepted,
|
||||||
|
)
|
||||||
writeJSON(w, http.StatusOK, map[string]bool{"ok": true})
|
writeJSON(w, http.StatusOK, map[string]bool{"ok": true})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -95,6 +95,27 @@ func TestFirewallEnrollAndBlocklist(t *testing.T) {
|
|||||||
if total, _ := bl["total"].(float64); total != 0 {
|
if total, _ := bl["total"].(float64); total != 0 {
|
||||||
t.Fatalf("accept-only want empty blocklist, total=%v", total)
|
t.Fatalf("accept-only want empty blocklist, total=%v", total)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reportBody := `{"status":"ok","prefix_count":0,"ip_count":0,"packets_dropped":42,"packets_accepted":1000,"source":"cp","kernel_method":"nft"}`
|
||||||
|
reqReport, _ := http.NewRequest(http.MethodPost, ts.URL+"/v1/firewall/apply-report", strings.NewReader(reportBody))
|
||||||
|
reqReport.Header.Set("Authorization", "Bearer "+tok)
|
||||||
|
reqReport.Header.Set("Content-Type", "application/json")
|
||||||
|
respReport, err := client.Do(reqReport)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer func() { _ = respReport.Body.Close() }()
|
||||||
|
if respReport.StatusCode != http.StatusOK {
|
||||||
|
b, _ := io.ReadAll(respReport.Body)
|
||||||
|
t.Fatalf("apply-report status=%d body=%s", respReport.StatusCode, b)
|
||||||
|
}
|
||||||
|
gotClient, err := srv.Store().GetFirewallClient(tenant, clientID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if gotClient.LastApplyPacketsDropped != 42 || gotClient.LastApplyPacketsAccepted != 1000 {
|
||||||
|
t.Fatalf("packet stats dropped=%d accepted=%d", gotClient.LastApplyPacketsDropped, gotClient.LastApplyPacketsAccepted)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFirewallEnrollBadSeed(t *testing.T) {
|
func TestFirewallEnrollBadSeed(t *testing.T) {
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ const firewallClientSelectCols = `
|
|||||||
id, name, COALESCE(hostname, ''), token_prefix, status,
|
id, name, COALESCE(hostname, ''), token_prefix, status,
|
||||||
last_seen_at, COALESCE(last_seen_at_source, ''), COALESCE(last_seen_ip, ''),
|
last_seen_at, COALESCE(last_seen_at_source, ''), COALESCE(last_seen_ip, ''),
|
||||||
last_apply_at, COALESCE(last_apply_status, ''), COALESCE(last_apply_error, ''),
|
last_apply_at, COALESCE(last_apply_status, ''), COALESCE(last_apply_error, ''),
|
||||||
COALESCE(last_apply_prefix_count, 0), COALESCE(last_apply_ip_count, 0), COALESCE(last_apply_source, ''),
|
COALESCE(last_apply_prefix_count, 0), COALESCE(last_apply_ip_count, 0),
|
||||||
|
COALESCE(last_apply_packets_dropped, 0), COALESCE(last_apply_packets_accepted, 0),
|
||||||
|
COALESCE(last_apply_source, ''),
|
||||||
COALESCE(client_version, ''), created_at, approved_at, approved_by_api_key_id, revoked_at`
|
COALESCE(client_version, ''), created_at, approved_at, approved_by_api_key_id, revoked_at`
|
||||||
|
|
||||||
func (p *Postgres) ListFirewallClients(tenantID string) ([]*store.FirewallClient, error) {
|
func (p *Postgres) ListFirewallClients(tenantID string) ([]*store.FirewallClient, error) {
|
||||||
@@ -167,12 +169,13 @@ func (p *Postgres) TouchFirewallClientLastSeen(id, source, clientIP, clientVersi
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Postgres) TouchFirewallClientLastApply(id, source, status, errMsg string, prefixCount, ipCount int) error {
|
func (p *Postgres) TouchFirewallClientLastApply(id, source, status, errMsg string, prefixCount, ipCount int, packetsDropped, packetsAccepted int64) error {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
_, err := p.pool.Exec(ctx, `
|
_, err := p.pool.Exec(ctx, `
|
||||||
UPDATE firewall_client SET last_apply_at=now(), last_apply_source=$2, last_apply_status=$3,
|
UPDATE firewall_client SET last_apply_at=now(), last_apply_source=$2, last_apply_status=$3,
|
||||||
last_apply_error=$4, last_apply_prefix_count=$5, last_apply_ip_count=$6
|
last_apply_error=$4, last_apply_prefix_count=$5, last_apply_ip_count=$6,
|
||||||
WHERE id=$1`, id, strings.TrimSpace(source), strings.TrimSpace(status), strings.TrimSpace(errMsg), prefixCount, ipCount)
|
last_apply_packets_dropped=$7, last_apply_packets_accepted=$8
|
||||||
|
WHERE id=$1`, id, strings.TrimSpace(source), strings.TrimSpace(status), strings.TrimSpace(errMsg), prefixCount, ipCount, packetsDropped, packetsAccepted)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -435,16 +438,17 @@ func scanFirewallClientRow(scan scanFn, tenantID string) (*store.FirewallClient,
|
|||||||
var approvedBy *string
|
var approvedBy *string
|
||||||
var lastSeen, lastApply, approved, revoked *time.Time
|
var lastSeen, lastApply, approved, revoked *time.Time
|
||||||
var prefixCount, ipCount *int
|
var prefixCount, ipCount *int
|
||||||
|
var packetsDropped, packetsAccepted *int64
|
||||||
if err := scan(
|
if err := scan(
|
||||||
&c.ID, &c.Name, &c.Hostname, &c.TokenPrefix, &c.Status,
|
&c.ID, &c.Name, &c.Hostname, &c.TokenPrefix, &c.Status,
|
||||||
&lastSeen, &c.LastSeenAtSource, &c.LastSeenIP,
|
&lastSeen, &c.LastSeenAtSource, &c.LastSeenIP,
|
||||||
&lastApply, &c.LastApplyStatus, &c.LastApplyError,
|
&lastApply, &c.LastApplyStatus, &c.LastApplyError,
|
||||||
&prefixCount, &ipCount, &c.LastApplySource,
|
&prefixCount, &ipCount, &packetsDropped, &packetsAccepted, &c.LastApplySource,
|
||||||
&c.ClientVersion, &c.CreatedAt, &approved, &approvedBy, &revoked,
|
&c.ClientVersion, &c.CreatedAt, &approved, &approvedBy, &revoked,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return finishFirewallClientScan(&c, lastSeen, lastApply, approved, revoked, approvedBy, prefixCount, ipCount), nil
|
return finishFirewallClientScan(&c, lastSeen, lastApply, approved, revoked, approvedBy, prefixCount, ipCount, packetsDropped, packetsAccepted), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func scanFirewallClientLookupRow(scan scanFn) (*store.FirewallClient, error) {
|
func scanFirewallClientLookupRow(scan scanFn) (*store.FirewallClient, error) {
|
||||||
@@ -452,19 +456,20 @@ func scanFirewallClientLookupRow(scan scanFn) (*store.FirewallClient, error) {
|
|||||||
var approvedBy *string
|
var approvedBy *string
|
||||||
var lastSeen, lastApply, approved, revoked *time.Time
|
var lastSeen, lastApply, approved, revoked *time.Time
|
||||||
var prefixCount, ipCount *int
|
var prefixCount, ipCount *int
|
||||||
|
var packetsDropped, packetsAccepted *int64
|
||||||
if err := scan(
|
if err := scan(
|
||||||
&c.TenantID, &c.ID, &c.Name, &c.Hostname, &c.TokenPrefix, &c.Status,
|
&c.TenantID, &c.ID, &c.Name, &c.Hostname, &c.TokenPrefix, &c.Status,
|
||||||
&lastSeen, &c.LastSeenAtSource, &c.LastSeenIP,
|
&lastSeen, &c.LastSeenAtSource, &c.LastSeenIP,
|
||||||
&lastApply, &c.LastApplyStatus, &c.LastApplyError,
|
&lastApply, &c.LastApplyStatus, &c.LastApplyError,
|
||||||
&prefixCount, &ipCount, &c.LastApplySource,
|
&prefixCount, &ipCount, &packetsDropped, &packetsAccepted, &c.LastApplySource,
|
||||||
&c.ClientVersion, &c.CreatedAt, &approved, &approvedBy, &revoked,
|
&c.ClientVersion, &c.CreatedAt, &approved, &approvedBy, &revoked,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return finishFirewallClientScan(&c, lastSeen, lastApply, approved, revoked, approvedBy, prefixCount, ipCount), nil
|
return finishFirewallClientScan(&c, lastSeen, lastApply, approved, revoked, approvedBy, prefixCount, ipCount, packetsDropped, packetsAccepted), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func finishFirewallClientScan(c *store.FirewallClient, lastSeen, lastApply, approved, revoked *time.Time, approvedBy *string, prefixCount, ipCount *int) *store.FirewallClient {
|
func finishFirewallClientScan(c *store.FirewallClient, lastSeen, lastApply, approved, revoked *time.Time, approvedBy *string, prefixCount, ipCount *int, packetsDropped, packetsAccepted *int64) *store.FirewallClient {
|
||||||
c.LastSeenAt = lastSeen
|
c.LastSeenAt = lastSeen
|
||||||
c.LastApplyAt = lastApply
|
c.LastApplyAt = lastApply
|
||||||
c.ApprovedAt = approved
|
c.ApprovedAt = approved
|
||||||
@@ -478,6 +483,12 @@ func finishFirewallClientScan(c *store.FirewallClient, lastSeen, lastApply, appr
|
|||||||
if ipCount != nil {
|
if ipCount != nil {
|
||||||
c.LastApplyIPCount = *ipCount
|
c.LastApplyIPCount = *ipCount
|
||||||
}
|
}
|
||||||
|
if packetsDropped != nil {
|
||||||
|
c.LastApplyPacketsDropped = *packetsDropped
|
||||||
|
}
|
||||||
|
if packetsAccepted != nil {
|
||||||
|
c.LastApplyPacketsAccepted = *packetsAccepted
|
||||||
|
}
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ type Backend interface {
|
|||||||
DeleteFirewallClient(tenantID, id string) error
|
DeleteFirewallClient(tenantID, id string) error
|
||||||
LookupFirewallClientByTokenHash(hash []byte) (*FirewallClient, error)
|
LookupFirewallClientByTokenHash(hash []byte) (*FirewallClient, error)
|
||||||
TouchFirewallClientLastSeen(id, source, clientIP, clientVersion string) error
|
TouchFirewallClientLastSeen(id, source, clientIP, clientVersion string) error
|
||||||
TouchFirewallClientLastApply(id, source, status, errMsg string, prefixCount, ipCount int) error
|
TouchFirewallClientLastApply(id, source, status, errMsg string, prefixCount, ipCount int, packetsDropped, packetsAccepted int64) error
|
||||||
ListActiveFirewallClientHashes() ([]FirewallClientAuthRow, error)
|
ListActiveFirewallClientHashes() ([]FirewallClientAuthRow, error)
|
||||||
ListApprovedFirewallClientsForReplication(tenantID string) ([]FirewallClientReplicationRow, error)
|
ListApprovedFirewallClientsForReplication(tenantID string) ([]FirewallClientReplicationRow, error)
|
||||||
|
|
||||||
|
|||||||
@@ -7,26 +7,28 @@ import (
|
|||||||
|
|
||||||
// FirewallClient is a Linux blocklist sync client enrolled via seed.
|
// FirewallClient is a Linux blocklist sync client enrolled via seed.
|
||||||
type FirewallClient struct {
|
type FirewallClient struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
TenantID string `json:"tenant_id,omitempty"`
|
TenantID string `json:"tenant_id,omitempty"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Hostname string `json:"hostname,omitempty"`
|
Hostname string `json:"hostname,omitempty"`
|
||||||
TokenPrefix string `json:"token_prefix"`
|
TokenPrefix string `json:"token_prefix"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
LastSeenAt *time.Time `json:"last_seen_at,omitempty"`
|
LastSeenAt *time.Time `json:"last_seen_at,omitempty"`
|
||||||
LastSeenAtSource string `json:"last_seen_at_source,omitempty"`
|
LastSeenAtSource string `json:"last_seen_at_source,omitempty"`
|
||||||
LastSeenIP string `json:"last_seen_ip,omitempty"`
|
LastSeenIP string `json:"last_seen_ip,omitempty"`
|
||||||
LastApplyAt *time.Time `json:"last_apply_at,omitempty"`
|
LastApplyAt *time.Time `json:"last_apply_at,omitempty"`
|
||||||
LastApplyStatus string `json:"last_apply_status,omitempty"`
|
LastApplyStatus string `json:"last_apply_status,omitempty"`
|
||||||
LastApplyError string `json:"last_apply_error,omitempty"`
|
LastApplyError string `json:"last_apply_error,omitempty"`
|
||||||
LastApplyPrefixCount int `json:"last_apply_prefix_count,omitempty"`
|
LastApplyPrefixCount int `json:"last_apply_prefix_count,omitempty"`
|
||||||
LastApplyIPCount int `json:"last_apply_ip_count,omitempty"`
|
LastApplyIPCount int `json:"last_apply_ip_count,omitempty"`
|
||||||
LastApplySource string `json:"last_apply_source,omitempty"`
|
LastApplyPacketsDropped int64 `json:"last_apply_packets_dropped,omitempty"`
|
||||||
ClientVersion string `json:"client_version,omitempty"`
|
LastApplyPacketsAccepted int64 `json:"last_apply_packets_accepted,omitempty"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
LastApplySource string `json:"last_apply_source,omitempty"`
|
||||||
ApprovedAt *time.Time `json:"approved_at,omitempty"`
|
ClientVersion string `json:"client_version,omitempty"`
|
||||||
ApprovedByAPIKeyID string `json:"approved_by_api_key_id,omitempty"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
RevokedAt *time.Time `json:"revoked_at,omitempty"`
|
ApprovedAt *time.Time `json:"approved_at,omitempty"`
|
||||||
|
ApprovedByAPIKeyID string `json:"approved_by_api_key_id,omitempty"`
|
||||||
|
RevokedAt *time.Time `json:"revoked_at,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// FirewallClientCreate is input for enroll (token hash supplied by caller).
|
// FirewallClientCreate is input for enroll (token hash supplied by caller).
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ func (m *Memory) TouchFirewallClientLastSeen(id, source, clientIP, clientVersion
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Memory) TouchFirewallClientLastApply(id, source, status, errMsg string, prefixCount, ipCount int) error {
|
func (m *Memory) TouchFirewallClientLastApply(id, source, status, errMsg string, prefixCount, ipCount int, packetsDropped, packetsAccepted int64) error {
|
||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
defer m.mu.Unlock()
|
defer m.mu.Unlock()
|
||||||
rec, ok := m.firewallClients[id]
|
rec, ok := m.firewallClients[id]
|
||||||
@@ -185,6 +185,8 @@ func (m *Memory) TouchFirewallClientLastApply(id, source, status, errMsg string,
|
|||||||
rec.LastApplyError = strings.TrimSpace(errMsg)
|
rec.LastApplyError = strings.TrimSpace(errMsg)
|
||||||
rec.LastApplyPrefixCount = prefixCount
|
rec.LastApplyPrefixCount = prefixCount
|
||||||
rec.LastApplyIPCount = ipCount
|
rec.LastApplyIPCount = ipCount
|
||||||
|
rec.LastApplyPacketsDropped = packetsDropped
|
||||||
|
rec.LastApplyPacketsAccepted = packetsAccepted
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
ALTER TABLE firewall_client
|
||||||
|
DROP COLUMN IF EXISTS last_apply_packets_dropped,
|
||||||
|
DROP COLUMN IF EXISTS last_apply_packets_accepted;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
ALTER TABLE firewall_client
|
||||||
|
ADD COLUMN last_apply_packets_dropped BIGINT NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN last_apply_packets_accepted BIGINT NOT NULL DEFAULT 0;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE firewall_client DROP COLUMN last_apply_packets_dropped;
|
||||||
|
ALTER TABLE firewall_client DROP COLUMN last_apply_packets_accepted;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE firewall_client ADD COLUMN last_apply_packets_dropped INTEGER NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE firewall_client ADD COLUMN last_apply_packets_accepted INTEGER NOT NULL DEFAULT 0;
|
||||||
@@ -147,8 +147,119 @@ if [[ -z "${TOTAL// }" ]]; then
|
|||||||
TOTAL=${#PREFIXES[@]}
|
TOTAL=${#PREFIXES[@]}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
PACKETS_DROPPED=0
|
||||||
|
PACKETS_ACCEPTED=0
|
||||||
|
KERNEL_METHOD=""
|
||||||
|
APPLIED_V4=0
|
||||||
|
|
||||||
|
count_ipv4_prefixes() {
|
||||||
|
local n=0 p
|
||||||
|
for p in "${PREFIXES[@]}"; do
|
||||||
|
[[ "$p" == *:* ]] && continue
|
||||||
|
n=$((n + 1))
|
||||||
|
done
|
||||||
|
APPLIED_V4=$n
|
||||||
|
}
|
||||||
|
|
||||||
|
nft_rule_packets() {
|
||||||
|
local line=$1
|
||||||
|
if [[ "$line" =~ counter[[:space:]]+packets[[:space:]]+([0-9]+) ]]; then
|
||||||
|
echo "${BASH_REMATCH[1]}"
|
||||||
|
else
|
||||||
|
echo 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_nft_counters() {
|
||||||
|
local table=inet name=evobgp_blocklist
|
||||||
|
nft list chain "$table" "$name" input >/dev/null 2>&1 || return 0
|
||||||
|
local drop_line
|
||||||
|
drop_line=$(nft -a list chain "$table" "$name" input 2>/dev/null | grep 'ip saddr @v4' | grep drop | head -1 || true)
|
||||||
|
if [[ -n "$drop_line" && "$drop_line" != *counter* ]]; then
|
||||||
|
local handle
|
||||||
|
handle=$(echo "$drop_line" | sed -n 's/.*# handle \([0-9]\+\).*/\1/p')
|
||||||
|
if [[ -n "$handle" ]]; then
|
||||||
|
nft delete rule "$table" "$name" input handle "$handle" 2>>"$LOG_FILE" || true
|
||||||
|
drop_line=""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [[ -z "$drop_line" ]]; then
|
||||||
|
nft add rule "$table" "$name" input ip saddr @v4 counter drop
|
||||||
|
fi
|
||||||
|
if ! nft list chain "$table" "$name" input 2>/dev/null | grep -qE '[[:space:]]counter[[:space:]]+accept'; then
|
||||||
|
nft add rule "$table" "$name" input counter accept
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
collect_nft_packet_stats() {
|
||||||
|
PACKETS_DROPPED=0
|
||||||
|
PACKETS_ACCEPTED=0
|
||||||
|
local line pkts
|
||||||
|
while IFS= read -r line; do
|
||||||
|
if [[ "$line" == *"ip saddr @v4"* && "$line" == *drop* ]]; then
|
||||||
|
pkts=$(nft_rule_packets "$line")
|
||||||
|
[[ -n "$pkts" ]] && PACKETS_DROPPED=$pkts
|
||||||
|
elif [[ "$line" == *counter* && "$line" == *accept* && "$line" != *@v4* ]]; then
|
||||||
|
pkts=$(nft_rule_packets "$line")
|
||||||
|
[[ -n "$pkts" ]] && PACKETS_ACCEPTED=$pkts
|
||||||
|
fi
|
||||||
|
done < <(nft list chain inet evobgp_blocklist input 2>/dev/null || true)
|
||||||
|
}
|
||||||
|
|
||||||
|
collect_ipset_packet_stats() {
|
||||||
|
PACKETS_DROPPED=0
|
||||||
|
PACKETS_ACCEPTED=0
|
||||||
|
local pkts
|
||||||
|
pkts=$(iptables -L INPUT -v -n -x 2>/dev/null | awk '/match-set evobgp_blocklist_v4/ {print $1; exit}')
|
||||||
|
[[ "$pkts" =~ ^[0-9]+$ ]] && PACKETS_DROPPED=$pkts
|
||||||
|
}
|
||||||
|
|
||||||
|
collect_packet_stats() {
|
||||||
|
case "${KERNEL_METHOD:-$BACKEND}" in
|
||||||
|
nft)
|
||||||
|
ensure_nft_counters
|
||||||
|
collect_nft_packet_stats
|
||||||
|
;;
|
||||||
|
ipset)
|
||||||
|
collect_ipset_packet_stats
|
||||||
|
;;
|
||||||
|
iptables)
|
||||||
|
PACKETS_DROPPED=$(iptables -L INPUT -v -n -x 2>/dev/null | awk '/DROP/ {s+=$1} END {print s+0}')
|
||||||
|
PACKETS_ACCEPTED=0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if command -v nft >/dev/null 2>&1 && nft list chain inet evobgp_blocklist input >/dev/null 2>&1; then
|
||||||
|
KERNEL_METHOD=nft
|
||||||
|
ensure_nft_counters
|
||||||
|
collect_nft_packet_stats
|
||||||
|
elif iptables -L INPUT -v -n -x 2>/dev/null | grep -q 'evobgp_blocklist_v4'; then
|
||||||
|
KERNEL_METHOD=ipset
|
||||||
|
collect_ipset_packet_stats
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
send_client_reports() {
|
||||||
|
collect_packet_stats
|
||||||
|
local km="${KERNEL_METHOD:-$BACKEND}"
|
||||||
|
local report
|
||||||
|
report=$(printf '{"status":"ok","prefix_count":%s,"ip_count":%s,"packets_dropped":%s,"packets_accepted":%s,"source":"cp","kernel_method":"%s"}' \
|
||||||
|
"${TOTAL:-0}" "${APPLIED_V4:-0}" "${PACKETS_DROPPED:-0}" "${PACKETS_ACCEPTED:-0}" "$km")
|
||||||
|
curl -fsS -X POST "${EVOBGP_CP_URL%/}/v1/firewall/apply-report" \
|
||||||
|
-H "Authorization: Bearer ${CLIENT_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "$report" >/dev/null 2>&1 || true
|
||||||
|
curl -fsS -X POST "${EVOBGP_CP_URL%/}/v1/firewall/heartbeat" \
|
||||||
|
-H "Authorization: Bearer ${CLIENT_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"source":"cp"}' >/dev/null 2>&1 || true
|
||||||
|
}
|
||||||
|
|
||||||
if [[ -f "$HASH_FILE" && "$(tr -d '\r\n' <"$HASH_FILE")" == "$HASH" && -n "$HASH" ]]; then
|
if [[ -f "$HASH_FILE" && "$(tr -d '\r\n' <"$HASH_FILE")" == "$HASH" && -n "$HASH" ]]; then
|
||||||
log "unchanged hash $HASH — skip kernel apply"
|
count_ipv4_prefixes
|
||||||
|
log "unchanged hash $HASH — skip kernel apply (ipv4=${APPLIED_V4})"
|
||||||
|
send_client_reports
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -184,8 +295,11 @@ apply_nft() {
|
|||||||
|
|
||||||
nft list chain "$table" "$name" input >/dev/null 2>&1 || {
|
nft list chain "$table" "$name" input >/dev/null 2>&1 || {
|
||||||
nft add chain "$table" "$name" input '{ type filter hook input priority 0; policy accept; }'
|
nft add chain "$table" "$name" input '{ type filter hook input priority 0; policy accept; }'
|
||||||
nft add rule "$table" "$name" input ip saddr @v4 drop
|
nft add rule "$table" "$name" input ip saddr @v4 counter drop
|
||||||
|
nft add rule "$table" "$name" input counter accept
|
||||||
}
|
}
|
||||||
|
ensure_nft_counters
|
||||||
|
KERNEL_METHOD=nft
|
||||||
APPLIED_V4=${#v4[@]}
|
APPLIED_V4=${#v4[@]}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,6 +316,7 @@ apply_ipset() {
|
|||||||
done
|
done
|
||||||
iptables -C INPUT -m set --match-set "$set" src -j DROP 2>/dev/null || \
|
iptables -C INPUT -m set --match-set "$set" src -j DROP 2>/dev/null || \
|
||||||
iptables -I INPUT -m set --match-set "$set" src -j DROP
|
iptables -I INPUT -m set --match-set "$set" src -j DROP
|
||||||
|
KERNEL_METHOD=ipset
|
||||||
APPLIED_V4=$n
|
APPLIED_V4=$n
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,6 +329,7 @@ apply_iptables_only() {
|
|||||||
iptables -C INPUT -s "$p" -j DROP 2>/dev/null || iptables -A INPUT -s "$p" -j DROP
|
iptables -C INPUT -s "$p" -j DROP 2>/dev/null || iptables -A INPUT -s "$p" -j DROP
|
||||||
n=$((n + 1))
|
n=$((n + 1))
|
||||||
done
|
done
|
||||||
|
KERNEL_METHOD=iptables
|
||||||
APPLIED_V4=$n
|
APPLIED_V4=$n
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,9 +343,11 @@ clear_block() {
|
|||||||
iptables) iptables -S INPUT | grep -i evobgp | sed 's/^-A /-D /' | while read -r line; do iptables $line 2>/dev/null || true; done ;;
|
iptables) iptables -S INPUT | grep -i evobgp | sed 's/^-A /-D /' | while read -r line; do iptables $line 2>/dev/null || true; done ;;
|
||||||
esac
|
esac
|
||||||
APPLIED_V4=0
|
APPLIED_V4=0
|
||||||
|
KERNEL_METHOD="${BACKEND:-auto}"
|
||||||
}
|
}
|
||||||
|
|
||||||
APPLIED_V4=0
|
APPLIED_V4=0
|
||||||
|
KERNEL_METHOD=""
|
||||||
if [[ "${TOTAL:-0}" == "0" || ${#PREFIXES[@]} -eq 0 ]]; then
|
if [[ "${TOTAL:-0}" == "0" || ${#PREFIXES[@]} -eq 0 ]]; then
|
||||||
clear_block
|
clear_block
|
||||||
log "cleared blocklist (api total=${TOTAL:-0}) backend=$BACKEND"
|
log "cleared blocklist (api total=${TOTAL:-0}) backend=$BACKEND"
|
||||||
@@ -244,14 +362,4 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo "$HASH" >"$HASH_FILE"
|
echo "$HASH" >"$HASH_FILE"
|
||||||
|
send_client_reports
|
||||||
REPORT=$(printf '{"status":"ok","prefix_count":%s,"ip_count":%s,"source":"cp"}' "${TOTAL:-0}" "${APPLIED_V4:-0}")
|
|
||||||
curl -fsS -X POST "${EVOBGP_CP_URL%/}/v1/firewall/apply-report" \
|
|
||||||
-H "Authorization: Bearer ${CLIENT_TOKEN}" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d "$REPORT" >/dev/null 2>&1 || true
|
|
||||||
|
|
||||||
curl -fsS -X POST "${EVOBGP_CP_URL%/}/v1/firewall/heartbeat" \
|
|
||||||
-H "Authorization: Bearer ${CLIENT_TOKEN}" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"source":"cp"}' >/dev/null 2>&1 || true
|
|
||||||
|
|||||||
Reference in New Issue
Block a user