#!/bin/zsh
#
# SSH Failed Login Monitor
# Detects failed SSH login attempts and sends alerts via Telegram
# Runs every minute via cron for near-instant detection
#

STATE_DIR="/Users/mattbruce/.openclaw/workspace/scripts/security-monitors/state"
LOG_FILE="/Users/mattbruce/.openclaw/workspace/scripts/security-monitors/logs/ssh-monitor.log"
ALERT_COOLDOWN_FILE="$STATE_DIR/ssh-alert-cooldown"
LAST_CHECK_FILE="$STATE_DIR/ssh-last-check"

# Create directories
mkdir -p "$(dirname $LOG_FILE)" "$STATE_DIR"

# Telegram config - read from OpenClaw config
TELEGRAM_CHAT_ID="${TELEGRAM_CHAT_ID:-default}"

# Timestamp helper
timestamp() {
  date '+%Y-%m-%d %H:%M:%S %Z'
}

# Log to file
log() {
  echo "[$(timestamp)] $1" >> "$LOG_FILE"
}

# Send Telegram alert via OpenClaw message tool
send_alert() {
  local message="$1"
  # Use the OpenClaw delivery queue or message tool
  # Since we're in a script, we'll write to a queue that can be picked up
  echo "$(timestamp) | ALERT | $message" >> "$STATE_DIR/alerts.queue"
  
  # Also try direct message if possible (this requires the OpenClaw agent to be running)
  # For now, we log and rely on the agent heartbeat to check this queue
}

# Get failed SSH attempts since last check
check_failed_ssh() {
  local last_check=0
  if [[ -f "$LAST_CHECK_FILE" ]]; then
    last_check=$(cat "$LAST_CHECK_FILE")
  fi
  
  local current_time=$(date +%s)
  echo "$current_time" > "$LAST_CHECK_FILE"
  
  # Method 1: Check last command for failed attempts
  local failed_attempts=0
  local attempt_details=""
  
  # On macOS, use 'last' command and filter for failed entries (they show as still logged in but with date)
  # Failed SSH attempts typically show in last as entries that don't have a logout time
  attempt_details=$(last -f /var/log/wtmp 2>/dev/null | grep -E "ssh|pts" | head -20 || echo "")
  
  # Method 2: Check macOS unified logs for SSH authentication failures (more reliable)
  # This captures PAM authentication failures
  if command -v log >/dev/null 2>&1; then
    local log_output
    log_output=$(log show --predicate 'subsystem == "com.openssh.sshd" OR process == "sshd"' --info --last 2m 2>/dev/null | grep -i "failed\|invalid\|authentication failure" | head -10 || echo "")
    if [[ -n "$log_output" ]]; then
      failed_attempts=$(echo "$log_output" | wc -l | tr -d ' ')
      attempt_details="$log_output"
    fi
  fi
  
  # Method 3: Check system.log for auth failures (fallback)
  if [[ "$failed_attempts" -eq 0 ]] && [[ -f /var/log/system.log ]]; then
    local system_log_entries
    system_log_entries=$(grep -i "authentication failure\|failed password" /var/log/system.log 2>/dev/null | tail -10 || echo "")
    if [[ -n "$system_log_entries" ]]; then
      # Count only recent entries (within last 2 minutes)
      local recent_entries
      recent_entries=$(echo "$system_log_entries" | grep "$(date '+%b %e')" || echo "")
      if [[ -n "$recent_entries" ]]; then
        failed_attempts=$(echo "$recent_entries" | wc -l | tr -d ' ')
        attempt_details="$recent_entries"
      fi
    fi
  fi
  
  # Get source IPs from the attempts
  local source_ips=""
  if [[ -n "$attempt_details" ]]; then
    source_ips=$(echo "$attempt_details" | grep -oE "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" | sort -u | head -5 | tr '\n' ', ')
  fi
  
  echo "${failed_attempts}|${source_ips}|${attempt_details}"
}

# Main monitoring logic
main() {
  local result
  result=$(check_failed_ssh)
  local failed_count=$(echo "$result" | cut -d'|' -f1)
  local source_ips=$(echo "$result" | cut -d'|' -f2)
  local details=$(echo "$result" | cut -d'|' -f3-)
  
  # Check cooldown (don't alert more than once per 5 minutes for same pattern)
  local cooldown_expired=true
  if [[ -f "$ALERT_COOLDOWN_FILE" ]]; then
    local last_alert=$(cat "$ALERT_COOLDOWN_FILE")
    local current_time=$(date +%s)
    if [[ $((current_time - last_alert)) -lt 300 ]]; then
      cooldown_expired=false
    fi
  fi
  
  if [[ "$failed_count" -gt 0 ]] && [[ "$cooldown_expired" == "true" ]]; then
    # Log the detection
    log "⚠️ SECURITY ALERT: $failed_count failed SSH attempt(s) detected"
    log "Source IPs: $source_ips"
    
    # Create alert message
    local hostname=$(hostname -s)
    local alert_msg="🚨 **SECURITY ALERT: Failed SSH Login Attempts** 🚨

**Host:** $hostname
**Time:** $(timestamp)
**Failed Attempts:** $failed_count
**Source IP(s):** ${source_ips:-Unknown}

Please check system security immediately.

_Detected by OpenClaw SSH Monitor_"
    
    # Send alert
    send_alert "$alert_msg"
    
    # Update cooldown
    date +%s > "$ALERT_COOLDOWN_FILE"
    
    # Log to daily security log
    local daily_log="/Users/mattbruce/.openclaw/workspace/memory/$(date '+%Y-%m-%d')-security.log"
    echo "SSH_FAILED_ATTEMPTS|$(timestamp)|$failed_count|$source_ips" >> "$daily_log"
  elif [[ "$failed_count" -gt 0 ]]; then
    log "Detected $failed_count failed SSH attempts (cooldown active, no alert sent)"
  fi
  
  # Always log that we checked (for debugging)
  if [[ "${DEBUG:-}" == "1" ]]; then
    log "Check completed: $failed_count failed attempts found"
  fi
}

# Run main function
main "$@"
