Ubuntu服务器默认安装后并不是为高强度生产环境准备的,很多安全策略和自动化机制都需要手动加固。如果你管理着多台Ubuntu服务器,还在逐台登录、手动更新、手动查看日志,不仅效率低,还容易遗漏关键风险。真正有效的做法是把日常运维中重复、高风险、易出错的操作脚本化,配合定时任务和监控告警,形成一套半自动甚至全自动的运维体系。下面直接讲具体怎么落地。
先做安全基线检查脚本
安全加固的第一步是知道服务器现状。写一个基线检查脚本,把Ubuntu系统里最容易被攻击的配置项全部检查一遍,输出明确结果。检查项包括:是否禁用了root的SSH密码登录、是否修改了默认SSH端口、防火墙是否开启、是否安装了fail2ban、是否有未授权的sudo权限账户、系统更新是否滞后、是否开启了不必要的服务等。
一个简化版的检查脚本可以这样写:
#!/bin/bash echo "=== SSH配置检查 ===" grep -E "^(PermitRootLogin|PasswordAuthentication|Port)" /etc/ssh/sshd_config 2>/dev/null echo "=== 防火墙状态 ===" sudo ufw status verbose 2>/dev/null || echo "UFW未安装或未启用" echo "=== Fail2ban状态 ===" systemctl is-active fail2ban 2>/dev/null || echo "fail2ban未运行" echo "=== Sudo权限账户 ===" grep -Po '^sudo:.*:\K.*$' /etc/group echo "=== 可更新包数量 ===" apt list --upgradable 2>/dev/null | grep -c upgradable echo "=== 监听中的端口 ===" ss -tulnp | grep LISTEN
把这个脚本保存为security_check.sh,加上执行权限后定期运行。更推荐的做法是把它接入到集中化运维平台或者定时任务里,每天跑一次,把结果追加到日志文件,再配合简单的关键词告警,比如发现“PermitRootLogin yes”就立刻通知你。
自动化安全更新与重启策略
Ubuntu的unattended-upgrades包可以自动安装安全更新,但很多人配置完就不管了,结果要么更新没生效,要么服务更新后需要重启但一直没重启,导致内核漏洞修了等于没修。正确做法是明确配置只自动安装安全更新,并且设置自动重启策略。
安装并启用:
sudo apt install unattended-upgrades -y sudo dpkg-reconfigure --priority=low unattended-upgrades
然后编辑/etc/apt/apt.conf.d/50unattended-upgrades,重点确认以下几行:
Unattended-Upgrade::Allowed-Origins {
"${distro_id}:${distro_codename}-security";
};
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "03:00";
这样系统只自动更新安全源里的包,避免更新普通软件包引入兼容性问题。自动重启放在凌晨三点,对业务影响最小。如果某些服务不能随便重启,可以在配置文件里用Unattended-Upgrade::Automatic-Reboot-WithUsers "false"来避免有用户登录时重启,或者把关键服务放到黑名单里。
另外建议配合一个更新后检查脚本,确认关键服务在重启后是否正常拉起。比如Nginx、MySQL、Redis这些,更新后自动执行systemctl is-active检查,异常就发告警。
SSH登录防护与动态封锁脚本
Ubuntu服务器最大的攻击面就是SSH。除了改端口、禁用密码登录、使用密钥认证这些基础操作,还需要动态识别暴力破解并自动封锁。fail2ban是标配,但默认配置对Ubuntu的日志格式适配有时不够精准,需要根据实际日志路径调整。
安装fail2ban后,创建一个本地配置/etc/fail2ban/jail.local:
[sshd] enabled = true port = 22 filter = sshd logpath = /var/log/auth.log maxretry = 3 bantime = 3600 findtime = 600
这个配置表示10分钟内失败3次就封禁1小时。对于攻击密集的服务器,可以把bantime调大到86400甚至更长。同时建议把封禁日志单独输出,方便后续分析攻击来源。
如果不想依赖fail2ban,也可以用纯脚本实现轻量级封锁。思路是定时分析/var/log/auth.log,提取失败登录的IP,超过阈值就用ufw拒绝。脚本示例:
#!/bin/bash
LOG=/var/log/auth.log
THRESHOLD=5
BLOCKLIST=/tmp/ssh_blocked_ips.txt
tail -n 5000 $LOG | grep "Failed password" | grep -oP 'from \K[0-9.]+' | sort | uniq -c | sort -rn | while read count ip; do
if [ $count -ge $THRESHOLD ]; then
if ! grep -q $ip $BLOCKLIST 2>/dev/null; then
sudo ufw deny from $ip to any
echo $ip >> $BLOCKLIST
echo "[$(date)] 已封锁 $ip,失败次数 $count" >> /var/log/ssh_auto_block.log
fi
fi
done
这个脚本可以每5分钟执行一次,对频繁攻击的IP自动封锁。优点是轻量、透明,缺点是功能不如fail2ban丰富,适合作为补充手段。
日志集中分析与异常检测
单台服务器的日志只能看到局部问题,多台服务器就需要把关键日志集中起来分析。Ubuntu上可以用rsyslog把auth日志、syslog、内核日志转发到一台日志服务器,再配合简单的脚本做异常模式匹配。
在日志服务器上配置rsyslog接收端:
# /etc/rsyslog.conf 取消注释 module(load="imudp") input(type="imudp" port="514")
在客户端Ubuntu服务器上添加转发规则:
# /etc/rsyslog.d/50-forward.conf auth,authpriv.* @日志服务器IP:514 *.* @日志服务器IP:514
日志集中后,写一个分析脚本,每天定时扫描关键事件:新增sudo用户、root登录成功、大量失败登录、异常服务停止、磁盘使用率超过阈值、内核报错等。把结果汇总成一份日报,通过邮件或企业微信机器人推送。
一个简单的异常扫描脚本片段:
#!/bin/bash
echo "=== 今日新增sudo用户 ==="
grep "useradd" /var/log/auth.log | grep sudo
echo "=== root登录记录 ==="
grep "Accepted" /var/log/auth.log | grep "root"
echo "=== 磁盘使用率超过80% ==="
df -h | awk 'NR>1 {gsub("%","",$5); if($5+0>80) print $1,$5"%"}'
echo "=== 服务异常停止 ==="
journalctl --since "today" -p err | grep -E "Failed|stopped|killed" | tail -20
这种脚本的价值在于把被动看日志变成主动发现异常,尤其是多台服务器时,能大幅减少人工巡检时间。
备份自动化与恢复验证
安全策略做得再好,数据丢失一样是灾难。Ubuntu服务器上最常见的备份需求是数据库、配置文件和网站目录。很多人用crontab定时tar打包,但从不验证备份是否可用,等到需要恢复时才发现备份文件损坏或者不完整。
一个可靠的备份脚本应该包含:备份、压缩、保留最近N份、校验、异地传输。以MySQL和网站目录为例:
#!/bin/bash
BACKUP_DIR=/backup
DATE=$(date +%F)
MYSQL_USER=backup
MYSQL_PASS=your_password
WEB_DIR=/var/www
mkdir -p $BACKUP_DIR/$DATE
mysqldump -u$MYSQL_USER -p$MYSQL_PASS --all-databases | gzip > $BACKUP_DIR/$DATE/db_all.sql.gz
tar -czf $BACKUP_DIR/$DATE/www.tar.gz $WEB_DIR
md5sum $BACKUP_DIR/$DATE/* > $BACKUP_DIR/$DATE/checksum.txt
# 保留最近7天
find $BACKUP_DIR -maxdepth 1 -type d -mtime +7 -exec rm -rf {} \;
# 异地传输示例(rsync到备份服务器)
rsync -avz --delete $BACKUP_DIR/ backup@备份服务器:/backup/ubuntu-server/
关键点是md5校验和异地传输。每周至少做一次恢复演练,把备份文件解压到测试目录,确认数据库能导入、网站文件完整。这个演练也可以脚本化,自动执行恢复并检查关键表是否存在。
资源监控与自动扩容告警
Ubuntu服务器在资源耗尽前往往会有征兆,比如内存使用率持续上升、磁盘IO等待变高、CPU负载异常。与其等服务器卡死再处理,不如用脚本定时采集指标,超过阈值就告警,必要时自动清理。
一个综合资源监控脚本:
#!/bin/bash
CPU_LOAD=$(uptime | awk -F'load average:' '{print $2}' | cut -d, -f1 | tr -d ' ')
MEM_USED=$(free | awk '/Mem/{printf("%.0f"), $3/$2*100}')
DISK_USED=$(df / | awk 'NR==2 {gsub("%","",$5); print $5}')
SWAP_USED=$(free | awk '/Swap/{if($2>0) printf("%.0f"), $3/$2*100; else print 0}')
if [ $(echo "$CPU_LOAD > 4" | bc) -eq 1 ]; then
echo "告警:CPU负载过高 $CPU_LOAD" >> /var/log/resource_alert.log
fi
if [ $MEM_USED -gt 90 ]; then
echo "告警:内存使用率 $MEM_USED%" >> /var/log/resource_alert.log
# 自动清理页缓存
sync && echo 1 > /proc/sys/vm/drop_caches
fi
if [ $DISK_USED -gt 85 ]; then
echo "告警:磁盘使用率 $DISK_USED%" >> /var/log/resource_alert.log
# 清理旧日志
find /var/log -type f -name "*.log" -mtime +30 -delete
fi
这个脚本不仅能告警,还能在内存和磁盘紧张时自动做无害清理。注意自动清理要保守,只清理确定无用的缓存和旧日志,避免误删业务数据。
定时任务统一管理与防冲突
脚本多了以后,crontab会变得混乱,执行时间冲突、重复执行、失败无感知是常见问题。建议把所有运维脚本统一放在/opt/scripts目录下,用统一的命名规范,比如backup_db.sh、security_check.sh、resource_monitor.sh。然后在crontab里集中管理,每个任务都加上日志输出和锁机制。
锁机制防止脚本未执行完又被触发:
#!/bin/bash
LOCK_FILE=/tmp/backup_db.lock
if [ -f $LOCK_FILE ]; then
echo "备份脚本已在运行,跳过本次执行"
exit 1
fi
touch $LOCK_FILE
trap "rm -f $LOCK_FILE" EXIT
# 实际备份逻辑
crontab配置示例:
# 每天凌晨2点备份 0 2 * * * /opt/scripts/backup_db.sh >> /var/log/backup_db.log 2>&1 # 每5分钟检查资源 */5 * * * * /opt/scripts/resource_monitor.sh >> /var/log/resource_monitor.log 2>&1 # 每小时做安全基线检查 0 * * * * /opt/scripts/security_check.sh >> /var/log/security_check.log 2>&1
所有脚本的输出都重定向到独立日志,方便回溯。如果脚本执行失败,可以通过日志里的错误信息快速定位,而不是等到业务出问题才发现。
用systemd timer替代cron实现更精细调度
对于需要精确控制执行间隔、失败重试、依赖关系的任务,systemd timer比cron更合适。比如某个备份任务需要在网络服务启动后才能执行,或者某个清理任务需要在备份完成后才运行。
创建一个timer单元:
# /etc/systemd/system/backup.service [Unit] Description=Database backup service After=network.target mysql.service [Service] Type=oneshot ExecStart=/opt/scripts/backup_db.sh
# /etc/systemd/system/backup.timer [Unit] Description=Run backup daily at 2am [Timer] OnCalendar=*-*-* 02:00:00 Persistent=true [Install] WantedBy=timers.target
启用timer:
sudo systemctl daemon-reload sudo systemctl enable backup.timer sudo systemctl start backup.timer
systemd timer的优势在于可以精确到秒、支持Persistent(错过执行时间后补跑)、可以设置Unit依赖、执行结果可以通过systemctl status直接查看。对于关键运维任务,强烈建议从cron迁移到systemd timer。
集中化配置管理与版本控制
当服务器数量超过5台,脚本和配置文件的同步就会成为新问题。每台机器手动拷贝脚本不仅低效,还容易出现版本不一致。把运维脚本和配置文件纳入Git管理,配合简单的拉取机制,可以做到一处更新、全集群生效。
在Git仓库中组织脚本:
ops-scripts/
├── common/
│ ├── security_check.sh
│ ├── resource_monitor.sh
│ └── backup_db.sh
├── web-server/
│ └── nginx_hardening.sh
└── db-server/
└── mysql_backup.sh
每台服务器上配置一个同步脚本,定时从Git仓库拉取最新脚本:
#!/bin/bash cd /opt/ops-scripts git pull origin main --quiet chmod +x common/*.sh
这样所有服务器的运维脚本版本一致,修改一次就能同步到所有机器。配合Git的提交记录,还能追溯每次变更内容和时间,大幅降低运维风险。
把这些脚本和机制组合起来,Ubuntu服务器的日常运维就能从手动操作转变成自动化流程。安全基线检查、自动更新、SSH防护、日志分析、备份验证、资源监控、定时调度、配置同步,每一块都有明确脚本支撑,并且互相配合。关键是先从小处开始,把最痛的那几个点脚本化,逐步扩展,最终形成一套适合自己业务规模的自动化运维体系。
