#!/bin/bash
# ============================================================
# SSL 인증서 점검 스크립트 (LH 스마트홈 플랫폼용)
# 용도: 홈넷서버 SSL 인증서 상태 종합 점검
# ============================================================

if [ "$#" -lt 3 ]; then
  echo "❗ 사용법: $0 <대상 IP 또는 도메인> <포트> <인증서 경로> [프록시 host:port]"
  echo "예시: $0 [IP]/homenet.hub 8094 /smahshare/svr_cert/ca_cert/lh-smah-ca-2024-cert.pem [IP]:13128"
  exit 1
fi

TARGET_HOST="$1"
TARGET_PORT="$2"
CERT_PATH="$3"
PROXY="${4:-}"  # 프록시 (선택사항)
LOG_FILE="ssl_check_$(date '+%Y%m%d_%H%M%S').log"
WARN_DAYS=30  # 만료 N일 전부터 경고

# --- 인증서 파일 존재 여부 확인 ---
if [ ! -f "$CERT_PATH" ]; then
  echo "❌ 인증서 파일이 존재하지 않습니다: $CERT_PATH"
  exit 1
fi

# --- 컨텍스트 패스 분리 ---
if [[ "$TARGET_HOST" == *"/"* ]]; then
  HOST_ONLY="${TARGET_HOST%%/*}"
  CONTEXT_PATH="/${TARGET_HOST#*/}"
else
  HOST_ONLY="$TARGET_HOST"
  CONTEXT_PATH=""
fi

TARGET_URL="https://${HOST_ONLY}:${TARGET_PORT}${CONTEXT_PATH}"

echo "============================================================" | tee -a "$LOG_FILE"
echo "  SSL 인증서 점검 리포트" | tee -a "$LOG_FILE"
echo "  점검일시: $(date '+%Y-%m-%d %H:%M:%S')" | tee -a "$LOG_FILE"
echo "  대상: ${HOST_ONLY}:${TARGET_PORT}" | tee -a "$LOG_FILE"
if [ -n "$PROXY" ]; then
  echo "  프록시: ${PROXY}" | tee -a "$LOG_FILE"
fi
echo "============================================================" | tee -a "$LOG_FILE"
echo "" | tee -a "$LOG_FILE"

# ============================================================
# [0] 네트워크 도달 가능 여부 확인
# ============================================================
echo "🔍 [0] 네트워크 연결 확인" | tee -a "$LOG_FILE"
echo "---------------------------------------------" | tee -a "$LOG_FILE"

if [ -n "$PROXY" ]; then
  echo "📡 프록시 경유 모드: ${PROXY}" | tee -a "$LOG_FILE"
  # 프록시 경유 시 curl로 연결 확인
  curl -sk --proxy "http://${PROXY}" --connect-timeout 5 "https://${HOST_ONLY}:${TARGET_PORT}/" -o /dev/null 2>&1
  NC_RESULT=$?
  if [ "$NC_RESULT" -eq 0 ] || [ "$NC_RESULT" -eq 60 ] || [ "$NC_RESULT" -eq 51 ]; then
    # 0=성공, 60=SSL검증실패(연결은 됨), 51=피어인증실패(연결은 됨)
    echo "✅ 프록시 경유 ${HOST_ONLY}:${TARGET_PORT} 연결 가능" | tee -a "$LOG_FILE"
    NC_RESULT=0
  else
    echo "❌ 프록시 경유 ${HOST_ONLY}:${TARGET_PORT} 연결 불가 (curl 코드: $NC_RESULT)" | tee -a "$LOG_FILE"
  fi
else
  if command -v nc &> /dev/null; then
    nc -zv -w 5 "$HOST_ONLY" "$TARGET_PORT" 2>&1 | tee -a "$LOG_FILE"
    NC_RESULT=$?
  elif command -v timeout &> /dev/null; then
    timeout 5 bash -c "echo > /dev/tcp/$HOST_ONLY/$TARGET_PORT" 2>&1
    NC_RESULT=$?
  else
    echo "⚠️ nc 명령어 없음, 네트워크 사전 확인 생략" | tee -a "$LOG_FILE"
    NC_RESULT=0
  fi

  if [ "$NC_RESULT" -ne 0 ]; then
    echo "❌ ${HOST_ONLY}:${TARGET_PORT} 포트 연결 불가 — 네트워크/방화벽 문제 확인 필요" | tee -a "$LOG_FILE"
    echo "   💡 프록시 경유가 필요할 수 있음: $0 $1 $2 $3 [IP]:13128" | tee -a "$LOG_FILE"
    echo "📝 상세 로그 파일: $(realpath "$LOG_FILE")"
    exit 1
  else
    echo "✅ ${HOST_ONLY}:${TARGET_PORT} 포트 연결 가능" | tee -a "$LOG_FILE"
  fi
fi
echo "" | tee -a "$LOG_FILE"

# ============================================================
# [1] 로컬 CA 인증서 정보 확인 (만료일 포함)
# ============================================================
echo "🔍 [1] 로컬 CA 인증서 정보 확인" | tee -a "$LOG_FILE"
echo "---------------------------------------------" | tee -a "$LOG_FILE"

echo "📄 인증서 파일: $CERT_PATH" | tee -a "$LOG_FILE"

# Subject / Issuer
openssl x509 -in "$CERT_PATH" -noout -subject -issuer 2>&1 | tee -a "$LOG_FILE"

# 만료일 확인
CERT_DATES=$(openssl x509 -in "$CERT_PATH" -noout -dates 2>&1)
echo "$CERT_DATES" | tee -a "$LOG_FILE"

# 만료일 D-day 계산
NOT_AFTER=$(echo "$CERT_DATES" | grep "notAfter" | cut -d= -f2)
if [ -n "$NOT_AFTER" ]; then
  EXPIRY_EPOCH=$(date -d "$NOT_AFTER" +%s 2>/dev/null)
  NOW_EPOCH=$(date +%s)
  if [ -n "$EXPIRY_EPOCH" ]; then
    DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
    if [ "$DAYS_LEFT" -lt 0 ]; then
      echo "🔴 로컬 CA 인증서 만료됨! (${DAYS_LEFT#-}일 전 만료)" | tee -a "$LOG_FILE"
    elif [ "$DAYS_LEFT" -lt "$WARN_DAYS" ]; then
      echo "🟡 로컬 CA 인증서 만료 임박! (${DAYS_LEFT}일 남음)" | tee -a "$LOG_FILE"
    else
      echo "🟢 로컬 CA 인증서 유효 (${DAYS_LEFT}일 남음)" | tee -a "$LOG_FILE"
    fi
  fi
fi
echo "" | tee -a "$LOG_FILE"

# ============================================================
# [2] SAN 검증
# ============================================================
echo "🔍 [2] 인증서 SAN에서 대상 서버 정보 확인" | tee -a "$LOG_FILE"
echo "---------------------------------------------" | tee -a "$LOG_FILE"

SAN=$(openssl x509 -in "$CERT_PATH" -noout -text 2>/dev/null | grep -A1 "Subject Alternative Name" | tail -n1 | sed 's/ *, */\n/g' | grep -i -E "DNS|IP")

if [ -z "$SAN" ]; then
  echo "⚠️ SAN 정보가 없습니다. CN(Common Name) 기반 인증서일 수 있음" | tee -a "$LOG_FILE"
  CN=$(openssl x509 -in "$CERT_PATH" -noout -subject 2>/dev/null | grep -oP 'CN\s*=\s*\K.*')
  echo "📄 CN: $CN" | tee -a "$LOG_FILE"
else
  echo "📄 인증서 SAN 목록:" | tee -a "$LOG_FILE"
  echo "$SAN" | tee -a "$LOG_FILE"
  if echo "$SAN" | grep -q "$HOST_ONLY"; then
    echo "✅ SAN에 대상 서버 정보(${HOST_ONLY})가 포함되어 있습니다." | tee -a "$LOG_FILE"
  else
    echo "❌ SAN에 대상 서버 정보(${HOST_ONLY})가 포함되어 있지 않습니다. 확인 필요." | tee -a "$LOG_FILE"
  fi
fi
echo "" | tee -a "$LOG_FILE"

# ============================================================
# [3] 서버측 인증서 직접 확인 (openssl s_client)
# ============================================================
echo "🔍 [3] 서버측 인증서 원격 확인 (openssl s_client)" | tee -a "$LOG_FILE"
echo "---------------------------------------------" | tee -a "$LOG_FILE"

OPENSSL_PROXY_OPT=""
if [ -n "$PROXY" ]; then
  OPENSSL_PROXY_OPT="-proxy $PROXY"
  echo "📡 프록시 경유: ${PROXY}" | tee -a "$LOG_FILE"
fi

SERVER_CERT_INFO=$(echo | timeout 10 openssl s_client -connect "${HOST_ONLY}:${TARGET_PORT}" $OPENSSL_PROXY_OPT -servername "$HOST_ONLY" 2>/dev/null)

if echo "$SERVER_CERT_INFO" | grep -q "BEGIN CERTIFICATE"; then
  echo "✅ 서버 TLS 연결 성공" | tee -a "$LOG_FILE"

  # 서버 인증서 만료일
  SERVER_DATES=$(echo "$SERVER_CERT_INFO" | openssl x509 -noout -dates 2>/dev/null)
  echo "$SERVER_DATES" | tee -a "$LOG_FILE"

  SERVER_NOT_AFTER=$(echo "$SERVER_DATES" | grep "notAfter" | cut -d= -f2)
  if [ -n "$SERVER_NOT_AFTER" ]; then
    SVR_EXPIRY_EPOCH=$(date -d "$SERVER_NOT_AFTER" +%s 2>/dev/null)
    if [ -n "$SVR_EXPIRY_EPOCH" ]; then
      SVR_DAYS_LEFT=$(( (SVR_EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
      if [ "$SVR_DAYS_LEFT" -lt 0 ]; then
        echo "🔴 서버 인증서 만료됨! (${SVR_DAYS_LEFT#-}일 전 만료) ← 인증서 재발급 필요!" | tee -a "$LOG_FILE"
      elif [ "$SVR_DAYS_LEFT" -lt "$WARN_DAYS" ]; then
        echo "🟡 서버 인증서 만료 임박! (${SVR_DAYS_LEFT}일 남음)" | tee -a "$LOG_FILE"
      else
        echo "🟢 서버 인증서 유효 (${SVR_DAYS_LEFT}일 남음)" | tee -a "$LOG_FILE"
      fi
    fi
  fi

  # 서버 인증서 Subject/Issuer
  echo "" | tee -a "$LOG_FILE"
  echo "📄 서버 인증서 상세:" | tee -a "$LOG_FILE"
  echo "$SERVER_CERT_INFO" | openssl x509 -noout -subject -issuer 2>/dev/null | tee -a "$LOG_FILE"

  # 인증서 체인 검증
  echo "" | tee -a "$LOG_FILE"
  VERIFY_RESULT=$(echo | timeout 10 openssl s_client -connect "${HOST_ONLY}:${TARGET_PORT}" $OPENSSL_PROXY_OPT -CAfile "$CERT_PATH" 2>&1 | grep "Verify return code")
  echo "🔗 체인 검증: $VERIFY_RESULT" | tee -a "$LOG_FILE"

else
  echo "❌ 서버 TLS 연결 실패 — TLS를 사용하지 않는 포트이거나 방화벽 차단" | tee -a "$LOG_FILE"
  echo "   💡 대안: 해당 서버에 직접 접속하여 인증서 파일 확인 필요" | tee -a "$LOG_FILE"
fi
echo "" | tee -a "$LOG_FILE"

# ============================================================
# [4] HTTPS 통신 테스트 (curl)
# ============================================================
echo "🔍 [4] HTTPS 통신 테스트" | tee -a "$LOG_FILE"
echo "---------------------------------------------" | tee -a "$LOG_FILE"

echo "🌐 curl 테스트: $TARGET_URL" | tee -a "$LOG_FILE"

CURL_PROXY_OPT=""
if [ -n "$PROXY" ]; then
  CURL_PROXY_OPT="--proxy http://${PROXY}"
fi

CURL_OUTPUT=$(curl --cacert "$CERT_PATH" $CURL_PROXY_OPT -v --connect-timeout 10 --max-time 30 "$TARGET_URL" -o /dev/null -w "\nHTTP_CODE:%{http_code}\nTIME_TOTAL:%{time_total}s\n" 2>&1)
CURL_RESULT=$?

echo "$CURL_OUTPUT" | tee -a "$LOG_FILE"

# curl 종료 코드별 원인 분류
echo "" | tee -a "$LOG_FILE"
case $CURL_RESULT in
  0)
    HTTP_CODE=$(echo "$CURL_OUTPUT" | grep "HTTP_CODE:" | cut -d: -f2)
    echo "✅ HTTPS 통신 성공 (HTTP ${HTTP_CODE})" | tee -a "$LOG_FILE"
    ;;
  6)
    echo "❌ DNS 해석 실패 — 도메인명 확인 필요" | tee -a "$LOG_FILE"
    ;;
  7)
    echo "❌ 연결 거부 — 서버 다운 또는 방화벽 차단" | tee -a "$LOG_FILE"
    ;;
  28)
    echo "❌ 연결 타임아웃 — 네트워크 경로 또는 방화벽 확인" | tee -a "$LOG_FILE"
    ;;
  35)
    echo "❌ SSL 연결 에러 — TLS 버전 불일치 또는 프로토콜 문제" | tee -a "$LOG_FILE"
    ;;
  51)
    echo "❌ SSL 피어 인증서 검증 실패 — 인증서 CN/SAN 불일치" | tee -a "$LOG_FILE"
    ;;
  58)
    echo "❌ 로컬 인증서 문제 — CA 인증서 파일 형식/경로 확인" | tee -a "$LOG_FILE"
    ;;
  60)
    echo "❌ SSL 인증서 검증 실패 — 인증서 만료 또는 CA 체인 불일치" | tee -a "$LOG_FILE"
    echo "   💡 이번 이슈(인증서 만료)와 동일한 증상입니다." | tee -a "$LOG_FILE"
    ;;
  *)
    echo "❌ HTTPS 통신 실패 (curl 종료 코드: $CURL_RESULT)" | tee -a "$LOG_FILE"
    ;;
esac

# ============================================================
# [5] 종합 결과
# ============================================================
echo "" | tee -a "$LOG_FILE"
echo "============================================================" | tee -a "$LOG_FILE"
echo "  📋 종합 결과 요약" | tee -a "$LOG_FILE"
echo "============================================================" | tee -a "$LOG_FILE"
echo "  대상 서버: ${HOST_ONLY}:${TARGET_PORT}" | tee -a "$LOG_FILE"
echo "  점검 URL:  ${TARGET_URL}" | tee -a "$LOG_FILE"
echo "  CA 인증서: ${CERT_PATH}" | tee -a "$LOG_FILE"

if [ -n "$DAYS_LEFT" ]; then
  if [ "$DAYS_LEFT" -lt 0 ]; then
    echo "  로컬 CA:   🔴 만료됨 (${DAYS_LEFT#-}일 전)" | tee -a "$LOG_FILE"
  elif [ "$DAYS_LEFT" -lt "$WARN_DAYS" ]; then
    echo "  로컬 CA:   🟡 ${DAYS_LEFT}일 남음" | tee -a "$LOG_FILE"
  else
    echo "  로컬 CA:   🟢 ${DAYS_LEFT}일 남음" | tee -a "$LOG_FILE"
  fi
fi

if [ -n "$SVR_DAYS_LEFT" ]; then
  if [ "$SVR_DAYS_LEFT" -lt 0 ]; then
    echo "  서버 인증서: 🔴 만료됨 (${SVR_DAYS_LEFT#-}일 전) ← 재발급 필요!" | tee -a "$LOG_FILE"
  elif [ "$SVR_DAYS_LEFT" -lt "$WARN_DAYS" ]; then
    echo "  서버 인증서: 🟡 ${SVR_DAYS_LEFT}일 남음" | tee -a "$LOG_FILE"
  else
    echo "  서버 인증서: 🟢 ${SVR_DAYS_LEFT}일 남음" | tee -a "$LOG_FILE"
  fi
fi

echo "  통신 결과: $([ $CURL_RESULT -eq 0 ] && echo '✅ 성공' || echo '❌ 실패')" | tee -a "$LOG_FILE"
echo "============================================================" | tee -a "$LOG_FILE"
echo "📝 상세 로그 파일: $(realpath "$LOG_FILE")" | tee -a "$LOG_FILE"
