Update ssh
This commit is contained in:
706
ssh
706
ssh
@@ -1,459 +1,351 @@
|
||||
#!/bin/bash
|
||||
# ========================================================
|
||||
# 🛡️ secure-ssh-setup.sh
|
||||
# 自动配置 SSH 服务 | 支持密钥登录 | 防火墙放行 | 安全加固
|
||||
# 作者:AI 助手 | 兼容 Ubuntu/CentOS/Debian/RHEL
|
||||
# 无需 Python,一键运行
|
||||
# ========================================================
|
||||
|
||||
# 自动更改SSH端口和root密码脚本(带自动回滚功能)
|
||||
# 支持 Ubuntu/Debian/CentOS/RHEL 系统
|
||||
set -euo pipefail # 严格模式:出错退出、未定义变量报错、管道错误捕获
|
||||
|
||||
set -e # 遇到错误立即退出
|
||||
# -------------------------------
|
||||
# 🔧 配置选项(可自定义)
|
||||
# -------------------------------
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
SSH_PORT=${SSH_PORT:-22} # SSH 端口(默认 22)
|
||||
ENABLE_PASSWORD_LOGIN=${ENABLE_PASSWORD_LOGIN:-yes} # 是否允许密码登录
|
||||
ENABLE_ROOT_LOGIN=${ENABLE_ROOT_LOGIN:-no} # 是否允许 root 密码登录(no 更安全)
|
||||
AUTO_INSTALL_SSH=${AUTO_INSTALL_SSH:-yes} # 是否自动安装 openssh-server
|
||||
USE_KEY_LOGIN=${USE_KEY_LOGIN:-yes} # 是否生成密钥并启用密钥登录
|
||||
|
||||
# 配置变量
|
||||
NEW_PORT="2222"
|
||||
NEW_PASSWORD="Xx3459635287"
|
||||
ROLLBACK_TIME=300 # 5分钟回滚时间(秒)
|
||||
CHECK_INTERVAL=10 # 检查间隔(秒)
|
||||
# -------------------------------
|
||||
# 🧰 工具函数
|
||||
# -------------------------------
|
||||
|
||||
# 全局变量
|
||||
BACKUP_FILE=""
|
||||
ORIGINAL_PORT=""
|
||||
OS=""
|
||||
|
||||
# 日志函数
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
log() {
|
||||
echo "[$(date +'%H:%M:%S')] 📝 $*"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
success() {
|
||||
echo "[$(date +'%H:%M:%S')] ✅ $*"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
log_debug() {
|
||||
echo -e "${BLUE}[DEBUG]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查root权限
|
||||
check_root() {
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
log_error "此脚本需要root权限运行"
|
||||
error() {
|
||||
echo "[$(date +'%H:%M:%S')] ❌ $*" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 检测系统类型
|
||||
warn() {
|
||||
echo "[$(date +'%H:%M:%S')] ⚠️ $*"
|
||||
}
|
||||
|
||||
# 检查命令是否存在
|
||||
has_cmd() {
|
||||
command -v "$1" &>/dev/null
|
||||
}
|
||||
|
||||
# 等待用户确认(可跳过)
|
||||
confirm() {
|
||||
if [[ "${FORCE:-}" == "yes" ]]; then
|
||||
return 0
|
||||
fi
|
||||
read -p "$1 [y/N]: " -n 1 -r
|
||||
echo
|
||||
[[ ! $REPLY =~ ^[Yy]$ ]] && return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
# -------------------------------
|
||||
# 🖥️ 检测系统信息
|
||||
# -------------------------------
|
||||
|
||||
detect_os() {
|
||||
if [[ -f /etc/redhat-release ]]; then
|
||||
OS="centos"
|
||||
elif [[ -f /etc/debian_version ]]; then
|
||||
OS="debian"
|
||||
elif grep -q "ubuntu" /etc/os-release; then
|
||||
OS="ubuntu"
|
||||
if [[ -f /etc/os-release ]]; then
|
||||
. /etc/os-release
|
||||
OS_NAME="$NAME"
|
||||
OS_ID="$ID"
|
||||
OS_VERSION="$VERSION_ID"
|
||||
elif [[ -f /etc/redhat-release ]]; then
|
||||
OS_NAME=$(cat /etc/redhat-release)
|
||||
OS_ID="rhel"
|
||||
else
|
||||
log_error "不支持的操作系统"
|
||||
exit 1
|
||||
OS_NAME=$(uname -s)
|
||||
OS_ID="unknown"
|
||||
fi
|
||||
log_info "检测到操作系统: $OS"
|
||||
log "检测到系统: $OS_NAME"
|
||||
}
|
||||
|
||||
# 获取当前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"
|
||||
}
|
||||
# -------------------------------
|
||||
# 🔧 安装 OpenSSH 服务(如未安装)
|
||||
# -------------------------------
|
||||
|
||||
# 备份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"
|
||||
install_ssh_server() {
|
||||
if has_cmd sshd || has_cmd ssh; then
|
||||
return 0
|
||||
else
|
||||
log_error "SSH服务未在端口 $NEW_PORT 监听"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ "$AUTO_INSTALL_SSH" != "yes" ]]; then
|
||||
error "SSH 服务未安装,且 AUTO_INSTALL_SSH=no,退出。"
|
||||
fi
|
||||
|
||||
log "OpenSSH 未安装,正在安装..."
|
||||
|
||||
case "$OS_ID" in
|
||||
ubuntu|debian)
|
||||
DEBIAN_FRONTEND=noninteractive apt-get update -qq
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y openssh-server
|
||||
;;
|
||||
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
|
||||
;;
|
||||
arch)
|
||||
pacman -S openssh
|
||||
;;
|
||||
suse|opensuse)
|
||||
zypper install -y openssh
|
||||
;;
|
||||
*)
|
||||
error "不支持的系统类型,无法自动安装 SSH"
|
||||
;;
|
||||
esac
|
||||
|
||||
if ! has_cmd sshd && ! has_cmd ssh; then
|
||||
error "安装失败,请手动安装 openssh-server"
|
||||
fi
|
||||
|
||||
success "OpenSSH 安装完成"
|
||||
}
|
||||
|
||||
# 手动确认验证函数
|
||||
manual_verification() {
|
||||
local start_time=$(date +%s)
|
||||
local current_time=0
|
||||
local elapsed_time=0
|
||||
# -------------------------------
|
||||
# ⚙️ 配置 SSH 服务
|
||||
# -------------------------------
|
||||
|
||||
echo "=========================================="
|
||||
log_info "⏰ 您有 5 分钟时间测试新配置"
|
||||
log_info "🔑 测试连接信息:"
|
||||
echo ""
|
||||
echo " IP: $(hostname -I | awk '{print $1}')"
|
||||
echo " 端口: $NEW_PORT"
|
||||
echo " 密码: $NEW_PASSWORD"
|
||||
echo ""
|
||||
echo " 测试命令:"
|
||||
echo " ssh root@$(hostname -I | awk '{print $1}') -p $NEW_PORT"
|
||||
echo ""
|
||||
log_info "💡 提示:请在新终端窗口中测试连接"
|
||||
log_info " 成功登录后请返回此窗口输入 'confirm' 确认"
|
||||
echo "=========================================="
|
||||
configure_ssh() {
|
||||
local config="/etc/ssh/sshd_config"
|
||||
local backup="${config}.backup.$(date +%s)"
|
||||
|
||||
while true; do
|
||||
current_time=$(date +%s)
|
||||
elapsed_time=$((current_time - start_time))
|
||||
local remaining_time=$((ROLLBACK_TIME - elapsed_time))
|
||||
|
||||
# 检查是否超时
|
||||
if [[ $elapsed_time -ge $ROLLBACK_TIME ]]; then
|
||||
log_warn "⏰ 超时:在 5 分钟内未收到确认"
|
||||
echo ""
|
||||
return 1
|
||||
# 备份原始配置
|
||||
if [[ ! -f "$backup" && -f "$config" ]]; then
|
||||
cp "$config" "$backup"
|
||||
success "SSH 配置已备份: $backup"
|
||||
fi
|
||||
|
||||
# 显示剩余时间
|
||||
if [[ $((elapsed_time % 30)) -eq 0 ]]; then
|
||||
echo ""
|
||||
log_info "剩余时间: $((remaining_time / 60))分$((remaining_time % 60))秒"
|
||||
echo "请输入 'confirm' 确认成功,或等待自动回滚..."
|
||||
fi
|
||||
# 确保目录存在
|
||||
mkdir -p /etc/ssh
|
||||
|
||||
# 设置超时读取,避免永久阻塞
|
||||
if read -t 10 -p "请输入 'confirm' 确认: " user_confirm; then
|
||||
if [[ $user_confirm == "confirm" ]]; then
|
||||
log_info "✅ 用户确认成功,新配置将永久生效"
|
||||
echo ""
|
||||
return 0
|
||||
# 写入基础配置(如果文件不存在)
|
||||
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
|
||||
log_warn "无效输入,请输入 'confirm'"
|
||||
fi
|
||||
# 修改现有配置
|
||||
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
|
||||
|
||||
# 每10秒显示一次提示
|
||||
if [[ $((elapsed_time % 10)) -eq 0 ]]; then
|
||||
echo "等待确认中... (剩余 $((remaining_time / 60))分$((remaining_time % 60))秒)"
|
||||
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
|
||||
done
|
||||
|
||||
if [[ -z "$service" ]]; then
|
||||
error "未找到 SSH 服务,请检查是否安装正确"
|
||||
fi
|
||||
|
||||
if systemctl is-active --quiet "$service"; then
|
||||
success "$service 已在运行"
|
||||
else
|
||||
log "启动 $service 服务..."
|
||||
systemctl start "$service" && systemctl enable "$service"
|
||||
success "$service 已启动并设置开机自启"
|
||||
fi
|
||||
|
||||
# 检查端口是否监听
|
||||
if ! has_cmd ss && has_cmd netstat; then
|
||||
if ! netstat -tuln | grep -q ":$SSH_PORT\b"; then
|
||||
warn "端口 $SSH_PORT 未监听,尝试重启..."
|
||||
systemctl restart "$service"
|
||||
sleep 2
|
||||
if ! netstat -tuln | grep -q ":$SSH_PORT\b"; then
|
||||
error "SSH 服务启动失败,请检查日志: journalctl -u $service"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
if ! ss -tuln | grep -q ":$SSH_PORT\b"; then
|
||||
warn "端口 $SSH_PORT 未监听,尝试重启..."
|
||||
systemctl restart "$service"
|
||||
sleep 2
|
||||
if ! ss -tuln | grep -q ":$SSH_PORT\b"; then
|
||||
error "SSH 服务启动失败,请检查日志: journalctl -u $service"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# 显示警告信息
|
||||
show_warning() {
|
||||
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
|
||||
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() {
|
||||
rm -f /tmp/old_root_password.hash
|
||||
unset SSH_PORT ENABLE_PASSWORD_LOGIN ENABLE_ROOT_LOGIN AUTO_INSTALL_SSH USE_KEY_LOGIN FORCE
|
||||
}
|
||||
|
||||
# 信号处理
|
||||
setup_signal_handlers() {
|
||||
trap 'echo; log_warn "接收到中断信号,开始回滚..."; rollback_changes; cleanup; exit 1' INT TERM
|
||||
}
|
||||
# -------------------------------
|
||||
# 🚀 主函数
|
||||
# -------------------------------
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
log_info "开始执行SSH配置脚本(带自动回滚功能)"
|
||||
log "开始配置 SSH 服务..."
|
||||
|
||||
# 设置信号处理
|
||||
setup_signal_handlers
|
||||
# 提示用户
|
||||
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
|
||||
|
||||
show_warning
|
||||
check_root
|
||||
detect_os
|
||||
get_current_ssh_port
|
||||
install_ssh_server
|
||||
configure_ssh
|
||||
start_ssh_service
|
||||
setup_firewall
|
||||
setup_key_login
|
||||
|
||||
# 执行配置更改
|
||||
change_ssh_port
|
||||
change_root_password
|
||||
configure_firewall
|
||||
check_selinux
|
||||
|
||||
if ! restart_ssh_service; then
|
||||
log_error "SSH服务重启失败,开始回滚"
|
||||
rollback_changes
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! verify_changes; then
|
||||
log_error "配置验证失败,开始回滚"
|
||||
rollback_changes
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 启动手动验证
|
||||
log_info "启动配置验证阶段..."
|
||||
if manual_verification; then
|
||||
log_info "✅ 配置验证成功!新SSH配置已生效"
|
||||
log_info "✅ 端口: $NEW_PORT"
|
||||
log_info "✅ 请妥善保管新密码"
|
||||
|
||||
# 询问是否保留原端口
|
||||
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
|
||||
|
||||
cleanup
|
||||
log_info "🎉 脚本执行完成!新配置已永久生效"
|
||||
else
|
||||
log_error "❌ 配置验证失败,开始回滚..."
|
||||
rollback_changes
|
||||
cleanup
|
||||
exit 1
|
||||
# 最终提示
|
||||
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
|
||||
main "$@"
|
||||
if [[ $(id -u) -ne 0 ]]; then
|
||||
error "请使用 sudo 或 root 权限运行此脚本"
|
||||
fi
|
||||
main "$@"
|
||||
cleanup
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user