# ============================================ # Remove peers from WireGuard # ============================================ function cmd::group::rm_peers() { local name="" force=false while [[ $# -gt 0 ]]; do case "$1" in --name) util::require_flag "--name" "${2:-}" || return 1; name="$2"; shift 2 ;; --force) force=true; shift ;; --help) cmd::group::help; return ;; *) log::error "Unknown flag: $1"; return 1 ;; esac done [[ -z "$name" ]] && log::error "Missing required flag: --name" && return 1 group::require_exists "$name" || return 1 local peers_list=() mapfile -t peers_list < <(group::peers "$name") local peer_count=${#peers_list[@]} [[ -z "${peers_list[0]:-}" ]] && peer_count=0 if [[ "$peer_count" -eq 0 ]]; then log::wg_warning "Group '${name}' has no peers" return 0 fi if ! $force; then read -r -p "Remove all ${peer_count} peers in group '${name}' from WireGuard? [y/N] " confirm case "$confirm" in [yY][eE][sS]|[yY]) ;; *) log::info "Aborted"; return 0 ;; esac fi load_command remove group::each_peer "$name" cmd::group::_rm_peer_cb log::wg_success "Removed peers from group '${name}' (definition kept)" } function cmd::group::_rm_peer_cb() { local peer_name="${1:-}" if ! group::_peer_exists_check "$peer_name"; then log::wg_warning "Peer '${peer_name}' no longer exists — skipping" return 0 fi cmd::remove::run --name "$peer_name" --force } # ============================================ # Block / Unblock # ============================================ function cmd::group::block() { local name="" force=false while [[ $# -gt 0 ]]; do case "$1" in --name) util::require_flag "--name" "${2:-}" || return 1; name="$2"; shift 2 ;; --force) force=true; shift ;; --help) cmd::group::help; return ;; *) log::error "Unknown flag: $1"; return 1 ;; esac done [[ -z "$name" ]] && log::error "Missing required flag: --name" && return 1 group::require_exists "$name" || return 1 local peers_list=() mapfile -t peers_list < <(group::peers "$name") if [[ ${#peers_list[@]} -eq 0 ]] || [[ -z "${peers_list[0]:-}" ]]; then log::wg_warning "Group '${name}' has no peers" return 0 fi local count=0 skipped=0 blocked_names=() local filtered=() for p in "${peers_list[@]:-}"; do [[ -n "$p" ]] && filtered+=("$p") done [[ ${#filtered[@]} -eq 0 ]] && log::wg_warning "Group '${name}' has no peers" && return 0 for peer_name in "${filtered[@]}"; do if cmd::group::_block_peer "$peer_name" "$name"; then (( count++ )) || true else (( skipped++ )) || true fi done if [[ "$count" -gt 0 ]]; then log::wg_block "All peers from ${name} have been blocked (${count} peers)." fi if [[ "$skipped" -gt 0 ]]; then log::wg_warning "${skipped} peers already blocked" fi } function cmd::group::unblock() { local name="" force=false while [[ $# -gt 0 ]]; do case "$1" in --name) util::require_flag "--name" "${2:-}" || return 1; name="$2"; shift 2 ;; --force) force=true; shift ;; --help) cmd::group::help; return ;; *) log::error "Unknown flag: $1"; return 1 ;; esac done [[ -z "$name" ]] && log::error "Missing required flag: --name" && return 1 group::require_exists "$name" || return 1 local peers_list=() mapfile -t peers_list < <(group::peers "$name") local filtered=() for p in "${peers_list[@]:-}"; do [[ -n "$p" ]] && filtered+=("$p"); done [[ ${#filtered[@]} -eq 0 ]] && log::wg_warning "Group '${name}' has no peers" && return 0 local count=0 skipped=0 for peer_name in "${filtered[@]}"; do if cmd::group::_unblock_peer "$peer_name" "$name"; then (( count++ )) || true else (( skipped++ )) || true fi done if [[ "$count" -gt 0 ]]; then log::wg_unblock "All peers from ${name} have been unblocked." fi if [[ "$skipped" -gt 0 ]]; then log::wg_warning "${skipped} peer(s) remain blocked (blocked directly or by other groups)" fi } function cmd::group::_block_peer() { local peer_name="${1:-}" group_name="${2:-}" if ! group::_peer_exists_check "$peer_name"; then log::wg_warning "Peer '${peer_name}' no longer exists — skipping" return 0 fi local client_ip client_ip=$(peers::get_ip "$peer_name") # Check if already blocked by this group local current_blocked_groups current_blocked_groups=$(block::get_groups "$peer_name") local IFS=',' for g in $current_blocked_groups; do if [[ "$g" == "$group_name" ]]; then log::wg_warning "${peer_name} — already blocked by group '${group_name}'" return 1 fi done # Add group to block tracking block::add_group "$peer_name" "$client_ip" "$group_name" # Apply fw rules only if peer is still in WG server (not yet blocked) if peers::exists_in_server "$peer_name"; then block::apply_full "$peer_name" "$client_ip" fi } function cmd::group::_unblock_peer() { local peer_name="${1:-}" group_name="${2:-}" if ! group::_peer_exists_check "$peer_name"; then log::wg_warning "Peer '${peer_name}' no longer exists — skipping" return 1 fi # Check if blocked by this group at all if ! block::has_file "$peer_name"; then log::wg_warning "${peer_name} — not blocked" return 1 fi local current_groups current_groups=$(block::get_groups "$peer_name") if [[ "$current_groups" != *"$group_name"* ]]; then log::wg_warning "${peer_name} — not blocked by group '${group_name}'" return 1 fi local client_ip client_ip=$(peers::get_ip "$peer_name") block::remove_group "$peer_name" "$client_ip" "$group_name" if block::is_blocked "$peer_name"; then local groups groups=$(block::get_groups "$peer_name") local direct direct=$(block::is_blocked_direct "$peer_name") if [[ "$direct" == "true" ]]; then log::wg_warning "${peer_name} — still blocked directly, skipping" else log::wg_warning "${peer_name} — still blocked by group(s): ${groups}, skipping" fi return 1 fi block::restore_peer "$peer_name" "$client_ip" block::remove_file "$peer_name" local rule rule=$(peers::get_meta "$peer_name" "rule") [[ -n "$rule" ]] && rule::exists "$rule" && \ rule::apply "$rule" "$client_ip" "$peer_name" } # ============================================ # Rule assign # ============================================ function cmd::group::rule() { local subcmd="${1:-help}" shift || true case "$subcmd" in assign) cmd::group::rule_assign "$@" ;; *) log::error "Unknown rule subcommand: '${subcmd}'" cmd::group::help return 1 ;; esac } function cmd::group::rule_assign() { local name="" rule="" while [[ $# -gt 0 ]]; do case "$1" in --name) util::require_flag "--name" "${2:-}" || return 1; name="$2"; shift 2 ;; --rule) util::require_flag "--rule" "${2:-}" || return 1; rule="$2"; shift 2 ;; --help) cmd::group::help; return ;; *) log::error "Unknown flag: $1"; return 1 ;; esac done [[ -z "$name" ]] && log::error "Missing required flag: --name" && return 1 [[ -z "$rule" ]] && log::error "Missing required flag: --rule" && return 1 group::require_exists "$name" || return 1 rule::require_exists "$rule" || return 1 local peers_list=() mapfile -t peers_list < <(group::peers "$name") [[ -z "${peers_list[0]:-}" ]] && log::wg_warning "Group '${name}' has no peers" && return 0 group::each_peer "$name" cmd::group::_rule_assign_cb "$rule" log::wg_success "Assigned rule '${rule}' to group '${name}'" } function cmd::group::_rule_assign_cb() { local peer_name="${1:-}" rule="${2:-}" if ! group::_peer_exists_check "$peer_name"; then log::wg_warning "Peer '${peer_name}' no longer exists — skipping" return 0 fi load_command rule cmd::rule::assign --name "$rule" --peer "$peer_name" }