23.2. FreeBSD 更新

Written by Tom Rhodes.
Based on notes provided by Colin Percival.

隨時套用安全性更新以及升級到新發佈的作業系統版本對管理一個持續運作的系統是非常重要的任務,FreeBSD 內含可以執行這兩項任務的工具程式,叫做 freebsd-update

這個工具程式支援使用 Binary 對 FreeBSD 做安全性與和錯誤更新,不需要手動編譯和安裝修補 (Patch) 或新核心。目前由安全性團隊提供支援的 Binary 更新可用於所有的架構和發行版。支援的發行版清單及各自的支援期限列於 https://www.FreeBSD.org/security/

這個工具程式也支援升級作業系統到次要的發佈版以及升級到另一個發佈版分支。在升級到新的發佈版本前,需先查看該版本的發佈公告,因為發行公告中包含了該發行版本的相關重要資訊。發行公告可自 https://www.FreeBSD.org/releases/ 取得。

注意:

如果有使用 crontab 來執行 freebsd-update(8),則必須在升級作業系統前先關閉。

本節將說明 freebsd-update 使用的設定檔, 示範如何套用安全性修補及如何升級到主要或次要的作業系統發行版,並討論升級作業系統的需要考量的事項。

23.2.1. 設定檔

freebsd-update 預設的設定檔不需變更即可運作。 部份使用者可能會想要調校位於 /etc/freebsd-update.conf 的預設設定檔來對程序有更好的控制。該設定檔中的註解均有說明可用的選項,但以下幾個項目可能需要進一步的說明:

# Components of the base system which should be kept updated.
Components world kernel

這個參數控制 FreeBSD 要保持最新版本的部份。 預設是更新整個基礎系統 (Base system) 和核心。 可指定個別元件,例如:src/basesrc/sys。 雖然如此,最好的選項是維持預設值,因為更改指定特定項目時需列出每一個需要的項目。時間一久可能會因為原始碼和 Binary 檔案沒有更新而造成慘重的後果。

# Paths which start with anything matching an entry in an IgnorePaths
# statement will be ignored.
IgnorePaths /boot/kernel/linker.hints

要保持特定的目錄在更新過程不被更動,例如 /bin/sbin,可以將他們的路徑加到此敘述中。 這個選項可以防止 freebsd-update 覆蓋本地的修改。

# 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 在合併時儲存使用者做的變更。

# 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)

# Directory in which to store downloaded updates and temporary
# files used by FreeBSD Update.
# WorkDir /var/db/freebsd-update

這個目錄是所有修補檔和暫存檔的存放處。當使用者進行版本升級時,這個位置應該要有至少 1GB 的可用磁碟空間。

# 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 將會假設 Components 清單已完成,將不會對清單之外的項目做變更。 實際上 freebsd-update 會將嘗試更新每一個屬於 Components 清單中的檔案。

23.2.2. 套用安全性修補

套用 FreeBSD 安全性修補的過程已經被簡化,讓系統管理員可使用 freebsd-update 來保持系統更新。更多有關 FreeBSD 安全性報告的資訊可以參考 節 13.11, “FreeBSD 安全報告”

FreeBSD 安全性修補可以使用以下指令下載並安裝。 第一個指令會偵測是否有可用的修補,如果有,將列出若執行修補後會變更的檔案清單。第二個指令將會套用修補。

# freebsd-update fetch
# freebsd-update install

如果更新套用了任何核心修補,系統將會需要重新開機以使用修補過的核心。如果修補套用在任何執行中的 Binary,受影響的應用程式應重新啟動來使用修補過的 Binary 版本。

加入以下項目至 /etc/crontab 可設定系統每天自動檢查更新一次:

@daily                                  root    freebsd-update cron

如果有新的修補,該程式會會自動下載,但不會執行。root 使用者會收到電子郵件通知複查該修補並手動執行 freebsd-update install 安裝。

如果有發生任何錯誤,freebsd-update 可以使用以下指令還原最後所做的變更:

# freebsd-update rollback
Uninstalling updates... done.

再次強調,若核心或任何核心模組有做過修改應重新啟動系統,以及任何受影響的 Binary 應重新執行。

只有 GENERIC 核心可使用 freebsd-update 自動更新。 如果有安裝自訂的核心,在 freebsd-update 完成安裝更新後,需要重新編譯和重新安裝。 雖然如此,如果 /boot/GENERIC 存在,freebsd-update 仍會偵測並更新 GENERIC 核心,即使該核心並非目前系統正在執行的核心。

注意:

隨時在 /boot/GENERIC 保留一份 GENERIC 核心的複本將有助於診斷各種問題及執行版本升級。請參考 節 23.2.3.1, “在 FreeBSD 9.X 及之後版本自訂核心” 來了解有關如何取得 GENERIC 核心的複本說明。

除非在 /etc/freebsd-update.conf 的預設設定檔被修改,否則 freebsd-update 將會安裝更新後的核心原始碼和其餘的更新,可依平常的方式執行重新編譯與重新安裝核心。

freebsd-update 發行的更新並非總是會更新核心。若核心的原始碼沒有被 freebsd-update install 修改則不需要重新編譯自訂的核心。雖然如此 freebsd-update 總是會更新 /usr/src/sys/conf/newvers.sh,目前修補的版本如 uname -r 執行結果中的 -p 數字,便是由該檔取得。即使沒有做任何其他變更,重新編譯自訂核心可讓 uname 準確的回報系統目前的修補版本。當維護多個系統時這會特別有用,因其可讓你快速評估每個系統安裝的更新。

23.2.3. 執行主要及次要版號升級

從 FreeBSD 的次要版本升級到另一個版本,例如從 FreeBSD 9.0 到 FreeBSD 9.1, 叫作 次要版本 (Minor version) 更新。 主要版本 (Major version) 更新發生在當 FreeBSD 從一個主要版本升級到主要版本升級到另一個主要版本時,例如從 FreeBSD 9.X 到 FreeBSD 10.X。 兩種更新都可以透過提供 freebsd-update 目標的發佈版本來執行。

注意:

如果系統正在執行自訂的核心,請在開始升級前,確定有保留一份 GENERIC 核心的複本在 /boot/GENERIC。 請參考 節 23.2.3.1, “在 FreeBSD 9.X 及之後版本自訂核心” 關於如何取得 GENERIC 核心複本的說明。

在 FreeBSD 9.0 系統執行以下指令,將會把系統升級至 FreeBSD 9.1:

# freebsd-update -r 9.1-RELEASE upgrade

收到這個指令後,freebsd-update 會開始評估設定檔和目前的系統來收集升級所需的資訊。 螢幕會顯示偵測到或沒偵測到的元件清單。例如:

Looking up update.FreeBSD.org mirrors... 1 mirrors found.
Fetching metadata signature for 9.0-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 將會嘗試下載所有升級需要的檔案。 在某些情況,會詢問使用者一些關於要安裝什麼或要如何繼續。

當使用自訂核心,上述的步驟將會產生如下的警告:

WARNING: This system is running a "MYKERNEL" kernel, which is not a
kernel configuration distributed as part of FreeBSD 9.0-RELEASE.
This kernel will not be updated: you MUST update the kernel manually
before running "/usr/sbin/freebsd-update install"

這時的警告可以安全地忽略,升級過程將會使用更新過的 GENERIC 核心來進行。

所有的修補都下載到本地系統之後, 將會開始套用更新。這個過程可能會花點時間,取決於機器的速度和工作量。設定檔將會被合併。 合併的過程中當檔案被合併或是手動合併畫面上出現編輯器時需要使用者操作。 每一個成功合併的結果將會顯示給使用者並繼續程序,失敗或忽略合併將會使程序中斷。使用者可能想要備份 /etc 並稍後手動合併重要的檔案,例如:master.passwdgroup

注意:

所有的修補與合併動作會在另一個目錄進行,並不會直接修改。當成功套用所有修補,所有設定檔已合併且過程順利,使用者可使用以下指令將變更安裝到磁碟:

# freebsd-update install

核心與核心模組會先修補,若系統正在執行自訂的核心,使用 nextboot(8) 來設定下次開機使用更新過的 /boot/GENERIC

# nextboot -k GENERIC

警告:

若機器在遠端進行更新,請在使用 GENERIC 核心重新開機前,請確定該核心含有所有系統所需的驅動程式以正常開機並連線至網路。特別是在執行的自訂核心有使用到由核心模組提供內建功能,請確定將這些模組已暫時使用 /boot/loader.conf 設定檔載入到 GENERIC 核心。建議關閉非必須的服務和磁碟與網路掛載直到升級程序完成。

機器現在應使用更新過的核心重新開機:

# shutdown -r now

一旦系統重新上線,使用以下指令繼續 freebsd-update。 由於程序的狀態已被儲存,freebsd-update 不會重頭開始,但會進行下一個階段並移除所有舊的共用程式庫和目標檔。

# freebsd-update install

注意:

取決於是否有任何程式庫版本編號衝突,也可能只有兩個而不是三個安裝階段。

升級程序現在完成了。如果所做的是主要的版本升級,則需依 節 23.2.3.2, “主要版號升級後的套件升級” 的說明重新安裝所有的 Port 和套件。

23.2.3.1. 在 FreeBSD 9.X 及之後版本自訂核心

在使用 freebsd-update 前,請確定已有 GENERIC 核心的複本於 /boot/GENERIC。若只編譯過一次自訂核心,那麼 /boot/kernel.old 就是 GENERIC 核心,只需要將該目錄重新命名為 /boot/kernel

若有編譯自訂核心過超過一次,或已經不曉得編譯自訂核心的次數,則需取得與目前作業系統版本相符的 GENERIC 核心複本。若可直接操作實體系統,則可以從安裝媒體取得 GENERIC 核心複本:

# mount /cdrom
# cd /cdrom/usr/freebsd-dist
# tar -C/ -xvf kernel.txz boot/kernel/kernel

或者,可以從原始碼重新編譯 GENERIC 核心:

# cd /usr/src
# make kernel __MAKE_CONF=/dev/null SRCCONF=/dev/null

這個核心要被 freebsd-update 認做 GENERIC 核心,GENERIC 設定檔必須不能做任何修改,也建議在編譯核心時不要使用其他特殊選項。

freebsd-update 僅需要 /boot/GENERIC 存在便可,因此不須重新開機進入 GENERIC

23.2.3.2. 主要版號升級後的套件升級

一般來說,已安裝的應用程式在次要版本升級仍可沒問題的正常執行。但主要版本升級會採用不同的應用程式 Binary 介面 (Application Binary Interfaces, ABIs),會導致大部份第三方應用程式無法正常執行。 因此在主要版本升級後,需要升及所有已安裝的套件和 Port,套件可以使用 pkg upgrade 來升級,而 Port 則需使用 ports-mgmt/portmaster 工具。

強制升級所有已安裝的套件會使用檔案庫中新版本的套件來取得目前套件,即使該版號沒有增加。由於在升級 FreeBSD 主要版本時會變更 ABI 版本,因此這是必要動作。強制升級可以執行以下指令來完成:

# pkg-static upgrade -f

重新編譯所有已安裝的應用程式可以執行以下指令來完成:

# portmaster -af

這個指令會在安裝每個應用程式有可設定選項時顯示設定畫面,並會等待使用者操作該畫面,要避免這種情況並使用預設的設定選項,可在上述指令加上 -G 參數。

完成軟體升級後,最後需執行 freebsd-update 來完成最後的升級動作:

# freebsd-update install

若有使用臨時 GENERIC 核心,便應在此時依據 章 8, 設定 FreeBSD 核心 的說明編譯並安裝新的自訂核心。

重新開機使用新的 FreeBSD 版本後,升級程序便正式完成。

23.2.4. 比對系統狀態

已安裝的 FreeBSD 版本狀態可以使用 freebsd-update IDS 與另一個已知良好的複本來做比對測試。 這個指令會評估目前版本的系統工具,程式庫和設定檔,可做為內建的入侵偵測系統來使用 (Intrusion Detection System, IDS)。

警告:

這個指令並非用來取代真正的 IDS,如 security/snort。由於 freebsd-update 儲存在磁碟上,被竄改的可能性是顯而易見的,雖然這個可能性會因使用 kern.securelevel 以及將 freebsd-update 在不使用時以唯讀儲存而降低,最好的解決方案是能夠與安全的磁碟,如 DVD 或儲存在外部的 USB 磁碟裝置比對系統。替代的方式是使用內建工具的 IDS 功能,在 節 13.2.6, “Binary 檢驗” 有詳細說明。

要開始比對,需指定輸出的檔案來儲存結果:

# freebsd-update IDS >> outfile.ids

系統將會開始檢查並且會產生相當長的檔案清單,內容包含發佈版本已知的與目前安裝版本的 SHA256 雜湊值會儲存到指定的輸出檔。

清單中的項目會相當的多,但輸出的格式可以很簡單的用來分析。例如,要取得與發佈版本不同的檔案清單,可使用以下指令:

# 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 選項。

本文及其他文件,可由此下載: ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/

若有 FreeBSD 方面疑問,請先閱讀 FreeBSD 相關文件,如不能解決的話,再洽詢 <questions@FreeBSD.org>。

關於本文件的問題,請洽詢 <doc@FreeBSD.org>。