Files
Linux_post_install/bin/pos-system-firewall
T
he 5ef38dc46f fix: resolve all 23 MAINTENANCE audit tickets
- deps guards before -h|--help in docker-health/ps, network-scan,
  usb-server, media-mp3/mp4 (--dry-run pre-scan kept); system-firewall
  gains usage()/--help; autostart/usb-automount get flags.sh + template
- install.sh: normalize N-M range syntax in --steps
- bin/pos: INTERACTIVE_CMDS += docker-compose docker-vbox network-hotspot
- common.sh: canonical XDG-aware CONFIG_DIR + DIM color var; notify.sh
  stderr fallback; ent_plugin_* registry renames (runtime plugin API kept)
- docker-compose SCALE_DIR/CONFIG_ENV env seams; ffmpeg in PACKAGES;
  scrcpy.sh exec bit
- docs: health is console-only (--send/--markdown removed), POS.md file
  refs for config/tree/entertainment, DEV.md no-guard exception, docmap/
  filetable regenerated (make gen), hand-maintained line rows bumped
- add scripts/lint-conventions.sh gate + Makefile lint target; record
  all VERIFIED outcomes in MAINTENANCE.md; AGENT_TODO Done entry
  (2026-08-14)
- gates: make gen/check/lint all green (0 FAIL, 0 WARN); bash -n sweep
  clean; restricted-PATH dep tests + step-matrix dry-runs verified
2026-08-14 12:57:00 -04:00

309 lines
9.4 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# POS: system firewall — Interactive UFW management
IFS=$'\n\t'
if [[ $EUID -ne 0 ]]; then
echo "ERROR: Please run as root (sudo)."
echo "Usage: sudo pos system firewall"
exit 1
fi
source "$(dirname "$0")/../lib/notify.sh" 2>/dev/null || source "$(dirname "$0")/notify.sh"
HISTORY=()
DRY_RUN=0
usage() {
cat <<'EOF'
Usage: sudo pos system firewall [--dry-run]
Interactive UFW management menu.
Options:
--dry-run Preview commands without executing them
-h, --help Show this help
EOF
exit 0
}
case "${1:-}" in
-h|--help) usage ;;
esac
if [[ "${1:-}" == "--dry-run" ]]; then
DRY_RUN=1
fi
log() { echo "[+] $*"; }
warn() { echo "[!] $*"; }
err() { echo "ERROR: $*" >&2; exit 1; }
run_cmd() {
local -a cmd=("$@")
printf "\n>>> %s\n" "${cmd[*]}"
read -rp "Execute this command? [y/N]: " confirm
if [[ "$confirm" =~ ^[Yy]$ ]]; then
if [[ "$DRY_RUN" -eq 1 ]]; then
echo "(dry-run) skipping execution"
else
"${cmd[@]}"
# Alert on mutating commands only (status queries are read-only)
if [[ "${cmd[1]:-}" != "status" ]]; then
notify_send "Firewall: ${cmd[*]}"
fi
fi
HISTORY+=("${cmd[*]}")
else
echo "Cancelled."
fi
}
build_ufw_cmd() {
local action="$1"
local direction="$2"
local proto="$3"
local from="$4"
local to="$5"
local port="$6"
local onif="$7"
local logmode="$8"
local comment="$9"
local insert_pos="${10:-}"
local suffix="${11:-}"
local -a cmd=(ufw)
if [[ -n "$insert_pos" ]]; then
if [[ "$insert_pos" == "prepend" ]]; then
cmd+=(prepend)
else
cmd+=(insert "$insert_pos")
fi
fi
cmd+=("$action")
[[ -n "$direction" ]] && cmd+=("$direction")
[[ -n "$onif" ]] && cmd+=(on "$onif")
[[ -n "$proto" ]] && cmd+=(proto "$proto")
[[ -n "$from" ]] && cmd+=(from "$from")
cmd+=(to "$to")
[[ -n "$port" ]] && cmd+=(port "$port")
[[ -n "$logmode" ]] && cmd+=("$logmode")
if [[ -n "$comment" ]]; then
local safe="${comment// /_}"
cmd+=(comment "$safe")
fi
[[ "$suffix" == "v6" ]] && cmd+=(v6)
run_cmd "${cmd[@]}"
}
prompt_ipver() {
local ver
read -rp "IP version (4 / 6 / both): " ver
echo "$ver"
}
apply_for_versions() {
local action="$1" direction="$2" proto="$3" from="$4" to="$5"
local port="$6" onif="$7" logmode="$8" comment="$9"
local insert_pos="${10:-}"
local ipver
ipver=$(prompt_ipver)
case "$ipver" in
4) build_ufw_cmd "$action" "$direction" "$proto" "$from" "$to" "$port" "$onif" "$logmode" "$comment" "$insert_pos" "" ;;
6) build_ufw_cmd "$action" "$direction" "$proto" "$from" "$to" "$port" "$onif" "$logmode" "$comment" "$insert_pos" "v6" ;;
both)
build_ufw_cmd "$action" "$direction" "$proto" "$from" "$to" "$port" "$onif" "$logmode" "$comment" "$insert_pos" ""
build_ufw_cmd "$action" "$direction" "$proto" "$from" "$to" "$port" "$onif" "$logmode" "$comment" "$insert_pos" "v6"
;;
*) echo "Invalid choice. Choose 4, 6 or both." ;;
esac
}
add_rule() {
echo
echo "Choose rule type:"
echo "1) Port/service (eg: port 8080 or 'ssh')"
echo "2) IP-based (from X to Y)"
echo "3) Directional port rule (in/out to any port ...)"
read -rp "Choice: " rtype
case "$rtype" in
1)
read -rp "Action (allow/deny/reject/limit) [allow]: " action
action=${action:-allow}
read -rp "Enter port number or service name (eg 'ssh' or '8080'): " port_or_svc
if [[ "$port_or_svc" =~ ^[0-9]+$ ]]; then
read -rp "Protocol (tcp/udp/any) [tcp]: " proto
proto=${proto:-tcp}
[[ "$proto" == "any" ]] && proto=""
read -rp "Interface (leave empty for any): " onif
read -rp "Log? (none/log/log-all) [none]: " logmode
[[ "$logmode" == "none" ]] && logmode=""
read -rp "Comment (optional): " comment
apply_for_versions "$action" "" "$proto" "" "any" "$port_or_svc" "$onif" "$logmode" "$comment"
else
read -rp "IP version (4 / 6 / both) [4]: " ipver
ipver=${ipver:-4}
case "$ipver" in
4) run_cmd ufw "$action" "$port_or_svc" ;;
6) run_cmd ufw "$action" "$port_or_svc" v6 ;;
both) run_cmd ufw "$action" "$port_or_svc"
run_cmd ufw "$action" "$port_or_svc" v6 ;;
*) echo "invalid ipver" ;;
esac
fi
;;
2)
read -rp "Action (allow/deny/reject) [deny]: " action
action=${action:-deny}
read -rp "From address/CIDR (eg 192.168.1.5 or 10.0.0.0/24): " from
read -rp "To address (leave empty for 'any') [any]: " to
to=${to:-any}
read -rp "Direction (in/out) [in]: " direction
direction=${direction:-in}
read -rp "Port (leave empty if not applicable): " port
read -rp "Protocol (tcp/udp/any) [any]: " proto
[[ "$proto" == "any" ]] && proto=""
read -rp "Interface (leave empty for any): " onif
read -rp "Log? (none/log/log-all) [none]: " logmode
[[ "$logmode" == "none" ]] && logmode=""
read -rp "Comment (optional): " comment
apply_for_versions "$action" "$direction" "$proto" "$from" "$to" "$port" "$onif" "$logmode" "$comment"
;;
3)
read -rp "Action (allow/deny/reject/limit) [allow]: " action
action=${action:-allow}
read -rp "Direction (in/out) [in]: " direction
direction=${direction:-in}
read -rp "Port number: " port
read -rp "Protocol (tcp/udp/any) [tcp]: " proto
[[ "$proto" == "any" ]] && proto=""
read -rp "On interface (leave empty for any): " onif
read -rp "From address (optional): " from
from=${from:-}
read -rp "To address [any]: " to
to=${to:-any}
read -rp "Log? (none/log/log-all) [none]: " logmode
[[ "$logmode" == "none" ]] && logmode=""
read -rp "Comment (optional): " comment
read -rp "Insert position (number/prepend/empty): " insert_pos
apply_for_versions "$action" "$direction" "$proto" "$from" "$to" "$port" "$onif" "$logmode" "$comment" "$insert_pos"
;;
*) echo "Unknown choice." ;;
esac
}
delete_rule() {
echo
echo "Delete rule by:"
echo "1) rule number (use 'ufw status numbered' to see numbers)"
echo "2) rule text (eg: 'allow 22/tcp')"
read -rp "Choice: " dch
case "$dch" in
1)
ufw status numbered
read -rp "Number to delete: " num
run_cmd ufw delete "$num"
;;
2)
read -rp "Exact rule text to delete (eg: deny 80/tcp): " ruletext
run_cmd ufw delete $ruletext
;;
*) echo "Unknown choice." ;;
esac
}
show_status() {
echo
echo "1) Simple status"
echo "2) Verbose status"
echo "3) Numbered status (useful for delete)"
read -rp "Choice: " sc
case "$sc" in
1) run_cmd ufw status ;;
2) run_cmd ufw status verbose ;;
3) run_cmd ufw status numbered ;;
*) echo "Unknown choice." ;;
esac
}
while true; do
cat <<'MENU'
==============================
UFW POWER — human friendly
==============================
1) Add rule (port/service/ip/directional)
2) Delete rule (by number or text)
3) Show status (simple / verbose / numbered)
4) Enable UFW
5) Disable UFW
6) Reset UFW (delete all rules)
7) Set default policy (incoming/outgoing)
8) Show executed commands history (so far)
0) Exit
------------------------------
MENU
read -rp "Choose: " opt
case "$opt" in
1) add_rule ;;
2) delete_rule ;;
3) show_status ;;
4) run_cmd ufw enable ;;
5) run_cmd ufw disable ;;
6)
echo "WARNING: ufw reset will disable and remove all rules."
read -rp "Type 'RESET' to confirm: " c
[[ "$c" == "RESET" ]] && run_cmd ufw reset || echo "Reset aborted."
;;
7)
read -rp "Default incoming policy (allow/deny/reject) [deny]: " defin
defin=${defin:-deny}
read -rp "Default outgoing policy (allow/deny/reject) [allow]: " defout
defout=${defout:-allow}
run_cmd ufw default "$defin" incoming
run_cmd ufw default "$defout" outgoing
;;
8)
echo
echo "Executed commands so far:"
echo
if [[ "${#HISTORY[@]}" -eq 0 ]]; then
echo "(none yet)"
else
for c in "${HISTORY[@]}"; do echo " - $c"; done
fi
;;
0)
echo
echo "Final executed commands summary:"
if [[ "${#HISTORY[@]}" -eq 0 ]]; then
echo "(no commands executed)"
else
for c in "${HISTORY[@]}"; do echo " - $c"; done
fi
echo "Goodbye — firewall remains watchful."
exit 0
;;
*) echo "Unknown option." ;;
esac
echo
read -rp "Press Enter to continue..."
clear
done