脚本简介

本脚本用于在 CentOS 7 和 Redhat 7 系统上升级 OpenSSH 服务到 10.2p1 版本,包含完整的升级流程和回退机制。

注意:需要优先升级OpenSSL 3.5.4 如果自己提前升级了 注意修改脚本编译环境那块的。

主要功能

  • 系统版本检查:自动检测系统类型和版本,仅支持 CentOS 7 和 Redhat 7
  • Telnet 备用连接:安装并启用 Telnet 服务作为备用连接方式
  • 依赖管理:自动安装编译所需的依赖包
  • 源码下载:从本地 HTTP 服务下载 OpenSSH 源码包
  • 备份机制:自动备份原有配置文件
  • 编译安装:配置编译选项并安装新版本
  • 服务管理:启动并验证 SSH 服务
  • 回退机制:升级失败时自动回退到默认版本
  • 日志记录:详细的操作日志,便于排查问题
  • Ansible 兼容:支持通过 Ansible 批量执行

准备工作

1. 准备 OpenSSH 源码包

openssh-10.2p1.tar.gz 文件放置在本地 HTTP 服务的根目录(192.168.1.11)。需要安装httpd服务,然后通过这个服务分发ssh升级包,有公网的话可以直接用 githup 那个链接。

2. 配置 Ansible 主机清单

如果需要批量执行,创建一个 hosts 文件:

# hosts 文件内容
[servers]
192.168.1.101
192.168.1.102

使用方法

单台服务器执行

  1. 将脚本上传到目标服务器:

    scp upgrade_openssh.sh root@服务器IP:/tmp/
    
  2. 执行脚本:

    chmod +x /tmp/upgrade_openssh.sh
    cd /tmp
    ./upgrade_openssh.sh
    

批量执行(使用 Ansible)

如果服务器特别多的话建议指定并发数量 ’-f 50‘ ansible 默认是5个并发。

ansible -i hosts servers -m script -a "/tmp/upgrade_openssh.sh"

执行流程

  1. 系统检查:验证系统版本是否符合要求
  2. Telnet 安装:安装并启动 Telnet 服务作为备用连接
  3. 依赖安装:安装编译所需的依赖包
  4. 源码下载:从本地 HTTP 服务下载 OpenSSH 源码
  5. 备份配置:备份现有的 SSH 配置文件
  6. 卸载旧版:卸载系统默认的 OpenSSH
  7. 编译安装:配置编译选项并安装新版本
  8. 服务启动:启动新的 SSH 服务
  9. 验证升级:检查 SSH 版本和服务状态
  10. 关闭 Telnet:升级成功后关闭 Telnet 服务

回退机制

如果升级过程中出现错误,脚本会自动执行回退操作:

  1. 停止当前 SSH 服务
  2. 从默认仓库安装 OpenSSH
  3. 恢复备份的配置文件
  4. 启动 SSH 服务

如果回退失败,脚本会保留 Telnet 服务,确保您仍然可以远程连接到主机进行排查。

日志管理

脚本会将所有操作记录到目标主机的 /var/log/openssh-upgrade.log 文件中,包括:

  • 执行时间
  • 操作步骤
  • 错误信息
  • 回退操作

您可以通过查看此日志文件来了解执行情况和排查问题。

注意事项

  1. 网络连接:确保目标服务器可以访问 192.168.1.11 上的 HTTP 服务
  2. 权限要求:执行脚本需要 root 权限
  3. OpenSSL 依赖:脚本默认使用 /usr/local/openssl-3.5.4 作为 OpenSSL 目录
  4. 安全提示:Telnet 服务仅在升级过程中临时使用,升级完成后会自动关闭
  5. 重复执行:脚本支持重复执行,会跳过已经完成的步骤
  6. 备份保留:每次执行都会创建新的备份文件,避免覆盖之前的备份
  7. 错误处理:升级失败时会自动回退,回退失败时会保留 Telnet 服务

脚本参数说明

脚本中的主要配置参数:

参数 说明 默认值
OPENSSH_VERSION OpenSSH 版本 10.2p1
OPENSSH_DOWNLOAD_URL 下载地址 http://192.168.1.11/openssh-10.2p1.tar.gz
OPENSSH_INSTALL_DIR 安装目录 /usr/local/openssh-10.2p1
OPENSSL_DIR OpenSSL 目录 /usr/local/openssl-3.5.4
LOG_FILE 日志文件 /var/log/openssh-upgrade.log

完整代码

#!/bin/bash
# OpenSSH 升级脚本 for CentOS 7/Redhat 7
set -e

# 配置参数
OPENSSH_VERSION="10.2p1"
OPENSSH_TARBALL="openssh-${OPENSSH_VERSION}.tar.gz"
OPENSSH_DOWNLOAD_URL="http://192.168.1.11/${OPENSSH_TARBALL}"
OPENSSH_INSTALL_DIR="/usr/local/openssh-${OPENSSH_VERSION}"
OPENSSL_DIR="/usr/local/openssl-3.5.4"
BACKUP_DATE=$(date +%Y%m%d_%H%M%S)
TMP_DIR="/tmp"
LOG_FILE="/var/log/openssh-upgrade.log"

# 日志输出
log_info() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] INFO: $1"
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] INFO: $1" >> ${LOG_FILE}
}

log_warning() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] WARNING: $1"
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] WARNING: $1" >> ${LOG_FILE}
}

log_error() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $1"
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $1" >> ${LOG_FILE}
}

# 检查root权限
check_root() {
    if [ "$(id -u)" -ne 0 ]; then
        log_error "需要root权限执行此脚本"
        exit 1
    fi
}

# 检查系统版本
test_system() {
    log_info "检查系统版本"
    
    os_name=$(cat /etc/os-release | grep '^NAME=' | cut -d'=' -f2 | tr -d '"')
    os_version=$(cat /etc/os-release | grep '^VERSION_ID=' | cut -d'=' -f2 | tr -d '"')
    
    log_info "当前系统: ${os_name} ${os_version}"
    
    # 仅支持CentOS 7和Redhat 7
    if [[ "${os_name}" == "CentOS Linux" && "${os_version}" == "7" ]] || \
       [[ "${os_name}" == "Red Hat Enterprise Linux Server" && "${os_version}" == "7" ]]; then
        log_info "系统版本验证通过"
    else
        log_error "仅支持CentOS 7和Redhat 7系统"
        exit 1
    fi
}

# 安装并启用Telnet服务
install_telnet() {
    log_info "安装Telnet服务作为备用连接"
    yum install -y telnet-server telnet xinetd  >/dev/null 2>&1
    
    # 配置Telnet
    cat > /etc/xinetd.d/telnet << 'EOF'
service telnet
{
    flags           = REUSE
    socket_type     = stream
    wait            = no
    user            = root
    server          = /usr/sbin/in.telnetd
    log_on_failure  += USERID
    disable         = no
}
EOF
    
    # 启动xinetd服务
    systemctl enable xinetd >/dev/null 2>&1
    systemctl start xinetd
    
    log_info "Telnet服务已安装并启动"
    log_warning "Telnet明文传输密码,仅在内网使用"
}

# 准备工作
prepare() {
    log_info "开始准备工作..."
    
    # 确认系统版本
    log_info "系统版本:"
    cat /etc/centos-release
    
    # 查看当前OpenSSH版本
    log_info "当前OpenSSH版本:"
    ssh -V
    
    # 安装编译依赖
    log_info "安装编译依赖..."
    yum groupinstall -y "Development Tools" >/dev/null 2>&1
    yum install -y wget curl gcc make zlib-devel pam-devel \
    libselinux-devel openssl-devel bzip2 gcc-c++ libstdc++* libcap* >/dev/null 2>&1
    
    log_info "准备工作完成"
}

# 下载OpenSSH源码
download_openssh() {
    log_info "下载OpenSSH ${OPENSSH_VERSION}源码到${TMP_DIR}目录..."
    cd ${TMP_DIR}
    log_info "从${OPENSSH_DOWNLOAD_URL}下载安装包..."
    wget ${OPENSSH_DOWNLOAD_URL}
    log_info "解压安装包..."
    tar -zxvf ${OPENSSH_TARBALL}  >/dev/null 2>&1
    cd openssh-${OPENSSH_VERSION}
    
    log_info "OpenSSH源码下载完成,已放置在${TMP_DIR}目录"
}

# 备份并卸载默认SSH
backup_and_remove() {
    log_info "备份并卸载默认SSH..."
    
    # 备份配置
    cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak-${BACKUP_DATE}
    cp /etc/pam.d/sshd /etc/pam.d/sshd.bak-${BACKUP_DATE}
    
    # 卸载旧版SSH
    yum -y remove openssh  >/dev/null 2>&1
    
    log_info "默认SSH已备份并卸载"
}

# 配置编译选项
configure_openssh() {
    log_info "配置编译选项..."
    
    ./configure \
      --prefix=${OPENSSH_INSTALL_DIR} \
      --sysconfdir=/etc/ssh \
      --with-ssl-dir=${OPENSSL_DIR} \
      --with-zlib \
      --with-pam \
      --with-privsep-path=/var/lib/sshd \
      --with-md5-passwords  >/dev/null 2>&1
    
    log_info "编译选项配置完成"
}

# 编译与安装
compile_and_install() {
    log_info "开始编译与安装..."
    
    # 编译
    make -j$(nproc)  >/dev/null 2>&1
    
    # 配置权限
    chmod 600 /etc/ssh/ssh_host_*
    chown root.root /etc/ssh/ssh_host_*
    
    # 安装
    make install  >/dev/null 2>&1
    cp -p contrib/redhat/sshd.init /etc/init.d/sshd
    chmod +x /etc/init.d/sshd
    
    # 配置允许root登录(根据需要)
    echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config
    echo 'UsePAM yes' >> /etc/ssh/sshd_config
    
    # 恢复PAM配置
    cp /etc/pam.d/sshd.bak-${BACKUP_DATE} /etc/pam.d/sshd 2>/dev/null || true
    
    # 创建软连接
    ln -sf ${OPENSSH_INSTALL_DIR}/sbin/sshd /usr/sbin/sshd
    ln -sf ${OPENSSH_INSTALL_DIR}/bin/ssh /usr/bin/ssh
    ln -sf ${OPENSSH_INSTALL_DIR}/bin/ssh-keygen /usr/bin/ssh-keygen
    ln -sf ${OPENSSH_INSTALL_DIR}/bin/ssh-keyscan /usr/bin/ssh-keyscan
    ln -sf ${OPENSSH_INSTALL_DIR}/bin/ssh-agent /usr/bin/ssh-agent
    ln -sf ${OPENSSH_INSTALL_DIR}/bin/scp /usr/bin/scp
    ln -sf ${OPENSSH_INSTALL_DIR}/bin/sftp /usr/bin/sftp
    ln -sf ${OPENSSH_INSTALL_DIR}/bin/ssh-add /usr/bin/ssh-add
    cp ${TMP_DIR}/openssh-${OPENSSH_VERSION}/contrib/ssh-copy-id /usr/bin/
    chmod +x /usr/bin/ssh-copy-id
    
    log_info "OpenSSH编译与安装完成"
}

# 启动SSH服务
start_ssh() {
    log_info "启动SSH服务..."
    
    systemctl daemon-reload
    systemctl enable sshd
    systemctl restart sshd
    
    log_info "SSH服务已启动"
}

# 验证升级
verify_upgrade() {
    log_info "验证升级结果..."
    
    log_info "新的OpenSSH版本:"
    ssh -V
    
    log_info "SSH服务状态:"
    ss -tnlp | grep sshd
    
    log_info "升级验证完成"
}

# 回退操作
rollback() {
    log_info "执行回退操作..."
    
    # 停止当前SSH服务
    systemctl stop sshd || true
    
    # 从默认仓库安装OpenSSH
    log_info "从默认仓库安装OpenSSH..."
    if ! yum install -y openssh openssh-server openssh-clients; then
        log_error "回退失败:无法安装OpenSSH"
        return 1
    fi
    
    # 恢复配置
    if [ -f "/etc/ssh/sshd_config.bak-${BACKUP_DATE}" ]; then
        cp /etc/ssh/sshd_config.bak-${BACKUP_DATE} /etc/ssh/sshd_config
    fi
    
    if [ -f "/etc/pam.d/sshd.bak-${BACKUP_DATE}" ]; then
        cp /etc/pam.d/sshd.bak-${BACKUP_DATE} /etc/pam.d/sshd
    fi
    
    # 重启SSH服务
    systemctl daemon-reload
    systemctl enable sshd >/dev/null 2>&1
    if ! systemctl restart sshd; then
        log_error "回退失败:无法启动SSH服务"
        return 1
    fi
    
    systemctl status sshd  >/dev/null 2>&1
    log_info "回退操作完成"
    return 0
}

# 关闭Telnet服务
close_telnet() {
    log_info "关闭Telnet服务..."
    
    # 停止xinetd服务
    systemctl stop xinetd
    
    # 禁止开机自启
    systemctl disable xinetd >/dev/null 2>&1
    
    # 禁用Telnet配置文件
    mv /etc/xinetd.d/telnet /etc/xinetd.d/telnet.disabled 2>/dev/null || true
    
    # 验证Telnet是否关闭
    log_info "验证Telnet是否关闭:"
    telnet 127.0.0.1 2>&1 | grep -E "Connection refused|无法连接"
    
    log_info "Telnet服务已关闭"
}

# 主函数
main() {
    check_root
    
    # 创建日志文件
    touch ${LOG_FILE}
    chmod 644 ${LOG_FILE}
    
    log_info "===================================="
    log_info "OpenSSH 升级脚本 for CentOS 7/Redhat 7"
    log_info "===================================="
    log_info "开始升级流程,日志将保存到 ${LOG_FILE}"
    
    # 检查系统版本
    test_system
    
    # 安装Telnet
    install_telnet
    
    # 准备工作
    prepare
    
    # 下载OpenSSH源码
    download_openssh
    
    # 备份并卸载默认SSH
    backup_and_remove
    
    # 配置编译选项
    configure_openssh
    
    # 编译与安装
    compile_and_install
    
    # 启动SSH服务
    start_ssh
    
    # 验证升级
    verify_upgrade
    
    # 自动关闭Telnet服务(ansible批量执行模式)
    log_info "===================================="
    log_info "升级完成!自动关闭Telnet服务"
    log_info "===================================="
    
    # 关闭Telnet服务
    close_telnet
    
    log_info "===================================="
    log_info "OpenSSH 升级流程已完成"
    log_info "详细日志请查看: ${LOG_FILE}"
    log_info "===================================="
}

# 处理错误
 trap 'log_error "升级过程中出现错误,执行回退操作..."; if rollback; then close_telnet; else log_warning "回退失败,保留Telnet服务以便排查问题"; fi; exit 1' ERR

# 执行主函数
main

总结

提示:在执行升级操作前,建议先在测试环境中验证脚本的可行性,确保升级过程不会影响生产环境的正常运行。同时,保持网络连接稳定,避免在升级过程中出现网络中断的情况。

希望本文对您有所帮助,如果您有任何问题或建议,欢迎在评论区留言讨论。


关注微信公众号 Linux容器运维 并回复关键字 “视频资料”,即可获取我们整理的 Kubernetes、Docker 容器、Python 编程、Linux 运维等教学视频合集(总计 548GB)。本资源仅面向学习交流使用,请遵守版权和使用规范,严禁商用、转售或违规传播。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐