RHEL / CentOs 7.9 离线升级OpenSSH完整指南

RHEL / CentOs 7.9 离线升级OpenSSH完整指南

文章目录

前言

OpenSSH作为服务器远程管理核心工具,其安全性至关重要。针对命令注入(CVE-2020-15778)、信息泄露(CVE-2020-14145)、访问控制错误(CVE-2019-6110)等高危漏洞,未修复可能导致未授权命令执行、敏感信息泄露等风险,对服务器安全构成严重威胁。

RHEL/CentOS 7.9默认OpenSSH版本无对应修复补丁,官方要求升级至8.4p1及以上版本。本次直接升级至最新稳定版openssh-10.2p1,既彻底规避漏洞风险,又优化密钥认证、日志审计等功能,强化远程管理安全性。

内网服务器断网环境无法在线升级,且OpenSSH升级重启sshd服务时,易因连接中断导致运维失控。为此,本次采用 离线打包+一键自动化脚本 方案,搭配telnet临时连接保障,解决升级痛点。

先在同版本联网测试机下载openssh-10.2p1源码包及编译依赖,打包后上传内网;通过自动化脚本实现全流程部署,减少人工失误。本文适用于RHEL/CentOS 7.9 64位内网服务器,提供安全高效的可复用升级指南。

一、操作环境准备

离线升级的核心前提是保障环境一致性,避免因系统版本、依赖组件差异导致编译失败或升级异常。因此需准备两台系统版本完全一致的 RHEL/CentOS 7.9 服务器,分工如下:一台具备外网访问权限(作为测试机,用于下载依赖和源码包),一台为目标内网服务器(需升级OpenSSH的主机)。

需重点校验两台服务器的内核信息、OpenSSH版本、OpenSSL版本等关键参数,建议通过自动化脚本收集并对比系统信息,确保环境无差异。

环境一致性检查脚本

以下脚本可一键收集系统核心信息,生成报告文件便于对比分析。两台服务器均需执行,确保输出结果一致。

shell 复制代码
cat > check_system.sh << 'EOF'
#!/bin/bash
echo "=== 系统信息收集报告 ==="
echo "收集时间: $(date)"
echo "主机名: $(hostname)"
echo ""

echo "=== 1. 系统版本 ==="
cat /etc/redhat-release
echo ""

echo "=== 2. 内核信息 ==="
uname -a
echo ""

echo "=== 3. OpenSSH版本 ==="
ssh -V 2>&1
echo ""

echo "=== 4. OpenSSL版本 ==="
openssl version
echo ""

echo "=== 5. SSH服务状态 ==="
systemctl status sshd --no-pager
echo ""

echo "=== 6. 已安装SSH相关包 ==="
rpm -qa | grep -E "openssh|ssh" | sort
echo ""

echo "=== 7. 系统架构 ==="
arch
echo ""

echo "=== 8. SELinux状态 ==="
getenforce
echo ""

echo "=== 9. 防火墙状态 ==="
systemctl status firewalld --no-pager
echo ""

echo "=== 10. 网络监听端口 ==="
ss -tlnp | grep :22
echo ""

echo "=== 11. SSH配置文件摘要 ==="
grep -E "^Protocol|^Port|^PermitRootLogin|^PasswordAuthentication" /etc/ssh/sshd_config
EOF

chmod +x check_system.sh
./check_system.sh > system_check_report.txt
  • 执行方式: 在两台服务器分别复制上述脚本,执行后会生成 system_check_report.txt 报告文件。

  • 对比重点: 需逐一核对两份报告中的系统版本、内核信息、OpenSSH/OpenSSL版本、系统架构,确保完全一致;对于SELinux、防火墙状态、SSH配置等非核心参数,可根据实际环境统一调整。

  • 差异处理: 若存在版本差异,需先将两台服务器环境同步(如更新内核、统一SSH相关包版本),再进入后续操作,避免兼容性问题。

  • 联网测试机: 与内网服务器环境一致,用于下载 openssh-10.2p1 源码包、编译依赖组件,最终打包所有资源供内网使用。

  • 内网目标机: 无外网访问权限,为本次OpenSSH升级的目标主机,后续需通过telnet保障临时连接,接收并部署离线资源。

我这边就只使用以下命令进行简单对比:

bash 复制代码
# 查看Red Hat Enterprise Linux详细版本
# cat /etc/os-release
# cat /etc/redhat-release
lsb_release -a 2>/dev/null || cat /etc/system-release

# 内核信息
uname -a

# OpenSSH客户端和服务器版本
ssh -V 2>&1

# 已安装SSH相关包
rpm -qa | grep -E "openssh|ssh" | sort

联网环境如下:

内网环境如下:

从上面两张图片的输出结果可以看出:核心参数完全一致,已经满足离线升级的环境一致性要求 ✅
关键信息对比

检查项 联网环境 内网环境 结果
系统版本 Red Hat Enterprise Linux Server release 7.9 (Maipo) Red Hat Enterprise Linux Server release 7.9 (Maipo) ✅一致
内核版本 3.10.0-1160.el7.x86_64 3.10.0-1160.el7.x86_64 ✅一致
OpenSSH 版本 OpenSSH_7.4p1 OpenSSH_7.4p1 ✅一致
已安装 SSH 相关包 libssh2-1.8.0-4.el7.x86_64、openssh-7.4p1-21.el7.x86_64 等 完全相同 ✅一致

接下来我们就可以在联网测试机上,开始下载 openssh-10.2p1 源码包和编译依赖了。

二、离线准备相关安装包

相较于手动下载源码、手动编译的传统方式,通过自动化脚本构建 RPM 包更适配 RHEL/CentOS 系统的包管理逻辑,不仅能简化离线部署流程,还能保留系统自带的包管理优势(如便捷回滚、依赖校验)。建议选用成熟的开源自动化构建仓库,实现 OpenSSH RPM 包的一键编译打包,大幅降低操作复杂度。下面的操作均在联网测试机上面执行。

一键自动构建RPM包

本次选用的 openssh-rpms仓库专为 RHEL/CentOS 系统设计,提供了完整的 OpenSSH RPM 包构建工具链,核心优势如下:

  • 内置版本配置文件,可灵活指定目标 OpenSSH 版本(如本次需构建的 10.2p1);
  • 脚本自动处理源码下载、依赖检查、编译打包全流程,无需手动干预;
  • 适配多发行版,可自动检测 RHEL/CentOS 7.9 系统环境,兼容性强。

仓库地址:https://github.com/boypt/openssh-rpms

仓库核心文件说明:

脚本/配置文件 功能说明
pullsrc.sh 自动从官方源下载指定版本的 OpenSSH 源代码
compile.sh 自动编译源代码并打包为 RPM 安装包
version.env 版本配置文件,可自定义 OpenSSH、OpenSSL 版本

一键打包具体步骤

  1. 克隆构建仓库
bash 复制代码
# 克隆开源构建仓库
git clone https://github.com/boypt/openssh-rpms
cd openssh-rpms
ls 
cat version.env
  1. 修改版本配置(可选)
    默认配置是最新版,可能非你想要的目标版本,可以需编辑 version.env 文件指定版本,如果你想升级到最新忽略即可:
bash 复制代码
# 编辑版本配置文件
vim version.env

# 关键配置修改(确保以下参数正确)
OPENSSH_VER="10.2p1"  # 目标OpenSSH版本
OPENSSL_VER="1.1.1w"  # 适配的OpenSSL版本(建议>=1.1.1)
# 其他参数保持默认即可

# 按ESC 输出:wq保存退出
  1. 安装构建依赖
    构建 RPM 包需先安装编译工具链和依赖库,执行以下命令:
bash 复制代码
# 安装开发工具组
yum groupinstall -y "Development Tools"
# 安装编译依赖
yum install -y imake rpm-build pam-devel krb5-devel zlib-devel libXt-devel libX11-devel gtk2-devel perl perl-IPC-Cmd perl-Time-Piece
  1. 一键构建 RPM 包
    脚本会自动下载指定版本的源码,并编译为 RPM 包:
bash 复制代码
# 自动下载源码 + 编译打包
./pullsrc.sh && ./compile.sh
1.简化的一键脚本
shell 复制代码
#!/bin/bash
# build-openssh.sh

set -e

echo "开始一键构建OpenSSH RPM包..."

# 检查依赖
if ! command -v rpmbuild &> /dev/null; then
    echo "安装构建依赖..."
    yum install -y "Development Tools"
    yum install -y imake rpm-build pam-devel krb5-devel zlib-devel libXt-devel libX11-devel gtk2-devel perl perl-IPC-Cmd perl-Time-Piece
fi

# 检查配置,如果需要特定版本
echo "当前配置:"
cat version.env

# 构建RPM包
./pullsrc.sh && ./compile.sh

echo "构建完成!RPM包位置:$(./compile.sh RPMDIR)"

参考文章:OpenSSH v10.2p1 for CentOS7 RPM 一键安装包

2.打包RPM包和依赖
shell 复制代码
#!/bin/bash
# save as: package-rpm.sh
# 企业级离线包构建(支持 yum localinstall)

WORK_DIR="/root/openssh-offline-rpms"
mkdir -p  $WORK_DIR
cd  $WORK_DIR

# 1. 先安装 createrepo(构建主机联网,可安全安装)
echo "安装 createrepo 工具..."
yum install -y yum-utils createrepo  # 关键:在构建主机安装

# 2. 复制生成的 RPM 包
mkdir -p rpms
find ~/openssh-rpms -name "*.rpm" -type f -exec cp {} rpms/ \;

# 3. 下载运行时依赖(RHEL 7.9 官方兼容)
echo "下载运行时依赖..."
cd rpms

# 首先下载必要的依赖,但排除openssh本身
yumdownloader --resolve --destdir=./ --exclude=openssh\* openssh-server openssh-clients openssh

# 然后手动下载可能遗漏的依赖
REQUIRED_DEPS="openssl zlib pam audit-libs libselinux krb5-libs libedit nss-util tcp_wrappers"
for dep in $REQUIRED_DEPS; do
        yumdownloader --resolve --destdir=./ $dep
        done

# 移除可能重复的openssh包(如果有的话)
rm -f openssh-*.rpm 2>/dev/null

# 复制回我们构建的openssh包
find ~/openssh-rpms -name "*.rpm" -type f -exec cp {} ./ \;

cd ..

# 4. 创建本地 YUM 仓库(关键!使用 createrepo)
echo "创建本地 YUM 仓库..."
createrepo rpms/

# 列出所有可用的包
echo "仓库中的RPM包列表:"
ls -la rpms/*.rpm

# 5. 创建安装脚本(支持 yum localinstall)
cat > install.sh << 'EOF'
#!/bin/bash
set -e

echo "=== OpenSSH RPM离线安装 (yum localinstall) ==="
echo "开始时间:  $(date)"

# 备份当前配置
BACKUP_DIR="/root/ssh-backup-$(date +%Y%m%d_%H%M%S)"
mkdir -p  $BACKUP_DIR
cp -rp /etc/ssh  $BACKUP_DIR/ 2>/dev/null || true
rpm -qa | grep openssh >  $BACKUP_DIR/openssh-packages.txt

# 获取当前目录路径
CURRENT_DIR=$(pwd)
echo "当前目录: $CURRENT_DIR"

# 检查rpms目录是否存在
if [ ! -d "$CURRENT_DIR/rpms" ]; then
    echo "错误: 找不到rpms目录!"
    echo "请确保在解压后的目录中运行此脚本"
    exit 1
fi

# 创建本地仓库配置文件
cat > /etc/yum.repos.d/local-openssh.repo << REPO_EOF
[local-openssh]
name=Local OpenSSH Repository
baseurl=file://$CURRENT_DIR/rpms
enabled=1
gpgcheck=0
priority=1
skip_if_unavailable=1
REPO_EOF

# 关键:临时禁用所有远程仓库配置
echo "备份并禁用所有远程仓库..."
if [ -d /etc/yum.repos.d ]; then
    TIMESTAMP=$(date +%Y%m%d_%H%M%S)
    BACKUP_DIR="/etc/yum.repos.d/backup_$TIMESTAMP"
    mkdir -p "$BACKUP_DIR"        
    # 移动除 local-openssh.repo 外的所有仓库配置文件
    for repo_file in /etc/yum.repos.d/*.repo; do
        if [ "$repo_file" != "/etc/yum.repos.d/local-openssh.repo" ] && [ -f "$repo_file" ]; then
            mv "$repo_file" "$BACKUP_DIR/"
        fi
    done
                                          
    echo "已备份 $(ls "$BACKUP_DIR"/*.repo 2>/dev/null | wc -l) 个远程仓库配置文件到 $BACKUP_DIR"
fi

echo "创建仓库配置文件: /etc/yum.repos.d/local-openssh.repo"
echo "仓库路径: file://$CURRENT_DIR/rpms"

# 列出可用的RPM包
echo "可用的RPM包:"
ls -la "$CURRENT_DIR/rpms/"*.rpm 2>/dev/null | wc -l
ls -la "$CURRENT_DIR/rpms/"*.rpm 2>/dev/null | head -20

# 清理缓存,但跳过yum makecache(关键修改!)
echo "清理yum缓存..."
yum clean all
echo "跳过yum makecache(离线环境无需创建网络缓存)..."

# 检查仓库是否可用
echo "检查本地仓库状态..."
yum repolist all 2>/dev/null || echo "仓库列表可能为空"

# 检查可用的openssh包
echo "检查本地仓库中的openssh包..."
yum --disablerepo="*" --enablerepo="local-openssh" list available 2>/dev/null | grep openssh || echo "使用本地仓库中的openssh包"

# 停止sshd服务
systemctl stop sshd
systemctl disable sshd.socket 2>/dev/null || true

# 卸载旧版本openssh
echo "卸载旧版openssh..."
rpm -e --nodeps openssh-server openssh-clients openssh 2>/dev/null || true

# 使用 yum install 安装
echo "开始安装新版本openssh..."
yum --disablerepo="*" --enablerepo="local-openssh" install -y openssh openssh-server openssh-clients

# 验证安装
echo "验证安装的openssh版本:"
rpm -qa | grep openssh
ssh -V 2>&1

# 启动服务
systemctl daemon-reload
systemctl enable sshd
systemctl start sshd

# 恢复远程仓库配置(可选)
if [ -d "$BACKUP_DIR" ] && [ -n "$(ls -A "$BACKUP_DIR"/*.repo 2>/dev/null)" ]; then
    read -p "是否恢复远程仓库配置?[y/N]: " -n 1 -r
    echo
    if [[ $REPLY =~ ^[Yy]$ ]]; then
       mv "$BACKUP_DIR"/*.repo /etc/yum.repos.d/ 2>/dev/null
       echo "远程仓库配置已恢复"
    else
       echo "远程仓库配置仍位于 $BACKUP_DIR,可手动恢复"
    fi
fi

echo "安装完成!当前OpenSSH版本: "
ssh -V
EOF

chmod +x install.sh

# 6. 打包(包含 repo 文件和 rpm 包)
cd  $WORK_DIR
tar -czf openssh-rpm-offline.tar.gz rpms/ install.sh

echo "✅ 离线RPM包已创建: openssh-rpm-offline.tar.gz"
echo "大小:  $(du -sh openssh-rpm-offline.tar.gz)"
echo "RPM包数量: $(find rpms -name "*.rpm" | wc -l)"

至此,我们已在联网测试机上完成了 OpenSSH 离线升级包的全量构建:最终生成的 openssh-rpm-offline.tar.gz 离线包包含了 openssh-10.2p1 核心 RPM 包、所有运行时依赖、本地 YUM 仓库配置文件及一键安装脚本,无需依赖外网即可完成内网服务器的完整升级。

该离线包的核心优势的是:通过 createrepo 构建本地 YUM 仓库,支持 yum localinstall 安装方式,可自动处理依赖关系,且保留了系统包管理的回滚能力;搭配自动化安装脚本,大幅降低内网部署的操作复杂度和人为失误风险。

接下来,我们将聚焦内网目标服务器,通过 「连接保障 + 离线包上传 + 一键脚本执行」 的流程,完成 OpenSSH 的安全升级。

三、内网服务器离线升级

在前两个章节中,我们已经完成了所有前置准备工作:验证了环境一致性,并在联网测试机上成功构建并打包了包含 OpenSSH 10.2p1 RPM 包、所有运行时依赖及一键安装脚本的完整离线包 openssh-rpm-offline.tar.gz。现在,我们将把工作重心转移到内网目标服务器上,执行最终的升级操作。

核心原则:安全第一,连接保活!

OpenSSH 升级过程中,任何配置错误或服务启动失败都可能导致 SSH 连接永久中断,使服务器"失联"。因此,**在执行任何升级操作前,必须建立一个独立于 SSH 的备用连接通道。**Telnet 虽然不安全,但在完全隔离的内网环境中,作为临时的应急通道是业界通用的最佳实践。

(一)升级前准备工作

1. 建立备用连接(关键步骤)

升级过程中SSH服务会重启,为防止连接中断导致无法管理服务器,必须建立备用连接,这里我们可以使用两种稳定的方案:一是使用telnet进行临时连接,二是为ssh新增其它的端口。(内网环境相关安装包如何上传见参考博文):

但是我安装过程中发现,其实这样安装并不会断开连接,所以,其实并不用telnet,开启两个ssh连接保活就行啦。

  1. 离线安装telnet服务(内网环境)
bash 复制代码
# 在公网主机上下载telnet相关RPM包
cd ~/openssh-offline-rpms
mkdir -p telnet-rpms
cd telnet-rpms

# 下载telnet服务端和客户端
repotrack telnet-server telnet
repotrack xinetd  # telnet-server需要xinetd

# 在内网主机安装telnet
cat > install-telnet.sh << 'EOF'
#!/bin/bash
# 安装telnet作为备用连接
set -e
echo "安装telnet备用连接..."

# 安装telnet RPM包
rpm -ivh telnet-*.rpm
rpm -ivh telnet-server-*.rpm
rpm -ivh xinetd-*.rpm

# 配置telnet
sed -i 's/disable.*=.*yes/disable = no/' /etc/xinetd.d/telnet
echo 'pts/0' >> /etc/securetty
echo 'pts/1' >> /etc/securetty

# 启动服务
systemctl start xinetd
systemctl enable xinetd

# 添加防火墙规则
firewall-cmd --permanent --add-service=telnet
firewall-cmd --reload

echo "telnet安装完成,端口23已开放"
EOF

chmod +x install-telnet.sh
./install-telnet.sh
  1. 或者使用第二个SSH端口
bash 复制代码
cat > add-second-ssh.sh << 'EOF'
#!/bin/bash
# 添加第二个SSH端口作为备用
set -e
echo "配置第二个SSH端口..."

# 备份原配置
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup.$(date +%Y%m%d)

# 添加第二个端口
echo "Port 2222" >> /etc/ssh/sshd_config
echo "AllowUsers root" >> /etc/ssh/sshd_config

# 重启SSH服务
systemctl reload sshd

# 防火墙开放新端口
firewall-cmd --permanent --add-port=2222/tcp
firewall-cmd --reload

echo "第二个SSH端口2222已开放"
EOF

chmod +x add-second-ssh.sh
./add-second-ssh.sh

这里关于设置telnet的话可以参考之前的文章:
RHEL/CentOS 7.9环境中离线安装Telnet服务完整指南

2. 上传并验证离线包

bash 复制代码
# 1. 上传离线包到内网服务器,可以使用U盘或者SCP等。
# 假设文件已上传到 /root/ 目录
cd /root/

# 2. 验证文件完整性
echo "验证离线包完整性..."
if [ ! -f "openssh-rpm-offline.tar.gz" ]; then
    echo "错误: 未找到离线包文件"
    exit 1
fi

# 查看文件信息
ls -lh openssh-rpm-offline.tar.gz
file openssh-rpm-offline.tar.gz

# 3. 解压离线包
echo "解压离线包..."
tar -xzf openssh-rpm-offline.tar.gz
cd rpms

# 4. 检查文件结构
echo "检查文件结构..."
ls -la
tree /root/rpms/ 2>/dev/null || ls -R /root/rpms/

3. 备份当前系统状态

bash 复制代码
cat > pre-upgrade-check.sh << 'EOF'
#!/bin/bash
# 升级前检查和备份
set -e

echo "=== 升级前系统检查 ==="
echo "检查时间: $(date)"

# 1. 检查当前系统
echo "1. 当前系统信息:"
cat /etc/redhat-release
uname -a
ssh -V

# 2. 检查磁盘空间
echo "2. 磁盘空间检查:"
df -h /
required_space=1000  # 需要至少1GB空间
available_space=$(df / --output=avail | tail -1 | tr -d ' ')
if [ $available_space -lt $required_space ]; then
    echo "警告: 磁盘空间不足,需要至少1GB空闲空间"
    read -p "是否继续? (y/N): " -n 1 -r
    echo
    if [[ ! $REPLY =~ ^[Yy]$ ]]; then
        exit 1
    fi
fi

# 3. 备份当前配置
echo "3. 备份当前配置..."
BACKUP_DIR="/root/openssh-pre-upgrade-backup-$(date +%Y%m%d_%H%M%S)"
mkdir -p $BACKUP_DIR

# 备份SSH配置文件
cp -rp /etc/ssh $BACKUP_DIR/
cp -rp /root/.ssh $BACKUP_DIR/root-ssh 2>/dev/null || true

# 备份当前安装的OpenSSH包
rpm -qa | grep -E "openssh|ssh" > $BACKUP_DIR/openssh-packages.txt
rpm -qi openssh openssh-server openssh-clients > $BACKUP_DIR/openssh-info.txt 2>/dev/null || true

# 备份二进制文件
which sshd >/dev/null 2>&1 && cp $(which sshd) $BACKUP_DIR/
which ssh >/dev/null 2>&1 && cp $(which ssh) $BACKUP_DIR/

# 备份PAM配置
cp -p /etc/pam.d/sshd $BACKUP_DIR/ 2>/dev/null || true

echo "备份完成: $BACKUP_DIR"

# 4. 检查备用连接
echo "4. 检查备用连接..."
if systemctl is-active xinetd >/dev/null 2>&1; then
    echo "✓ Telnet服务已启动"
elif netstat -tlnp | grep -q :2222; then
    echo "✓ 备用SSH端口2222已监听"
else
    echo "⚠ 未检测到备用连接,建议先建立备用连接"
    read -p "是否继续升级? (y/N): " -n 1 -r
    echo
    if [[ ! $REPLY =~ ^[Yy]$ ]]; then
        exit 1
    fi
fi

echo "升级前检查完成 ✓"
EOF

chmod +x pre-upgrade-check.sh
./pre-upgrade-check.sh

(二)执行离线升级

在解压目录下执行之前准备好的脚本即可,建议保障升级失败仍可以远程连接。

bash 复制代码
# 先确保解压
tar -xzf openssh-rpm-offline.tar.gz

# 方法1: 简单快速升级(推荐tlenet下使用)
cd /root
./install.sh

# 方法2: 使用screen防止中断
screen -S ssh-upgrade
cd /root
./install.sh

# 方法3: 使用nohup后台运行(推荐)
cd /root
nohup ./install.sh > upgrade.log 2>&1 &
tail -f upgrade.log

这里我们看到版本号已经显示为OpenSSH_10.2p1了,说明我们升级成功。

(三)升级后验证

最简单是使用ssh root@127.0.0.1进行连接测试,能正常连接说明升级成功。

  1. 验证脚本
bash 复制代码
cat > verify-upgrade.sh << 'EOF'
#!/bin/bash
# 升级后验证脚本

echo "=== OpenSSH升级验证报告 ==="
echo "验证时间: $(date)"
echo ""

echo "1. 版本验证"
echo "----------"
echo -n "SSH客户端版本: "
ssh -V 2>&1
echo -n "SSH服务器版本: "
/usr/sbin/sshd -V 2>&1 || echo "无法获取sshd版本"
echo ""

echo "2. 服务状态"
echo "----------"
systemctl status sshd --no-pager
echo ""

echo "3. 进程检查"
echo "----------"
ps aux | grep sshd | grep -v grep
echo -n "sshd进程数: "
ps aux | grep sshd | grep -v grep | wc -l
echo ""

echo "4. 端口监听"
echo "----------"
echo "TCP监听端口:"
netstat -tlnp | grep sshd
echo ""
echo "SS连接状态:"
ss -tlnp | grep :22
echo ""

echo "5. 连接测试"
echo "----------"
echo "测试本地连接:"
timeout 5 ssh -o BatchMode=yes -o ConnectTimeout=3 -o StrictHostKeyChecking=no -o PasswordAuthentication=no localhost echo "✓ 本地连接成功" 2>&1 || echo "⚠ 本地连接失败"
echo ""

echo "6. 配置文件检查"
echo "--------------"
echo "配置文件位置: /etc/ssh/sshd_config"
echo "配置文件权限:"
ls -la /etc/ssh/sshd_config
echo ""
echo "配置文件语法检查:"
if /usr/sbin/sshd -t 2>&1; then
    echo "✓ 配置文件语法正确"
else
    echo "✗ 配置文件有错误"
    /usr/sbin/sshd -t
fi
echo ""

echo "7. 日志检查"
echo "----------"
echo "最近5分钟日志:"
journalctl -u sshd --since="5 minutes ago" --no-pager | tail -20
echo ""

echo "8. 防火墙检查"
echo "-----------"
if systemctl is-active firewalld >/dev/null 2>&1; then
    echo "防火墙状态: 运行中"
    echo "SSH服务规则:"
    firewall-cmd --list-services | grep -q ssh && echo "✓ SSH服务已允许" || echo "✗ SSH服务未允许"
else
    echo "防火墙状态: 未运行"
fi
echo ""

echo "9. SELinux检查"
echo "------------"
echo -n "SELinux状态: "
getenforce
if getenforce | grep -q "Enforcing"; then
    echo "检查SSH端口标签:"
    semanage port -l | grep ssh_port_t
fi
echo ""
echo "验证完成: $(date)"
EOF

chmod +x verify-upgrade.sh
./verify-upgrade.sh 
./verify-upgrade.sh > upgrade-verify-report.txt
  1. 功能测试
bash 复制代码
cat > functional-test.sh << 'EOF'
#!/bin/bash
# OpenSSH功能测试

echo "=== OpenSSH 10.2p1 功能测试 ==="
echo ""

# 测试1: 密钥认证
echo "1. 密钥对生成测试"
ssh-keygen -t rsa -f /tmp/test_key -N "" -q
if [ $? -eq 0 ]; then
    echo "✓ RSA密钥生成成功"
else
    echo "✗ RSA密钥生成失败"
fi

# 测试2: SFTP功能
echo "2. SFTP功能测试"
echo "quit" | sftp -o BatchMode=yes -o ConnectTimeout=5 localhost 2>&1 | grep -q "Connection closed" && echo "✓ SFTP连接正常" || echo "⚠ SFTP连接异常"

# 测试3: SCP功能
echo "3. SCP功能测试"
echo "test" > /tmp/test_scp.txt
scp -o BatchMode=yes -o ConnectTimeout=5 /tmp/test_scp.txt localhost:/tmp/test_scp_copy.txt 2>&1 | grep -q "test_scp.txt" && echo "✓ SCP功能正常" || echo "⚠ SCP功能异常"

# 测试4: 加密算法支持
echo "4. 加密算法支持"
ssh -Q cipher | head -5 | xargs echo "支持的加密算法示例:"

# 测试5: 认证方法
echo "5. 认证方法支持"
ssh -Q auth | xargs echo "支持的认证方法:"

# 清理
rm -f /tmp/test_key /tmp/test_key.pub /tmp/test_scp.txt /tmp/test_scp_copy.txt

echo ""
echo "功能测试完成"
EOF

chmod +x functional-test.sh
./functional-test.sh

四、常见问题与解决方案

❌ 问题1:repomd.xml: [Errno 14] Couldn't open file...

现象

执行 install.sh 时 YUM 报错找不到 repomd.xml

原因

构建离线包时 createrepo 未成功执行(构建主机未安装该工具),导致 rpms/ 目录缺失 repodata/

解决方案 (二选一):

✅ 方案A:重新构建离线包(推荐)

bash 复制代码
# 在构建主机执行
cd /root/openssh-offline-rpms/rpms
yum install -y createrepo
createrepo .  # 生成 repodata 目录
cd .. && tar -czf openssh-rpm-offline-fixed.tar.gz rpms/ install.sh

验证:ls rpms/repodata/repomd.xml 必须存在!

✅ 方案B:修改安装脚本(紧急修复)

bash 复制代码
# 在内网服务器替换 install.sh 内容
# 在内网服务器执行(覆盖原 install.sh)
cat > install.sh << 'EOF'
#!/bin/bash
set -e

echo "=== OpenSSH RPM离线安装 (直接RPM安装) ==="
echo "开始时间:  $ (date)"

# 备份配置
BACKUP_DIR="/root/ssh-backup- $ (date +%Y%m%d_%H%M%S)"
mkdir -p  $ BACKUP_DIR
cp -rp /etc/ssh  $ BACKUP_DIR/ 2>/dev/null || true
rpm -qa | grep openssh >  $ BACKUP_DIR/openssh-packages.txt

# 停止sshd(保活关键!)
systemctl stop sshd
systemctl disable sshd.socket 2>/dev/null || true

# 直接安装所有RPM(自动处理依赖顺序)
echo "正在安装RPM包..."
rpm -Uvh rpms/*.rpm 2>&1 | tee  $ BACKUP_DIR/install.log

# 启动新服务
systemctl daemon-reload
systemctl enable sshd
systemctl start sshd

echo "✅ 安装完成!"
ssh -V
EOF

chmod +x install.sh
./install.sh
# 核心修改:删除 YUM 仓库配置,改用 rpm -Uvh rpms/*.rpm

优势:无需 repodata,100% 兼容 RHEL 7.9 离线环境

注意:确保离线包已包含所有依赖(yumdownloader --resolve 已下载)

❌ 问题2:内网离线环境中yum命令因无法连接远程仓库而失败

现象:

在内网离线环境中执行yum命令时,报错"Could not resolve host: mirrors.aliyun.com",提示"One of the configured repositories failed (CentOS 7 Base)",导致操作无法继续。

原因分析:
1.内网环境无外网连接: 服务器无法解析域名 mirrors.aliyun.com
2.yum默认尝试所有仓库: 即使通过 --disablerepo指定了本地仓库,yum命令在运行时仍会检查所有配置的仓库
3.缓存机制问题: yum makecache会尝试连接所有仓库更新元数据

解决方案
方案1:临时禁用远程仓库(推荐)

在执行yum命令时明确禁用所有远程仓库:

bash 复制代码
# 禁用所有仓库,只启用本地仓库
yum --disablerepo="*" --enablerepo="local-openssh" [command]

# 示例:安装openssh
yum --disablerepo="*" --enablerepo="local-openssh" install -y openssh openssh-server openssh-clients

方案2:永久禁用远程仓库配置

备份并移除所有远程仓库配置文件:

bash 复制代码
# 创建备份目录
mkdir -p /etc/yum.repos.d/backup_$(date +%Y%m%d_%H%M%S)

# 备份并移动所有远程仓库配置
for repo_file in /etc/yum.repos.d/*.repo; do
    if [[ $repo_file != *"local-openssh"* ]]; then
        mv "$repo_file" /etc/yum.repos.d/backup_*/ 2>/dev/null || true
    fi
done

# 验证只剩下本地仓库
ls -la /etc/yum.repos.d/*.repo

方案3:配置跳过不可用仓库

修改仓库配置,使其在网络不可用时自动跳过:

bash 复制代码
# 为所有仓库添加 skip_if_unavailable=1
for repo_file in /etc/yum.repos.d/*.repo; do
    if grep -q "^\[" "$repo_file"; then
        # 在每个仓库节后添加 skip_if_unavailable=1
        sed -i '/^\[.*\]/a skip_if_unavailable=1' "$repo_file"
    fi
done

方案4:修改安装脚本避免网络操作

在 install.sh中关键修改点:

bash 复制代码
# 1. 跳过 yum makecache(离线环境不需要)
echo "清理yum缓存..."
yum clean all
# 注释掉或删除下面这行
# yum makecache

# 2. 在仓库配置中添加 skip_if_unavailable=1
cat > /etc/yum.repos.d/local-openssh.repo << EOF
[local-openssh]
name=Local OpenSSH Repository
baseurl=file://$(pwd)/rpms
enabled=1
gpgcheck=0
priority=1
skip_if_unavailable=1
EOF

方案5:使用yum-config-manager管理仓库

bash 复制代码
# 禁用指定仓库
yum-config-manager --disable centos7-base

# 或者设置跳过不可用仓库
yum-config-manager --save --setopt=centos7-base.skip_if_unavailable=true

🔍 验证解决效果

bash 复制代码
# 验证yum仓库状态
yum repolist all

# 验证本地仓库可用性
yum --disablerepo="*" --enablerepo="local-openssh" list available

# 测试openssh安装
yum --disablerepo="*" --enablerepo="local-openssh" install -y openssh --downloadonly

后记

历时数日的OpenSSH离线升级实践,让我深刻体会到在复杂的内网环境下进行系统组件升级的挑战与收获。这次实践不仅解决了一个具体的技术问题,更是一次对系统架构、离线部署和安全运维的全面思考。

技术实践的启示

  • 离线部署的艺术: 在无外网访问的环境中,每一个依赖包、每一个配置项都需要预先考虑周全。通过构建完整的离线YUM仓库,我们不仅解决了OpenSSH的升级问题,更为企业内网的软件分发建立了一套可复用的标准流程。这种"自包含"的部署包设计思想,在如今多云、混合云架构日益普及的背景下尤为重要。
  • 安全与稳定的平衡: OpenSSH作为服务器的"入口",其安全性不言而喻。但升级过程中最让人心惊胆战的是"连接中断"的风险。通过telnet保活、双重验证机制,我们在追求安全的同时也保障了操作的稳定性。这提醒我们,在生产环境中,任何变更都需要有"Plan B",任何操作都要有回滚方案。
  • 自动化的力量: 从环境检查、依赖收集、编译构建到最终部署,全程脚本化不仅提升了效率,更重要的是减少了人为错误。特别是在凌晨变更窗口有限的情况下,自动化脚本能大幅降低操作压力和出错概率。

工程化的思考

  • 版本管理的智慧: 选择OpenSSH 10.2p1而不是简单的"最新版",是基于稳定性、兼容性和安全性的综合考量。在企业环境中,软件版本的选择往往是艺术与科学的结合------既要修复已知漏洞,又要避免引入未知风险。
  • 文档即资产: 详细的实施文档不仅是操作指南,更是团队的知识沉淀。在本文的编写过程中,我特意记录了每个失败案例和解决方案,这些经验教训对于未来处理类似问题具有重要参考价值。
  • 可观测性的重要性: 升级后的验证环节往往被忽视,但恰恰是确保变更成功的关键。我们设计的验证脚本覆盖了服务状态、功能测试、安全配置等多个维度,确保升级不仅是"版本号变了",更是整个SSH服务都健康运行。

对未来的展望

这次实践让我看到了传统运维向云原生运维转型的必要性。如果能将这种离线升级过程容器化、Pipeline化,将其纳入持续交付流程,将会大幅提升企业内网环境下的运维效率。

同时,我也在思考如何将这类离线包的制作过程进一步产品化------开发一个通用的离线包构建工具,支持多种系统、多种软件的离线部署,这将是很有价值的开源项目。

致谢

感谢开源社区的无私奉献,从OpenSSH的维护者到自动化构建脚本的贡献者,正是这些默默付出的开发者让我们能够站在巨人的肩膀上解决问题。也要感谢在测试环境中"牺牲"的多台虚拟机,它们用重启和错误帮助我们找到了最佳实践。

技术之路永无止境,每一次问题的解决都是下一次探索的起点。希望这份实践记录能为遇到类似挑战的同行提供参考,也期待听到更多的改进建议和经验分享。

相关推荐
蜡笔小新拯救世界2 小时前
简单rce的ctf题目绕过
linux·c++·web安全·c#
运维有小邓@2 小时前
如何在 CentOS 主机上配置集中式 Syslog 服务器
linux·服务器·centos
市安2 小时前
负载均衡入门:HAProxy 双 Web 节点集群配置与验证
linux·运维·服务器·网络·nginx·负载均衡·haproxy
强风7942 小时前
Linux—Socket编程TCP
linux·服务器·tcp/ip
_OP_CHEN2 小时前
【Linux系统编程】(二十二)从磁盘物理结构到地址映射:Ext 系列文件系统硬件底层原理深度剖析
linux·操作系统·文件系统·c/c++·计算机硬件·ext文件系统·磁盘寻址
一直跑2 小时前
通过所里的服务器连接到组里的服务器,然后可视化组里的文件和代码,并修改等操作(VScode/vscode/mobaxterm)
linux·运维·服务器
码农编程录3 小时前
【notes11】并发与竞争
linux
mobai73 小时前
Ubuntu环境上安装NTP服务
linux·运维·ubuntu
郝学胜-神的一滴3 小时前
Linux Socket编程核心:深入解析sockaddr数据结构族
linux·服务器·c语言·网络·数据结构·c++·架构