OpenEuler 22.03 手动升级 OpenSSH 至 10.2p1 完整方案
本文档提供在 OpenEuler 22.03 LTS 系统上,将 OpenSSH 手动升级至 10.2p1 版本的完整、安全的操作方案。方案包含准备、升级、验证、回滚四个核心环节,并重点集成了对 SELinux 策略的自动化处理。
1、方案概述
| 项目 | 说明 |
| 目标 | 将 OpenEuler 22.03 上的 OpenSSH 安全地升级至 10.2p1 版本 |
| 核心风险 | 升级过程中 SSH 服务中断导致服务器失联 |
| 关键技术点 | 1. 维护备援连接2. 自动化处理 SELinux 策略3. 提供完整回滚方案 |
| 预计耗时 | 30 - 60 分钟 |
懒得写过程了,改成一键升级脚本,同时会询问是否禁用root登录,使用user登录。建议提前建立好user用户。运行脚本使用root权限,同时给脚本加上执行权限
#!/bin/bash
# OpenEuler 22.03 OpenSSH 10.2p1 一键升级脚本
set -e
# 配置变量
OPENSSH_VERSION="10.2p1"
OPENSSH_URL="https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-${OPENSSH_VERSION}.tar.gz"
BACKUP_DIR="/opt/backup/ssh_$(date +%Y%m%d_%H%M%S)"
LOG_FILE="/var/log/openssh_upgrade_$(date +%Y%m%d_%H%M%S).log"
SELINUX_MODULE_NAME="my_sshd_fix"
# 颜色输出定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
info() { echo -e "${GREEN}[INFO]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1"; }
# 权限检查
if [[ $EUID -ne 0 ]]; then
error "此脚本需要root权限运行,请使用 sudo 执行"
exit 1
fi
# 磁盘空间检查函数
check_disk_space() {
local required_space=500
local available_space=$(df /tmp | awk 'NR==2 {print int($4/1024)}')
if [ $available_space -lt $required_space ]; then
error "磁盘空间不足,需要至少 ${required_space}MB,当前可用: ${available_space}MB"
exit 1
fi
}
# 检查备援连接函数
check_backup_connection() {
echo -e "\n${YELLOW}=== 备援连接检查 ===${NC}"
echo -e "当前SSH连接信息:"
echo -e " 本地IP: $(who am i | awk '{print $5}' | tr -d '()')"
echo -e " 登录用户: $(whoami)"
echo -e " 登录时间: $(who am i | awk '{print $3, $4}')"
echo -e " 当前会话: $(tty)"
echo -e "\n${GREEN}请确认以下备援连接方式之一已准备就绪:${NC}"
echo -e " ✓ 物理控制台访问"
echo -e " ✓ IPMI/iDRAC远程管理"
echo -e " ✓ 串口控制台"
echo -e " ✓ VNC/远程桌面"
echo -e " ✓ 其他SSH会话(保持连接)"
echo -e "\n${RED}警告:升级过程中SSH服务会重启,可能导致当前连接中断!${NC}"
echo -e "${YELLOW}如果升级失败,备援连接将用于恢复系统访问。${NC}"
}
# 检查SELinux状态
check_selinux_status() {
info "检查SELinux状态..."
if command -v getenforce >/dev/null 2>&1; then
local selinux_status=$(getenforce 2>/dev/null)
info "SELinux状态: ${selinux_status}"
if [ "$selinux_status" = "Enforcing" ]; then
info "SELinux处于强制模式,将进行策略处理"
elif [ "$selinux_status" = "Permissive" ]; then
warn "SELinux处于宽松模式,建议切换到强制模式"
else
warn "SELinux已禁用"
fi
# 检查SELinux相关工具
if ! command -v semodule >/dev/null 2>&1; then
warn "SELinux工具未安装,将安装policycoreutils-python-utils"
fi
return 0
else
warn "SELinux未安装或不可用"
return 1
fi
}
# 检查并创建备份目录
create_backup() {
info "创建备份目录 ${BACKUP_DIR}"
sudo mkdir -p "${BACKUP_DIR}"
}
# 执行系统备份
backup_system() {
info "开始系统备份..."
# 备份关键文件
sudo tar -czvf "${BACKUP_DIR}/ssh_files.tar.gz" \
/etc/ssh \
/usr/bin/ssh \
/usr/sbin/sshd \
/usr/bin/ssh-keygen \
/usr/bin/scp \
/usr/bin/sftp \
/lib/systemd/system/sshd.service \
/etc/pam.d/sshd 2>>${LOG_FILE} || warn "部分文件备份失败"
# 备份SELinux策略
sudo semodule -B | tee -a ${LOG_FILE}
info "系统备份完成,备份文件保存在: ${BACKUP_DIR}"
}
# 安装编译依赖
install_dependencies() {
info "安装编译依赖包..."
sudo dnf install -y gcc make zlib-devel openssl-devel pam-devel \
systemd-devel wget tar policycoreutils-python-utils >>${LOG_FILE} 2>&1
}
# 改进的下载函数
download_openssh() {
info "下载OpenSSH ${OPENSSH_VERSION} 源码..."
# 设置下载超时和重试
local max_retries=3
local timeout=60
local retry_count=0
while [ $retry_count -lt $max_retries ]; do
info "尝试下载 (第 $((retry_count + 1)) 次)..."
if wget --timeout=${timeout} --tries=1 "${OPENSSH_URL}" -O "openssh-${OPENSSH_VERSION}.tar.gz" 2>>${LOG_FILE}; then
info "源码包下载成功"
break
else
retry_count=$((retry_count + 1))
if [ $retry_count -lt $max_retries ]; then
warn "下载失败,${timeout}秒后重试..."
sleep ${timeout}
else
error "下载失败,已尝试 ${max_retries} 次"
exit 1
fi
fi
done
# 下载签名文件
if wget --timeout=${timeout} "${OPENSSH_URL}.sig" -O "openssh-${OPENSSH_VERSION}.tar.gz.sig" 2>>${LOG_FILE}; then
info "签名文件下载成功"
else
warn "签名文件下载失败,跳过签名验证"
fi
# 验证文件完整性
if [ -f "openssh-${OPENSSH_VERSION}.tar.gz" ]; then
local file_size=$(stat -c%s "openssh-${OPENSSH_VERSION}.tar.gz")
if [ $file_size -gt 1000000 ]; then # 至少1MB
info "源码包大小: $(($file_size / 1024 / 1024))MB"
else
error "源码包文件异常,大小: ${file_size} bytes"
exit 1
fi
else
error "源码包文件不存在"
exit 1
fi
# 简单的校验和验证
local sha_check=$(sha256sum "openssh-${OPENSSH_VERSION}.tar.gz" | awk '{print $1}')
info "源码包SHA256校验和: ${sha_check}"
warn "请手动验证上述校验和是否与官网公布的一致!"
sleep 3
}
# 编译安装OpenSSH
compile_install() {
info "开始编译安装OpenSSH..."
tar -zxvf "openssh-${OPENSSH_VERSION}.tar.gz" >>${LOG_FILE}
cd "openssh-${OPENSSH_VERSION}"
# 根据系统资源调整编译参数
local cpu_cores=$(nproc)
local make_jobs=$((cpu_cores > 4 ? 4 : cpu_cores))
echo -e "${GREEN}配置编译环境...${NC}"
./configure --prefix=/usr/local --sysconfdir=/etc/ssh \
--with-pam --with-md5-passwords --with-systemd >>${LOG_FILE} 2>&1
echo -e "${GREEN}开始编译 (使用 ${make_jobs} 个并行任务)...${NC}"
make -j ${make_jobs} >>${LOG_FILE} 2>&1
echo -e "${GREEN}安装OpenSSH...${NC}"
sudo make install >>${LOG_FILE} 2>&1
info "OpenSSH编译安装完成"
}
# 配置系统链接
setup_symlinks() {
info "配置系统符号链接..."
# 备份原文件并创建新链接
sudo mv /usr/bin/ssh /usr/bin/ssh.backup
sudo mv /usr/sbin/sshd /usr/sbin/sshd.backup
sudo mv /usr/bin/ssh-keygen /usr/bin/ssh-keygen.backup
sudo ln -sf /usr/local/bin/ssh /usr/bin/ssh
sudo ln -sf /usr/local/sbin/sshd /usr/sbin/sshd
sudo ln -sf /usr/local/bin/ssh-keygen /usr/bin/ssh-keygen
sudo ln -sf /usr/local/bin/scp /usr/bin/scp
sudo ln -sf /usr/local/bin/sftp /usr/bin/sftp
info "符号链接配置完成"
}
# 优化setup_selinux函数
setup_selinux() {
info "配置SELinux策略..."
# 确保policycoreutils-python-utils已安装
if ! rpm -q policycoreutils-python-utils >/dev/null 2>&1; then
warn "policycoreutils-python-utils未安装,正在安装..."
sudo dnf install -y policycoreutils-python-utils >>${LOG_FILE} 2>&1
if [ $? -ne 0 ]; then
error "policycoreutils-python-utils安装失败"
return 1
fi
fi
# 1. 恢复文件安全上下文
info "恢复文件安全上下文..."
sudo restorecon -rv /etc/ssh >>${LOG_FILE} 2>&1
sudo restorecon -v /usr/local/bin/ssh /usr/local/sbin/sshd \
/usr/bin/ssh /usr/sbin/sshd >>${LOG_FILE} 2>&1
# 等待SELinux策略生效
sleep 2
# 2. 生成并安装自定义策略模块
if sudo grep 'avc:.*denied.*sshd' /var/log/audit/audit.log 2>/dev/null; then
info "检测到SELinux拒绝记录,生成策略模块..."
# 清理旧的策略模块(如果存在)
sudo semodule -r ${SELINUX_MODULE_NAME} 2>/dev/null || true
# 生成新的策略模块
sudo grep 'avc:.*denied.*sshd' /var/log/audit/audit.log | \
sudo audit2allow -M ${SELINUX_MODULE_NAME} >>${LOG_FILE} 2>&1
# 安装策略模块
if [ -f "${SELINUX_MODULE_NAME}.pp" ]; then
sudo semodule -i ${SELINUX_MODULE_NAME}.pp >>${LOG_FILE} 2>&1
info "SELinux策略模块 ${SELINUX_MODULE_NAME} 已安装"
# 验证策略模块安装
if sudo semodule -l | grep -q ${SELINUX_MODULE_NAME}; then
info "策略模块验证成功"
else
warn "策略模块安装可能失败"
fi
else
error "策略模块文件生成失败"
fi
else
warn "未发现SELinux拒绝记录,跳过策略模块安装"
fi
# 3. 重新加载systemd
info "重新加载systemd配置..."
sudo systemctl daemon-reload >>${LOG_FILE} 2>&1
}
# 统一的配置修复函数
fix_ssh_config() {
local config_file="/etc/ssh/sshd_config"
info "修复SSH配置兼容性问题..."
# 确保备份目录存在
if [ ! -d "${BACKUP_DIR}" ]; then
info "创建备份目录 ${BACKUP_DIR}"
mkdir -p "${BACKUP_DIR}"
fi
# 备份原始配置
cp "$config_file" "${BACKUP_DIR}/sshd_config.before_fix"
# 修复所有GSSAPI相关问题
info "修复GSSAPI配置兼容性..."
sudo sed -i.bak \
-e 's/^GSSAPIAuthentication/#GSSAPIAuthentication # Disabled in OpenSSH 10.2p1/' \
-e 's/^GSSAPICleanupCredentials/#GSSAPICleanupCredentials # Disabled in OpenSSH 10.2p1/' \
-e 's/^GSSAPIKexAlgorithms/#GSSAPIKexAlgorithms # Disabled in OpenSSH 10.2p1/' \
-e 's/^GSSAPIDelegateCredentials/#GSSAPIDelegateCredentials # Disabled in OpenSSH 10.2p1/' \
-e 's/^GSSAPIStrictAcceptorCheck/#GSSAPIStrictAcceptorCheck # Disabled in OpenSSH 10.2p1/' \
-e 's/^GSSAPIKeyExchange/#GSSAPIKeyExchange # Disabled in OpenSSH 10.2p1/' \
"${config_file}"
# 确保必要的配置存在
ensure_required_config "${config_file}"
# 验证配置
local test_result=$(/usr/local/sbin/sshd -t 2>&1)
local exit_code=$?
if [ $exit_code -eq 0 ]; then
info "SSH配置修复成功"
return 0
else
error "SSH配置修复失败,错误信息:"
echo "$test_result"
return 1
fi
}
# 添加配置确保函数
ensure_required_config() {
local config_file="$1"
# 确保必要的配置存在
if ! grep -q "^Port " "${config_file}"; then
echo "Port 22" | sudo tee -a "${config_file}"
fi
if ! grep -q "^Protocol " "${config_file}"; then
echo "Protocol 2" | sudo tee -a "${config_file}"
fi
# 添加现代加密算法
if ! grep -q "^KexAlgorithms " "${config_file}"; then
echo "KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256" | sudo tee -a "${config_file}"
fi
}
# 添加新的函数来确保SSH安全配置:
ensure_ssh_security_config() {
local config_file="$1"
info "确保SSH安全配置..."
# 确保必要的安全配置存在
if ! grep -q "^PasswordAuthentication" "$config_file"; then
echo "PasswordAuthentication yes" >> "$config_file"
info "添加PasswordAuthentication配置"
fi
if ! grep -q "^PubkeyAuthentication" "$config_file"; then
echo "PubkeyAuthentication yes" >> "$config_file"
info "添加PubkeyAuthentication配置"
fi
if ! grep -q "^AuthorizedKeysFile" "$config_file"; then
echo "AuthorizedKeysFile .ssh/authorized_keys" >> "$config_file"
info "添加AuthorizedKeysFile配置"
fi
# 确保没有冲突的配置
if grep -q "^#PermitRootLogin" "$config_file"; then
sed -i 's/^#PermitRootLogin.*/PermitRootLogin no/' "$config_file"
info "更新注释的PermitRootLogin配置"
fi
}
# 修改验证逻辑,提供更详细的错误信息:
validate_ssh_config_detailed() {
local config_file="$1"
info "详细验证SSH配置..."
# 检查配置文件语法
local test_result=$(/usr/local/sbin/sshd -t 2>&1)
local exit_code=$?
if [ $exit_code -eq 0 ]; then
info "SSH配置语法验证通过"
return 0
else
error "SSH配置语法验证失败:"
echo -e "${RED}=== 错误详情 ===${NC}"
echo "$test_result"
echo -e "${RED}================${NC}"
# 尝试自动修复常见问题
warn "尝试自动修复配置问题..."
fix_common_ssh_config_issues "$config_file"
# 再次验证
test_result=$(/usr/local/sbin/sshd -t 2>&1)
exit_code=$?
if [ $exit_code -eq 0 ]; then
info "自动修复成功,SSH配置验证通过"
return 0
else
error "自动修复失败,SSH配置仍有问题"
return 1
fi
fi
}
# 添加常见SSH配置问题修复函数:
fix_common_ssh_config_issues() {
local config_file="$1"
info "修复常见SSH配置问题..."
# 修复可能的配置问题
# 1. 确保每行配置格式正确
sed -i 's/^[[:space:]]*PermitRootLogin[[:space:]]*$/PermitRootLogin no/' "$config_file"
# 2. 移除重复的配置项
awk '!seen[$0]++' "$config_file" > "${config_file}.tmp" && mv "${config_file}.tmp" "$config_file"
# 3. 确保配置项前后没有多余空格
sed -i 's/^[[:space:]]*//' "$config_file"
sed -i 's/[[:space:]]*$//' "$config_file"
# 4. 确保每行以换行符结尾
echo "" >> "$config_file"
}
# 配置SSH禁止root登录
configure_ssh_disable_root() {
local config_file="/etc/ssh/sshd_config"
local backup_file="${BACKUP_DIR}/sshd_config.root_login.backup"
info "配置SSH禁止root登录..."
# 确保备份目录存在
if [ ! -d "${BACKUP_DIR}" ]; then
info "创建备份目录 ${BACKUP_DIR}"
mkdir -p "${BACKUP_DIR}"
fi
# 备份原始配置
cp "$config_file" "$backup_file"
info "SSH配置已备份到: $backup_file"
# 检查是否已存在PermitRootLogin配置
if grep -q "^PermitRootLogin" "$config_file"; then
# 更新现有配置
sed -i 's/^PermitRootLogin.*/PermitRootLogin no/' "$config_file"
info "已更新PermitRootLogin配置为no"
else
# 添加新配置
echo "PermitRootLogin no" >> "$config_file"
info "已添加PermitRootLogin no配置"
fi
# 验证配置语法 - 使用更详细的错误信息
local test_result=$(/usr/local/sbin/sshd -t 2>&1)
local exit_code=$?
if [ $exit_code -eq 0 ]; then
info "SSH配置验证通过"
return 0
else
error "SSH配置验证失败,错误信息:"
echo "$test_result"
error "恢复备份配置"
cp "$backup_file" "$config_file"
return 1
fi
}
# 添加交互式询问
ask_disable_root_login() {
echo -e "\n${YELLOW}=== SSH安全配置 ===${NC}"
read -p "是否禁用SSH root登录?(y/N): " disable_root
if [[ $disable_root =~ ^[Yy]([Ee][Ss])?$ ]]; then
echo -e "${RED}警告:禁用root登录后,请确保有其他用户可以通过SSH登录!${NC}"
echo -e "${YELLOW}建议先创建普通用户并授予sudo权限${NC}"
read -p "确认禁用root登录?(Y/n): " confirm_disable
if [[ $confirm_disable =~ ^[Nn]([Oo])?$ ]]; then
echo "已取消禁用root登录"
return 1
fi
return 0
else
echo "保持root登录启用"
return 1
fi
}
# 重启服务
restart_services() {
info "重启SSH服务..."
sudo systemctl restart sshd
# 验证服务状态
if sudo systemctl is-active sshd >/dev/null 2>&1; then
info "SSH服务重启成功"
sudo systemctl status sshd --no-pager -l
else
error "SSH服务重启失败"
return 1
fi
}
# 验证升级结果
verify_upgrade() {
info "验证升级结果..."
echo -e "\n${GREEN}=== 升级验证 ===${NC}"
echo "新版本: $(/usr/local/bin/ssh -V 2>&1)"
echo "系统版本: $(ssh -V 2>&1)"
echo "服务状态: $(systemctl is-active sshd)"
echo -e "监听端口: $(sudo ss -tlnp | grep sshd || echo '未找到')"
# 测试本地连接
if timeout 5s ssh -o BatchMode=yes -o ConnectTimeout=5 localhost echo "连接测试成功" 2>/dev/null; then
info "本地连接测试成功"
else
warn "本地连接测试失败,请检查日志"
fi
}
# 回滚函数
rollback() {
error "升级失败,开始回滚..."
# 恢复二进制文件
sudo mv -f /usr/bin/ssh.backup /usr/bin/ssh 2>/dev/null || true
sudo mv -f /usr/sbin/sshd.backup /usr/sbin/sshd 2>/dev/null || true
sudo mv -f /usr/bin/ssh-keygen.backup /usr/bin/ssh-keygen 2>/dev/null || true
# 恢复SSH配置
if [ -f "${BACKUP_DIR}/sshd_config.root_login.backup" ]; then
info "恢复SSH root登录配置..."
cp "${BACKUP_DIR}/sshd_config.root_login.backup" /etc/ssh/sshd_config
fi
# 恢复SELinux策略
info "恢复SELinux策略..."
sudo semodule -r ${SELINUX_MODULE_NAME} 2>/dev/null || true
# 恢复文件安全上下文
info "恢复文件安全上下文..."
sudo restorecon -rv /etc/ssh 2>/dev/null || true
sudo restorecon -v /usr/bin/ssh /usr/sbin/sshd 2>/dev/null || true
# 重新加载systemd并重启服务
sudo systemctl daemon-reload
sudo systemctl restart sshd
# 验证恢复结果
if /usr/local/sbin/sshd -t 2>/dev/null; then
info "回滚完成,系统已恢复至升级前状态"
else
error "回滚后SSH配置仍有问题,请手动检查"
echo -e "${YELLOW}建议手动执行以下命令:${NC}"
echo "sudo restorecon -rv /etc/ssh"
echo "sudo restorecon -v /usr/local/bin/ssh /usr/local/sbin/sshd /usr/bin/ssh /usr/sbin/sshd"
echo "sudo systemctl daemon-reload"
echo "sudo systemctl restart sshd"
fi
}
# 设置错误捕获
trap rollback ERR
# 主函数
main() {
echo -e "${GREEN}=== OpenEuler 22.03 OpenSSH 一键升级脚本 ===${NC}"
echo -e "开始时间: $(date)"
echo -e "日志文件: ${LOG_FILE}"
echo -e "备份目录: ${BACKUP_DIR}"
# 预检查
check_disk_space
check_backup_connection
check_selinux_status
# 添加详细的备援连接提示
echo -e "\n${RED}=== 重要警告:建立备援连接 ===${NC}"
echo -e "${YELLOW}在执行此脚本前,请务必建立备援连接方式,以防升级过程中SSH连接中断!${NC}"
echo -e "\n${GREEN}推荐的备援连接方法:${NC}"
echo -e "1. ${YELLOW}物理控制台访问${NC} - 直接通过服务器控制台"
echo -e "2. ${YELLOW}IPMI/iDRAC远程管理${NC} - 通过带外管理接口"
echo -e "3. ${YELLOW}串口控制台${NC} - 通过串口连接"
echo -e "4. ${YELLOW}VNC/远程桌面${NC} - 如果系统支持图形界面"
echo -e "5. ${YELLOW}多SSH会话${NC} - 保持多个SSH连接(风险较高)"
echo -e "\n${GREEN}备援连接验证步骤:${NC}"
echo -e "1. 建立备援连接并测试可用性"
echo -e "2. 在当前SSH会话中运行: ${YELLOW}who${NC} 查看当前登录用户"
echo -e "3. 在备援连接中运行: ${YELLOW}ps aux | grep sshd${NC} 监控SSH进程"
echo -e "4. 确保备援连接可以执行: ${YELLOW}systemctl restart sshd${NC}"
echo -e "\n${RED}如果没有备援连接,请立即停止脚本执行!${NC}"
echo -e "${YELLOW}按 Ctrl+C 取消执行,或按 Enter 继续(风险自负)${NC}"
# 用户确认
read -p "请确认已建立备援连接并输入 'YES' 继续: " confirm
if [[ ! "$confirm" =~ ^[Yy][Ee][Ss]$ ]]; then
error "用户取消执行,脚本退出"
exit 1
fi
# 创建备份目录
create_backup
# 执行升级流程
backup_system
install_dependencies
download_openssh
compile_install
setup_symlinks
# 先修复GSSAPI配置问题
fix_ssh_config
# 再配置SSH安全选项
if ask_disable_root_login; then
configure_ssh_disable_root
fi
# 然后处理SELinux
setup_selinux
# 最后重启服务并验证
restart_services
verify_upgrade
info "升级完成! 建议在新窗口中测试SSH连接后再退出当前会话。"
info "如果遇到问题,备份文件保存在: ${BACKUP_DIR}"
}
# 执行主函数
main "$@"

浙公网安备 33010602011771号