#!/bin/bash

# 全宇宙最强SSH加固脚本
# 功能：检测操作系统、依赖项，加固SSH服务器，下载公钥并输出详细日志

# 日志文件
LOG_FILE="/var/log/ssh_hardening_$(date +%Y%m%d_%H%M%S).log"

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m' # No Color

# 日志函数
log() {
    local level="$1"
    local message="$2"
    local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
    
    echo -e "[$timestamp] [$level] $message" | tee -a "$LOG_FILE"
}

# 错误处理函数
handle_error() {
    local step="$1"
    local message="$2"
    log "ERROR" "步骤 $step 失败: $message"
    echo -e "${RED}错误: $message${NC}"
    exit 1
}

# 检测命令是否存在（通过文件系统查找）
check_command_file() {
    local cmd_path=$(command -v "$1")
    if [ -z "$cmd_path" ] || [ ! -x "$cmd_path" ]; then
        return 1
    fi
    log "INFO" "找到 $1 命令: $cmd_path"
    return 0
}

# 获取完整操作系统名称
get_full_os_name() {
    case "$OS" in
        "rhel")
            echo "Red Hat Enterprise Linux $OS_VERSION"
            ;;
        "centos")
            echo "CentOS Linux $OS_VERSION"
            ;;
        "fedora")
            echo "Fedora Linux $OS_VERSION"
            ;;
        "almalinux")
            echo "AlmaLinux $OS_VERSION"
            ;;
        "rocky")
            echo "Rocky Linux $OS_VERSION"
            ;;
        "debian")
            echo "Debian GNU/Linux $OS_VERSION"
            ;;
        "ubuntu")
            echo "Ubuntu $OS_VERSION"
            ;;
        "linuxmint")
            echo "Linux Mint $OS_VERSION"
            ;;
        "arch")
            echo "Arch Linux"
            ;;
        "manjaro")
            echo "Manjaro Linux"
            ;;
        "alpine")
            echo "Alpine Linux $OS_VERSION"
            ;;
        "gentoo")
            echo "Gentoo Linux"
            ;;
        "sles")
            echo "SUSE Linux Enterprise Server $OS_VERSION"
            ;;
        "opensuse-leap")
            echo "openSUSE Leap $OS_VERSION"
            ;;
        "amazon")
            echo "Amazon Linux $OS_VERSION"
            ;;
        *)
            echo "$OS $OS_VERSION"
            ;;
    esac
}

# 检测操作系统
detect_os() {
    log "INFO" "开始检测操作系统..."
    
    if [ -f "/etc/os-release" ]; then
        . /etc/os-release
        OS="$ID"
        OS_VERSION="$VERSION_ID"
        
        # 特殊处理
        if [ "$OS" = "ol" ]; then
            OS="oracle"
        elif [ "$OS" = "amzn" ]; then
            OS="amazon"
        fi
        
        OS_FULL=$(get_full_os_name)
    elif [ -f "/etc/redhat-release" ]; then
        OS="rhel"
        OS_VERSION=$(cat /etc/redhat-release | grep -oE '[0-9]+' | head -1)
        OS_FULL="Red Hat Enterprise Linux $OS_VERSION"
    elif [ -f "/etc/debian_version" ]; then
        OS="debian"
        OS_VERSION=$(cat /etc/debian_version)
        OS_FULL="Debian GNU/Linux $OS_VERSION"
    elif [ -f "/etc/arch-release" ]; then
        OS="arch"
        OS_FULL="Arch Linux"
    elif [ -f "/etc/alpine-release" ]; then
        OS="alpine"
        OS_VERSION=$(cat /etc/alpine-release)
        OS_FULL="Alpine Linux $OS_VERSION"
    else
        handle_error "1" "无法检测操作系统类型"
    fi
    
    log "INFO" "检测到操作系统: $OS_FULL"
    echo -e "${GREEN}检测到操作系统: $OS_FULL${NC}"
    
    # 确定服务管理系统类型
    if [ "$OS" = "alpine" ]; then
        SERVICE_MANAGER="openrc"
    elif [ -f "/run/systemd/system" ]; then
        SERVICE_MANAGER="systemd"
    elif command -v service &>/dev/null; then
        SERVICE_MANAGER="sysvinit"
    else
        handle_error "1" "无法确定服务管理系统类型"
    fi
    
    log "INFO" "服务管理系统: $SERVICE_MANAGER"
}

# 安装依赖
install_dependencies() {
    log "INFO" "开始安装依赖..."
    
    # 检测并安装curl
    if ! check_command_file "curl"; then
        log "INFO" "未找到curl命令，准备安装..."
        
        case "$OS" in
            "debian" | "ubuntu" | "linuxmint" | "oracle")
                apt update || handle_error "2" "更新包索引失败"
                apt install -y curl || handle_error "2" "安装curl失败"
                ;;
            "rhel" | "centos" | "fedora" | "almalinux" | "rocky" | "amazon")
                dnf install -y curl || handle_error "2" "安装curl失败"
                ;;
            "arch" | "manjaro")
                pacman -Syu --noconfirm curl || handle_error "2" "安装curl失败"
                ;;
            "alpine")
                apk update || handle_error "2" "更新包索引失败"
                apk add curl || handle_error "2" "安装curl失败"
                ;;
            "gentoo")
                emerge -q net-misc/curl || handle_error "2" "安装curl失败"
                ;;
            "sles" | "opensuse-leap")
                zypper -n install curl || handle_error "2" "安装curl失败"
                ;;
            *)
                handle_error "2" "不支持的操作系统: $OS，无法安装curl"
                ;;
        esac
        
        log "INFO" "curl安装成功"
    else
        log "INFO" "curl已安装"
    fi
    
    # 检测并安装OpenSSH服务器
    SSHD_CONFIG="/etc/ssh/sshd_config"
    if [ ! -f "$SSHD_CONFIG" ]; then
        log "INFO" "未找到sshd_config文件，开始安装OpenSSH服务器..."
        
        case "$OS" in
            "debian" | "ubuntu" | "linuxmint" | "oracle")
                apt update || handle_error "2" "更新包索引失败"
                apt install -y openssh-server || handle_error "2" "安装OpenSSH服务器失败"
                ;;
            "rhel" | "centos" | "fedora" | "almalinux" | "rocky" | "amazon")
                dnf install -y openssh-server || handle_error "2" "安装OpenSSH服务器失败"
                ;;
            "arch" | "manjaro")
                pacman -Syu --noconfirm openssh || handle_error "2" "安装OpenSSH服务器失败"
                ;;
            "alpine")
                apk update || handle_error "2" "更新包索引失败"
                apk add openssh-server || handle_error "2" "安装OpenSSH服务器失败"
                
                # 在Alpine上创建默认配置
                if [ ! -f "$SSHD_CONFIG" ]; then
                    cp /etc/ssh/sshd_config.sample "$SSHD_CONFIG" || handle_error "2" "创建SSH配置文件失败"
                fi
                ;;
            "gentoo")
                emerge -q net-misc/openssh || handle_error "2" "安装OpenSSH服务器失败"
                ;;
            "sles" | "opensuse-leap")
                zypper -n install openssh || handle_error "2" "安装OpenSSH服务器失败"
                ;;
            *)
                handle_error "2" "不支持的操作系统: $OS"
                ;;
        esac
        
        # 再次检查配置文件是否存在
        if [ ! -f "$SSHD_CONFIG" ]; then
            handle_error "2" "安装OpenSSH服务器后仍未找到sshd_config文件"
        fi
        
        log "INFO" "OpenSSH服务器安装成功"
    else
        log "INFO" "找到sshd_config文件，OpenSSH服务器已安装"
    fi
    
    log "INFO" "依赖检查完成"
    echo -e "${GREEN}依赖检查完成${NC}"
}

# 备份SSH配置
backup_ssh_config() {
    local config_file="/etc/ssh/sshd_config"
    local backup_file="${config_file}.bak_$(date +%Y%m%d_%H%M%S)"
    
    log "INFO" "备份SSH配置文件到: $backup_file"
    
    if [ -f "$config_file" ]; then
        cp "$config_file" "$backup_file" || handle_error "3" "备份SSH配置文件失败"
        log "INFO" "SSH配置文件备份成功"
    else
        handle_error "3" "SSH配置文件不存在: $config_file"
    fi
}

# 下载公钥
download_public_key() {
    local key_url="https://xiwaer.com/onekey/cnfug.keys"
    local authorized_keys="/root/.ssh/authorized_keys"
    local ssh_dir="/root/.ssh"
    
    log "INFO" "开始下载公钥文件: $key_url"
    
    # 创建.ssh目录（如果不存在）
    if [ ! -d "$ssh_dir" ]; then
        mkdir -p "$ssh_dir"
        chmod 700 "$ssh_dir"
        chown root:root "$ssh_dir"
        log "INFO" "创建.ssh目录，权限设置为700"
    else
        # 确保.ssh目录权限正确
        if [ "$(stat -c %a "$ssh_dir")" != "700" ]; then
            chmod 700 "$ssh_dir"
            log "INFO" "修正.ssh目录权限为700"
        fi
        log "INFO" ".ssh目录已存在，权限正确"
    fi
    
    # 下载公钥
    if ! curl -sSL -o "$authorized_keys" "$key_url"; then
        handle_error "4" "下载公钥文件失败，可能是网络问题或URL无效"
    fi
    
    # 确保公钥文件权限正确
    chmod 600 "$authorized_keys"
    chown root:root "$authorized_keys"
    log "INFO" "设置authorized_keys权限为600"
    
    # 显示公钥文件信息
    log "INFO" "公钥文件内容:"
    cat "$authorized_keys" | while read -r line; do
        log "INFO" "  $line"
    done
    
    log "INFO" "公钥文件下载并配置成功"
    echo -e "${GREEN}公钥文件下载并配置成功${NC}"
}

# 加固SSH配置
harden_ssh_config() {
    local config_file="/etc/ssh/sshd_config"
    
    log "INFO" "开始加固SSH配置..."
    
    # 禁用root登录
    if [ "$OS" != "alpine" ]; then
        sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin no/' "$config_file"
        log "INFO" "已禁用root直接登录"
    else
        # 在Alpine上保持PermitRootLogin配置不变，因为Alpine默认需要它
        log "INFO" "在Alpine上保留PermitRootLogin配置"
    fi
    
    # 禁用密码认证
    sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' "$config_file"
    log "INFO" "已禁用密码认证，仅允许密钥认证"
    
    # 禁用空密码
    sed -i 's/^#\?PermitEmptyPasswords.*/PermitEmptyPasswords no/' "$config_file"
    log "INFO" "已禁用空密码"
    
    # 禁用X11转发
    sed -i 's/^#\?X11Forwarding.*/X11Forwarding no/' "$config_file"
    log "INFO" "已禁用X11转发"
    
    # 限制登录尝试次数
    # sed -i 's/^#\?MaxAuthTries.*/MaxAuthTries 3/' "$config_file"
    # log "INFO" "已将最大认证尝试次数设置为3次"
    
    # 设置空闲超时
   # sed -i 's/^#\?ClientAliveInterval.*/ClientAliveInterval 300/' "$config_file"
   # sed -i 's/^#\?ClientAliveCountMax.*/ClientAliveCountMax 0/' "$config_file"
   # log "INFO" "已设置客户端存活间隔为300秒"
   # log "INFO" "已设置空闲超时为0（禁用）"
    
    # 禁用SSH版本1
    #sed -i 's/^#\?Protocol.*/Protocol 2/' "$config_file"
    #log "INFO" "已禁用SSH版本1，仅使用SSH版本2"
    
    # 禁用SSH代理转发
    #sed -i 's/^#\?AllowAgentForwarding.*/AllowAgentForwarding no/' "$config_file"
    #log "INFO" "已禁用SSH代理转发"
    
    # 禁用TCP转发
   # sed -i 's/^#\?AllowTcpForwarding.*/AllowTcpForwarding no/' "$config_file"
  #  log "INFO" "已禁用TCP转发"
    
    
    # 确保启用了公钥认证
    if ! grep -q "^PubkeyAuthentication" "$config_file"; then
        echo "PubkeyAuthentication yes" >> "$config_file"
        log "INFO" "已启用公钥认证"
    else
        sed -i 's/^#\?PubkeyAuthentication.*/PubkeyAuthentication yes/' "$config_file"
        log "INFO" "已确保公钥认证已启用"
    fi
    
    # 指定公钥文件位置
    if ! grep -q "^AuthorizedKeysFile" "$config_file"; then
        echo "AuthorizedKeysFile .ssh/authorized_keys" >> "$config_file"
        log "INFO" "已设置公钥文件位置"
    fi
    
    log "INFO" "SSH配置加固完成"
    echo -e "${GREEN}SSH配置加固完成${NC}"
}

# 管理服务函数
manage_service() {
    local action="$1"
    local service_name="sshd"
    
    case "$SERVICE_MANAGER" in
        "systemd")
            log "INFO" "使用systemctl $action $service_name"
            systemctl "$action" "$service_name" || return 1
            ;;
        "openrc")
            log "INFO" "使用rc-service $action $service_name"
            rc-service "$service_name" "$action" || return 1
            
            # 对于enable操作，使用rc-update
            if [ "$action" = "enable" ]; then
                log "INFO" "使用rc-update add $service_name"
                rc-update add "$service_name" || return 1
            fi
            ;;
        "sysvinit")
            log "INFO" "使用service $service_name $action"
            service "$service_name" "$action" || return 1
            
            # 对于enable操作，尝试使用chkconfig或update-rc.d
            if [ "$action" = "enable" ]; then
                if command -v chkconfig &>/dev/null; then
                    log "INFO" "使用chkconfig启用$service_name"
                    chkconfig "$service_name" on || return 1
                elif command -v update-rc.d &>/dev/null; then
                    log "INFO" "使用update-rc.d启用$service_name"
                    update-rc.d "$service_name" enable || return 1
                else
                    log "WARNING" "无法确定如何启用$service_name服务，可能需要手动配置"
                fi
            fi
            ;;
        *)
            log "ERROR" "不支持的服务管理系统: $SERVICE_MANAGER"
            return 1
            ;;
    esac
    
    return 0
}

# 重启SSH服务
restart_ssh_service() {
    log "INFO" "准备重启SSH服务..."
    
    # 停止服务
    if ! manage_service "stop"; then
        handle_error "6" "停止SSH服务失败"
    fi
    
    # 启动服务
    if ! manage_service "start"; then
        handle_error "6" "启动SSH服务失败"
    fi
    
    # 启用服务开机自启
    if ! manage_service "enable"; then
        log "WARNING" "启用SSH服务开机自启失败，可能需要手动配置"
    else
        log "INFO" "SSH服务已设置为开机自启"
    fi
    
    # 检查服务状态
    log "INFO" "检查SSH服务状态..."
    if [ "$SERVICE_MANAGER" = "systemd" ]; then
        if ! systemctl is-active --quiet sshd; then
            handle_error "6" "SSH服务未成功启动"
        fi
        log "INFO" "SSH服务状态: $(systemctl is-active sshd)"
    elif [ "$SERVICE_MANAGER" = "openrc" ]; then
        if ! rc-service sshd status | grep -q "started"; then
            handle_error "6" "SSH服务未成功启动"
        fi
        log "INFO" "SSH服务状态: running"
    else
        log "INFO" "无法自动验证服务状态，请手动检查"
    fi
    
    log "INFO" "SSH服务重启完成"
    echo -e "${GREEN}SSH服务重启完成${NC}"
    
    # 显示SSH配置验证信息
    log "INFO" "验证SSH配置:"
    if command -v sshd &>/dev/null; then
        sshd -t 2>&1 | while read -r line; do
            log "INFO" "  $line"
        done
        if [ $? -eq 0 ]; then
            log "INFO" "SSH配置验证通过"
        else
            log "WARNING" "SSH配置验证失败，可能存在配置错误"
        fi
    else
        log "WARNING" "无法验证SSH配置，未找到sshd命令"
    fi
}

# 显示最终配置
display_final_config() {
    local config_file="/etc/ssh/sshd_config"
    log "INFO" "显示最终SSH配置..."
    
    echo -e "${GREEN}\n===== 最终SSH配置 (/etc/ssh/sshd_config) ====${NC}"
    
    # 显示配置文件内容并添加中文注释
    cat "$config_file" | grep -v '^#\|^$' | while read -r line; do
        case "$line" in
            "PermitRootLogin no")
                echo -e "${GREEN}$line${NC}    # 禁用root用户直接登录"
                ;;
            "PermitRootLogin yes")
                echo -e "${YELLOW}$line${NC}    # 在Alpine上允许root登录"
                ;;
            "PasswordAuthentication no")
                echo -e "${GREEN}$line${NC}    # 禁用密码认证，仅使用密钥认证"
                ;;
            "PermitEmptyPasswords no")
                echo -e "${GREEN}$line${NC}    # 禁用空密码"
                ;;
            "X11Forwarding no")
                echo -e "${GREEN}$line${NC}    # 禁用X11转发"
                ;;
            "PubkeyAuthentication yes")
                echo -e "${GREEN}$line${NC}    # 启用公钥认证"
                ;;
            "AuthorizedKeysFile .ssh/authorized_keys")
                echo -e "${GREEN}$line${NC}    # 指定公钥文件位置"
                ;;
            *)
                echo -e "${GREEN}$line${NC}"
                ;;
        esac
    done
    
    echo -e "${GREEN}=========================================${NC}"
    
    # 显示关键目录和文件权限
    echo -e "${GREEN}\n===== SSH相关目录和文件权限 ====${NC}"
    echo -e "${GREEN}/root/.ssh 目录权限: $(stat -c %a /root/.ssh)${NC}"
    if [ -f "/root/.ssh/authorized_keys" ]; then
        echo -e "${GREEN}/root/.ssh/authorized_keys 文件权限: $(stat -c %a /root/.ssh/authorized_keys)${NC}"
    else
        echo -e "${RED}/root/.ssh/authorized_keys 文件不存在${NC}"
    fi
    echo -e "${GREEN}/etc/ssh/sshd_config 文件权限: $(stat -c %a /etc/ssh/sshd_config)${NC}"
    echo -e "${GREEN}=========================================${NC}"
}

# 主函数
main() {
    echo -e "${GREEN}===== SSH加固脚本开始执行 ====${NC}"
    log "INFO" "===== SSH加固脚本开始执行 ===="
    
    # 检查是否为root用户
    if [ "$(id -u)" -ne 0 ]; then
        handle_error "0" "请使用root用户运行此脚本"
    fi
    
    # 步骤1: 检测操作系统
    detect_os
    
    # 步骤2: 安装依赖
    install_dependencies
    
    # 步骤3: 备份SSH配置
    backup_ssh_config
    
    # 步骤4: 下载公钥
    download_public_key
    
    # 步骤5: 加固SSH配置
    harden_ssh_config
    
    # 步骤6: 重启SSH服务
    restart_ssh_service
    
    # 步骤7: 显示最终配置
    display_final_config
    
    log "INFO" "===== SSH加固脚本执行完成 ===="
    echo -e "${GREEN}===== SSH加固脚本执行完成 ====${NC}"
    echo -e "${GREEN}详细日志已保存到: $LOG_FILE${NC}"
}

# 执行主函数
main    