#!/usr/bin/env bash set -euo pipefail SCRIPT_NAME="$(basename "$0")" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" SKILL_ROOT="$(dirname "$SCRIPT_DIR")" : "${OUTPUT_DIR:=$SKILL_ROOT/outputs}" : "${BACKUP_DIR:=$OUTPUT_DIR/backups}" : "${TEMPLATE_DIR:=$SKILL_ROOT/templates}" log_info() { echo "[INFO] $(date -Iseconds) $*"; } log_warn() { echo "[WARN] $(date -Iseconds) $*" >&2; } log_error() { echo "[ERROR] $(date -Iseconds) $*" >&2; } die() { log_error "$@"; exit 1; } ensure_fail2ban() { if command -v fail2ban-client &>/dev/null; then return 0 fi log_warn "fail2ban not found; attempting install (Debian/Ubuntu)" if command -v apt-get &>/dev/null; then sudo apt-get update -y sudo apt-get install -y fail2ban else die "fail2ban not installed and apt-get not available. Install manually." fi } render_jail() { local port="$1" sed "s/{{SSH_PORT}}/$port/g" "$TEMPLATE_DIR/fail2ban_jail.tpl" } main() { mkdir -p "$OUTPUT_DIR" "$BACKUP_DIR" local enabled="${FAIL2BAN_ENABLE:-true}" if [[ "$enabled" != "true" && "$enabled" != "1" ]]; then log_info "FAIL2BAN_ENABLE disabled; skipping" exit 0 fi local dry="${DRY_RUN:-1}" [[ "$dry" == "0" ]] || die "Refusing to apply with DRY_RUN=$dry. Export DRY_RUN=0 to proceed." ensure_fail2ban local ssh_port="${SSH_PORT:-22}" if [[ -f /etc/fail2ban/jail.local ]]; then sudo cp -a /etc/fail2ban/jail.local "$BACKUP_DIR/jail.local.before" fi log_info "Writing /etc/fail2ban/jail.local" render_jail "$ssh_port" | sudo tee /etc/fail2ban/jail.local >/dev/null log_info "Enabling and restarting fail2ban" sudo systemctl enable fail2ban sudo systemctl restart fail2ban log_info "fail2ban setup complete" } [[ "${BASH_SOURCE[0]}" == "$0" ]] && main "$@"