26.2.更新 FreeBSD ================= 及时应用安全补丁和升级到新版的操作系统是系统持续性管理的一个重要方面。FreeBSD 有一个叫做 ``freebsd-update`` 的工具,可以用来执行这两项任务。 这个工具支持 FreeBSD 安全和勘误的二进制更新,而不需要手动编译和安装补丁或新内核。二进制更新适用于目前安全团队所支持的所有架构和版本。所支持的版本列表和它们的预计结束日期列在 https://www.freebsd.org/security/\ 。 该工具也支持将操作系统进行次要版本更新,或升级到另一个开发分支。在升级到一个新的版本之前,请查看其发行公告,因为它包含了与此版本有关的重要信息。发行公告可从 https://www.freebsd.org/releases/ 获得。 **注意** 如果在 `crontab(5) `__ 使用了 `freebsd-update(8) `__ 功能,在升级操作系统之前必须将其禁用。 本节介绍了 ``freebsd-update`` 使用的配置文件,示范了如何应用安全补丁以及如何升级到一个次要或主要的操作系统版本,并讨论了升级操作系统时的一些注意事项。 26.2.1.配置文件 --------------- ``freebsd-update`` 依据默认的配置文件运行。一些用户可能想对 **/etc/freebsd-update.conf** 中的默认配置进行调整,以便更好的控制这个过程。该文件中的注释解释了可用的选项,但是以下内容可能需要更详细的说明: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # Components of the base system which should be kept updated. Components world kernel 这个参数控制了 FreeBSD 的哪些部分会被保持在最新状态。默认情况是更新整个基本系统和内核。也可以指定个别组件,例如 **src/base** 或 **src/sys**\ 。然而,最好的选择是保持默认,因为改变它以包括特定的项目需要列出每个需要的项目。随着时间的推移,这可能会产生灾难性的后果,因为源代码和二进制文件可能变得不同步。 .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # Paths which start with anything matching an entry in an IgnorePaths # statement will be ignored. IgnorePaths /boot/kernel/linker.hints 要想在更新过程中不触动指定的目录,例如 **/bin** 或 **/sbin**\ ,可以在该语句中添加它们的路径。这个选项可以用来防止 ``freebsd-update`` 覆盖本地的修改。 .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # Paths which start with anything matching an entry in an UpdateIfUnmodified # statement will only be updated if the contents of the file have not been # modified by the user (unless changes are merged; see below). UpdateIfUnmodified /etc/ /var/ /root/ /.cshrc /.profile 这个选项将只更新指定目录中未修改的配置文件。用户所做的任何修改都会阻止这些文件的自动更新。还有一个选项 ``KeepModifiedMetadata``\ ,它将指示 ``freebsd-update`` 在合并过程中保存更改。 .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # When upgrading to a new FreeBSD release, files which match MergeChanges # will have any local changes merged into the version from the new release. MergeChanges /etc/ /var/named/etc/ /boot/device.hints 带有配置文件的目录列表,\ ``freebsd-update`` 应该尝试合并这些文件。文件合并过程是一系列类似于 `mergemaster(8) `__ 的 `diff(1) `__ 补丁,但选项较少。合并后要么接受,要么打开一个编辑器,要么导致 ``freebsd-update`` 中止。如果有疑问,可以备份 **/etc** 并接受合并。关于 ``mergemaster`` 的更多信息,见 `mergemaster(8) `__\ 。 .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # Directory in which to store downloaded updates and temporary # files used by FreeBSD Update. # WorkDir /var/db/freebsd-update 这个目录是放置所有修补程序和临时文件的地方。在用户进行版本升级的情况下,这个位置应该至少有 1GB 的可用磁盘空间。 .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # When upgrading between releases, should the list of Components be # read strictly (StrictComponents yes) or merely as a list of components # which *might* be installed of which FreeBSD Update should figure out # which actually are installed and upgrade those (StrictComponents no)? # StrictComponents no 当这个选项设置为 ``yes`` 时,\ ``freebsd-update`` 将认为组件列表是完整的,并且不会尝试在列表之外进行修改。实际上,\ ``freebsd-update`` 将尝试更新属于组件列表的每个文件。 更多细节,请参阅 `freebsd-update.conf(5) `__ 。 26.2.2.应用安全补丁 ------------------- 应用 FreeBSD 安全补丁的过程被简化了,使得管理员能够使用 ``freebsd-update`` 来保持系统的补丁完整性。更多关于 FreeBSD 安全公告的信息可以在 `FreeBSD 安全公告 `__\ 中找到。 可以通过下列命令来下载和安装 FreeBSD 的安全补丁。第一条命令将确定是否有任何未完成的补丁,如果有的话,将列出在应用补丁时将被修改的文件。第二条命令将应用这些补丁。 .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # freebsd-update fetch # freebsd-update install 如果更新应用了任何内核补丁,系统将需要重启,以便启动到打了补丁的内核。如果该补丁应用于任何正在运行的二进制文件,受影响的应用程序应该被重新启动,以便使用二进制文件的补丁版本。 **注意** 通常情况下,用户需要准备好重启系统。要知道系统是否因为内核更新而需要重启,执行命令 ``freebsd-version -k`` 和 ``uname -r``\ 。如果输出结果不同,就重新启动系统。 可以通过在 **/etc/crontab** 中添加这个条目,将系统配置为每天自动检查一次更新。 .. raw:: latex \diilbookstyleinputcell .. code:: shell-session @daily root freebsd-update cron 如果存在补丁,它们将被自动下载,但不会被应用。根用户将被发送一封电子邮件,这样就可以通过 ``freebsd-update install`` 来查看和手动安装补丁。 如果出了什么问题,\ ``freebsd-update`` 有能力通过下面的命令回滚到上一组修改: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # freebsd-update rollback Uninstalling updates... done. 同样,如果内核或任何内核模块被修改,应该重新启动系统,任何受影响的二进制文件也应该被重新启动。 只有 **GENERIC** 内核可以被 ``freebsd-update`` 自动更新。如果安装的是定制内核,那么在 ``freebsd-update`` 完成安装更新后,必须重新编译并重新安装内核。默认的内核名称是 **GENERIC**\ 。可以用 `uname(1) `__ 命令来验证它的安装。 **注意** 在 **/boot/GENERIC** 中始终保留了一份 **GENERIC** 内核的副本。它在诊断各种问题和进行版本升级时都会有帮助。请参阅 `FreeBSD 9.X 及更高版本的定制内核 `__ 以了解如何获得 **GENERIC** 内核的副本。 除非 **/etc/freebsd-update.conf** 中的默认配置被改变,否则 ``freebsd-update`` 将和其他的更新一起安装更新的内核源码。可以按照常规方法重新编译和安装新的定制内核。 由 ``freebsd-update`` 发布的更新并不总是涉及内核。如果内核源码没有被 ``freebsd-update`` 安装修改,就没有必要重新编译定制内核。然而,\ ``freebsd-update`` 会一直更新 **/usr/src/sys/conf/newvers.sh**,当前的补丁级别,由 ``uname -r`` 报告的 ``p`` 数字表示,可以从该文件获得。编译定制内核,即使没有其他变化,也可以让 ``uname`` 准确地报告系统的当前补丁级别。这在维护多个系统时特别有帮助,因为它可以快速评估每个系统中安装的更新。 26.2.3.执行次要和主要版本的升级 ------------------------------- 从 FreeBSD 的一个小版本升级到另一个小版本被称为 *次要* 版本升级。例如: - 从 FreeBSD 13.1 到 13.2 *主要* 版本升级会增加主要版本号,例如: - 从 FreeBSD 12.4 到 13.2 这两种类型的升级都可以通过包含 ``freebsd-update`` 支持的 release 版本来完成。 **警告** 在每个新的 ``RELEASE`` 发布之后, FreeBSD 软件包编译服务器在一定时期内将并 **不** 使用新版本的操作系统。这为许多在(系统)发布后不立即进行升级的用户提供了连续性。比如说: - 使用 13.1 和 13.2 的用户的软件包将在运行 13.1 的服务器上进行编译,直到 13.1 的支持结束。 ——但是,严格来讲: - 一个在 13.1 上编译的内核模块可能并 **不** 适合于 13.2。 因此,在任何次要或主要的操作系统版本升级中,如果你的软件包要求包括任一内核模块: - **准备好从源代码编译该模块。** .. **注意** 如果系统正在运行定制内核,在开始升级之前,请确保在 **/boot/GENERIC** 中有一份 **GENERIC** 内核的副本。请参考 `FreeBSD 9.X 及以后版本的定制内核 `__\ ,以了解如何获得 **GENERIC** 内核的副本。 在 FreeBSD 13.1 系统上运行下面的命令时,会将其升级到 FreeBSD 13.2: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # freebsd-update -r 13.2-RELEASE upgrade 在收到命令后,\ ``freebsd-update`` 将评估配置文件和当前系统,试图收集必要的信息来执行升级。屏幕上会显示哪些组件已经被检测到,哪些没有被检测到。比方说: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session Looking up update.FreeBSD.org mirrors... 1 mirrors found. Fetching metadata signature for 13.2-RELEASE from update1.FreeBSD.org... done. Fetching metadata index... done. Inspecting system... done. The following components of FreeBSD seem to be installed: kernel/smp src/base src/bin src/contrib src/crypto src/etc src/games src/gnu src/include src/krb5 src/lib src/libexec src/release src/rescue src/sbin src/secure src/share src/sys src/tools src/ubin src/usbin world/base world/info world/lib32 world/manpages The following components of FreeBSD do not seem to be installed: kernel/generic world/catpages world/dict world/doc world/games world/proflibs Does this look reasonable (y/n)? y 在这时候,\ ``freebsd-update`` 将尝试下载所有升级所需的文件。在某些情况下,用户可能会被提示关于安装什么或如何进行的问题。 当使用定制内核时,上述步骤将产生类似于以下的警告: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session WARNING: This system is running a "MYKERNEL" kernel, which is not a kernel configuration distributed as part of FreeBSD 13.1-RELEASE. This kernel will not be updated: you MUST update the kernel manually before running "/usr/sbin/freebsd-update install" 这个警告在此时可以被安全地忽略。更新后的 **GENERIC** 内核将被用作升级过程中的一个中间步骤。 所有的补丁被下载到本地系统后,它们将被应用。这个过程可能需要一些时间,取决于机器的速度和工作量。然后,配置文件将被合并。合并过程需要一些用户干预,因为文件可能会被合并,或者屏幕上会出现一个编辑器用于手动合并。在这个过程中,每次成功的合并结果都会显示给用户。合并失败或被忽略将导致进程中止。用户可能希望对 **/etc** 进行备份,并在以后手动合并重要文件(如 **master.passwd** 和 **group**\ )。 **注意** 系统还没有被改变,因为所有的补丁和合并都在另一个目录中进行。一旦所有的补丁都被成功应用,所有的配置文件都被合并,而且看起来这个过程会很顺利,用户就可以使用下面的命令将修改提交到磁盘: .. code:: shell-session # freebsd-update install 内核和内核模块将首先被打上补丁。如果系统使用的是定制内核,请使用 `nextboot(8) `__ 将下次启动时的内核设置为更新的 **/boot/GENERIC**\ : .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # nextboot -k GENERIC .. **警告** 在使用 **GENERIC** 内核重启之前,如果被更新的机器是通过远程访问的话,请确保它包含了系统正常启动和连接网络所需的所有驱动程序。特别是,如果正在运行的定制内核包含了通常由内核模块提供的内置功能,请确保使用 **/boot/loader.conf** 工具将这些模块临时加载到 **GENERIC** 内核中。建议禁用非必要的服务,以及任何磁盘和网络挂载,直到升级过程完成。 现在应该用升级后的内核重新启动机器: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # shutdown -r now 系统重新上线后,使用下面的命令重新启动 ``freebsd-update``\ 。由于进程的状态已经被保存,\ ``freebsd-update`` 不会从头开始,而是进入下一阶段并删除所有旧的共享库和对象文件。 .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # freebsd-update install .. **注意** 取决于是否有任何库的版本号被提升,安装阶段可能只有两个,而非三个。 现在升级已经完成。如果这是一次主要版本的升级,请按照在\ `主要版本升级软件包 `__\ 中的说明来重新安装所有的 port 和软件包。 26.2.3.1.在 FreeBSD 9.X 及以后版本的定制内核 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 在使用 ``freebsd-update`` 之前,请确保在 **/boot/GENERIC** 中存在一个 **GENERIC**\ 内核的副本。如果一个定制内核只被编译过一次,那么 **/boot/kernel.old** 中的内核就是 **GENERIC** 内核,只需将这个目录重命名为 **/boot/GENERIC**\ 。 如果一个定制的内核被编译了不止一次,或者不知道这个定制的内核被编译了多少次,那么请获取一份与当前操作系统版本相匹配的 ``GENERIC`` 内核。如果可以对系统进行物理访问,可以从安装介质中安装 ``GENERIC`` 内核的副本: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # mount /cdrom # cd /cdrom/usr/freebsd-dist # tar -C/ -xvf kernel.txz boot/kernel/kernel 另外,也可以从源代码中重新编译和安装 ``GENERIC`` 内核: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # cd /usr/src # make kernel __MAKE_CONF=/dev/null SRCCONF=/dev/null 要使这个内核被 ``freebsd-update`` 识别为 **GENERIC** 内核,必须没有以任何方式对 **GENERIC** 配置文件进行修改。此外,我们还建议在编译内核时不要使用任何其他特殊的选项。 由于 ``freebsd-update`` 只需要 **/boot/GENERIC** 的存在,因此不需要重新启动到 **GENERIC** 内核。 26.2.3.2.在主要版本升级后升级软件包 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 一般来说,已安装的应用程序在小版本升级后会继续工作,不会有问题。主要版本使用不同的应用程序二进制接口 (ABI),这将破坏大多数第三方应用程序。在主要版本升级后,所有安装的软件包和 port 都需要升级。包可以用 ``pkg upgrade`` 来升级。要升级已安装的 port,请使用诸如 `ports-mgmt/portmaster `__ 等工具。 要对所有已安装的软件包进行强制升级并用软件库中的新版本替换这些软件包,即使版本号没有增加。这是由于在 FreeBSD 的主要版本之间进行升级时 ABI 版本的变化所要求的。可以通过执行以下操作来完成强制升级: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # pkg-static upgrade -f 重新编译所有已安装的应用程序可以用这个命令来完成: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # portmaster -af 这个命令将显示每个有可配置选项的应用程序的配置屏幕,并等待用户与这些屏幕互动。要防止这种行为,只使用默认选项,在上述命令中加上 ``-G``\ 。 软件升级完成后,最后一次调用 ``freebsd-update`` 来结束升级过程,以便把升级过程中的所有问题都解决。 .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # freebsd-update install 如果临时使用了 **GENERIC** 内核,这时可以按照\ `配置 FreeBSD 内核 `__\ 的说明建立并安装一个新的定制内核。 重新启动机器进入新的 FreeBSD 版本。升级过程现在已经完成。 26.2.4.系统状态比对 ------------------- 可以使用 ``freebsd-update IDS`` 来测试所安装的 FreeBSD 版本与已知的良好副本的状态。这个命令可以评估系统工具、库和配置文件的当前版本,并可以作为一个内置的入侵检测系统(IDS)使用。 **警告** 这个命令不能替代真正的 IDS,如 `security/snort `__\ 。由于 ``freebsd-update`` 将数据存储在磁盘上,遭篡改的可能性是很大的。虽然可以通过使用 ``kern.securelevel`` 和在不使用时将 ``freebsd-update`` 数据存储在只读文件系统中来减少这种可能性,但更好的解决方案是将系统与安全磁盘进行比较,如 DVD 或安全存储的外部 USB 磁盘设备。关于使用内置工具提供 IDS 功能的替代方法在\ `二进制验证 `__\ 中有所说明。 要开始比对,指定输出文件,将结果保存到: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # freebsd-update IDS >> outfile.ids 现在将检查系统,输出一个冗长的文件列表,以及版本中的已知值和当前安装的 SHA256 哈希值,将被传输到指定的输出文件。 清单中的条目非常长,但输出格式可以很容易地被解析。例如,要获得与发行版中不同的所有文件的列表,请执行以下命令: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # cat outfile.ids | awk '{ print $1 }' | more /etc/master.passwd /etc/motd /etc/passwd /etc/pf.conf 这个样本输出已经被截断了,因为还有很多文件存在。一些文件有自然的修改:例如,如果用户被添加到系统中,\ **/etc/passwd** 将被修改。内核模块可能有所不同,因为 ``freebsd-update`` 可能已经更新了它们。要排除特定的文件或目录,可以在 **/etc/freebsd-update.conf** 中的 ``IDSIgnorePaths`` 选项中添加它们。