wgctl/modules/block.module.sh

241 lines
No EOL
6.8 KiB
Bash

#!/usr/bin/env bash
# ============================================
# Block file management
# ============================================
# ── Core state queries ─────────────────────
function block::file() {
local name="${1:?name required}"
ctx::block::path "${name}.block"
}
function block::has_file() {
local name="${1:?}"
[[ -f "$(block::file "$name")" ]]
}
function block::has_specific_rules() {
local name="${1:?}"
block::has_file "$name" || return 1
while IFS="|" read -r bname btype target port proto; do
[[ -z "$btype" ]] && continue
[[ "$btype" != "full" ]] && return 0
done < <(block::get_rules "$name")
return 1
}
function block::is_blocked() {
local name="${1:?}"
block::has_file "$name" || return 1
local result
result=$(json::block_is_blocked "$(block::file "$name")")
[[ "$result" == "true" ]]
}
function block::is_blocked_direct() {
local name="${1:?}"
block::has_file "$name" || { echo "false"; return 0; }
json::block_get_direct "$(block::file "$name")"
}
function block::get_groups() {
local name="${1:?}"
block::has_file "$name" || return 0
json::block_get_groups "$(block::file "$name")"
}
function block::get_rules() {
local name="${1:?}"
block::has_file "$name" || return 0
json::block_get_rules "$(block::file "$name")"
}
# ── State mutations ────────────────────────
function block::set_direct() {
local name="${1:?}" client_ip="${2:?}" value="${3:-true}"
local file
file=$(block::file "$name")
json::block_set_direct "$file" "$client_ip" "$value"
}
function block::add_group() {
local name="${1:?}" client_ip="${2:?}" group="${3:?}"
local file
file=$(block::file "$name")
json::block_add_group "$file" "$client_ip" "$group"
}
function block::remove_group() {
local name="${1:?}" client_ip="${2:?}" group="${3:?}"
local file
file=$(block::file "$name")
json::block_remove_group "$file" "$client_ip" "$group"
}
function block::add_rule() {
local name="${1:?}" client_ip="${2:?}"
local rule_type="${3:?}" rule_name="${4:-}"
local target="${5:-}" port="${6:-}" proto="${7:-}"
local file
file=$(block::file "$name")
json::block_add_rule "$file" "$client_ip" "$rule_type" \
"$rule_name" "$target" "$port" "$proto"
}
function block::remove_rule() {
local name="${1:?}"
local rule_type="${2:?}" target="${3:-}" port="${4:-}" proto="${5:-}"
local file
file=$(block::file "$name")
json::block_remove_rule "$file" "$rule_type" "$target" "$port" "$proto"
}
function block::remove_file() {
local name="${1:?}"
rm -f "$(block::file "$name")"
}
function block::rename() {
local name="${1:?}" new_name="${2:?}"
local old_file new_file
old_file=$(block::file "$name")
new_file=$(block::file "$new_name")
[[ -f "$old_file" ]] && mv "$old_file" "$new_file"
}
# ── High level operations ──────────────────
function block::apply_full() {
local name="${1:?}" client_ip="${2:?}"
fw::flush_peer "$client_ip"
fw::block_all "$client_ip" "$name"
block::add_rule "$name" "$client_ip" "full" "full block"
local public_key endpoint
public_key=$(keys::public "$name") || return 1
endpoint=$(monitor::endpoint_for_key "$public_key")
[[ -n "$endpoint" ]] && monitor::watch "$endpoint" "$name"
peers::remove_from_server "$name"
peers::reload
}
function block::restore_peer() {
local name="${1:?}" client_ip="${2:?}"
fw::unblock_all "$client_ip"
fw::flush_peer "$client_ip"
monitor::unwatch_client "$name"
if ! peers::exists_in_server "$name"; then
local public_key
public_key=$(keys::public "$name") || return 1
peers::add_to_server "$name" "$public_key" "$client_ip"
peers::reload
fi
return 0
}
function block::restore_rules_for() {
local name="${1:?}" client_ip="${2:?}"
while IFS="|" read -r bname btype target port proto; do
[[ -z "$btype" ]] && continue
case "$btype" in
ip) fw::block_ip "$client_ip" "$target" "append" ;;
port) fw::block_port "$client_ip" "$target" "$port" \
"${proto:-tcp}" "append" ;;
subnet) fw::block_subnet "$client_ip" "$target" "append" ;;
esac
done < <(block::get_rules "$name")
}
function block::restore_all() {
while IFS= read -r peer_name; do
block::has_file "$peer_name" || continue
local client_ip
client_ip=$(peers::get_ip "$peer_name")
[[ -z "$client_ip" ]] && continue
while IFS="|" read -r bname btype target port proto; do
[[ -z "$btype" ]] && continue
case "$btype" in
full) fw::block_all "$client_ip" "$peer_name" ;;
ip) fw::block_ip "$client_ip" "$target" ;;
port) fw::block_port "$client_ip" "$target" "$port" "${proto:-tcp}" ;;
subnet) fw::block_subnet "$client_ip" "$target" ;;
esac
done < <(block::get_rules "$peer_name")
done < <(peers::all)
}
# ── Display helpers ────────────────────────
function block::format_rules() {
local name="${1:?}"
block::has_file "$name" || return 0
while IFS="|" read -r bname btype target port proto; do
[[ -z "$btype" ]] && continue
local display="" ann=""
case "$btype" in
full)
display="all traffic"
;;
ip)
display="$target"
ann=$(net::annotate "$target")
;;
port)
display="${target}:${port}:${proto}"
ann=$(net::annotate "${target}:${port}:${proto}")
;;
subnet)
display="$target"
ann=$(net::annotate "${target%%/*}")
;;
esac
local label="$bname"
# If bname wasn't set (equals type default), clear it
case "$label" in
full|ip|port|subnet|"") label="" ;;
esac
# Suppress label if it matches annotation
if [[ -n "$ann" && -n "$label" && \
("$ann" == "$label" || "$ann" == "${label}:"*) ]]; then
label=""
fi
# log::debug "label='$label' ann='$ann' match=$([ "$ann" == "$label" ] && echo yes || echo no)"
printf " \033[0;31m-\033[0m %-20s \033[0;37m%s%s\033[0m\n" \
"$display" \
"${label:+${label} }" \
"${ann:+→ ${ann}}"
done < <(block::get_rules "$name")
return 0
}
# function block::format_rules() {
# local name="${1:?}"
# block::has_file "$name" || return 0
# while IFS="|" read -r bname btype target port proto; do
# [[ -z "$btype" ]] && continue
# local display
# case "$btype" in
# full) display="all traffic" ;;
# ip) display="$target" ;;
# port) display="${target}:${port}:${proto}" ;;
# subnet) display="$target" ;;
# esac
# local label="${bname:-$btype}"
# printf " \033[0;31m-\033[0m %-30s \033[0;37m%s\033[0m\n" \
# "$display" "$label"
# done < <(block::get_rules "$name")
# }