213 lines
No EOL
6.1 KiB
Bash
213 lines
No EOL
6.1 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
# ============================================
|
|
# Lifecycle
|
|
# ============================================
|
|
|
|
function cmd::fw::on_load() {
|
|
flag::register --peer
|
|
flag::register --type
|
|
flag::register --rule
|
|
flag::register --no-nflog
|
|
flag::register --no-accept
|
|
flag::register --no-drop
|
|
}
|
|
|
|
# ============================================
|
|
# Help
|
|
# ============================================
|
|
|
|
function cmd::fw::help() {
|
|
cat <<EOF
|
|
Usage: wgctl fw [subcommand] [options]
|
|
|
|
Inspect and manage firewall rules.
|
|
|
|
Subcommands:
|
|
list Show FORWARD chain rules (default)
|
|
nat Show NAT/PREROUTING rules
|
|
flush-nat Flush NAT rules for a subnet
|
|
count Show rule counts by type
|
|
|
|
Options for list:
|
|
--peer <name> Filter by peer name
|
|
--rule <rule> Filter by rule name (shows all peers with that rule)
|
|
--no-nflog Hide NFLOG rules
|
|
--no-accept Hide ACCEPT rules
|
|
--no-drop Hide DROP rules
|
|
|
|
Examples:
|
|
wgctl fw list
|
|
wgctl fw list --peer phone-nuno
|
|
wgctl fw list --rule guest
|
|
wgctl fw list --no-nflog
|
|
wgctl fw nat
|
|
wgctl fw count
|
|
wgctl fw flush-nat --subnet 10.1.103.0/24
|
|
EOF
|
|
}
|
|
|
|
# ============================================
|
|
# Run
|
|
# ============================================
|
|
|
|
function cmd::fw::run() {
|
|
local subcmd="${1:-list}"
|
|
|
|
# If first arg is a flag, default to list
|
|
if [[ "$subcmd" == --* ]]; then
|
|
subcmd="list"
|
|
else
|
|
shift || true
|
|
fi
|
|
|
|
case "$subcmd" in
|
|
list) cmd::fw::list "$@" ;;
|
|
nat) cmd::fw::nat "$@" ;;
|
|
flush-nat) cmd::fw::flush_nat "$@" ;;
|
|
clean) cmd::fw::clean ;;
|
|
count) cmd::fw::count ;;
|
|
help) cmd::fw::help ;;
|
|
*) log::error "Unknown subcommand: $subcmd"; return 1 ;;
|
|
esac
|
|
}
|
|
|
|
function cmd::fw::list() {
|
|
local peer="" type="" rule=""
|
|
local show_nflog=true show_accept=true show_drop=true
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--peer) peer="$2"; shift 2 ;;
|
|
--rule) rule="$2"; shift 2 ;;
|
|
--type) type="$2"; shift 2 ;;
|
|
--no-nflog) show_nflog=false; shift ;;
|
|
--no-accept) show_accept=false; shift ;;
|
|
--no-drop) show_drop=false; shift ;;
|
|
*) shift ;;
|
|
esac
|
|
done
|
|
|
|
# Rule filter — collect all IPs for peers with this rule
|
|
if [[ -n "$rule" ]]; then
|
|
log::section "Firewall Rules (FORWARD) — rule: ${rule}"
|
|
printf "\n"
|
|
local found=false
|
|
while IFS= read -r peer_name; do
|
|
local ip
|
|
ip=$(peers::get_ip "$peer_name")
|
|
[[ -z "$ip" ]] && continue
|
|
printf " \033[0;37m── %s (%s)\033[0m\n" "$peer_name" "$ip"
|
|
iptables -L FORWARD -n -v | grep -F "$ip" \
|
|
| cmd::fw::_print_filtered "$show_nflog" "$show_accept" "$show_drop" || true
|
|
found=true
|
|
done < <(peers::with_rule "$rule")
|
|
$found || log::wg_warning "No peers found with rule: ${rule}"
|
|
printf "\n"
|
|
return 0
|
|
fi
|
|
|
|
log::section "Firewall Rules (FORWARD)"
|
|
printf "\n"
|
|
|
|
if [[ -n "$peer" ]]; then
|
|
local ip
|
|
ip=$(peers::get_ip "$peer")
|
|
[[ -z "$ip" ]] && log::error "Peer not found: $peer" && return 1
|
|
iptables -L FORWARD -n -v | grep -F "$ip" \
|
|
| cmd::fw::_print_filtered "$show_nflog" "$show_accept" "$show_drop" || true
|
|
elif [[ -n "$type" ]]; then
|
|
local subnet
|
|
subnet=$(config::subnet_for "$type")
|
|
[[ -z "$subnet" ]] && log::error "Unknown type: $type" && return 1
|
|
iptables -L FORWARD -n -v | grep -F "$subnet" \
|
|
| cmd::fw::_print_filtered "$show_nflog" "$show_accept" "$show_drop"
|
|
else
|
|
iptables -L FORWARD -n -v \
|
|
| grep -v "^Chain\|^target\|^$\|ACCEPT.*0\.0\.0\.0.*0\.0\.0\.0" \
|
|
| cmd::fw::_print_filtered "$show_nflog" "$show_accept" "$show_drop"
|
|
fi
|
|
|
|
printf "\n"
|
|
}
|
|
|
|
function cmd::fw::nat() {
|
|
log::section "NAT Rules (PREROUTING)"
|
|
printf "\n"
|
|
iptables -t nat -L PREROUTING -n -v | while IFS= read -r rule; do
|
|
[[ -z "$rule" ]] && continue
|
|
ui::firewall_rule "$rule"
|
|
done
|
|
printf "\n"
|
|
}
|
|
|
|
function cmd::fw::flush_nat() {
|
|
local type=""
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--type) type="$2"; shift 2 ;;
|
|
*) shift ;;
|
|
esac
|
|
done
|
|
|
|
if [[ -z "$type" ]]; then
|
|
log::error "Missing required flag: --type"
|
|
return 1
|
|
fi
|
|
|
|
local subnet
|
|
subnet=$(config::subnet_for "$type")
|
|
if [[ -z "$subnet" ]]; then
|
|
log::error "Unknown type: $type"
|
|
return 1
|
|
fi
|
|
|
|
local nat_linenums=()
|
|
while IFS= read -r linenum; do
|
|
[[ -n "$linenum" ]] && nat_linenums+=("$linenum")
|
|
done < <(iptables -t nat -L PREROUTING -n --line-numbers | grep -F "${subnet}" | awk '{print $1}')
|
|
|
|
local count=0
|
|
for (( i=${#nat_linenums[@]}-1; i>=0; i-- )); do
|
|
iptables -t nat -D PREROUTING "${nat_linenums[$i]}" 2>/dev/null || true
|
|
(( count++ )) || true
|
|
done
|
|
|
|
log::wg_success "Flushed ${count} NAT rules for type '${type}' (${subnet}.0/24)"
|
|
}
|
|
|
|
function cmd::fw::stats() {
|
|
log::section "Firewall Stats"
|
|
iptables -L FORWARD -n -v | grep -v "^Chain\|^target\|^$" | \
|
|
awk 'NR>1 {printf " %8s pkts %8s bytes %s\n", $1, $2, $0}'
|
|
}
|
|
|
|
function cmd::fw::count() {
|
|
log::section "Firewall Rule Counts"
|
|
local drop accept nflog total
|
|
drop=$(iptables -L FORWARD -n | grep -c "^DROP" || echo 0)
|
|
accept=$(iptables -L FORWARD -n | grep -c "^ACCEPT" || echo 0)
|
|
nflog=$(iptables -L FORWARD -n | grep -c "^NFLOG" || echo 0)
|
|
total=$(( drop + accept + nflog ))
|
|
printf "\n %-10s %s\n" "DROP:" "$drop"
|
|
printf " %-10s %s\n" "ACCEPT:" "$accept"
|
|
printf " %-10s %s\n" "NFLOG:" "$nflog"
|
|
printf " %-10s %s\n" "TOTAL:" "$total"
|
|
printf "\n"
|
|
}
|
|
|
|
function cmd::fw::clean() {
|
|
log::section "Duplicate Rule Report"
|
|
iptables-save | sort | uniq -d | grep "^-A FORWARD"
|
|
}
|
|
|
|
function cmd::fw::_print_filtered() {
|
|
local show_nflog="$1" show_accept="$2" show_drop="$3"
|
|
while IFS= read -r rule; do
|
|
[[ -z "$rule" ]] && continue
|
|
if ! $show_nflog && [[ "$rule" =~ NFLOG ]]; then continue; fi
|
|
if ! $show_accept && [[ "$rule" =~ ACCEPT ]]; then continue; fi
|
|
if ! $show_drop && [[ "$rule" =~ DROP ]]; then continue; fi
|
|
ui::firewall_rule "$rule"
|
|
done
|
|
} |