这个问题问得非常关键,阿杰 👍。一旦服务器被入侵,最正确的做法 不是只清理木马或打补丁,而是要彻底重建,以保证系统干净、可控。下面给你一个完整的流程,涵盖 重建、镜像、安全配置恢复:
一、为什么要重建?
- 黑客入侵后,系统可能被植入 后门程序、Rootkit,难以完全查杀。
- 关键配置、日志可能被篡改,继续使用风险极大。
- 重建干净环境比盲目修复更可靠。
二、重建服务器流程
1. 数据备份
- 立即隔离服务器(断开外网),避免进一步扩散。
- 备份需要的数据(数据库、业务数据、配置文件),不要备份可执行文件和可疑脚本。
- 对备份数据进行安全扫描(如
clamscan
、rkhunter
)。
2. 重装系统 / 使用官方镜像
- 推荐:直接通过云厂商控制台 重置系统 或 重新创建实例,选择官方最新镜像(如 CentOS Stream、Rocky Linux、Ubuntu LTS 等)。
- 确保基础系统干净,来源可靠。
- 建议打上最新补丁:
yum update -y # CentOS / Rocky / AlmaLinux apt update && apt upgrade -y # Ubuntu / Debian
3. 最小化安装 + 分区策略
- 安装时只保留必要组件,减少攻击面。
- 建议
/var
,/home
,/tmp
单独分区,设置noexec,nosuid,nodev
,限制恶意脚本执行。
三、安全镜像构建
在全新系统上,搭建好业务环境后:
- 安装必要的软件与依赖(Web 服务、数据库等)。
- 进行安全加固:
- 关闭不必要的服务:
systemctl disable telnet systemctl disable ftp
- 配置防火墙(Firewalld/iptables),仅开放必要端口。
- 修改 SSH 端口,禁止 root 远程登录,强制使用密钥认证。
- 安装 Fail2Ban / SSHGuard 防爆破。
- 关闭不必要的服务:
- 配置日志与监控:
- 启用
auditd
、rsyslog
,并远程备份日志,防篡改。 - 部署 IDS/IPS(如 Wazuh、OSSEC)。
- 启用
- 创建安全快照/镜像
- 云主机可使用 快照 功能保存此干净状态。
- 裸机服务器可用
Packer
或Clonezilla
制作自定义安全镜像。
四、配置恢复策略
- 恢复业务配置
- 从备份中恢复 必要配置文件(如
/etc/nginx/
,/etc/httpd/
, 数据库配置等)。 - 手动审查配置,避免导入可能包含后门的脚本。
- 从备份中恢复 必要配置文件(如
- 恢复业务数据
- 导入数据库、业务文件。
- 恢复前先进行安全扫描(查木马、webshell)。
- 重新部署代码
- 从 可信源代码仓库(Git、SVN) 拉取最新版本,而不是直接拷贝旧服务器文件。
- 确保依赖库通过包管理工具(pip、npm、yum 等)重新安装。
五、后续安全措施
- 最小权限原则:用户、数据库、服务账号都只给最小权限。
- 定期打补丁:系统与应用保持更新。
- 文件完整性监控:用
AIDE
或Tripwire
检测篡改。 - 自动化运维:使用 Ansible/SaltStack 建立“可重复部署”的脚本,快速重建。
- 定期快照:一旦再次入侵,可以快速回滚。
✅ 总结:
- 先隔离 → 备份数据 → 重装系统(官方镜像)。
- 构建干净安全镜像,打补丁、最小化安装、安全加固。
- 恢复配置与数据,严格审查。
- 建立快照与自动化运维,避免重复劳动。
稳了,阿杰!给你一份可直接落地的一键安全基线脚本(支持 RHEL 系列:CentOS/Rocky/Alma/CentOS Stream 与 Debian/Ubuntu)。它会完成:新建管理员账户与密钥登录、加固 SSH、开启防火墙、Fail2Ban、防爆破、自动更新、auditd、AIDE 完整性监控、sysctl 内核加固、时间同步等。脚本是幂等的,多次运行也安全。
使用说明
- 准备好你的 SSH 公钥(
ssh-ed25519 ...
或ssh-rsa ...
)。 - 按需修改脚本顶部变量(用户名、端口、放行业务端口、远程日志地址等)。
- 上机执行:
sudo bash harden-server.sh
- 脚本会在修改 SSH 前把 你的公钥写入新用户,并测试配置通过才重启 sshd,避免把你锁在门外。
一键安全基线脚本(harden-server.sh
)
#!/usr/bin/env bash
set -euo pipefail
#############################
# ==== 可按需修改的参数 ====
#############################
NEW_ADMIN_USER="devops"
NEW_ADMIN_SUDO_NOPASSWD=true # true/false 是否免密码 sudo(建议 true,之后再改回)
SSH_PORT="52222" # 新 SSH 端口
ADMIN_PUBKEY="ssh-ed25519 AAAA...你的公钥 ... user@host"
ALLOW_PORTS=("80" "443") # 业务需要放行的端口(TCP)
ENABLE_AUTO_UPDATES=true # 自动安全更新
REMOTE_SYSLOG="" # 如 "10.0.0.10:514" 留空则不配置
TIMEZONE="Asia/Shanghai" # 时区
#############################
log() { echo -e "\e[1;32m[+] $*\e[0m"; }
warn(){ echo -e "\e[1;33m[!] $*\e[0m"; }
err() { echo -e "\e[1;31m[-] $*\e[0m" >&2; }
require_root() {
if [[ $EUID -ne 0 ]]; then err "请用 root 运行"; exit 1; fi
}
detect_os() {
if command -v dnf >/dev/null 2>&1; then PKG=dnf; OSFAM="rhel"
elif command -v yum >/dev/null 2>&1; then PKG=yum; OSFAM="rhel"
elif command -v apt >/dev/null 2>&1; then PKG=apt; OSFAM="debian"
else err "不支持的系统"; exit 1; fi
log "已检测到: $OSFAM ($PKG)"
}
pkg_update() {
case "$OSFAM" in
rhel) $PKG -y makecache || true; $PKG -y update || true ;;
debian) $PKG update -y; $PKG dist-upgrade -y || true ;;
esac
}
install_pkgs() {
case "$OSFAM" in
rhel)
$PKG -y install sudo curl vim git jq chrony fail2ban aide aide-cli rsyslog \
policycoreutils-python-utils || true
# firewalld 优先;若无则安装
if ! command -v firewall-cmd >/dev/null 2>&1; then $PKG -y install firewalld; fi
systemctl enable --now chronyd rsyslog firewalld || true
;;
debian)
DEBIAN_FRONTEND=noninteractive $PKG install -y sudo curl vim git jq chrony fail2ban \
aide-common aide rsyslog ufw
systemctl enable --now chrony rsyslog || true
;;
esac
}
create_admin_user() {
if id "$NEW_ADMIN_USER" >/dev/null 2>&1; then
log "用户 $NEW_ADMIN_USER 已存在"
else
log "创建管理员用户 $NEW_ADMIN_USER"
useradd -m -s /bin/bash "$NEW_ADMIN_USER"
fi
mkdir -p /home/$NEW_ADMIN_USER/.ssh
chmod 700 /home/$NEW_ADMIN_USER/.ssh
echo "$ADMIN_PUBKEY" > /home/$NEW_ADMIN_USER/.ssh/authorized_keys
chmod 600 /home/$NEW_ADMIN_USER/.ssh/authorized_keys
chown -R $NEW_ADMIN_USER:$NEW_ADMIN_USER /home/$NEW_ADMIN_USER/.ssh
if $NEW_ADMIN_SUDO_NOPASSWD; then
echo "$NEW_ADMIN_USER ALL=(ALL) NOPASSWD:ALL" >/etc/sudoers.d/90-$NEW_ADMIN_USER
else
echo "$NEW_ADMIN_USER ALL=(ALL) ALL" >/etc/sudoers.d/90-$NEW_ADMIN_USER
fi
chmod 440 /etc/sudoers.d/90-$NEW_ADMIN_USER
}
configure_timezone() {
timedatectl set-timezone "$TIMEZONE" || true
timedatectl set-ntp true || true
log "时区与 NTP 已配置为 $TIMEZONE"
}
harden_sshd() {
log "备份并加固 SSH"
mkdir -p /etc/ssh/sshd_config.d
cp -a /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%s) || true
local conf="/etc/ssh/sshd_config.d/99-hardening.conf"
cat > "$conf" <<EOF
# 安全基线
Port $SSH_PORT
Protocol 2
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no
UsePAM yes
ClientAliveInterval 300
ClientAliveCountMax 2
LoginGraceTime 30
MaxAuthTries 3
MaxStartups 10:30:100
AllowUsers $NEW_ADMIN_USER
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
EOF
# 语法测试通过再重启,防止锁死
if sshd -t 2>/tmp/sshd_test.err; then
systemctl restart sshd || systemctl restart ssh || true
log "SSHD 已重启并启用新端口 $SSH_PORT(请确认安全组已放行)"
else
warn "SSHD 配置测试失败,已保留原配置:$(cat /tmp/sshd_test.err)"
mv /etc/ssh/sshd_config.bak.* /etc/ssh/sshd_config.bak || true
fi
}
configure_firewall() {
log "配置防火墙与端口放行"
if [[ "$OSFAM" == "rhel" ]]; then
systemctl enable --now firewalld
firewall-cmd --permanent --add-service=ssh >/dev/null 2>&1 || true
firewall-cmd --permanent --add-port=${SSH_PORT}/tcp
for p in "${ALLOW_PORTS[@]}"; do firewall-cmd --permanent --add-port=${p}/tcp; done
firewall-cmd --reload
firewall-cmd --list-all
else
ufw default deny incoming
ufw default allow outgoing
ufw allow ${SSH_PORT}/tcp
for p in "${ALLOW_PORTS[@]}"; do ufw allow ${p}/tcp; done
yes | ufw enable
ufw status verbose
fi
}
configure_fail2ban() {
log "配置 Fail2Ban 防爆破"
mkdir -p /etc/fail2ban/jail.d
cat > /etc/fail2ban/jail.d/sshd-hardening.local <<'EOF'
[sshd]
enabled = true mode = aggressive port = ssh filter = sshd backend = systemd maxretry = 4 findtime = 10m bantime = 1h ignorecommand = EOF # 映射到自定义端口 sed -i “s/^port = ssh/port = ${SSH_PORT}/” /etc/fail2ban/jail.d/sshd-hardening.local systemctl enable –now fail2ban fail2ban-client reload fail2ban-client status sshd || true } configure_sysctl() { log “内核参数加固 (sysctl)” cat > /etc/sysctl.d/99-hardening.conf <<‘EOF’ # 网络与内核防护 net.ipv4.ip_forward = 0 net.ipv4.conf.all.send_redirects = 0 net.ipv4.conf.default.send_redirects = 0 net.ipv4.conf.all.accept_source_route = 0 net.ipv4.conf.default.accept_source_route = 0 net.ipv4.conf.all.accept_redirects = 0 net.ipv4.conf.default.accept_redirects = 0 net.ipv4.conf.all.secure_redirects = 0 net.ipv4.conf.default.secure_redirects = 0 net.ipv4.conf.all.rp_filter = 1 net.ipv4.conf.default.rp_filter = 1 net.ipv4.tcp_syncookies = 1 net.ipv4.icmp_echo_ignore_broadcasts = 1 net.ipv4.icmp_ignore_bogus_error_responses = 1 kernel.kptr_restrict = 2 kernel.randomize_va_space = 2 kernel.dmesg_restrict = 1 fs.protected_hardlinks = 1 fs.protected_symlinks = 1 kernel.unprivileged_bpf_disabled = 1 EOF sysctl –system } configure_updates() { if ! $ENABLE_AUTO_UPDATES; then warn “已跳过自动更新配置”; return fi log “配置自动安全更新” if [[ “$OSFAM” == “rhel” ]]; then $PKG -y install dnf-automatic || $PKG -y install yum-cron || true systemctl enable –now dnf-automatic.timer || systemctl enable –now yum-cron || true # 邮件通知可按需配置 /etc/dnf/automatic.conf else DEBIAN_FRONTEND=noninteractive $PKG install -y unattended-upgrades dpkg-reconfigure -f noninteractive unattended-upgrades # 如需仅安全更新,可编辑 /etc/apt/apt.conf.d/50unattended-upgrades fi } configure_auditd_aide() { log “配置 auditd 与 AIDE 完整性基线” systemctl enable –now auditd || true # 初始化 AIDE(首次较慢) if [[ ! -f /var/lib/aide/aide.db.gz && ! -f /var/lib/aide/aide.db ]]; then log “初始化 AIDE 数据库(可能需要几分钟)” aide –init || true if [[ -f /var/lib/aide/aide.db.new.gz ]]; then mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz fi fi # 简单的每天巡检任务 cat > /etc/cron.daily/aide-check <<‘EOF’ #!/bin/sh /usr/sbin/aide –check >/var/log/aide-check.log 2>&1 || true EOF chmod +x /etc/cron.daily/aide-check } configure_rsyslog_remote() { if [[ -z “$REMOTE_SYSLOG” ]]; then warn “未配置远程 Syslog,已跳过(可在脚本顶部设置 REMOTE_SYSLOG)” return fi log “配置远程日志转发到 $REMOTE_SYSLOG” local host=”${REMOTE_SYSLOG%:*}” local port=”${REMOTE_SYSLOG##*:}” cat > /etc/rsyslog.d/60-remote.conf <<EOF # 远程日志(TCP) *.* @@${host}:${port} EOF systemctl restart rsyslog } lock_root_password_if_needed() { # 仅在密钥登录可用时锁定 root 密码 if grep -qE “^${NEW_ADMIN_USER}:” /etc/passwd && [[ -s /home/$NEW_ADMIN_USER/.ssh/authorized_keys ]]; then log “可选:锁定 root 密码以禁用本地口令登录(不影响密钥与 sudo)” passwd -l root || true else warn “未发现管理员密钥,出于安全考虑不锁定 root 密码” fi } final_tips() { log “安全基线完成 ✅” echo “==> 建议动作:” echo ” 1) 确认能用: ssh -p ${SSH_PORT} ${NEW_ADMIN_USER}@服务器IP” echo ” 2) 在云控制台/安全组放行 TCP ${SSH_PORT}、${ALLOW_PORTS[*]}” echo ” 3) 业务上线前,用 ‘aide –check’ 自检文件完整性” echo ” 4) 建议创建云快照/镜像,作为“干净基线”” } main() { require_root detect_os pkg_update install_pkgs create_admin_user configure_timezone harden_sshd configure_firewall configure_fail2ban configure_sysctl configure_updates configure_auditd_aide configure_rsyslog_remote lock_root_password_if_needed final_tips } main “$@”
小提示与可选项
- 先放行端口再换 SSH 端口:公有云记得同步修改安全组,或者先临时保留原 22 端口,以免断连。
- SELinux:RHEL 系建议保持 Enforcing;若你更熟悉,可用
semanage port -a -t ssh_port_t -p tcp $SSH_PORT
显式放行自定义端口(脚本已安装所需工具)。 - Web/DB 端口:将需要暴露的端口加到
ALLOW_PORTS
数组,如("80" "443" "3306")
。 - 远程日志:把 SIEM/日志中心地址填入
REMOTE_SYSLOG="10.0.0.10:514"
,便于篡改取证。 - 自动更新:生产环境若有变更窗口,可把
ENABLE_AUTO_UPDATES=false
,改为告警+人工批。 - 再次入侵快速回滚:跑完脚本、全量打补丁、部署业务后,立刻创建 云快照/镜像。
发表回复