#!/bin/bash
set -euo pipefail

# Proxmox Certificate Verification Script
# Verifies ACME certificate installation and validity on Proxmox nodes

# Color codes for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# Configuration
NODES=(
    "ml110-01.sankofa.nexus:192.168.11.10"
    "r630-01.sankofa.nexus:192.168.11.11"
)
PORT=8006
WARN_DAYS=30  # Warn if certificate expires in less than 30 days

log() {
    echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $*" >&2
}

info() {
    echo -e "${GREEN}✓${NC} $*"
}

warn() {
    echo -e "${YELLOW}⚠${NC} $*" >&2
}

error() {
    echo -e "${RED}✗${NC} $*" >&2
}

# Check if openssl is available
check_openssl() {
    if ! command -v openssl &> /dev/null; then
        error "openssl is not installed. Please install openssl to use this script."
        exit 1
    fi
}

# Get certificate expiration date
get_cert_expiry() {
    local hostname=$1
    local ip=$2
    local port=$3
    
    # Connect and extract expiration date
    local expiry=$(echo | openssl s_client -connect "${ip}:${port}" -servername "${hostname}" 2>/dev/null | \
                   openssl x509 -noout -enddate 2>/dev/null | \
                   cut -d= -f2)
    
    if [ -z "${expiry}" ]; then
        return 1
    fi
    
    echo "${expiry}"
}

# Calculate days until expiration
days_until_expiry() {
    local expiry_date=$1
    
    # Convert expiry date to epoch timestamp
    # Try Linux date format first, then macOS format
    local expiry_epoch
    if expiry_epoch=$(date -d "${expiry_date}" +%s 2>/dev/null); then
        :
    elif expiry_epoch=$(date -j -f "%b %d %H:%M:%S %Y %Z" "${expiry_date}" +%s 2>/dev/null); then
        :
    else
        return 1
    fi
    
    local now_epoch=$(date +%s)
    local diff=$((expiry_epoch - now_epoch))
    local days=$((diff / 86400))
    
    echo "${days}"
}

# Get certificate issuer
get_cert_issuer() {
    local hostname=$1
    local ip=$2
    local port=$3
    
    local issuer=$(echo | openssl s_client -connect "${ip}:${port}" -servername "${hostname}" 2>/dev/null | \
                   openssl x509 -noout -issuer 2>/dev/null | \
                   sed 's/issuer=//')
    
    echo "${issuer}"
}

# Get certificate subject
get_cert_subject() {
    local hostname=$1
    local ip=$2
    local port=$3
    
    local subject=$(echo | openssl s_client -connect "${ip}:${port}" -servername "${hostname}" 2>/dev/null | \
                    openssl x509 -noout -subject 2>/dev/null | \
                    sed 's/subject=//')
    
    echo "${subject}"
}

# Get certificate SANs (Subject Alternative Names)
get_cert_sans() {
    local hostname=$1
    local ip=$2
    local port=$3
    
    local sans=$(echo | openssl s_client -connect "${ip}:${port}" -servername "${hostname}" 2>/dev/null | \
                 openssl x509 -noout -text 2>/dev/null | \
                 grep -A1 "Subject Alternative Name" | \
                 sed 's/.*DNS://g' | \
                 tr ',' '\n' | \
                 sed 's/^ *//' | \
                 grep -v "^$")
    
    echo "${sans}"
}

# Test SSL connection
test_ssl_connection() {
    local hostname=$1
    local ip=$2
    local port=$3
    
    # Test connection and get certificate
    local output=$(echo | openssl s_client -connect "${ip}:${port}" -servername "${hostname}" 2>&1)
    
    if echo "${output}" | grep -q "Verify return code: 0"; then
        return 0
    else
        return 1
    fi
}

# Verify certificate for a node
verify_node_cert() {
    local node_info=$1
    local hostname=$(echo "${node_info}" | cut -d: -f1)
    local ip=$(echo "${node_info}" | cut -d: -f2)
    
    log "Checking certificate for ${hostname} (${ip}:${PORT})..."
    
    # Test SSL connection
    if ! test_ssl_connection "${hostname}" "${ip}" "${PORT}"; then
        error "SSL connection failed for ${hostname}"
        return 1
    fi
    
    info "SSL connection successful"
    
    # Get certificate details
    local issuer=$(get_cert_issuer "${hostname}" "${ip}" "${PORT}")
    local subject=$(get_cert_subject "${hostname}" "${ip}" "${PORT}")
    local expiry=$(get_cert_expiry "${hostname}" "${ip}" "${PORT}")
    
    if [ -z "${expiry}" ]; then
        error "Could not retrieve certificate expiration for ${hostname}"
        return 1
    fi
    
    # Check if certificate is from Let's Encrypt
    if echo "${issuer}" | grep -qi "lets encrypt\|let's encrypt\|letsencrypt"; then
        info "Certificate issuer: Let's Encrypt"
    else
        warn "Certificate issuer: ${issuer}"
        warn "Certificate may not be from Let's Encrypt"
    fi
    
    # Display certificate subject
    info "Certificate subject: ${subject}"
    
    # Check expiration
    local days=$(days_until_expiry "${expiry}")
    
    if [ "${days}" -lt 0 ]; then
        error "Certificate expired ${days#-} days ago!"
        error "Expiration date: ${expiry}"
        return 1
    elif [ "${days}" -lt "${WARN_DAYS}" ]; then
        warn "Certificate expires in ${days} days (${expiry})"
        warn "Renewal should occur automatically, but please monitor"
    else
        info "Certificate expires in ${days} days (${expiry})"
    fi
    
    # Get and display SANs
    local sans=$(get_cert_sans "${hostname}" "${ip}" "${PORT}")
    if [ -n "${sans}" ]; then
        info "Certificate SANs:"
        echo "${sans}" | while read -r san; do
            echo "    - ${san}"
        done
    fi
    
    # Verify hostname matches
    if echo "${subject}" | grep -qi "${hostname}" || echo "${sans}" | grep -qi "${hostname}"; then
        info "Hostname ${hostname} matches certificate"
    else
        warn "Hostname ${hostname} may not match certificate"
        warn "Subject: ${subject}"
    fi
    
    echo ""
    return 0
}

# Main verification function
main() {
    log "Proxmox Certificate Verification Script"
    log "========================================"
    echo ""
    
    check_openssl
    
    local total_nodes=${#NODES[@]}
    local passed=0
    local failed=0
    
    for node_info in "${NODES[@]}"; do
        if verify_node_cert "${node_info}"; then
            ((passed++))
        else
            ((failed++))
        fi
    done
    
    echo ""
    log "========================================"
    log "Verification Summary"
    log "========================================"
    info "Total nodes checked: ${total_nodes}"
    info "Passed: ${passed}"
    if [ "${failed}" -gt 0 ]; then
        error "Failed: ${failed}"
        exit 1
    else
        info "All certificates are valid"
        exit 0
    fi
}

# Run main function
main "$@"

