mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-10-29 18:55:13 +08:00
🐤 Sync 2026-09-07 02:18:00
This commit is contained in:
1 parent
65e584daaa
commit
4b545cbffa
30 files changed
+1911
-1238
No files matched your search
@@ -4,6 +4,7 @@
|
||||
# GENERATED FILE — do not edit. Run: ./scripts/gen-all.sh
|
||||
# source: core/fwlive-log.js CLASSIFY_SPEC
|
||||
# Shared isFirewallEvent parity logic (shell). Sourced by fwlive-log-filter.sh and tests.
|
||||
# Sourced library: do not add set -euo here (callers own strict mode, #291 C3).
|
||||
# One awk process classifies a batch (MODE=json) or one message (default).
|
||||
|
||||
_fwlive_run_classify() {
|
||||
|
||||
@@ -8,6 +8,14 @@
|
||||
#
|
||||
# Perf (#219): one jsonfilter for @.log[*] plus one awk classify. Process
|
||||
# count is constant per poll, not O(entries).
|
||||
#
|
||||
# Entry point (pipeline). The classifier sibling is sourced and must not
|
||||
# set -euo itself (#291 C3).
|
||||
set -eu
|
||||
# pipefail: BusyBox ash supports it; Debian dash (host `sh`) rejects a
|
||||
# bare `set -o pipefail`. Probe in a subshell (same class as #244).
|
||||
# shellcheck disable=SC3040 # pipefail is ash/bash; dash probe is a subshell
|
||||
(set -o pipefail) 2>/dev/null && set -o pipefail
|
||||
|
||||
if ! command -v jsonfilter >/dev/null 2>&1; then
|
||||
command -v logger >/dev/null 2>&1 && logger -t fwlive "jsonfilter not found; cannot filter firewall logs"
|
||||
@@ -26,5 +34,7 @@ printf '%s' '{"log":['
|
||||
# Prefer stdin over -s: Linux MAX_ARG_STRLEN is 128KiB; a raised logd ring
|
||||
# (or paused FETCH_LINES_MAX poll) can exceed that and make jsonfilter fail
|
||||
# while this script still printed {"log":[]} and exited 0 (#234).
|
||||
printf '%s' "$input" | jsonfilter -e '@.log[*]' 2>/dev/null | _fwlive_filter_json_entries
|
||||
# jsonfilter miss / empty @.log is not fatal; must still close JSON (#220).
|
||||
# set -e + pipefail cannot apply to this pipeline (#291 C3).
|
||||
printf '%s' "$input" | jsonfilter -e '@.log[*]' 2>/dev/null | _fwlive_filter_json_entries || true
|
||||
printf '%s' ']}'
|
||||
@@ -3,6 +3,11 @@
|
||||
# Copyright 2025-2026 Lucas Albers <lucas.b.albers@gmail.com>
|
||||
#
|
||||
# WAN zone logging helpers for ubus fwlive (logging_status / enable / disable).
|
||||
#
|
||||
# Sourced library (rpcd plugin, package prerm). Do not `set -euo pipefail`
|
||||
# here: prerm is best-effort (`restore ... || logger`; exit 0) and callers
|
||||
# expect soft failures. The rpcd entry point enables strict mode; critical
|
||||
# paths use explicit `|| return 1` / `|| true` (#244, #291 C3).
|
||||
|
||||
NF_LOG_IPV4='/proc/sys/net/netfilter/nf_log/2'
|
||||
NF_LOG_IPV6='/proc/sys/net/netfilter/nf_log/10'
|
||||
@@ -121,11 +126,16 @@ find_wan_zone_section() {
|
||||
# Match anonymous (@zone[N]) and named (e.g. wan) sections whose name option
|
||||
# is 'wan'. Prefer the first section whose type is zone (issue #168); skip
|
||||
# non-zone sections that happen to share name='wan'.
|
||||
# uci missing / no wan zone is empty, not fatal. pipefail + set -e
|
||||
# cannot apply to this pipeline (#291 C3).
|
||||
_zones=$(uci -q show firewall 2>/dev/null \
|
||||
| sed -n "s/^firewall\.\([^.]*\)\.name='wan'$/\1/p")
|
||||
| sed -n "s/^firewall\.\([^.]*\)\.name='wan'$/\1/p") || true
|
||||
for zone in $_zones; do
|
||||
[ -n "$zone" ] || continue
|
||||
[ "$(uci -q get "firewall.${zone}" 2>/dev/null)" = "zone" ] || continue
|
||||
# uci -q get exits 1 on a missing section. Capture with || true so
|
||||
# set -e cannot abort inside "$(…)" before || continue (#291 C3).
|
||||
_type=$(uci -q get "firewall.${zone}" 2>/dev/null || true)
|
||||
[ "$_type" = "zone" ] || continue
|
||||
printf '%s' "$zone"
|
||||
return 0
|
||||
done
|
||||
@@ -133,14 +143,16 @@ find_wan_zone_section() {
|
||||
}
|
||||
|
||||
firewall_changes_pending() {
|
||||
pending="$(uci -q changes firewall 2>/dev/null)"
|
||||
# uci miss is "no pending changes". set -e cannot apply (#291 C3).
|
||||
pending="$(uci -q changes firewall 2>/dev/null || true)"
|
||||
[ -n "$pending" ]
|
||||
}
|
||||
|
||||
wan_zone_log_value() {
|
||||
zone="$1"
|
||||
[ -n "$zone" ] || return 1
|
||||
uci -q get "firewall.${zone}.log" 2>/dev/null
|
||||
# Unset option is a valid empty value; uci -q get exits 1 (#291 C3).
|
||||
uci -q get "firewall.${zone}.log" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# Resolve a firewall section id to its canonical cfgXXXX form (issue B-1 /
|
||||
@@ -284,7 +296,8 @@ maybe_snapshot_wan_log_baseline() {
|
||||
restore_wan_log_baseline() {
|
||||
path="$(wan_log_baseline_path)"
|
||||
[ -f "$path" ] || return 0
|
||||
baseline=$(cat "$path" 2>/dev/null)
|
||||
# Empty file is a valid "option was unset" baseline (#291 C3).
|
||||
baseline=$(cat "$path" 2>/dev/null || true)
|
||||
zone=$(find_wan_zone_section)
|
||||
if [ -z "$zone" ]; then
|
||||
logger -t fwlive "WAN log baseline restore skipped: no WAN zone" 2>/dev/null || true
|
||||
@@ -363,7 +376,7 @@ wan_filter_log_clear_value() {
|
||||
read_nf_log_backend() {
|
||||
path="$1"
|
||||
[ -f "$path" ] || return 1
|
||||
val=$(cat "$path" 2>/dev/null)
|
||||
val=$(cat "$path" 2>/dev/null) || return 1
|
||||
[ -n "$val" ] && [ "$val" != 'none' ]
|
||||
}
|
||||
|
||||
@@ -403,8 +416,9 @@ collect_logging_blockers() {
|
||||
check_nf_log_ipv4 || logging_blockers_append 'nf_log_ipv4_missing'
|
||||
check_nf_log_ipv6 || logging_blockers_append 'nf_log_ipv6_missing'
|
||||
|
||||
[ -n "$LOGGING_BLOCKERS" ] || return 0
|
||||
return 1
|
||||
# Report via LOGGING_BLOCKERS, not exit status: return 1 would abort
|
||||
# build_logging_status_json under set -e (#291 C3).
|
||||
return 0
|
||||
}
|
||||
|
||||
collect_logging_warnings() {
|
||||
@@ -414,8 +428,8 @@ collect_logging_warnings() {
|
||||
# Warnings are diagnostics only — do not gate the enable-logging CTA.
|
||||
command -v timeout >/dev/null 2>&1 || logging_warnings_append 'timeout_missing'
|
||||
|
||||
[ -n "$LOGGING_WARNINGS" ] || return 0
|
||||
return 1
|
||||
# Report via LOGGING_WARNINGS, not exit status (#291 C3).
|
||||
return 0
|
||||
}
|
||||
|
||||
json_null_or_string() {
|
||||
@@ -430,8 +444,13 @@ json_null_or_string() {
|
||||
|
||||
build_logging_status_json() {
|
||||
zone=$(find_wan_zone_section)
|
||||
log_val=$(wan_zone_log_value "$zone")
|
||||
limit_val=$( [ -n "$zone" ] && uci -q get "firewall.${zone}.log_limit" 2>/dev/null )
|
||||
# Empty zone / unset log bit are valid; set -e cannot apply (#291 C3).
|
||||
log_val=$(wan_zone_log_value "$zone") || log_val=
|
||||
# Unset log_limit is a valid empty value; uci -q get exits 1 (#291 C3).
|
||||
limit_val=
|
||||
if [ -n "$zone" ]; then
|
||||
limit_val=$(uci -q get "firewall.${zone}.log_limit" 2>/dev/null || true)
|
||||
fi
|
||||
wan_log=false
|
||||
if wan_filter_log_enabled "$log_val"; then
|
||||
wan_log=true
|
||||
|
||||
@@ -8,6 +8,17 @@
|
||||
# resolve — reverse DNS for IP addresses (BusyBox nslookup)
|
||||
# logging_status — WAN zone logging readiness (no args)
|
||||
# enable_wan_logging / disable_wan_logging — opt-in WAN zone log=1 (no args)
|
||||
#
|
||||
# Entry point (rpcd plugin). Sourced helpers inherit these options. Do not
|
||||
# copy set -euo into fwlive-logging.sh — prerm sources that file alone (#222)
|
||||
# and keeps soft-fail / || return 1 contracts (#244, #291 C3).
|
||||
set -eu
|
||||
# pipefail: BusyBox ash (device) supports it; Debian dash (host `sh` in
|
||||
# CI) rejects `set -o pipefail` as a startup abort. Probe in a subshell —
|
||||
# a failed `set` in-process is fatal even under `if` (same class as #244
|
||||
# failed exec).
|
||||
# shellcheck disable=SC3040 # pipefail is ash/bash; dash probe is a subshell
|
||||
(set -o pipefail) 2>/dev/null && set -o pipefail
|
||||
|
||||
FW4_TAG='!fw4: '
|
||||
LIBEXEC_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
@@ -83,7 +94,9 @@ map_add() {
|
||||
}
|
||||
|
||||
uci_rule_names() {
|
||||
uci -q show firewall 2>/dev/null | sed -n "s/^firewall\.@rule\[[0-9]*\]\.name='\(.*\)'$/\1/p"
|
||||
# uci missing / empty firewall config is "no names", not fatal.
|
||||
# pipefail + set -e cannot apply to this pipeline (#291 C3).
|
||||
uci -q show firewall 2>/dev/null | sed -n "s/^firewall\.@rule\[[0-9]*\]\.name='\(.*\)'$/\1/p" || true
|
||||
}
|
||||
|
||||
is_uci_style_name() {
|
||||
@@ -396,7 +409,8 @@ poll_lines_from_input() {
|
||||
json_get_var first 1
|
||||
json_select ..
|
||||
# shellcheck disable=SC2154 # set by json_get_var above
|
||||
case "$first" in
|
||||
# Missing addresses[1] leaves first unset; set -u (#291 C3).
|
||||
case "${first:-}" in
|
||||
''|*[!0-9]*) ;;
|
||||
*) lines="$first" ;;
|
||||
esac
|
||||
@@ -462,9 +476,10 @@ resolve_hostname() {
|
||||
[ -n "$ip" ] || return 1
|
||||
is_resolvable_address "$ip" || return 1
|
||||
command -v nslookup >/dev/null 2>&1 || return 1
|
||||
out=$(run_with_timeout "$RESOLVE_TIMEOUT" nslookup "$ip")
|
||||
# timeout/nslookup miss is "no PTR", not a plugin abort (#291 C3).
|
||||
out=$(run_with_timeout "$RESOLVE_TIMEOUT" nslookup "$ip") || return 1
|
||||
[ -n "$out" ] || return 1
|
||||
name=$(parse_nslookup_name "$out")
|
||||
name=$(parse_nslookup_name "$out") || return 1
|
||||
[ -n "$name" ] || return 1
|
||||
printf '%s' "$name"
|
||||
}
|
||||
@@ -598,11 +613,14 @@ resolve_addresses() {
|
||||
now=$(date +%s)
|
||||
[ $((now - start)) -ge "$RESOLVE_BUDGET" ] && break
|
||||
json_get_var ip "$idx"
|
||||
if ! is_resolvable_address "$ip"; then
|
||||
# Missing/empty element: set -u cannot read bare $ip (#291 C3).
|
||||
if ! is_resolvable_address "${ip:-}"; then
|
||||
idx=$((idx + 1))
|
||||
continue
|
||||
fi
|
||||
name=$(resolve_hostname "$ip")
|
||||
# NXDOMAIN / timeout: resolve_hostname returns 1. set -e
|
||||
# cannot apply to this assignment (#291 C3).
|
||||
name=$(resolve_hostname "$ip") || name=
|
||||
if [ -n "$name" ]; then
|
||||
map_add "$ip" "$name"
|
||||
count=$((count + 1))
|
||||
@@ -789,6 +807,17 @@ Address: 127.0.0.1:53")
|
||||
|
||||
run_logging_selftest || return 1
|
||||
|
||||
# Strict-mode smoke (#291 C3): status JSON uses soft-fail helpers
|
||||
# (uci miss, empty WAN zone). Must not abort under dash/ash set -e.
|
||||
_status=$(build_logging_status_json) || {
|
||||
echo "build_logging_status_json: aborted under set -e" >&2
|
||||
return 1
|
||||
}
|
||||
case "$_status" in
|
||||
*'"blockers":'*) ;;
|
||||
*) echo "build_logging_status_json: missing blockers: $_status" >&2; return 1 ;;
|
||||
esac
|
||||
|
||||
if ! command -v jshn >/dev/null 2>&1; then
|
||||
echo "skip: jshn not available (poll cap via jshn not tested)" >&2
|
||||
return 0
|
||||
@@ -834,7 +863,7 @@ Address: 127.0.0.1:53")
|
||||
return 0
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
case "${1:-}" in
|
||||
__selftest)
|
||||
run_selftest
|
||||
exit $?
|
||||
@@ -845,22 +874,22 @@ case "$1" in
|
||||
exit $?
|
||||
;;
|
||||
__poll_clamp)
|
||||
poll_clamp_lines "$2"
|
||||
poll_clamp_lines "${2:-}"
|
||||
exit $?
|
||||
;;
|
||||
__tmp_dir_ok)
|
||||
_fwlive_tmp_dir_ok "$2"
|
||||
_fwlive_tmp_dir_ok "${2:-}"
|
||||
exit $?
|
||||
;;
|
||||
__resolve_one)
|
||||
if name=$(resolve_hostname "$2"); then
|
||||
if name=$(resolve_hostname "${2:-}"); then
|
||||
printf '%s\n' "$name"
|
||||
exit 0
|
||||
fi
|
||||
exit 1
|
||||
;;
|
||||
__parse_nslookup)
|
||||
name=$(parse_nslookup_name "$2")
|
||||
name=$(parse_nslookup_name "${2:-}")
|
||||
if [ -n "$name" ]; then
|
||||
printf '%s\n' "$name"
|
||||
exit 0
|
||||
@@ -874,15 +903,16 @@ case "$1" in
|
||||
echo '{"rules":{},"poll":{"addresses":[]},"resolve":{"addresses":[]},"logging_status":{},"enable_wan_logging":{},"disable_wan_logging":{}}'
|
||||
;;
|
||||
call)
|
||||
case "$2" in
|
||||
case "${2:-}" in
|
||||
rules)
|
||||
build_rules_map
|
||||
;;
|
||||
poll)
|
||||
poll_logs "$3"
|
||||
# rpcd may omit argv $3 and pass JSON on stdin (#291 C3).
|
||||
poll_logs "${3:-}"
|
||||
;;
|
||||
resolve)
|
||||
resolve_addresses "$3"
|
||||
resolve_addresses "${3:-}"
|
||||
;;
|
||||
logging_status)
|
||||
build_logging_status_json
|
||||
|
||||
Reference in new issue
Block a user