Update ssh

This commit is contained in:
2025-11-07 18:20:19 +08:00
committed by GitHub
parent 56337277ec
commit b94582c015

724
ssh
View File

@@ -1,459 +1,351 @@
#!/bin/bash #!/bin/bash
# ========================================================
# 🛡️ secure-ssh-setup.sh
# 自动配置 SSH 服务 | 支持密钥登录 | 防火墙放行 | 安全加固
# 作者AI 助手 | 兼容 Ubuntu/CentOS/Debian/RHEL
# 无需 Python一键运行
# ========================================================
# 自动更改SSH端口和root密码脚本带自动回滚功能 set -euo pipefail # 严格模式:出错退出、未定义变量报错、管道错误捕获
# 支持 Ubuntu/Debian/CentOS/RHEL 系统
set -e # 遇到错误立即退出 # -------------------------------
# 🔧 配置选项(可自定义)
# -------------------------------
# 颜色定义 SSH_PORT=${SSH_PORT:-22} # SSH 端口(默认 22
RED='\033[0;31m' ENABLE_PASSWORD_LOGIN=${ENABLE_PASSWORD_LOGIN:-yes} # 是否允许密码登录
GREEN='\033[0;32m' ENABLE_ROOT_LOGIN=${ENABLE_ROOT_LOGIN:-no} # 是否允许 root 密码登录no 更安全)
YELLOW='\033[1;33m' AUTO_INSTALL_SSH=${AUTO_INSTALL_SSH:-yes} # 是否自动安装 openssh-server
BLUE='\033[0;34m' USE_KEY_LOGIN=${USE_KEY_LOGIN:-yes} # 是否生成密钥并启用密钥登录
NC='\033[0m' # No Color
# 配置变量 # -------------------------------
NEW_PORT="2222" # 🧰 工具函数
NEW_PASSWORD="Xx3459635287" # -------------------------------
ROLLBACK_TIME=300 # 5分钟回滚时间
CHECK_INTERVAL=10 # 检查间隔(秒)
# 全局变量 log() {
BACKUP_FILE="" echo "[$(date +'%H:%M:%S')] 📝 $*"
ORIGINAL_PORT=""
OS=""
# 日志函数
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
} }
log_warn() { success() {
echo -e "${YELLOW}[WARN]${NC} $1" echo "[$(date +'%H:%M:%S')] ✅ $*"
} }
log_error() { error() {
echo -e "${RED}[ERROR]${NC} $1" echo "[$(date +'%H:%M:%S')] ❌ $*" >&2
exit 1
} }
log_debug() { warn() {
echo -e "${BLUE}[DEBUG]${NC} $1" echo "[$(date +'%H:%M:%S')] ⚠️ $*"
} }
# 检查root权限 # 检查命令是否存在
check_root() { has_cmd() {
if [[ $EUID -ne 0 ]]; then command -v "$1" &>/dev/null
log_error "此脚本需要root权限运行"
exit 1
fi
} }
# 检测系统类型 # 等待用户确认(可跳过)
detect_os() { confirm() {
if [[ -f /etc/redhat-release ]]; then if [[ "${FORCE:-}" == "yes" ]]; then
OS="centos"
elif [[ -f /etc/debian_version ]]; then
OS="debian"
elif grep -q "ubuntu" /etc/os-release; then
OS="ubuntu"
else
log_error "不支持的操作系统"
exit 1
fi
log_info "检测到操作系统: $OS"
}
# 获取当前SSH端口
get_current_ssh_port() {
local ssh_config="/etc/ssh/sshd_config"
if grep -q "^Port" "$ssh_config"; then
ORIGINAL_PORT=$(grep "^Port" "$ssh_config" | awk '{print $2}')
else
ORIGINAL_PORT="22"
fi
log_info "检测到当前SSH端口: $ORIGINAL_PORT"
}
# 备份SSH配置文件
backup_ssh_config() {
local ssh_config="/etc/ssh/sshd_config"
BACKUP_FILE="/etc/ssh/sshd_config.backup.$(date +%Y%m%d%H%M%S)"
if [[ -f "$ssh_config" ]]; then
cp "$ssh_config" "$BACKUP_FILE"
log_info "SSH配置文件已备份到: $BACKUP_FILE"
else
log_error "SSH配置文件不存在: $ssh_config"
exit 1
fi
}
# 更改SSH端口
change_ssh_port() {
local ssh_config="/etc/ssh/sshd_config"
log_info "正在更改SSH端口为: $NEW_PORT"
# 检查端口是否已被使用
if netstat -tuln 2>/dev/null | grep ":$NEW_PORT " > /dev/null; then
log_error "端口 $NEW_PORT 已被占用"
exit 1
fi
if ss -tuln 2>/dev/null | grep ":$NEW_PORT " > /dev/null; then
log_error "端口 $NEW_PORT 已被占用"
exit 1
fi
# 备份原配置
backup_ssh_config
# 注释掉原有的Port行
sed -i 's/^Port/#Port/g' "$ssh_config"
# 添加新的Port配置
echo "Port $NEW_PORT" >> "$ssh_config"
# 确保允许root登录
sed -i 's/^#PermitRootLogin yes/PermitRootLogin yes/g' "$ssh_config"
sed -i 's/^PermitRootLogin no/PermitRootLogin yes/g' "$ssh_config"
sed -i 's/^#PermitRootLogin no/PermitRootLogin yes/g' "$ssh_config"
if ! grep -q "^PermitRootLogin" "$ssh_config"; then
echo "PermitRootLogin yes" >> "$ssh_config"
fi
log_info "SSH端口已更改为: $NEW_PORT"
}
# 更改root密码
change_root_password() {
local old_password_backup=""
log_info "正在更改root密码..."
# 备份旧密码哈希(用于回滚)
old_password_backup=$(grep '^root:' /etc/shadow | cut -d: -f2)
echo "$old_password_backup" > /tmp/old_root_password.hash
# 更改root密码
echo "root:$NEW_PASSWORD" | chpasswd
if [[ $? -eq 0 ]]; then
log_info "root密码已成功更改"
log_warn "新密码: $NEW_PASSWORD"
log_warn "请妥善保管密码并及时修改"
else
log_error "更改root密码失败"
exit 1
fi
}
# 配置防火墙
configure_firewall() {
log_info "配置防火墙规则..."
case $OS in
"centos"|"rhel")
if command -v firewall-cmd &> /dev/null; then
firewall-cmd --permanent --add-port=$NEW_PORT/tcp
firewall-cmd --reload
log_info "Firewalld已配置端口 $NEW_PORT"
elif command -v iptables &> /dev/null; then
iptables -I INPUT -p tcp --dport $NEW_PORT -j ACCEPT
service iptables save 2>/dev/null || iptables-save > /etc/sysconfig/iptables
log_info "iptables已配置端口 $NEW_PORT"
fi
;;
"ubuntu"|"debian")
if command -v ufw &> /dev/null && ufw status | grep -q "active"; then
ufw allow $NEW_PORT/tcp
log_info "UFW已配置端口 $NEW_PORT"
elif command -v iptables &> /dev/null; then
iptables -I INPUT -p tcp --dport $NEW_PORT -j ACCEPT
iptables-save > /etc/iptables/rules.v4 2>/dev/null || true
log_info "iptables已配置端口 $NEW_PORT"
fi
;;
esac
}
# 检查SELinux
check_selinux() {
if command -v getenforce &> /dev/null; then
if [[ $(getenforce) != "Disabled" ]]; then
log_warn "检测到SELinux已启用需要配置SELinux规则"
if command -v semanage &> /dev/null; then
semanage port -a -t ssh_port_t -p tcp $NEW_PORT 2>/dev/null || \
semanage port -m -t ssh_port_t -p tcp $NEW_PORT
log_info "SELinux已配置允许SSH端口 $NEW_PORT"
else
log_warn "SELinux已启用但semanage命令不可用建议安装policycoreutils-python-utils"
fi
fi
fi
}
# 重启SSH服务
restart_ssh_service() {
log_info "重启SSH服务..."
case $OS in
"centos"|"rhel")
if systemctl is-active sshd &> /dev/null; then
systemctl restart sshd
else
systemctl restart ssh
fi
;;
"ubuntu"|"debian")
systemctl restart ssh
;;
esac
sleep 2 # 等待服务完全启动
if systemctl is-active ssh >/dev/null 2>&1 || systemctl is-active sshd >/dev/null 2>&1; then
log_info "SSH服务重启成功"
else
log_error "SSH服务重启失败"
return 1
fi
}
# 回滚函数
rollback_changes() {
log_warn "开始回滚配置..."
# 恢复SSH配置文件
if [[ -f "$BACKUP_FILE" ]]; then
cp "$BACKUP_FILE" "/etc/ssh/sshd_config"
log_info "已恢复SSH配置文件"
fi
# 恢复root密码
if [[ -f "/tmp/old_root_password.hash" ]]; then
local old_hash=$(cat /tmp/old_root_password.hash)
usermod -p "$old_hash" root 2>/dev/null
rm -f /tmp/old_root_password.hash
log_info "已恢复root密码"
fi
# 重启SSH服务
restart_ssh_service
# 恢复防火墙规则
case $OS in
"centos"|"rhel")
if command -v firewall-cmd &> /dev/null; then
firewall-cmd --permanent --remove-port=$NEW_PORT/tcp
firewall-cmd --reload
fi
;;
"ubuntu"|"debian")
if command -v ufw &> /dev/null && ufw status | grep -q "active"; then
ufw delete allow $NEW_PORT/tcp
fi
;;
esac
log_info "已恢复防火墙规则"
log_info "回滚完成!已恢复所有原始配置"
}
# 验证更改
verify_changes() {
log_info "验证更改..."
# 检查SSH服务状态
case $OS in
"centos"|"rhel")
if systemctl is-active sshd &> /dev/null; then
systemctl status sshd --no-pager -l | head -5
else
systemctl status ssh --no-pager -l | head -5
fi
;;
"ubuntu"|"debian")
systemctl status ssh --no-pager -l | head -5
;;
esac
# 检查端口监听
if ss -tuln | grep ":$NEW_PORT " > /dev/null || netstat -tuln 2>/dev/null | grep ":$NEW_PORT " > /dev/null; then
log_info "SSH服务正在监听端口 $NEW_PORT"
return 0 return 0
else
log_error "SSH服务未在端口 $NEW_PORT 监听"
return 1
fi fi
read -p "$1 [y/N]: " -n 1 -r
echo
[[ ! $REPLY =~ ^[Yy]$ ]] && return 1
return 0
} }
# 手动确认验证函数 # -------------------------------
manual_verification() { # 🖥️ 检测系统信息
local start_time=$(date +%s) # -------------------------------
local current_time=0
local elapsed_time=0
echo "==========================================" detect_os() {
log_info "⏰ 您有 5 分钟时间测试新配置" if [[ -f /etc/os-release ]]; then
log_info "🔑 测试连接信息:" . /etc/os-release
echo "" OS_NAME="$NAME"
echo " IP: $(hostname -I | awk '{print $1}')" OS_ID="$ID"
echo " 端口: $NEW_PORT" OS_VERSION="$VERSION_ID"
echo " 密码: $NEW_PASSWORD" elif [[ -f /etc/redhat-release ]]; then
echo "" OS_NAME=$(cat /etc/redhat-release)
echo " 测试命令:" OS_ID="rhel"
echo " ssh root@$(hostname -I | awk '{print $1}') -p $NEW_PORT" else
echo "" OS_NAME=$(uname -s)
log_info "💡 提示:请在新终端窗口中测试连接" OS_ID="unknown"
log_info " 成功登录后请返回此窗口输入 'confirm' 确认" fi
echo "==========================================" log "检测到系统: $OS_NAME"
}
while true; do # -------------------------------
current_time=$(date +%s) # 🔧 安装 OpenSSH 服务(如未安装)
elapsed_time=$((current_time - start_time)) # -------------------------------
local remaining_time=$((ROLLBACK_TIME - elapsed_time))
# 检查是否超时 install_ssh_server() {
if [[ $elapsed_time -ge $ROLLBACK_TIME ]]; then if has_cmd sshd || has_cmd ssh; then
log_warn "⏰ 超时:在 5 分钟内未收到确认" return 0
echo "" fi
return 1
fi
# 显示剩余时间 if [[ "$AUTO_INSTALL_SSH" != "yes" ]]; then
if [[ $((elapsed_time % 30)) -eq 0 ]]; then error "SSH 服务未安装,且 AUTO_INSTALL_SSH=no退出。"
echo "" fi
log_info "剩余时间: $((remaining_time / 60))分$((remaining_time % 60))秒"
echo "请输入 'confirm' 确认成功,或等待自动回滚..."
fi
# 设置超时读取,避免永久阻塞 log "OpenSSH 未安装,正在安装..."
if read -t 10 -p "请输入 'confirm' 确认: " user_confirm; then
if [[ $user_confirm == "confirm" ]]; then case "$OS_ID" in
log_info "✅ 用户确认成功,新配置将永久生效" ubuntu|debian)
echo "" DEBIAN_FRONTEND=noninteractive apt-get update -qq
return 0 DEBIAN_FRONTEND=noninteractive apt-get install -y openssh-server
else ;;
log_warn "无效输入,请输入 'confirm'" centos|rhel|almalinux|rocky)
yum install -y openssh-server || true
# 对于较新系统使用 dnf
if ! has_cmd sshd && has_cmd dnf; then
dnf install -y openssh-server
fi fi
fi ;;
arch)
pacman -S openssh
;;
suse|opensuse)
zypper install -y openssh
;;
*)
error "不支持的系统类型,无法自动安装 SSH"
;;
esac
# 每10秒显示一次提示 if ! has_cmd sshd && ! has_cmd ssh; then
if [[ $((elapsed_time % 10)) -eq 0 ]]; then error "安装失败,请手动安装 openssh-server"
echo "等待确认中... (剩余 $((remaining_time / 60))分$((remaining_time % 60))秒)" fi
success "OpenSSH 安装完成"
}
# -------------------------------
# ⚙️ 配置 SSH 服务
# -------------------------------
configure_ssh() {
local config="/etc/ssh/sshd_config"
local backup="${config}.backup.$(date +%s)"
# 备份原始配置
if [[ ! -f "$backup" && -f "$config" ]]; then
cp "$config" "$backup"
success "SSH 配置已备份: $backup"
fi
# 确保目录存在
mkdir -p /etc/ssh
# 写入基础配置(如果文件不存在)
if [[ ! -f "$config" ]]; then
log "创建新的 sshd_config 文件"
{
echo "Port $SSH_PORT"
echo "PermitRootLogin $( [[ "$ENABLE_ROOT_LOGIN" == "yes" ]] && echo "yes" || echo "prohibit-password" )"
echo "PasswordAuthentication $ENABLE_PASSWORD_LOGIN"
echo "PubkeyAuthentication yes"
echo "AuthorizedKeysFile .ssh/authorized_keys"
echo "ChallengeResponseAuthentication no"
echo "UsePAM yes"
echo "X11Forwarding yes"
echo "PrintMotd no"
echo "AcceptEnv LANG LC_*"
echo "Subsystem sftp /usr/lib/openssh/sftp-server"
} > "$config"
else
# 修改现有配置
sed -i "s/^#*Port .*/Port $SSH_PORT/" "$config"
sed -i "s/^#*PermitRootLogin .*/PermitRootLogin $( [[ "$ENABLE_ROOT_LOGIN" == "yes" ]] && echo "yes" || echo "prohibit-password" )/" "$config"
sed -i "s/^#*PasswordAuthentication .*/PasswordAuthentication $ENABLE_PASSWORD_LOGIN/" "$config"
sed -i "s/^#*PubkeyAuthentication .*/PubkeyAuthentication yes/" "$config"
sed -i "s/^#*AuthorizedKeysFile .*/AuthorizedKeysFile .ssh\/authorized_keys/" "$config"
fi
success "SSH 配置已更新 (端口: $SSH_PORT)"
}
# -------------------------------
# ▶️ 启动并启用 SSH 服务
# -------------------------------
start_ssh_service() {
local service=""
for s in sshd ssh; do
if systemctl list-unit-files | grep -q "$s"; then
service="$s"
break
fi fi
done done
}
# 显示警告信息 if [[ -z "$service" ]]; then
show_warning() { error "未找到 SSH 服务,请检查是否安装正确"
echo "=========================================="
log_warn " 重要警告!"
echo "=========================================="
log_warn "此脚本将执行以下操作:"
log_warn " • 更改SSH端口为 $NEW_PORT"
log_warn " • 更改root密码为 $NEW_PASSWORD"
log_warn " • 修改防火墙配置"
log_warn " • 重启SSH服务"
echo "=========================================="
log_warn "安全机制:"
log_warn " • 如果5分钟内没有确认新配置工作正常"
log_warn " • 系统将自动回滚到原始配置"
echo "=========================================="
log_warn "在继续之前,请确保:"
log_warn " • 您有服务器的物理访问权限"
log_warn " • 您了解操作风险"
log_warn " • 您已经备份了重要数据"
echo "=========================================="
# 直接等待回车,避免输入问题
echo -e "${YELLOW}按回车键继续,或按 Ctrl+C 取消...${NC}"
read
}
# 清理函数
cleanup() {
rm -f /tmp/old_root_password.hash
}
# 信号处理
setup_signal_handlers() {
trap 'echo; log_warn "接收到中断信号,开始回滚..."; rollback_changes; cleanup; exit 1' INT TERM
}
# 主函数
main() {
log_info "开始执行SSH配置脚本带自动回滚功能"
# 设置信号处理
setup_signal_handlers
show_warning
check_root
detect_os
get_current_ssh_port
# 执行配置更改
change_ssh_port
change_root_password
configure_firewall
check_selinux
if ! restart_ssh_service; then
log_error "SSH服务重启失败开始回滚"
rollback_changes
exit 1
fi fi
if ! verify_changes; then if systemctl is-active --quiet "$service"; then
log_error "配置验证失败,开始回滚" success "$service 已在运行"
rollback_changes else
exit 1 log "启动 $service 服务..."
systemctl start "$service" && systemctl enable "$service"
success "$service 已启动并设置开机自启"
fi fi
# 启动手动验证 # 检查端口是否监听
log_info "启动配置验证阶段..." if ! has_cmd ss && has_cmd netstat; then
if manual_verification; then if ! netstat -tuln | grep -q ":$SSH_PORT\b"; then
log_info "✅ 配置验证成功新SSH配置已生效" warn "端口 $SSH_PORT 未监听,尝试重启..."
log_info "✅ 端口: $NEW_PORT" systemctl restart "$service"
log_info "✅ 请妥善保管新密码" sleep 2
if ! netstat -tuln | grep -q ":$SSH_PORT\b"; then
# 询问是否保留原端口 error "SSH 服务启动失败,请检查日志: journalctl -u $service"
echo "=========================================="
if read -p "是否同时保留原SSH端口 $ORIGINAL_PORT? (y/n): " keep_original && [[ $keep_original == "y" || $keep_original == "Y" ]]; then
log_info "恢复原端口配置..."
# 恢复原端口配置
if [[ -f "$BACKUP_FILE" ]]; then
# 从备份文件中提取原Port配置
original_port_line=$(grep "^Port" "$BACKUP_FILE" | head -1)
if [[ -n "$original_port_line" ]]; then
echo "$original_port_line" >> "/etc/ssh/sshd_config"
else
echo "Port $ORIGINAL_PORT" >> "/etc/ssh/sshd_config"
fi
else
echo "Port $ORIGINAL_PORT" >> "/etc/ssh/sshd_config"
fi
if restart_ssh_service; then
log_info "现在支持双端口: $ORIGINAL_PORT 和 $NEW_PORT"
else
log_error "重启SSH服务失败但新端口配置已生效"
fi fi
fi fi
cleanup
log_info "🎉 脚本执行完成!新配置已永久生效"
else else
log_error "❌ 配置验证失败,开始回滚..." if ! ss -tuln | grep -q ":$SSH_PORT\b"; then
rollback_changes warn "端口 $SSH_PORT 未监听,尝试重启..."
cleanup systemctl restart "$service"
exit 1 sleep 2
if ! ss -tuln | grep -q ":$SSH_PORT\b"; then
error "SSH 服务启动失败,请检查日志: journalctl -u $service"
fi
fi
fi fi
} }
# 检查是否直接运行脚本 # -------------------------------
# 🔥 配置防火墙
# -------------------------------
setup_firewall() {
if has_cmd ufw; then
if ! ufw status | grep -q inactive; then
ufw allow "$SSH_PORT/tcp" && success "UFW: 放行端口 $SSH_PORT"
else
log "UFW 处于非活动状态,启用中..."
yes | ufw enable && ufw allow "$SSH_PORT/tcp"
success "UFW 已启用并放行 $SSH_PORT"
fi
elif has_cmd firewall-cmd; then
if firewall-cmd --state &>/dev/null; then
firewall-cmd --permanent --add-port="$SSH_PORT"/tcp --zone=public
firewall-cmd --reload
success "Firewalld: 放行端口 $SSH_PORT"
else
warn "Firewalld 未运行"
fi
elif has_cmd iptables; then
iptables -A INPUT -p tcp --dport "$SSH_PORT" -j ACCEPT
success "Iptables: 放行端口 $SSH_PORT"
else
warn "未检测到 UFW、Firewalld 或 Iptables跳过防火墙配置"
fi
}
# -------------------------------
# 🔑 配置密钥登录
# -------------------------------
setup_key_login() {
if [[ "$USE_KEY_LOGIN" != "yes" ]]; then
return
fi
local user="${SUDO_USER:-$USER}"
local home
home=$(getent passwd "$user" | cut -d: -f6) || error "无法获取用户 $user 的家目录"
if [[ -z "$home" || ! -d "$home" ]]; then
error "用户 $user 的家目录不存在"
fi
local ssh_dir="$home/.ssh"
local key_file="$ssh_dir/id_rsa"
local pub_key="$key_file.pub"
local auth_keys="$ssh_dir/authorized_keys"
mkdir -p "$ssh_dir"
chmod 700 "$ssh_dir"
# 生成密钥(如果不存在)
if [[ ! -f "$key_file" ]]; then
log "为用户 $user 生成 SSH 密钥对..."
if sudo -u "$user" ssh-keygen -t rsa -b 2048 -f "$key_file" -N "" </dev/null 2>/dev/null; then
success "密钥已生成: $key_file"
else
warn "密钥生成失败,跳过"
return
fi
fi
# 添加公钥到 authorized_keys
if [[ -f "$pub_key" ]]; then
mkdir -p "$ssh_dir"
cat "$pub_key" >> "$auth_keys" 2>/dev/null || true
chmod 600 "$auth_keys"
chown -R "$user:$user" "$ssh_dir"
success "公钥已添加到 $auth_keys支持密钥登录"
fi
}
# -------------------------------
# 🧹 清理临时变量
# -------------------------------
cleanup() {
unset SSH_PORT ENABLE_PASSWORD_LOGIN ENABLE_ROOT_LOGIN AUTO_INSTALL_SSH USE_KEY_LOGIN FORCE
}
# -------------------------------
# 🚀 主函数
# -------------------------------
main() {
log "开始配置 SSH 服务..."
# 提示用户
echo
echo "配置摘要:"
echo " SSH 端口: $SSH_PORT"
echo " 允许密码登录: $ENABLE_PASSWORD_LOGIN"
echo " 允许 root 登录: $ENABLE_ROOT_LOGIN"
echo " 自动生成密钥: $USE_KEY_LOGIN"
echo
if ! confirm "确认执行配置?"; then
log "用户取消,退出。"
exit 1
fi
detect_os
install_ssh_server
configure_ssh
start_ssh_service
setup_firewall
setup_key_login
# 最终提示
echo
echo "=================================================="
echo "✅ SSH 配置完成!"
echo "🔑 登录方式:"
echo " 密钥登录: ssh -i ~/.ssh/id_rsa $USER@localhost"
echo " 或复制公钥到远程: ssh-copy-id user@host"
if [[ "$SSH_PORT" != "22" ]]; then
echo " 注意端口: -p $SSH_PORT"
fi
echo "💡 建议:生产环境关闭 PasswordAuthentication"
echo "=================================================="
}
# -------------------------------
# ✅ 执行
# -------------------------------
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
if [[ $(id -u) -ne 0 ]]; then
error "请使用 sudo 或 root 权限运行此脚本"
fi
main "$@" main "$@"
cleanup
fi fi