在Ubuntu服务器上,安全更新不及时是导致系统被入侵的最常见原因之一。很多运维人员以为安装了系统就万事大吉,结果几年后才发现内核漏洞、OpenSSL漏洞一个都没修补。unattended-upgrades这个包就是专门解决这个问题的,它能让你在完全不干预的情况下,自动安装关键安全补丁。但默认配置往往不够用,或者根本就没启用,我们需要把它调教到生产环境可用的状态。
确认unattended-upgrades是否已安装并启用绝大多数Ubuntu发行版在安装时会询问你是否启用自动更新,如果你选了是,那么unattended-upgrades就已经在运行了。但我们还是得亲手验证一下。先执行安装命令,如果已安装会直接提示,没安装就装上:
sudo apt update sudo apt install unattended-upgrades -y
安装完成后,立即检查它的运行状态。Ubuntu 16.04及更高版本使用systemd来管理定时任务,不再依赖传统的cron。执行下面这条命令查看定时器是否活跃:
sudo systemctl status apt-daily-upgrade.timer
输出中如果看到"Active: active (waiting)",说明定时器已经在等待触发。如果没有启用,手动启动并设置为开机自启:
sudo systemctl enable apt-daily-upgrade.timer sudo systemctl start apt-daily-upgrade.timer
这里有个容易被忽略的细节:apt-daily-upgrade.timer负责实际的升级操作,还有一个apt-daily.timer负责更新软件包列表。两者必须同时正常工作,自动更新才能跑通。用同样的方法检查apt-daily.timer的状态,确保它也是活跃的。
核心配置文件详解主配置文件位于/etc/apt/apt.conf.d/50unattended-upgrades。这个文件控制着哪些更新源、哪些包会被自动处理。用编辑器打开它:
sudo nano /etc/apt/apt.conf.d/50unattended-upgrades
文件内容看起来像一段代码块,实际上是APT的配置语法。我们逐段拆解。首先是允许的更新源配置,它决定从哪些软件仓库拉取自动更新。默认情况下只开启了安全更新源,这是最保守也最推荐的做法。典型的配置段落长这样:
Unattended-Upgrade::Allowed-Origins {
"${distro_id}:${distro_codename}-security";
"${distro_id}ESMApps:${distro_codename}-apps-security";
"${distro_id}ESM:${distro_codename}-infra-security";
};
第一行是标准的安全更新源,比如Ubuntu 22.04对应的就是"Ubuntu:jammy-security"。下面两行是Ubuntu Pro的扩展安全维护源,如果你没有订阅Ubuntu Pro,这两行实际上不会生效,但保留它们也不会报错。如果你需要自动更新来自其他第三方源的包,可以在这里添加对应的源标识,但强烈建议只保留安全更新,避免自动更新把业务依赖的特定版本软件给升挂了。
黑名单机制:哪些包坚决不能自动更新同一个配置文件中有一个Package-Blacklist段落,用来列出你不想自动更新的软件包。这个机制在生产环境中至关重要。某些软件包升级后可能需要重启服务、迁移配置,或者新版本与现有架构不兼容,这些都应该加入黑名单。配置示例如下:
Unattended-Upgrade::Package-Blacklist {
"linux-image-generic";
"linux-headers-generic";
"nginx";
"mysql-server";
"redis-server";
};
内核相关的包被列入黑名单是常见做法,因为内核更新需要重启才能生效,而自动重启可能导致业务中断。nginx、mysql这类核心服务同样建议手动升级,以便在维护窗口内完成并验证。如果你的服务器使用了特定版本的编程语言运行时,比如php8.1-fpm,也可以考虑加入黑名单,防止小版本升级引入不兼容变更。
自动清理旧内核和依赖包长期运行的Ubuntu服务器,/boot分区经常被旧内核塞满,导致无法安装新内核甚至系统更新失败。unattended-upgrades可以自动清理不再需要的包。在配置文件中找到或添加以下行:
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true"; Unattended-Upgrade::Remove-New-Unused-Dependencies "true"; Unattended-Upgrade::Remove-Unused-Dependencies "true";
第一行是自动删除不再使用的内核包,第二行是删除因更新而不再被依赖的新增依赖包,第三行是删除系统中所有未被使用的依赖。这三项全部设为true能有效防止磁盘空间被无用包占满。但要注意,Remove-Unused-Dependencies这个选项比较激进,它会扫描整个系统的依赖关系,如果某个包是手动安装后又不再被任何包依赖,可能会被误删。如果你习惯手动安装一些独立软件包,建议至少保留这一项为false,或者定期手动执行apt autoremove来确认后再清理。
控制更新频率与时间窗口默认情况下,apt-daily-upgrade.timer会在每天的一个随机时间点触发更新,这个随机化是为了避免全球所有Ubuntu服务器同时向镜像站发起请求。但在生产环境中,你可能希望把更新限制在凌晨低峰时段。定时器的配置文件位于/lib/systemd/system/apt-daily-upgrade.timer,我们不应该直接修改这个文件,而是用systemd的override机制来覆盖。创建覆盖目录并编辑:
sudo systemctl edit apt-daily-upgrade.timer
在打开的编辑器中输入以下内容,把更新时间固定在凌晨3点到4点之间:
[Timer] OnCalendar= OnCalendar=*-*-* 03:00 RandomizedDelaySec=3600
第一行清空原有的定时设置,第二行指定每天凌晨3点触发,第三行设置随机延迟最多3600秒也就是1小时。这样实际更新会在3点到4点之间的某个随机时刻开始,既避开了业务高峰,又保留了分散请求压力的随机性。apt-daily.timer负责更新包列表,它应该在升级之前运行,所以也要做类似配置,把时间设在凌晨2点:
sudo systemctl edit apt-daily.timer
[Timer] OnCalendar= OnCalendar=*-*-* 02:00 RandomizedDelaySec=1800
修改完成后,需要重新加载systemd配置并重启定时器:
sudo systemctl daemon-reload sudo systemctl restart apt-daily.timer sudo systemctl restart apt-daily-upgrade.timer邮件通知与日志监控
自动更新跑起来了,但你肯定想知道它到底更新了什么。unattended-upgrades支持通过邮件发送更新报告。在配置文件中找到或添加以下行:
Unattended-Upgrade::Mail "your-email@example.com"; Unattended-Upgrade::MailReport "only-on-error";
MailReport有三个可选值:always总是发送、only-on-error仅出错时发送、never从不发送。生产环境建议设为only-on-error,这样正常情况下不会产生邮件噪音,一旦更新失败或出现异常,运维人员能立即收到通知。要确保邮件能发出去,服务器需要配置好MTA,比如安装mailutils或postfix并设置relayhost。如果不想折腾邮件服务,也可以直接查看日志文件/var/log/unattended-upgrades/unattended-upgrades.log,所有更新操作的详细记录都在里面。
自动重启策略:要不要让服务器自己重启这是争议最大的配置项。内核更新后如果不重启,实际上新内核并未生效,服务器仍然运行着有漏洞的旧内核。unattended-upgrades可以配置为在必要时自动重启。配置项如下:
Unattended-Upgrade::Automatic-Reboot "false"; Unattended-Upgrade::Automatic-Reboot-Time "02:00";
默认是false不重启。如果你的服务器做了高可用架构,单台重启不影响整体服务,可以设为true并指定重启时间。但绝大多数场景下,建议保持false,改用其他手段来处理内核重启问题。比如使用Canonical的Livepatch服务在线修补内核漏洞而无需重启,或者把内核更新列入黑名单,在计划维护窗口内手动升级并重启。强行让生产服务器自动重启,风险远大于收益。
验证配置是否生效的干跑测试改完配置后,不要直接等定时任务触发,先用干跑模式验证配置正确性。执行以下命令模拟一次自动更新过程,它只会输出将要执行的操作而不实际安装任何东西:
sudo unattended-upgrades --dry-run --debug
输出会详细列出它扫描了哪些源、发现了哪些可更新的包、哪些包被黑名单过滤掉了、哪些包会实际升级。仔细检查这个输出,确认黑名单生效、更新源正确、没有意外的包被纳入自动更新范围。如果输出中有任何你不认识的包即将被升级,立即暂停,查清楚后再决定是否加入黑名单。
多架构环境下的特殊处理如果你的服务器是ARM架构或者混合架构环境,unattended-upgrades的行为与x86_64完全一致,不需要额外配置。但有一点值得注意:某些专有软件或驱动只针对特定架构发布,自动更新时可能从错误的源拉取包。检查/etc/apt/sources.list以及/etc/apt/sources.list.d/目录下的所有源文件,确保每个源的架构限定符正确。比如如果你用的是树莓派上的Ubuntu,确认源中没有残留的amd64条目,否则unattended-upgrades在更新包列表时会报错,导致整个自动更新流程中断。
与配置管理工具的配合如果你使用Ansible、Puppet或Chef管理服务器集群,unattended-upgrades的配置应该纳入代码化管理。把50unattended-upgrades文件作为模板下发,黑名单列表通过变量维护,定时器覆盖文件也由配置管理工具统一部署。这样新增一台服务器时,自动更新策略自动生效,不会出现遗漏。同时,配置管理工具本身可以定期检查unattended-upgrades的运行状态,如果发现定时器未激活或日志中出现连续错误,触发告警。这种分层防御的思路,让自动更新从单机行为变成整个基础设施的基线能力。
常见故障排查清单自动更新静默失败是最危险的情况,你以为它在工作,实际上已经停摆好几个月。排查时按以下顺序检查:首先确认两个定时器都在运行,systemctl list-timers | grep apt能看到下次触发时间;其次检查/var/log/unattended-upgrades/目录下的日志文件,看最近的日志条目时间是否新鲜,有没有报错信息;然后手动执行apt update确认所有源都能正常访问,源地址不可达是自动更新失败的首要原因;接着检查磁盘空间,/boot分区满了会导致内核更新失败;最后确认/etc/apt/apt.conf.d/20auto-upgrades文件是否存在,内容应该是两行APT::Periodic::Update-Package-Lists "1"和APT::Periodic::Unattended-Upgrade "1",这个文件控制APT周期性任务的开关,如果被误删或值被改成0,整个自动更新机制就停摆了。
安全更新之外:ESM与Ubuntu Pro的延伸能力标准安全更新只覆盖Ubuntu主仓库和 Universe 仓库中官方支持的包,期限通常是5年。5年后如果你还在使用该版本,安全更新就断了。Ubuntu Pro免费版提供10年的扩展安全维护,覆盖更多软件包。如果你的服务器运行的是较老的LTS版本,注册Ubuntu Pro后,unattended-upgrades会自动识别ESM源并从中拉取更新。注册免费版只需要一个Ubuntu One账号,在服务器上执行pro attach即可。对于合规要求严格的行业,这10年的支持周期能大幅降低系统迁移压力。
总结要点unattended-upgrades是Ubuntu服务器安全基线中最基础也最容易被忽视的一环。配置的核心在于三点:限定更新源只包含安全更新、用黑名单保护关键业务包、通过日志和邮件确保更新过程可观测。定时器的时间窗口设置要根据业务低谷来调整,自动重启功能默认关闭,内核更新要么用Livepatch要么手动处理。干跑测试是每次修改配置后的必要步骤,不要跳过。把这些配置固化到配置管理工具中,让每台服务器从诞生的第一天起就具备自动安全更新能力,这才是运维该有的工程化思维。
