大多数成功的服务器入侵,靠的不是精巧的 exploit,而是一个补丁在数月前就已发布的漏洞。原因通常不是懒:在一台跑着业务的服务器上手动更新是件吓人的事,因为 apt upgrade 有时会重启谁也没要求它重启的东西。自动更新能了结这个问题,但它名声不好——而这份名声恰好与它被草率配置的程度相称。
我们来看一套配置:安全补丁自己到来,而 PHP 的版本不会一夜之间变掉。
安装
sudo apt install unattended-upgrades apt-listchanges
sudo dpkg-reconfigure -plow unattended-upgrades
这个对话框会生成 /etc/apt/apt.conf.d/20auto-upgrades。请看看结果是什么:
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
这里的 1 表示「每天」。这一切由 systemd 的计时器驱动,而不是经典的 cron:
systemctl list-timers | grep apt
最要紧的一点:只要安全更新
主文件是 /etc/apt/apt.conf.d/50unattended-upgrades。里面有一份 Allowed-Origins 清单(在 Debian 上叫 Origins-Pattern),而默认情况下那里只有安全仓库是启用的。就让它保持这样。
把 -updates 那行的注释去掉——「让所有东西都更新吧」——这种诱惑很强。别这么做:updates 仓库带来的是新版本而不只是修复,而夜里的意外正是从那里来的。安全和新鲜是两件不同的事,而后一件最好在你坐在键盘前的时候做。
如果某个软件包不该被自动更新,那么它有一份黑名单:
Unattended-Upgrade::Package-Blacklist {
"mariadb-server";
"php8.3-fpm";
};
请有意识地使用它:这里的每一行都意味着「这个我自己更新」——而这是一个必须兑现的承诺。
重启
关键的一行:
Unattended-Upgrade::Automatic-Reboot "false";
对一台带站点的服务器来说,把它留在 false 是对的:凌晨三点毫无预告地重启,比一个推迟一天的补丁更糟。但这个决定有一个强制性的后半段,而正是这后半段被人忘掉。
当内核、libc 或 openssl 被更新时,系统会创建文件 /var/run/reboot-required。在重启之前,修好的版本躺在磁盘上,而旧的仍在内存里运行——也就是说,尽管软件包算是更新过了,漏洞哪儿也没去。请明确地检查它:
ls -l /var/run/reboot-required
cat /var/run/reboot-required.pkgs
第二个文件写明了究竟是什么让重启成为必要。规则很简单:看到这个标记,就在接下来一两天内安排一个窗口,而不是「哪天再说」。
库是单独的一类。openssl 被更新了,而 nginx 和 PHP-FPM 仍带着内存里的旧版本继续运行,重启标记压根不会出现。谁需要重启由 needrestart 告诉你:
sudo apt install needrestart
sudo needrestart -b
只要把它设成「只报告、不发问」模式——否则它会在一次非交互式安装的中途开始提问,把安装挂在那儿。为此请往 /etc/needrestart/conf.d/ 里放一个含有 $nrconf{restart} = 'l'; 这一行的文件。
确认它确实在工作
不做实际安装的试运行:
sudo unattended-upgrade --dry-run --debug
输出会显示哪些包符合规则,哪些被拒绝以及原因。实际发生了什么则在日志里:
sudo tail -50 /var/log/unattended-upgrades/unattended-upgrades.log
此刻有多少更新在等着:
apt list --upgradable 2>/dev/null | grep -i security
最后这条命令是其中最有用的。配置好的自动更新有一种悄无声息坏掉的习惯:/boot 满了、仓库换了密钥、某次手动改动之后计时器被关掉了。表面上一切正常,而补丁已经几个月没装上了。
/boot 上的空间
经典情形:旧内核不被删除,/boot 分区被填满,新内核装不上,于是更新整体停摆。每隔几个月值得看一眼:
df -h /boot
sudo apt autoremove --purge
或者在同一个 50unattended-upgrades 里打开自动清理:Remove-Unused-Kernel-Packages "true" 和 Remove-Unused-Dependencies "true"。
留给人的部分
自动更新去掉的是例行操作,而不是盯着看这件事。有两个问题你在任何时刻都该答得上来:有多少安全更新在等着,以及重启标记是否已经立起。这两个答案在页面上就是一行字,而全部意义在于这一行能自己映入眼帘,而不是等你每季度主动去查一次。