#!/usr/bin/env bash
set -euo pipefail

# centos7-ssh-hardening-check.sh
#
# Purpose:
#   Validate SSH hardening readiness on CentOS 7 before disabling password login.
#   This script is intentionally non-destructive: it only checks local and remote
#   configuration signals you provide and reports findings.
#
# Usage examples:
#   ./centos7-ssh-hardening-check.sh --sshd-config /etc/ssh/sshd_config
#   ./centos7-ssh-hardening-check.sh --host example-host --user admin --port 22
#   ./centos7-ssh-hardening-check.sh --host bastion.example --user ops --key ~/.ssh/id_ed25519 --mfa-required yes
#
# Exit codes:
#   0 = checks completed successfully
#   1 = one or more checks failed or an unexpected error occurred

SSHD_CONFIG="/etc/ssh/sshd_config"
REMOTE_HOST=""
REMOTE_USER=""
REMOTE_PORT="22"
REMOTE_KEY=""
MFA_REQUIRED="unknown"
EXPECTED_KEYS_FILE=""
SHOW_HELP=false

usage() {
  cat <<'EOF'
CentOS 7 SSH hardening readiness checker

Options:
  --sshd-config PATH      Local sshd_config to inspect (default: /etc/ssh/sshd_config)
  --host HOST             Remote host to test with SSH (optional)
  --user USER             Remote username for SSH test (optional)
  --port PORT             SSH port for remote test (default: 22)
  --key PATH              Private key path for remote test (optional)
  --mfa-required VALUE    Expected MFA status: yes|no|unknown (default: unknown)
  --expected-keys FILE    File containing expected authorized key fingerprints or comments (optional)
  -h, --help              Show this help

Notes:
  - No configuration changes are made.
  - If --host is supplied, the script performs a non-interactive SSH reachability/authentication check.
  - If --expected-keys is supplied, the script checks that the file exists and is readable.
EOF
}

log() {
  printf '[%s] %s\n' "$1" "$2"
}

failures=0
check() {
  local status="$1"
  local message="$2"
  if [[ "$status" == "pass" ]]; then
    log "PASS" "$message"
  else
    log "FAIL" "$message"
    failures=$((failures + 1))
  fi
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    --sshd-config)
      SSHD_CONFIG="${2:-}"
      shift 2
      ;;
    --host)
      REMOTE_HOST="${2:-}"
      shift 2
      ;;
    --user)
      REMOTE_USER="${2:-}"
      shift 2
      ;;
    --port)
      REMOTE_PORT="${2:-}"
      shift 2
      ;;
    --key)
      REMOTE_KEY="${2:-}"
      shift 2
      ;;
    --mfa-required)
      MFA_REQUIRED="${2:-unknown}"
      shift 2
      ;;
    --expected-keys)
      EXPECTED_KEYS_FILE="${2:-}"
      shift 2
      ;;
    -h|--help)
      SHOW_HELP=true
      shift
      ;;
    *)
      log "ERROR" "Unknown argument: $1"
      usage
      exit 1
      ;;
  esac
done

if [[ "$SHOW_HELP" == true ]]; then
  usage
  exit 0
fi

log "INFO" "Starting SSH hardening readiness checks"

# 1) Local sshd_config sanity checks
if [[ -r "$SSHD_CONFIG" ]]; then
  if grep -Eq '^[[:space:]]*PasswordAuthentication[[:space:]]+no([[:space:]]|$)' "$SSHD_CONFIG"; then
    check pass "PasswordAuthentication is set to no in $SSHD_CONFIG"
  else
    check fail "PasswordAuthentication is not explicitly set to no in $SSHD_CONFIG"
  fi

  if grep -Eq '^[[:space:]]*PubkeyAuthentication[[:space:]]+yes([[:space:]]|$)' "$SSHD_CONFIG"; then
    check pass "PubkeyAuthentication is enabled in $SSHD_CONFIG"
  else
    check fail "PubkeyAuthentication is not explicitly set to yes in $SSHD_CONFIG"
  fi

  if grep -Eq '^[[:space:]]*PermitRootLogin[[:space:]]+no([[:space:]]|$)' "$SSHD_CONFIG"; then
    check pass "PermitRootLogin is restricted in $SSHD_CONFIG"
  else
    log "WARN" "PermitRootLogin is not explicitly set to no in $SSHD_CONFIG"
  fi
else
  check fail "Cannot read sshd config file: $SSHD_CONFIG"
fi

# 2) Expected key inventory file, if provided
if [[ -n "$EXPECTED_KEYS_FILE" ]]; then
  if [[ -r "$EXPECTED_KEYS_FILE" ]]; then
    check pass "Expected key inventory file is readable: $EXPECTED_KEYS_FILE"
  else
    check fail "Expected key inventory file is missing or unreadable: $EXPECTED_KEYS_FILE"
  fi
fi

# 3) MFA expectation note
case "$MFA_REQUIRED" in
  yes)
    log "PASS" "MFA is expected for the target access path; verify that your PAM/identity provider flow is working"
    ;;
  no)
    log "WARN" "MFA is not expected for this path; confirm this is intentional and documented"
    ;;
  unknown)
    log "INFO" "MFA requirement not specified; no validation performed"
    ;;
  *)
    check fail "Invalid value for --mfa-required: $MFA_REQUIRED (use yes|no|unknown)"
    ;;
esac

# 4) Optional remote SSH test
if [[ -n "$REMOTE_HOST" || -n "$REMOTE_USER" || -n "$REMOTE_KEY" ]]; then
  if [[ -z "$REMOTE_HOST" || -z "$REMOTE_USER" ]]; then
    check fail "To run a remote SSH test, provide both --host and --user"
  else
    SSH_CMD=(ssh -o BatchMode=yes -o ConnectTimeout=10 -p "$REMOTE_PORT")
    if [[ -n "$REMOTE_KEY" ]]; then
      if [[ -r "$REMOTE_KEY" ]]; then
        SSH_CMD+=(-i "$REMOTE_KEY")
        log "INFO" "Using provided key for remote test"
      else
        check fail "Private key is missing or unreadable: $REMOTE_KEY"
      fi
    fi
    SSH_CMD+=("${REMOTE_USER}@${REMOTE_HOST}" "true")

    if [[ $failures -eq 0 ]]; then
      if "${SSH_CMD[@]}" >/dev/null 2>&1; then
        check pass "Non-interactive SSH authentication succeeded for ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PORT}"
      else
        check fail "Non-interactive SSH authentication failed for ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PORT}"
      fi
    fi
  fi
fi

# 5) Report summary
if [[ $failures -eq 0 ]]; then
  log "INFO" "All completed checks passed"
  exit 0
else
  log "INFO" "Completed with $failures failure(s); review before disabling password authentication"
  exit 1
fi