wgctl/commands/preset.command.sh
2026-05-06 23:02:12 +00:00

199 lines
4.9 KiB
Bash

#!/usr/bin/env bash
# ============================================
# Help
# ============================================
function cmd::preset::help() {
cat <<EOF
Usage: wgctl preset <subcommand> [options]
Manage firewall presets.
Subcommands:
list, ls List available presets
add, new, create Add a new preset
remove, rm, del Remove a preset
Options for add:
--name <name> Preset name (e.g. no-jellyfin)
--desc <description> Human readable description
--block-ip <ip> Block specific IP (repeatable)
--block-subnet <cidr> Block subnet (repeatable)
--block-port <ip:port:proto> Block specific port (repeatable)
Examples:
wgctl preset list
wgctl preset add --name no-jellyfin --desc "Block Jellyfin" --block-ip 10.0.0.210 --block-port 10.0.0.210:8096:tcp
wgctl preset remove --name no-jellyfin
EOF
}
# ============================================
# Run
# ============================================
function cmd::preset::run() {
local subcmd="${1:-help}"
shift || true
case "$subcmd" in
list|ls) cmd::preset::list "$@" ;;
add|new|create) cmd::preset::add "$@" ;;
remove|rm|del|delete) cmd::preset::remove "$@" ;;
help) cmd::preset::help ;;
*)
log::error "Unknown subcommand: '${subcmd}'"
cmd::preset::help
return 1
;;
esac
}
# ============================================
# List
# ============================================
function cmd::preset::list() {
local dir
dir="$(ctx::presets)"
local presets=("${dir}"/*.preset)
if [[ ! -f "${presets[0]}" ]]; then
log::wg_preset "No presets configured"
return 0
fi
log::section "Available Presets"
printf "\n %-25s %-40s %s\n" "NAME" "DESCRIPTION" "RULES"
printf " %s\n" "$(printf '─%.0s' {1..75})"
for preset_file in "${dir}"/*.preset; do
[[ -f "$preset_file" ]] || continue
# Reset vars before sourcing
local PRESET_NAME="" PRESET_DESC=""
local BLOCK_IPS="" BLOCK_SUBNETS="" BLOCK_PORTS=""
source "$preset_file"
local rules=""
[[ -n "$BLOCK_IPS" ]] && rules+="IPs:$(echo "$BLOCK_IPS" | wc -w) "
[[ -n "$BLOCK_SUBNETS" ]] && rules+="Subnets:$(echo "$BLOCK_SUBNETS" | wc -w) "
[[ -n "$BLOCK_PORTS" ]] && rules+="Ports:$(echo "$BLOCK_PORTS" | wc -w)"
printf " %-25s %-40s %s\n" \
"$PRESET_NAME" \
"${PRESET_DESC:-}" \
"${rules:-}"
done
printf "\n"
}
# ============================================
# Add
# ============================================
function cmd::preset::add() {
local name=""
local desc=""
local block_ips=()
local block_subnets=()
local block_ports=()
while [[ $# -gt 0 ]]; do
case "$1" in
--name) name="$2"; shift 2 ;;
--desc) desc="$2"; shift 2 ;;
--block-ip) block_ips+=("$2"); shift 2 ;;
--block-subnet) block_subnets+=("$2"); shift 2 ;;
--block-port) block_ports+=("$2"); shift 2 ;;
--help) cmd::preset::help; return ;;
*)
log::error "Unknown flag: $1"
cmd::preset::help
return 1
;;
esac
done
if [[ -z "$name" ]]; then
log::error "Missing required flag: --name"
return 1
fi
if [[ ${#block_ips[@]} -eq 0 && ${#block_subnets[@]} -eq 0 && ${#block_ports[@]} -eq 0 ]]; then
log::error "At least one of --block-ip, --block-subnet, or --block-port is required"
return 1
fi
local preset_file
preset_file="$(ctx::preset::path "${name}.preset")"
if [[ -f "$preset_file" ]]; then
log::error "Preset already exists: ${name}"
return 1
fi
cat > "$preset_file" <<EOF
# wgctl preset — ${name}
PRESET_NAME="${name}"
PRESET_DESC="${desc}"
BLOCK_IPS="${block_ips[*]:-}"
BLOCK_SUBNETS="${block_subnets[*]:-}"
BLOCK_PORTS="${block_ports[*]:-}"
EOF
log::wg_success "Preset created: ${name}"
}
# ============================================
# Remove
# ============================================
function cmd::preset::remove() {
local name=""
local force=false
while [[ $# -gt 0 ]]; do
case "$1" in
--name) name="$2"; shift 2 ;;
--force) force=true; shift ;;
--help) cmd::preset::help; return ;;
*)
log::error "Unknown flag: $1"
cmd::preset::help
return 1
;;
esac
done
if [[ -z "$name" ]]; then
log::error "Missing required flag: --name"
return 1
fi
local preset_file
preset_file="$(ctx::preset::path "${name}.preset")"
if [[ ! -f "$preset_file" ]]; then
log::error "Preset not found: ${name}"
return 1
fi
if ! $force; then
read -r -p "Are you sure you want to remove preset '${name}'? [y/N] " confirm
case "$confirm" in
[yY][eE][sS]|[yY]) ;;
*)
log::info "Aborted"
return 0
;;
esac
fi
rm -f "$preset_file"
log::wg_success "Preset removed: ${name}"
}