特别注意:

升级openssl及openssh有一定风险,建议在测试环境或UAT环境运行无误后再在生产环境运行。升级过程中请保持活动的连接窗口,切勿中途中断!为避免升级失败无法重连服务器,请复制一个连接窗口以备不时之需,或自行配置Telnet服务预留另一个远程连接通道。

最近更新:

2025-09-30 把openssl适配到3.5.3版本,openssh适配到9.9p2,虽然openssh官网已经有10.0p1和10.0p2版本,但是。。。但是他们居然把版本号打包错了,导致下载安装后版本号对不上!4月发布到现在过了快5个月了也没修复,真是无力吐槽。

2025-09-01 推翻原脚本,整个脚本全部重写,加入了回滚功能,但未测试。新版本建议安装zlib版本1.3以上,opnessl 3.0以上,openssh 9.0以上,放弃对centos/rhel 6版本的支持

2024-12-26 修复了几个因openssl 3.0版本带来的逻辑错误

2024-12-17 官方已停止支持openssl 1.1,切换至openssl 3.0

2024-10-09 增加了脚本运行锁,如非正常退出需手动删除锁文件

2024-10-09 修复了Rocky Linux 8系列升级重启后sshd服务没启动的BUG

2024-09-10 修复了openssl执行文件替换不彻底的BUG

2024-09-02 更新了对麒麟V10的支持,如有不同版本号的欢迎测试反馈

2024-07-23 修正了6系列pam.d/sshd文件复制错误的BUG

2024-07-18 修改了zlib版本判断逻辑

2024-07-11 临时处理了一下openEuler 22.03 SP3以上版本openssl版本回退的问题

2024-07-05 修复了一个zlib旧版本重复的bug

2024-07-03 修复了部分可能发生的错误

2024-07-02 更新链接到openssl 1.1.1w,openssh到9.8p1

已知问题:

  • openssh弃用rsa算法,因此xshell 5版本将因为"找不到匹配的host key算法"错误而无法连接
#!/bin/bash
########################## 蜈蚣出品 ##########################
# Function : zlib openssl openssh update                    #
# Platform : Centos7.x-8.x & Rocky8.x & openEuler 20.x-24.x #
# Version  : 3.0.1                                          #
# Date     : 2025-10-01                                     #
#############################################################
clear
OLD_LANG=$LANG
export LANG="en_US.UTF-8"

# ------------------------------- 变量定义 -------------------------------
USER_ZLIB_VER="1.3.1"
USER_OPENSSL_VER="3.5.3"
USER_OPENSSH_VER="9.9p2"

ZLIB_URL="https://www.zlib.net/fossils/zlib-${USER_ZLIB_VER}.tar.gz"
OPENSSL_URL="https://www.openssl.org/source/openssl-${USER_OPENSSL_VER}.tar.gz"
OPENSSH_URL="https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-${USER_OPENSSH_VER}.tar.gz"

PACKAGES="gcc gcc-c++ make autoconf automake zlib-devel openssl-devel pam-devel glibc glibc-devel libselinux-devel pcre-devel perl-IPC-Cmd perl-CPAN perl-Time-Piece"

SHELL_HOME="/opt/upsslssh"
INSTALL_PATH="/usr/local"
LOCK_FILE="/tmp/$(basename "$0").lock"
BACKUP_DIR="${SHELL_HOME}/backup/$(date +%Y%m%d%H%M%S)"
LOG_FILE="${SHELL_HOME}/log/$(date +%Y%m%d%H%M%S).log"
SUPPORTED_DISTROS=("centos:7-8" "rhel:7-8" "rocky:8-8" "openeuler:20-24" "kylin:10-10" "hce:1-2")

# ------------------------------- 创建目录 -------------------------------
mkdir -p ${SHELL_HOME} ${SHELL_HOME}/log ${SHELL_HOME}/backup &>/dev/null

# ------------------------------- 现有版本信息 -------------------------------
OLD_ZLIB_VER="$([[ -f /usr/include/zlib.h ]] && grep 'ZLIB_VERSION.*[0-9]' /usr/include/zlib.h | awk -F'"' '{print $2}')"
OLD_OPENSSL_VER="$(`command -v openssl` version | awk '{print $2}')"
OLD_OPENSSH_VER="$(`command -v ssh` -V 2>&1 | awk -F',' '{print $1}' | awk -F'_' '{print $2}')"

# ------------------------------- 日志函数 -------------------------------
log() {
    local type=$1 msg=$2
    local reset="\033[0m"
    case $type in
        INFO)   color="\033[0m";;
        SUCC)   color="\033[92m";;
        WARN)   color="\033[93m";;
        ATTN)   color="\033[96m";;
        ERROR)  color="\033[91m";;
        *)      color="\033[0m";;
    esac
    local time=$(date +"%Y-%m-%d %H:%M:%S")
    echo -e "[${time}] ${color}[${type}] ${msg}${reset}" | tee -a "$LOG_FILE" 2>/dev/null
}

# ------------------------------- 系统检查 -------------------------------
check_root() {
    [[ $EUID -ne 0 ]] && { log ERROR "必须以root用户或root权限运行"; exit 1; }
}

check_cpu() {
    [[ $(uname -m) == "x86_64" ]] && CPU_ARCH=64
    [[ $(getconf LONG_BIT) == "64" ]] && LONG_BIT=64
    [[ "$CPU_ARCH" == "64" && "$LONG_BIT" == "64" ]] && LIB_BIT=64
}

check_os() {
    ! command -v bc &>/dev/null && { log ERROR "需要bc计算工具,请安装"; exit 1; }
    [[ ! -f /etc/os-release ]] && { log ERROR "无法识别操作系统"; exit 1; }
    source /etc/os-release
    local dist_id=$(echo "$ID" | tr '[:upper:]' '[:lower:]')
    local version_id=$(echo "$VERSION_ID" | cut -d. -f1 | sed 's/[^0-9]//g')

    local supported=false
    for dist in "${SUPPORTED_DISTROS[@]}"; do
        IFS=':' read -r name range <<< "$dist"
        if [[ "$dist_id" == "$name" ]]; then
            IFS='-' read -r min max <<< "$range"
            if (( $(echo "$version_id >= $min" | bc -l) )) && (( $(echo "$version_id <= $max" | bc -l) )); then
                supported=true
                break
            fi
        fi
    done

    $supported || { log ERROR "不支持的操作系统: $ID $VERSION_ID"; exit 1; }
    log INFO "检测到支持的操作系统: $ID $VERSION_ID"
}

# ------------------------------- 版本号比较 -------------------------------
# 返回值: 1 (小于), 2 (大于), 3 (等于), 4 (错误入参)
version_compare() {
    if [ $# -ne 2 ]; then
        return 4
    fi
    
    local v1="$1"
    local v2="$2"

    if [ -z "$v1" ] || [ -z "$v2" ]; then
        return 4
    fi

    normalize_version() {
        local version="$1"
        if [ -z "$version" ]; then
            echo ""
            return
        fi

        IFS='.' read -ra parts <<< "$version"
        local i=$((${#parts[@]} - 1))

        while [ $i -ge 0 ]; do

            if [[ "${parts[i]}" =~ ^[0-9]+$ ]] && [ "${parts[i]}" -eq 0 ]; then
                ((i--))
            else
                break
            fi
        done

        if [ $i -lt 0 ]; then
            echo "0"
        else
            local normalized=""
            for ((j=0; j<=i; j++)); do
                if [ -z "$normalized" ]; then
                    normalized="${parts[j]}"
                else
                    normalized="$normalized.${parts[j]}"
                fi
            done
            echo "$normalized"
        fi
    }

    local norm_v1=$(normalize_version "$v1")
    local norm_v2=$(normalize_version "$v2")

    if [ "$norm_v1" = "$norm_v2" ]; then
        return 3
    fi

    local sorted=$(echo -e "$norm_v1\n$norm_v2" | sort -V 2>/dev/null)
    if [ $? -ne 0 ]; then
        return 4
    fi
    
    local first=$(echo "$sorted" | head -n1)

    if [ "$norm_v1" = "$first" ]; then
        return 1
    else
        return 2
    fi
}

check_ver() {
    version_compare "$USER_ZLIB_VER" "1.3" || { [[ $? -eq 1 ]] && { log ERROR "zlib安装版本建议不低于1.3,请修改"; exit 1; }; }
    version_compare "$USER_OPENSSL_VER" "3.0" || { [[ $? -eq 1 ]] && { log ERROR "openssl安装版本建议不低于3.0,请修改"; exit 1; }; }
    version_compare "$USER_OPENSSL_VER" "3.5.99" || { [[ $? -eq 2 ]] && { log ERROR "本脚本暂未适配openssl 3.6以上版本,请修改"; exit 1; }; }
    version_compare "$USER_OPENSSH_VER" "9.0" || { [[ $? -eq 1 ]] && { log ERROR "openssh安装版本建议不低于9.0,请修改"; exit 1; }; }
    version_compare "$USER_OPENSSH_VER" "9.99" || { [[ $? -eq 2 ]] && { log ERROR "本脚本暂未适配openssh 10.0以上版本,请修改"; exit 1; }; }
}

# ------------------------------- 锁机制 -------------------------------
acquire_lock() {
    exec 200>"$LOCK_FILE"
    flock -xn 200 || { log ERROR "脚本已在运行,请勿重复执行"; exit 1; }
    echo $$ >&200
}

release_lock() {
    flock -u 200
    rm -f "$LOCK_FILE"
}

# ------------------------------- 倒计时 -------------------------------
countdown() {
    local count=11
    local time=$(date +"%Y-%m-%d %H:%M:%S")
    log ATTN "即将升级Zlib版本至${USER_ZLIB_VER},升级OpenSSL版本至${USER_OPENSSL_VER},升级OpenSSH版本至${USER_OPENSSH_VER},"
    log ATTN "升级过程中请保持活动的连接窗口,切勿中途中断!为避免升级失败无法重连服务器,"
    log ATTN "请复制一个连接窗口以备不时之需,或自行配置Telnet服务预留另一个远程连接通道。"
    echo -en [`date +"%Y-%m-%d %H:%M:%S"`] "\033[96m[ATTN] 升级脚本即将开始,如暂不升级请在倒计时结束前按Ctrl+C终止脚本,倒计时: \033[0m"
    tput sc
    while true
    do
        if [[ $count -ge 1 ]] ; then
            let count--
            sleep 1
            tput rc
            tput ed
            echo -en "\033[31m$count \033[0m"
        else
            break
        fi
    done
    echo -e "" && log INFO "启动安装流程 . . ."
}

# ------------------------------- 版本检测 -------------------------------
get_installed_version() {
    local pkg=$1
    case $pkg in
        zlib)     [[ -f /usr/include/zlib.h ]] && grep 'ZLIB_VERSION.*[0-9]' /usr/include/zlib.h | awk -F'"' '{print $2}' ;;
        openssl)  command -v openssl >/dev/null && `command -v openssl` version | awk '{print $2}' ;;
        openssh)  command -v ssh >/dev/null && `command -v ssh` -V 2>&1 | awk -F',' '{print $1}' | awk -F'_' '{print $2}' ;;
        ssh-ssl)  command -v ssh >/dev/null && `command -v ssh` -V 2>&1 | awk -F', ' '{print $2}' | awk -F ' ' '{print $2}' ;;
    esac
}

# ------------------------------- 依赖检查 -------------------------------
install_deps() {
    log INFO "正在重建yum源缓存 . . ."
    yum makecache -C 1>/dev/null 2>>"$LOG_FILE" || yum clean all 1>/dev/null 2>>"$LOG_FILE" && yum makecache 1>/dev/null 2>>"$LOG_FILE" || { log ERROR "重建yum源缓存失败,请检查yum配置或网络连通性,脚本退出"; exit 1; }
    log INFO "开始安装依赖库 . . ."
    local deps=(${PACKAGES})
    for dep in "${deps[@]}"; do
        if ! yum list installed "$dep" &>/dev/null; then
            log INFO "安装依赖: $dep"
            if ! yum install -y "$dep" 1>/dev/null 2>>"$LOG_FILE" ; then
                log ERROR "依赖安装失败: $dep"
                exit 1
            fi
        else
            log INFO "依赖已安装: $dep"
        fi
    done
}

# ------------------------------- 包下载校验 -------------------------------
download_pkg() {
    local pkg=$1 ver=$2 url=$3
    local pkg_file="${SHELL_HOME}/${pkg}-${ver}.tar.gz"
    local RETRYCOUNT=0
    local MAX_RETRY=3
    local RETRY_DELAY=5

    if [ -f "$pkg_file" ] ; then
        log INFO "本地存在:${pkg}-${ver}包"
        if tar tzf "$pkg_file" 1>/dev/null 2>>"$LOG_FILE" ; then
            log SUCC "本地${pkg}-${ver}包检查可用"
            return 0
        else
            rm -f "$pkg_file"
            log WARN "本地${pkg}-${ver}包损坏,已删除将重新下载 . . ."
        fi
    else
        log WARN "本地${pkg}-${ver}包不存在"
    fi

    while [ $RETRYCOUNT -lt $MAX_RETRY ]; do
        ((RETRYCOUNT++))
        log INFO "开始第 $RETRYCOUNT 次下载(剩余重试次数:$((MAX_RETRY - RETRYCOUNT + 1))). . ."

        if curl -fL --max-time 30 -o "$pkg_file" "$url" 1>/dev/null 2>>"$LOG_FILE" ; then
            log SUCC "${pkg}-${ver}包下载成功"
            if tar tzf "$pkg_file" 1>/dev/null 2>>"$LOG_FILE" ; then
                log INFO "包校验通过: $pkg_file"
                return 0
            else
                rm -f "$pkg_file"
                log WARN "下载的${pkg}-${ver}包损坏,已删除将重新下载 . . ."
            fi
        else
            if [ $RETRYCOUNT -lt $MAX_RETRY ]; then
                log WARN "下载失败,将在 $RETRY_DELAY 秒后进行第 $((RETRYCOUNT + 1)) 次重试 . . ."
                sleep $RETRY_DELAY
            fi
        fi
    done

    log ERROR "已达到最大重试次数 $MAX_RETRY,下载失败,脚本退出。"
    return 1
}

# ------------------------------- 备份与回滚 -------------------------------
backup_files() {
    local pkg=$1
    mkdir -p "$BACKUP_DIR"
    log INFO "备份${pkg}文件到$BACKUP_DIR"

    case $pkg in
        zlib)
            tar czPf "$BACKUP_DIR/"zlib.h.tar.gz /usr/include/zlib.h 1>/dev/null 2>>"$LOG_FILE"
            tar czPf "$BACKUP_DIR/"zconf.h.tar.gz /usr/include/zconf.h 1>/dev/null 2>>"$LOG_FILE"
            tar czPf "$BACKUP_DIR/"zliblibz.tar.gz /usr/lib*/libz.so* 1>/dev/null 2>>"$LOG_FILE"
            tar czPf "$BACKUP_DIR/"zlib.ld.so.conf.tar.gz /etc/ld.so.conf 1>/dev/null 2>>"$LOG_FILE"
            return 0
            ;;
        openssl)
            local openssllibpath=$(ldd `command -v openssl` | grep "libssl" | awk -F' ' '{print $3}' | awk -F'/[^/]*$' '{print $1}')
            tar czPf "$BACKUP_DIR/"openssllibssl.tar.gz "$openssllibpath/"libssl* 1>/dev/null 2>>"$LOG_FILE"
            tar czPf "$BACKUP_DIR/"openssllibcry.tar.gz "$openssllibpath/"libcry* 1>/dev/null 2>>"$LOG_FILE"
            tar czPf "$BACKUP_DIR/"openssl.tar.gz `command -v openssl` 1>/dev/null 2>>"$LOG_FILE"
            [[ -f /usr/local/bin/openssl ]] && tar czPf "$BACKUP_DIR/"usr_local_bin_openssl.tar.gz /usr/local/bin/openssl 1>/dev/null 2>>"$LOG_FILE"
            [[ -f /usr/local/lib*/libssl.so* ]] && tar czPf "$BACKUP_DIR/"usr_local_lib_libssl.tar.gz /usr/local/lib*/libssl.so* 1>/dev/null 2>>"$LOG_FILE"
            [[ -f /usr/local/lib*/libcrypto.so* ]] && tar czPf "$BACKUP_DIR/"usr_local_lib_libcrypto.tar.gz /usr/local/lib*/libcrypto.so* 1>/dev/null 2>>"$LOG_FILE"
            tar czPf "$BACKUP_DIR/"openssl.ld.so.conf.tar.gz /etc/ld.so.conf 1>/dev/null 2>>"$LOG_FILE"
            return 0
            ;;
        openssh)
            tar czPf "$BACKUP_DIR/"etc_ssh.tar.gz /etc/ssh 1>/dev/null 2>>"$LOG_FILE"
            tar czPf "$BACKUP_DIR/"usr_bin_ssh.tar.gz /usr/bin/ssh* 1>/dev/null 2>>"$LOG_FILE"
            tar czPf "$BACKUP_DIR/"usr_sbin_sshd.tar.gz /usr/sbin/sshd 1>/dev/null 2>>"$LOG_FILE"
            tar czPf "$BACKUP_DIR/"sshd.service.tar.gz /usr/lib/systemd/system/sshd.service 1>/dev/null 2>>"$LOG_FILE"
            tar czPf "$BACKUP_DIR/"etc_pam.d_sshd.tar.gz /etc/pam.d/sshd 1>/dev/null 2>>"$LOG_FILE"
            [[ -f /etc/init.d/sshd ]] && tar czPf "$BACKUP_DIR/"init.d_sshd.tar.gz /etc/init.d/sshd 1>/dev/null 2>>"$LOG_FILE"
            return 0
            ;;
    esac
}

rollback() {
    log WARN "检测到安装失败,启动回滚 . . ."
    local pkg=$1

    case $pkg in
        zlib)
            rm -rf ${INSTALL_PATH}/zlib-$USER_OPENSSL_VER
            [[ -f "$BACKUP_DIR/"zlib.h.tar.gz ]] && tar xzPf "$BACKUP_DIR/"zlib.h.tar.gz 1>/dev/null 2>>"$LOG_FILE"
            [[ -f "$BACKUP_DIR/"zconf.h.tar.gz ]] && tar xzPf "$BACKUP_DIR/"zconf.h.tar.gz 1>/dev/null 2>>"$LOG_FILE"
            [[ -f "$BACKUP_DIR/"zliblibz.tar.gz ]] && tar xzPf "$BACKUP_DIR/"zliblibz.tar.gz 1>/dev/null 2>>"$LOG_FILE"
            [[ -f "$BACKUP_DIR/"zlib.ld.so.conf.tar.gz ]] && tar xzPf "$BACKUP_DIR/"zlib.ld.so.conf.tar.gz 1>/dev/null 2>>"$LOG_FILE"
            ldconfig &>/dev/null
            ;;
        openssl)
            rm -rf ${INSTALL_PATH}/openssl-$USER_OPENSSL_VER
            [[ -f "$BACKUP_DIR/"openssllibssl.tar.gz ]] && tar xzPf "$BACKUP_DIR/"openssllibssl.tar.gz 1>/dev/null 2>>"$LOG_FILE"
            [[ -f "$BACKUP_DIR/"openssllibcry.tar.gz ]] && tar xzPf "$BACKUP_DIR/"openssllibcry.tar.gz 1>/dev/null 2>>"$LOG_FILE"
            [[ -f "$BACKUP_DIR/"openssl.tar.gz ]] && tar xzPf "$BACKUP_DIR/"openssl.tar.gz 1>/dev/null 2>>"$LOG_FILE"
            [[ -f "$BACKUP_DIR/"usr_local_bin_openssl.tar.gz ]] && tar xzPf "$BACKUP_DIR/"usr_local_bin_openssl.tar.gz 1>/dev/null 2>>"$LOG_FILE"
            [[ -f "$BACKUP_DIR/"usr_local_lib_libssl.tar.gz ]] && tar xzPf "$BACKUP_DIR/"usr_local_lib_libssl.tar.gz 1>/dev/null 2>>"$LOG_FILE"
            [[ -f "$BACKUP_DIR/"usr_local_lib_libcrypto.tar.gz ]] && tar xzPf "$BACKUP_DIR/"usr_local_lib_libcrypto.tar.gz 1>/dev/null 2>>"$LOG_FILE"
            [[ -f "$BACKUP_DIR/"openssl.ld.so.conf.tar.gz ]] && tar xzPf "$BACKUP_DIR/"openssl.ld.so.conf.tar.gz 1>/dev/null 2>>"$LOG_FILE"
            #yum reinstall -y openssl openssl-devel &>/dev/null
            ldconfig &>/dev/null
            ;;
        openssh)
            rm -rf ${INSTALL_PATH}/openssh-$USER_OPENSSL_VER
            [[ -f "$BACKUP_DIR/"etc_ssh.tar.gz ]] && tar xzPf "$BACKUP_DIR/"etc_ssh.tar.gz 1>/dev/null 2>>"$LOG_FILE"
            [[ -f "$BACKUP_DIR/"usr_bin_ssh.tar.gz ]] && tar xzPf "$BACKUP_DIR/"usr_bin_ssh.tar.gz 1>/dev/null 2>>"$LOG_FILE"
            [[ -f "$BACKUP_DIR/"usr_sbin_sshd.tar.gz ]] && tar xzPf "$BACKUP_DIR/"usr_sbin_sshd.tar.gz 1>/dev/null 2>>"$LOG_FILE"
            [[ -f "$BACKUP_DIR/"sshd.service.tar.gz ]] && tar xzPf "$BACKUP_DIR/"sshd.service.tar.gz 1>/dev/null 2>>"$LOG_FILE"
            [[ -f "$BACKUP_DIR/"etc_pam.d_sshd.tar.gz ]] && tar xzPf "$BACKUP_DIR/"etc_pam.d_sshd.tar.gz 1>/dev/null 2>>"$LOG_FILE"
            [[ -f "$BACKUP_DIR/"init.d_sshd.tar.gz ]] && tar xzPf "$BACKUP_DIR/"init.d_sshd.tar.gz 1>/dev/null 2>>"$LOG_FILE"
            #yum reinstall -y openssh openssh-clients openssh-server &>/dev/null
            systemctl restart sshd &>/dev/null
            ;;
    esac
    log WARN "${pkg}回滚完成"
}

# ------------------------------- 安装主体 -------------------------------
install_software() {
    local softname=$1
    local ver=$2
    local software_configure=$3

    case $softname in
        zlib)
            local old_ver=${OLD_ZLIB_VER}
            local download_url=${ZLIB_URL}
            ;;
        openssl)
            local old_ver=${OLD_OPENSSL_VER}
            local download_url=${OPENSSL_URL}
            ;;
        openssh)
            local old_ver=${OLD_OPENSSH_VER}
            local download_url=${OPENSSH_URL}
            ;;
    esac

    log INFO "开始安装:${softname}-${old_ver} --> ${softname}-${ver}"

    download_pkg "$softname" "$ver" "$download_url" || { popd &>/dev/null; exit 1; }

    local pkg_file="${SHELL_HOME}/${softname}-${ver}.tar.gz"
    [[ -z "$pkg_file" ]] && return 1

    backup_files "$softname" || return 1

    log INFO "正在解压${softname}-${ver}.tar.gz源码包 . . ."
    tar zxf "$pkg_file" -C ${SHELL_HOME} || { log ERROR "${softname}-${ver}源码包解压失败"; return 1; }

    local build_dir="${SHELL_HOME}/${softname}-${ver}"
    pushd "$build_dir" &>/dev/null || { rollback ${softname}; return 1; }

    log INFO "正在编译安装${softname}-${ver} . . ."
    ${software_configure} 1>/dev/null 2>>"$LOG_FILE" || { log ERROR "${softname}-${ver}编译安装失败"; popd &>/dev/null; return 1; }

    log INFO "正在编译安装${softname}-${ver} --> make -j$(nproc) . . ."
    make -j$(nproc) 1>/dev/null 2>>"$LOG_FILE" || { log ERROR "${softname}-${ver}编译安装失败"; popd &>/dev/null; return 1; }

    log INFO "正在编译安装${softname}-${ver} --> make install . . ."
    make install 1>/dev/null 2>>"$LOG_FILE" || { log ERROR "${softname}-${ver}编译安装失败"; popd &>/dev/null; return 1; }

    popd &>/dev/null || return 1
}

install_zlib() {
    local ver=$1
    local softname=zlib

    version_compare "$(get_installed_version ${softname})" "$ver" || { [[ $? -eq 2 ]] && { log ATTN "${softname}高于目标版本,跳过安装"; return 0; }; }
    [[ $(get_installed_version ${softname}) == "$ver" ]] && { log ATTN "${softname}已是目标版本,跳过安装"; return 0; }
    [[ $CPU_ARCH == 64 ]] && local CONFSET="--64"
    install_software "$softname" "$ver" "./configure --prefix=/usr ${CONFSET}" || { rollback zlib; return 1; }
    [[ $LONG_BIT == 64 ]] && [[ -f /lib/libz.so.$ver ]] && \cp -rfP /lib/libz.* /lib64 && log SUCC "复制libz库文件成功" || { log ERROR "复制libz库文件失败"; rollback zlib; return 1; }
    chmod 755 /lib*/libz.so* &>/dev/null
    sed -i "/zlib/ s/^\(.*\)$/#\1/g" /etc/ld.so.conf &>/dev/null && log ATTN "注释/etc/ld.so.conf旧配置信息"
    sed -i "/zlib/ s/^\(.*\)$/#\1/g" /etc/ld.so.conf.d/zlib.conf &>/dev/null && log ATTN "注释/etc/ld.so.conf.d/zlib.conf旧配置信息"
    rm -rf /etc/ld.so.cache &>/dev/null
    ldconfig &>/dev/null
    [[ $(get_installed_version zlib) == "$ver" ]] && log SUCC "$softname-$ver 安装成功:$softname-$(get_installed_version zlib)" || { log ERROR "$softname-$ver 安装失败"; rollback zlib; return 1; }
    return 0
}

install_openssl() {
    local ver=$1
    local softname=openssl

    version_compare "$(get_installed_version ${softname})" "$ver" || { [[ $? -eq 2 ]] && { log ATTN "${softname}高于目标版本,跳过安装"; OPENSSL_INSTALL_PATH="$INSTALL_PATH/$(get_installed_version ${softname})"; return 0; }; }
    [[ $(get_installed_version ${softname}) == "$ver" ]] && { log ATTN "${softname}已是目标版本,跳过安装"; OPENSSL_INSTALL_PATH="$INSTALL_PATH/openssl-$ver"; return 0; }
    [[ $CPU_ARCH == 64 ]] && local CONFSET="-m64"
    install_software "$softname" "$ver" "./config --prefix=$INSTALL_PATH/openssl-$ver shared zlib enable-ssl3 enable-ssl3-method enable-mdc2 enable-md2 $CONFSET" || { OPENSSL_INSTALL_PATH="$INSTALL_PATH/openssl-$ver"; rollback openssl; return 1; }

    [[ $LIB_BIT == 64 && -d ${INSTALL_PATH}/openssl-$ver/libx32 ]] && ln -s ${INSTALL_PATH}/openssl-$ver/libx32 ${INSTALL_PATH}/openssl-$ver/lib64
    [[ ! -n $LIB_BIT && -d ${INSTALL_PATH}/openssl-$ver/libx32 ]] && ln -s ${INSTALL_PATH}/openssl-$ver/libx32 ${INSTALL_PATH}/openssl-$ver/lib

    \cp -rf ${INSTALL_PATH}/openssl-$ver/bin/openssl /usr/bin/openssl &>/dev/null && log SUCC "复制openssl执行文件成功" || { rollback openssl; return 1; }
    chmod 755 /usr/bin/openssl &>/dev/null
    [[ -f /usr/local/bin/openssl ]] && rm -rf /usr/local/bin/openssl &>/dev/null

    \cp -rfL ${INSTALL_PATH}/openssl-$ver/lib"$LIB_BIT"/libssl.so /lib"$LONG_BIT"/libssl.so.$ver &>/dev/null && log SUCC "复制libssl库文件成功" || { rollback openssl; return 1; }
    \cp -rfL ${INSTALL_PATH}/openssl-$ver/lib"$LIB_BIT"/libcrypto.so /lib"$LONG_BIT"/libcrypto.so.$ver &>/dev/null && log SUCC "复制libcrypto库文件成功" || { rollback openssl; return 1; }
    chmod 755 /lib"$LONG_BIT"/libssl.so.$ver &>/dev/null
    chmod 755 /lib"$LONG_BIT"/libcrypto.so.$ver &>/dev/null

    ln -sf /lib"$LONG_BIT"/libssl.so.$ver /lib"$LONG_BIT"/libssl.so &>/dev/null
    ln -sf /lib"$LONG_BIT"/libssl.so.$ver /lib"$LONG_BIT"/libssl.so.3 &>/dev/null
    ln -sf /lib"$LONG_BIT"/libcrypto.so.$ver /lib"$LONG_BIT"/libcrypto.so &>/dev/null
    ln -sf /lib"$LONG_BIT"/libcrypto.so.$ver /lib"$LONG_BIT"/libcrypto.so.3 &>/dev/null

    [[ -f /usr/local/lib*/libssl.so* ]] && rm -rf /usr/local/lib*/libssl.so* &>/dev/null
    [[ -f /usr/local/lib*/libcrypto.so* ]] && rm -rf /usr/local/lib*/libcrypto.so* &>/dev/null

    grep -v "^#" /etc/ld.so.conf | grep 'openssl' &>/dev/null && sed -i "/openssl/ s/^\(.*\)$/#\1/g" /etc/ld.so.conf &>/dev/null && log ATTN "已注释/etc/ld.so.conf旧配置信息"
    rm -rf /etc/ld.so.cache &>/dev/null
    ldconfig &>/dev/null
    [[ $(get_installed_version openssl) == "$ver" ]] && log SUCC "$softname-$ver 安装成功:`openssl version`" || { log ERROR "$softname-$ver 安装失败"; rollback openssl; return 1; }

    OPENSSL_INSTALL_PATH="$INSTALL_PATH/openssl-$ver"

    return 0
}

install_openssh() {
    local ver=$1
    local softname=openssh

    version_compare "$(get_installed_version ${softname})" "$ver" || { [[ $? -eq 2 ]] && { log ATTN "${softname}高于目标版本,跳过安装"; return 0; }; }
    [[ $(get_installed_version ${softname}) == "$ver" && $(get_installed_version ssh-ssl) == $(get_installed_version openssl) ]] && { log ATTN "${softname}已是目标版本,跳过安装"; return 0; }
    [[ -d $OPENSSL_INSTALL_PATH ]] && local with_ssl_dir="--with-ssl-dir=$OPENSSL_INSTALL_PATH"
    install_software "$softname" "$ver" "./configure --prefix=/usr --sysconfdir=/etc/ssh --localstatedir=/var/run/sshd --with-pam --with-zlib --with-selinux --without-hardening --without-openssl-header-check $with_ssl_dir" || { rollback openssh; return 1; }

    sed_config_1() {
        local key=$1
        local value=$2
        local confpath=$3
        if grep -qE "^[[:space:]]*#?${key}" ${confpath} 2>>"$LOG_FILE" ; then
            sed -i "/^\s*${key}/ s/^\s*//" ${confpath} 2>>"$LOG_FILE"
            sed -i "/^\s*${key}/ s/^\(.*\)$/#\1/g" ${confpath} 2>>"$LOG_FILE"
            sed -i "0,/^#${key}.*/s/^#${key}.*/${value}/" ${confpath} 2>>"$LOG_FILE"
        else
            echo -e "\n${value}" >> ${confpath} 2>>"$LOG_FILE"
        fi
    }

    sed_config_2() {
        local key=$1
        local value=$2
        local confpath=$3
        if grep -qE "^[[:space:]]*${key}.*${value}.*" ${confpath} 2>>"$LOG_FILE" ; then
            local config1=$(grep -E "^\s*${key}.*${value}.*" ${confpath})
            local config2=$(echo ${config1/${value},/})
            sed -i "/^${key}.*/s/^${key}.*/${config2}/" ${confpath} 2>>"$LOG_FILE"
        fi
    }

    log ATTN "为确保登陆正常,配置文件将被修改为允许root登陆"
    sed_config_1 "PasswordAuthentication" "PasswordAuthentication yes" "/etc/ssh/sshd_config"
    sed_config_1 "PermitRootLogin" "PermitRootLogin yes" "/etc/ssh/sshd_config"

    log ATTN "优化openssh配置以符合最低信息安全要求"
    sed_config_1 "UsePAM" "UsePAM yes" "/etc/ssh/sshd_config"
    sed_config_1 "Subsystem" "Subsystem sftp internal-sftp -l INFO -f AUTH" "/etc/ssh/sshd_config"
    sed_config_2 "HostkeyAlgorithms" "ssh-dss" "/etc/ssh/sshd_config"
    sed_config_2 "HostKeyAlgorithms" "ssh-dss" "/etc/ssh/sshd_config"
    sed_config_2 "PubkeyAcceptedKeyTypes" "ssh-dss" "/etc/ssh/sshd_config"
    sed_config_2 "PubKeyAcceptedKeyTypes" "ssh-dss" "/etc/ssh/sshd_config"

    if [[ ! -f "/etc/pam.d/sshd" ]] ; then
        if [[ -f "$BACKUP_DIR/"etc_pam.d_sshd.tar.gz ]] ; then
            log INFO "正在恢复/etc/pam.d/sshd文件"
            tar xzPf "$BACKUP_DIR/"etc_pam.d_sshd.tar.gz &>/dev/null
        else
            log INFO "正在创建/etc/pam.d/sshd文件"
                cat > /etc/pam.d/sshd << EOF
#%PAM-1.0
auth       required     pam_sepermit.so
auth       substack     password-auth
auth       include      postlogin
# Used with polkit to reauthorize users in remote sessions
-auth      optional     pam_reauthorize.so prepare
account    required     pam_nologin.so
account    include      password-auth
password   include      password-auth
# pam_selinux.so close should be the first session rule
session    required     pam_selinux.so close
session    required     pam_loginuid.so
# pam_selinux.so open should only be followed by sessions to be executed in the user context
session    required     pam_selinux.so open env_params
session    required     pam_namespace.so
session    optional     pam_keyinit.so force revoke
session    include      password-auth
session    include      postlogin
# Used with polkit to reauthorize users in remote sessions
-session   optional     pam_reauthorize.so prepare
EOF
        fi
    fi

    log INFO "正在修复openssh失效的配置. . ."
    chmod 600 /etc/ssh/ssh_host*
    if [[ $(sshd -t 2>&1 | grep -E "^/etc/.*line.*option" | wc -l) -ne 0 ]] ; then
        sshd -t 1>/dev/null 2>>"$LOG_FILE"
        sshd -t 2>&1 | grep -E "^/etc/.*Unsupported option" | awk -F' ' '($5=="option"){print $6}' | sed -e 's/\r$//' | tr "\n" " " | sed -e 's/,$/\n/' > /tmp/sshdconfig
        sshd -t 2>&1 | grep -E "^/etc/.*Deprecated option" | awk -F' ' '($5=="option"){print $6}' | sed -e 's/\r$//' | tr "\n" " " | sed -e 's/,$/\n/' >> /tmp/sshdconfig
        sshd -t 2>&1 | grep -E "^/etc/.*Bad configuration option" | awk -F' ' '($6=="option:"){print $7}' | sed -e 's/\r$//' | tr "\n" " " | sed -e 's/,$/\n/' >> /tmp/sshdconfig
        for sshdconfig in $(cat /tmp/sshdconfig); do
            sed -i "/^\s*$sshdconfig/ s/^\(.*\)$/#\1/g" /etc/ssh/sshd_config
            log ATTN "注释openssh失效的配置 $sshdconfig"
        done
        rm -rf /tmp/sshdconfig
    fi

    [[ $ID == "rocky" ]] && sed_config_1 "CRYPTO_POLICY" "CRYPTO_POLICY=" "/etc/sysconfig/sshd"

    restorecon -Rv /usr/sbin/sshd /etc/ssh /usr/bin/ssh &>/dev/null
    chmod 755 /usr/sbin/sshd
    chmod 755 /usr/bin/ssh

    systemctl restart sshd 1>/dev/null 2>>"$LOG_FILE" && log SUCC "openssh-$ver 服务启动成功" || { log ERROR "openssh 服务启动失败"; rollback openssh; return 1; }
    [[ $(get_installed_version openssh) == "$ver" ]] && log SUCC "$softname-$ver 安装成功:`ssh -V 2>&1`" || { log ERROR "$softname-$ver 安装失败"; rollback openssh; return 1; }
    return 0
}

# ------------------------------- 结束安装 -------------------------------
end_install() {
    rm -rf $SHELL_HOME/zlib-${USER_ZLIB_VER} &>/dev/null
    rm -rf $SHELL_HOME/openssl-${USER_OPENSSL_VER} &>/dev/null
    rm -rf $SHELL_HOME/openssh-${USER_OPENSSH_VER} &>/dev/null
    chown `logname`.`logname` $SHELL_HOME/ -R &>/dev/null
    find $SHELL_HOME -type f -exec chmod 644 {} \; &>/dev/null
    find $SHELL_HOME -type d -exec chmod 755 {} \; &>/dev/null
    log ATTN "安装目录: $SHELL_HOME"
    log ATTN "备份目录: $BACKUP_DIR"
    log ATTN "日志文件: $LOG_FILE"
    export LANG=$OLD_LANG
}

# ------------------------------- 主流程 -------------------------------
main() {
    acquire_lock
    trap release_lock EXIT
    check_root
    check_cpu
    check_os
    check_ver
    countdown
    install_deps

    local all_success=true
    install_zlib "$USER_ZLIB_VER" || all_success=false
    install_openssl "$USER_OPENSSL_VER" || all_success=false
    install_openssh "$USER_OPENSSH_VER" || all_success=false

    if $all_success; then
        log SUCC "所有组件安装成功!"
        end_install
    else
        log ERROR "部分组件安装失败,已尝试回滚"
        end_install
        exit 1
    fi
}

main

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐