《NGINX 源碼建構》練習 參考解答

說明《NGINX 源碼建構》練習的最佳實務解法

https://hackmd.io/@brlin/nginx-build-from-source-reference-answer

內容大綱

智慧財產授權條款

除另外標註之內容外本作品的內容以《Creative Commons 姓名標示-相同方式分享》授權條款第 4.0 國際版或其任意更近期版本釋出供大眾於授權範圍內自由使用

請使用 林博仁(Buo-ren, Lin) <https://brlin.me> 作為滿足「姓名標示」授權要求的表彰內容

如有任何問題或其他要求請聯繫 buo.ren.lin+legal@gmail.com

目標

  • 下載 NGINX 當前最新版本軟體的來源程式碼source code,將其建構並安裝到可以正常運作的狀態
  • 必須支持下列功能:
    • HTTPS 服務支援(支援之 SSL/TLS 版本不限)

前備條件(prerequisites)

以下說明完成本練習所需要的前備條件:

  • 您的練習環境電腦須有安裝下列軟體:
    • Docker(桌面或命令列界面版本)
  • 您須有基本的 Linux 命令列界面操作經驗
  • 您須有基本的 Docker 容器操作經驗

重現環境

以下說明應該能重現本筆記之結果的環境細節:

作業系統

Ubuntu 23.10 AMD64(透過 Ubuntu 22.04 容器container

Docker

Docker 容器的執行環境:

$ docker --version
Docker version 24.0.5, build ced0996

NGINX

1.25.3

操作流程

以下說明完成本練習的相關流程:

詳閱 NGINX 官方的源碼安裝說明文件

大部分開放來源碼open-source軟體的專案官方的文件通常會有一些源碼建構軟體的重要資訊,所以事先詳閱是強烈建議的

於搜尋引擎查得並訪問 nginx.org NGINX 開源版官網(非 nginx.com 商業版官網),您可以於右側導覽欄找到官方說明文件的頁面(紅框處):

NGINX 官方文件頁面在官網首頁導覽列的位置示意圖

就可以找到可能跟源碼建構相關的說明文件(紅框處):

NGINX 官方文件頁面跟源碼建構可能有關的官方文件示意圖

主要有關的說明文件應該是這個:Building nginx from Sources,之後遇到問題時可以回來確認是不是某個眉角沒有注意到

創建並啟動一 Ubuntu 22.04 Docker 容器作為練習環境

為確保重現環境的一致性且避免練習期間的操作影響到主端系統,我們建立一個 Ubuntu 22.04 的 Docker 容器並在其內進行後續的練習操作。以 root 身份執行下列命令自 ubuntu:22.04 容器映像container image創建一個新的容器:

docker_run_opts=(
    # 離開容器的作業階段時自動摧毀容器,節省磁碟空間使用
    --rm
    
    # 啟用標準輸入裝置(stdin)支援(使 bash shell 
    # 互動式模式(interactive mode)得以正常運行)
    --interactive
    
    # 啟用偽 Teletype(TTY) 終端機模擬功能(使 bash shell 
    # 互動式模式得以正常運行)
    --tty
    
    # 設定人類可讀的 Docker 容器名稱
    # (於 `docker ps`、`docker container list` 命令中呈現)
    --name nginx-source-build
    
    # 設定容器內的主機名稱(於 Bash 提示字串呈現)
    --hostname nginx-source-build
    
    # 將容器內的 HTTP 跟 HTTPS 標準通訊埠發布到宿主機的回送(loopback)網路界面上,方便測試 NGINX 服務功能
    --publish 127.0.0.1:80:80/tcp
    --publish 127.0.0.1:443:443/tcp
    
    # 規避 Ubuntu Docker 容器無法正確輸入與顯示中文字元的問題
    --env LANG=C.UTF-8
)
docker run "${docker_run_opts[@]}" ubuntu:22.04

附註:

  • array=(...) 為 Bash 的索引式陣列indexed array語法,如上範例命令為例使用此語法可以以 Bash 註解的形式為命令參數新增說明文字
  • "${array[@]}" 為 Bash 陣列的其中一種參數展開parameter expansion語法,會將陣列中的元素以空白字元為分隔插入於展開處,您可以在展開命令前面加上 echo 命令(如 echo "${array[@]}")以預覽展開結果
  • 如您的使用者為 docker 使用者群組的成員則可以以一般使用者身份運行前述 docker run 命令(允許使用者直接訪問 Docker 服務有潛在的安全風險,應僅開放給完全信任的使用者使用)
  • 如您位於僅能透過一 HTTP(S) 正向代理服務器才能連到網際網路的網路環境(如企業內部網路或是使用 TetherFi 應用分享手機的行動網路給電腦),您可以在執行 docker run 命令前額外執行下列命令將本地的 HTTP(S) 正向代理服務設定(須已設定相關環境變數)繼承給 Docker 容器使用:
    • docker_run_opts+=(--env http_proxy)
    • docker_run_opts+=(--env https_proxy)
    • docker_run_opts+=(--env no_proxy)

執行命令後您應可以取得容器內 Bash 殼層shell互動式操作環境的命令提示文字command prompt

$ sudo docker run "${docker_run_opts[@]}" ubuntu:22.04
root@nginx-source-build:/# 

改用本地下載速度較快的 Ubuntu 軟體庫(software repository) 鏡像站(mirror site)

因為 Ubuntu 的 Docker 容器預設使用(應該是在英國跟美國的)archive.ubuntu.com 軟體庫服務於台灣本地下載軟體的速度較慢,我們可以改用本地的軟體庫鏡像服務來減少軟體下載所需時間

以 root 身份於文字終端執行下列命令已將軟體庫地址替換為台灣本地鏡像站的地址:

sed_opts=(
    # 直接修改文件而非將修改後的結果輸出到標準輸出裝置(stdout)
    --in-place
    
    # 要套用的 sed 表達式(將每行跟 `//archive\.u` 正規表達式(regular expression)
    # 式樣(pattern)比對(match)到的字串替換為 `//tw.archive.u` 字串)
    --expression='s@//archive\.u@//tw.archive.u@'
)
sed "${sed_opts[@]}" /etc/apt/sources.list

建立/重建本地的 APT 軟體包索引(package index)資料

因為 APT 軟體來源的設定已經變更了,我們需要重新建立本地的 APT 軟體包索引資料以獲取當前軟體庫的軟體包清單與下載地址等相關資訊

以 root 身份於文字終端執行下列命令以建立/重建本地的 APT 軟體包索引資料:

apt update

如果設定軟體庫鏡像站的步驟順利完成的話,您應該可於命令輸出中找到自 tw.archive.ubuntu.com 伺服器下載資料的進度報告訊息:

root@43e688c2defb:/# apt update
Get:1 http://tw.archive.ubuntu.com/ubuntu jammy InRelease [270 kB]
Get:2 http://tw.archive.ubuntu.com/ubuntu jammy-updates InRelease [119 kB]   
Get:3 http://tw.archive.ubuntu.com/ubuntu jammy-backports InRelease [109 kB] 
Get:4 http://tw.archive.ubuntu.com/ubuntu jammy/universe amd64 Packages [17.5 MB]

    ...stripped...

Fetched 29.1 MB in 4s (8156 kB/s)                           
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
7 packages can be upgraded. Run 'apt list --upgradable' to see them.

取得 NGINX 的軟體來源程式碼(source code)

NGINX 開源版官方網站的下載頁可以找到當前最新版本(推定為指開發主線mainline版)的 NGINX 來源程式碼封存檔archive的下載地址:

NGINX 軟體下載頁中的 1.25.3 當前開發主線版本的源碼包下載連結示意圖

下載頁中紅框的 nginx-1.25.3 超連結為 NGINX 的 1.25.3 版開發主線版本的來源程式碼封存檔的下載連結

附註: NGINX 的開發主線mainline版與穩定stable版的差異:

  • 相對於穩定版使用偶數的次版本minor version版號,開發主線版使用奇數的次版本版號
  • 每隔 4 到 6 週開發主線分支mainline branch會釋出包含新功能與修正的新的開發主線版本
  • 每年 NGINX 專案維護者會將當前的開發主線分支內容叉分fork為新的穩定版分支stable branch進行維護(前一個的穩定版分支則不再被建議使用deprecated),該分支只會於需要時自開發主線分支揀選cherry-pick/向前移植backport重大問題的修正

詳細資訊參閱 Introducing NGINX 1.10 and 1.11 - NGINX 文章的 Explaining NGINX’s Version Numbering 章節

由於 GNU+Linux 作業系統可以使用 curl 等 Web 客戶端軟體直接於文字終端下載 NGINX 的軟體來源碼包,因 Ubuntu 22.04 官方容器映像預設不提供 curl 軟體的原因我們需要以 root 身份執行下列命令手動進行安裝:

apt install curl

然後執行下列命令下載 1.25.3 版的 NGINX 的來源程式碼封存檔:

curl_opts=(
    # 跟隨 301/302 轉址(如果有)
    --location
    
    # 使用下載網址 URL 末端片段作為下載資源的檔案名稱
    --remote-name
)
curl "${curl_opts[@]}" https://nginx.org/download/nginx-1.25.3.tar.gz

驗證 NGINX 軟體來源碼封存檔的資料完整性(data integrity)

為避免 NGINX 開源版官方網站的 NGINX 軟體來源碼封存檔遭到惡意竄改造成安裝 NGINX 的主機資料被竊取或被當作 DDoS 攻擊的肉雞所以必須要驗證其資料完整性,首先需要確認該軟體有提供哪種驗證資料完整性的方案

查看 NGINX 開源版官方網站下載頁可以發現 nginx-1.25.3 超連結 右邊的 pgp 超連結(紅框處)為透過 Pretty Good Privacy(PGP) 驗證 NGINX 1.25.3 軟體來源碼封存檔資料完整性的數位簽名檔案signature file

NGINX 軟體下載頁中的 1.25.3 當前開發主線(mainline)釋出版本的源碼包的 PGP 數位簽名文件下載連結示意圖

執行下列命令下載 1.25.3 版 NGINX 來源程式碼封存檔驗證資料完整性用的 PGP 數位簽名檔:

curl_opts=(
    # 跟隨 301/302 轉址(如果有)
    --location
    
    # 使用下載網址 URL 末端片段作為下載資源的檔案名稱
    --remote-name
)
curl "${curl_opts[@]}" https://nginx.org/download/nginx-1.25.3.tar.gz.asc

目前 GNU/Linux 作業系統主流的 PGP 操作軟體為 GNU Privacy Guard(GnuPG)以 root 身份執行下列命令以進行安裝:

apt install gnupg

接下來可以試著執行下列命令以進行 NGINX 軟體來源碼封存檔的資料完整性驗證:

gpg_opts=(
    # 驗證經數位簽名之文件的資料完整性
    --verify nginx-1.25.3.tar.gz.asc nginx-1.25.3.tar.gz
)
gpg "${gpg_opts[@]}"

如果命令輸出下列錯誤訊息代表您的 GnuPG 鑰匙圈尚未匯入簽發issue這個數位簽名的人的公鑰:

$ gpg --verify nginx-1.25.3.tar.gz.asc nginx-1.25.3.tar.gz
gpg: directory '/root/.gnupg' created
gpg: keybox '/root/.gnupg/pubring.kbx' created
gpg: Signature made Tue Oct 24 15:42:51 2023 UTC
gpg:                using RSA key 13C82A63B603576156E30A4EA0EA981B66B0D967
gpg:                issuer "k.pavlov@f5.com"
gpg: Can't check signature: No public key

您可以透過「nginx pgp verify」搜尋關鍵字可以在 NGINX 開源版官方網站找到應該是 k.pavlov@f5.com 對應的 Konstantin Pavlov 這個專案維護者的 PGP 公鑰

NGINX 開源版官方網站 PGP public keys 網頁中的  Konstantin Pavlov 專案維護者的 PGP 公鑰下載連結示意圖

執行下列命令下載 Konstantin Pavlov 專案維護者的 PGP 公鑰

curl_opts=(
    # 跟隨 301/302 轉址(如果有)
    --location
    
    # 使用下載網址 URL 末端片段作為下載資源的檔案名稱
    --remote-name
)
curl "${curl_opts[@]}" https://nginx.org/keys/thresh.key

然後執行下列命令將 Konstantin Pavlov 專案維護者的 PGP 公鑰匯入自己的 PGP 鑰匙圈中:

gpg --import thresh.key

命令執行順利的話應該可以看到下列輸出:

$ gpg --import thresh.key
gpg: key A0EA981B66B0D967: 10 signatures not checked due to missing keys
gpg: /root/.gnupg/trustdb.gpg: trustdb created
gpg: key A0EA981B66B0D967: public key "Konstantin Pavlov <thresh@nginx.com>" imported
gpg: Total number processed: 1
gpg:               imported: 1
gpg: no ultimately trusted keys found

附註: 命令輸出中的下列警告訊息為正常現象(PGP 信任網未建立):

  • 10 signatures not checked due to missing keys
  • no ultimately trusted keys found

附註: 您另可透過第三方 PGP 金鑰服務器keyserver來獲取該簽發者的簽名公鑰:

$ gpg --keyserver keyserver.ubuntu.com --recv-keys 13C82A63B603576156E30A4EA0EA981B66B0D967
gpg: /root/.gnupg/trustdb.gpg: trustdb created
gpg: key A0EA981B66B0D967: public key "Konstantin Pavlov <thresh@nginx.com>" imported
gpg: Total number processed: 1
gpg:               imported: 1

如果公鑰匯入順利且 NGINX 軟體來源碼封存檔未被竄改的話再次驗證應該可以順利驗證成功(下列輸出第五行的「Good signature」關鍵字):

$ gpg --verify nginx-1.25.3.tar.gz.asc nginx-1.25.3.tar.gz gpg: Signature made Tue Oct 24 15:42:51 2023 UTC gpg: using RSA key 13C82A63B603576156E30A4EA0EA981B66B0D967 gpg: issuer "k.pavlov@f5.com" gpg: Good signature from "Konstantin Pavlov <thresh@nginx.com>" [unknown] gpg: aka "Konstantin Pavlov <k.pavlov@f5.com>" [unknown] gpg: WARNING: This key is not certified with a trusted signature! gpg: There is no indication that the signature belongs to the owner. Primary key fingerprint: 13C8 2A63 B603 5761 56E3 0A4E A0EA 981B 66B0 D967

警告: 使用此方式驗證 NGINX 來源程式碼封存檔的資料完整性有下列問題:

故於資安實務上這種作法我們並無法 100% 確認這個檔案一定是沒問題的,如何有效避免這些問題非本文章的說明範疇,請搜尋 PGP web of trust 關鍵字以自行了解相關資訊

解開 NGINX 的軟體來源碼封存檔

執行下列命令以解開 NGINX 的軟體來源碼封存檔:

tar_opts=(
  # 選擇解開封存檔 tar 操作模式
  --extract
  
  # 指定要解開的封存檔路徑
  --file nginx-1.25.3.tar.gz
)
tar "${tar_opts[@]}"

您應可於當前作業目錄current working directory找到解開的 nginx-1.25.3 軟體來源碼目錄:

$ ls nginx-*
nginx-1.25.3.tar.gz  nginx-1.25.3.tar.gz.asc

nginx-1.25.3:
CHANGES     LICENSE  auto  configure  html  src
CHANGES.ru  README   conf  contrib    man

配置 NGINX 軟體的軟體建構細節

據前面一開始提到的 Building nginx from Sources 官網說明文件所稱,NGINX(如同大多數開放來源碼軟體)使用一個叫做 configure 的軟體建構配置程式進行軟體的軟體建構細節配置,一般來說會進行下列功能:

  • 偵測不同作業系統與處理器平台的特性(如 32/64 位元、是否支持特定指令集與應用程式界面(API)等)
  • 設定(通常是於建構時期build-time於二進位程式中寫死的)軟體安裝前綴路徑installation prefix與各種資源(如可執行檔案executable file程式庫檔案library file軟體配置檔案configuration file運行紀錄檔log file等等)的安裝與存取路徑
  • 偵測系統中是否存在必要mandatory選用optional之,與軟體相容的依賴組件dependency及其版本(如果依賴組件未安裝於系統預設路徑的話,得由使用者指定其路徑)
  • 決定軟體於建構時期build-time要編入哪些功能feature(如果沒有手動指定的話,可能會根據系統中依賴軟體的有無來決定是否編入)
  • 要使用哪一個軟體開發工具鏈toolchain程式(如預處理器preprocessor編譯器compiler連結器linker等)來進行軟體建構
  • 其他使用者自定義的細節(如軟體的可執行檔是否要有特定的檔案名稱前綴prefix等)

最後軟體建構配置程式會生成用於實際進行軟體建構的程序供我們於後續步驟使用

附註: 觀察 NGINX 來源碼目錄中的內容您或許會注意到 NGINX 並不是使用常規的軟體建構系統software build system(GNU Autotools/CMake/Maven)而是使用專案專有之模組化的 Shell 腳本來進行軟體建構的配置與建構程式生成

我們先執行下列命令切換作業目錄working directory到 NGINX 1.25.3 軟體來源碼的目錄中,方便後續要進行的操作:

cd nginx-1.25.3

我們可以於前述之上游專案的說明文件或是 configure 程式印出的的幫助訊息查看可以 NGINX 軟體建構配置的項目,因為上游專案的文件不一定跟我們使用的版本一致的關係所以這邊我們以 configure 程式的輸出作為主要的建構參考:

nginx-1.25.3 $ ./configure --help

  --help                             print this message

  --prefix=PATH                      set installation prefix
  --sbin-path=PATH                   set nginx binary pathname

    ...stripped...

……NGINX 的軟體建構配置程式的輸出實在是太長了,因為 Ubuntu 22.04 Docker 容器映像中預設提供的 more 分頁器pager 沒辦法自由查看上一頁的內容的關係,我們可以 root 身份執行下列命令改裝功能比較強大的 less 分頁器:

apt install less

然後就可以執行下列命令,用 Bash 殼層操作界面shell輸入/輸出資料流重導向Input/Output redirection功能來一頁一頁瀏覽 ./configure --help 命令的輸出:

./configure --help | less

執行  命令後的終端機畫面示意圖

您可以於 less 分頁器中使用 / 按鍵搜尋特定的關鍵字keyword快速查找要啟用的功能,自 ./configure --help 命令的輸出我們可以知道要啟用 HTTPS(SSL/TLS) 支援需要於 configure 程式的執行命令中新增 --with-http_ssl_module 命令選項:

自  程式的幫助訊息中找到啟用 HTTPS 支援的命令選項的示意圖

接下來我們就可以迭代地iteratively執行下列命令來配置 NGINX 的軟體建構,並根據其錯誤或警告訊息來準備建構所需要的環境:

./configure --with-http_ssl_module

首先 NGINX 軟體建構配置程式抱怨找不到 C 語言程式編譯器compiler

nginx-1.25.3 $ ./configure --with-http_ssl_module
checking for OS
 + Linux 6.1.0-1026-oem x86_64
checking for C compiler ... not found

./configure: error: C compiler cc is not found

於 GNU/Linux 系統中常見的提供 C 語言程式編譯器的軟體為 GNU Compiler Collection(GCC)Clang,我們以 root 身份安裝 GCC:

apt install gcc

附註: 您也可以執行下列命令搜尋 Ubuntu 軟體庫提供之,可能提供 C 語言程式編譯器的軟體包:

apt search 'C compiler' | less

注意不是每個 C 語言程式編譯器(的各個版本)都支持 NGINX 軟體軟體建構所需要的編譯器特性

然後再一次執行 NGINX 的軟體建構配置程式,這一次它抱怨找不到 PCRE 程式庫:

$ ./configure --with-http_ssl_module

    ...stripped...

checking for PCRE2 library ... not found
checking for PCRE library ... not found
checking for PCRE library in /usr/local/ ... not found
checking for PCRE library in /usr/include/pcre/ ... not found
checking for PCRE library in /usr/pkg/ ... not found
checking for PCRE library in /opt/local/ ... not found

./configure: error: the HTTP rewrite module requires the PCRE library.
You can either disable the module by using --without-http_rewrite_module
option, or install the PCRE library into the system, or build the PCRE library
statically from the source with nginx by using --with-pcre=<path> option.

Debian 系 GNU/Linux 作業系統的 APT 軟體包管理器package manager會將軟體的不同組成元件component 分開打包成獨立的軟體包 以節省系統的儲存空間使用,於源碼建構時軟體的依賴組件我們主要會安裝它的「軟體開發用檔案development files(包含但不限於 C/C++ 語言程式的標頭檔案header file、用於靜態連結static linking靜態程式庫static library檔案與軟體開發者用的參考文件developer reference等等)」的軟體包,此類軟體包通常會使用 -dev 的軟體包名後綴suffix(如果是程式庫則通常會有 lib 的軟體包名前綴prefix),我們可以執行下列命令將吻合此名稱式樣name pattern的軟體包通通找出來:

apt_search_opts=(
    # 只比對軟體包名稱而非軟體描述文字
    --names-only
)
apt search "${apt_search_opts[@]}" '^libpcre.*-dev$'

附註:

  • ^libpcre.*-dev$ 為 POSIX 風格的正規表達式語法(參閱 apt-cache(8) 與 regex(7) 的 manpage 使用手冊頁面)
  • RedHat 系 GNU/Linux 作業系統散布版中軟體開發用檔案的軟體包取名慣例為 -devel 後綴

apt search 命令的輸出我們可以發現 Ubuntu 軟體庫有同時提供第一版第二版的 PCRE 程式庫:

$ apt search --names-only '^libpcre.*-dev'
Sorting... Done
Full Text Search... Done

    ...stripped...

libpcre2-dev/jammy-updates,jammy-security 10.39-3ubuntu0.1 amd64
  New Perl Compatible Regular Expression Library - development files

libpcre3-dev/jammy-updates,jammy-security 2:8.39-13ubuntu0.22.04.1 amd64
  Old Perl 5 Compatible Regular Expression Library - development files

附註: 對您沒看錯,同 libpcre3-dev 軟體包的文字描述所稱,實際上該軟體包為第一版而非第三版的 PCRE 程式庫(翻桌)

因自 NGINX 軟體建構配置程序的輸出可以判斷 NGINX 有相容第二版的 PCRE 程式庫,這邊選擇以 root 身份執行下列命令安裝第二版 PCRE 程式庫的軟體開發用文件:

apt install libpcre2-dev

附註: NGINX 於 1.21.5 版(2021/12/28)起才新增了第二版 PCRE 程式庫的支援,如建構舊版 NGINX 則須安裝第一版 PCRE 程式庫的軟體開發用檔案

然後再一次執行 NGINX 的軟體建構配置程式,這一次它抱怨找不到 OpenSSL 程式庫的安裝:

nginx-1.25.3 $ ./configure --with-http_ssl_module

    ...stripped...

checking for PCRE2 library ... found
checking for OpenSSL library ... not found
checking for OpenSSL library in /usr/local/ ... not found
checking for OpenSSL library in /usr/pkg/ ... not found
checking for OpenSSL library in /opt/local/ ... not found

./configure: error: SSL modules require the OpenSSL library.
You can either do not enable the modules, or install the OpenSSL library
into the system, or build the OpenSSL library statically from the source
with nginx by using --with-openssl=<path> option.

附註: 因為 NGINX 需要的是 OpenSSL 軟體建構時期build-time用的檔案而非執行時期runtime用的檔案,所以就算您的系統明顯有安裝基於 OpenSSL 程式庫的軟體 NGINX 的軟體安裝配置程序仍然會提示未安裝 OpenSSL 程式庫。具體會使用的檔案可以於軟體來源碼目錄中執行下列命令確認:

grep_opts=(
    # 不區分大小寫
    --ignore-case
    
    # 遞迴地搜尋指定目錄下的所有檔案
    --recursive
)
grep "${grep_opts[@]}" openssl auto/

使用先前 PCRE 程式庫的經驗,我們可以執行下列命令搜尋提供 OpenSSL 程式庫的軟體包:

apt_search_opts=(
    # 只比對軟體包名稱而非軟體描述文字
    --names-only
)
apt search "${apt_search_opts[@]}" 'libopenssl.*-dev'

……但是什麼都找不到呢:

$ apt search "${apt_search_opts[@]}" 'libopenssl.*-dev'
Sorting... Done
Full Text Search... Done

我們把搜尋條件再調鬆一點,現在終於有結果了但是好像都不是我們要的:

$ apt search "${apt_search_opts[@]}" 'openssl.*-dev'
Sorting... Done
Full Text Search... Done
golang-github-mendersoftware-openssl-dev/jammy 1.1.0-2ubuntu2 all
  OpenSSL bindings for Go.

libcurl4-openssl-dev/jammy-updates,jammy-security 7.81.0-1ubuntu1.15 amd64
  development files and documentation for libcurl (OpenSSL flavour)

libghc-hsopenssl-dev/jammy 0.11.4.18-1ubuntu1 amd64
  partial OpenSSL binding for Haskell

libghc-hsopenssl-x509-system-dev/jammy 0.1.0.3-5build4 amd64
  use system's native CA certificate store with HsOpenSSL

    ...stripped...

……我們把搜尋條件再調鬆一點,這次我們找到了跟 OpenSSL 同名的 openssl 軟體包,但是還是沒有找到看起來有提供 OpenSSL 軟體開發用檔案的軟體包:

$ apt search "${apt_search_opts[@]}" openssl
Sorting... Done
Full Text Search... Done

    ...stripped...

openssl/jammy-updates,jammy-security,now 3.0.2-0ubuntu1.12 amd64 [installed,automatic]
  Secure Sockets Layer toolkit - cryptographic utility

    ...stripped...

看來我們的這個作法已經走到了一個死胡同了,我們試試看能不能從其他地方找到有價值的線索。如果您有使用前面附註提到之 grep 命令查詢跟 OpenSSL 程式庫有關的軟體建構配置邏輯的話您應該會注意到在 auto/lib/openssl/conf shell 小腳本scriptlet中的依賴軟體檢查邏輯有嘗試載入一個叫做 openssl/ssl.h 的 C 程式語言標頭檔案:

NGINX 軟體建構配置程序的 OpenSSL 程式庫安裝偵測邏輯片段示意圖

我們可以使用 apt-file 工具來查詢所有有提供這個檔案路徑式樣的軟體包,首先先以 root 身份執行下列命令安裝軟體:

apt install apt-file

然後執行下列命令建立 apt-file 軟體的本地快取資料cache

apt-file update

最後再執行下列命令搜尋所有提供 openssl/ssl.h 路徑結尾之檔案的軟體包:

apt_file_search_opts=(
    # 使用 Perl 正規表達式(而非純文字)搜尋
    --regexp
)
apt-file search "${apt_file_search_opts[@]}" 'openssl/ssl\.h$'
$ apt-file search "${apt_file_search_opts[@]}" 'openssl/ssl\.h$'
android-libboringssl-dev: /usr/include/android/openssl/ssl.h
libnode-dev: /usr/include/node/openssl/ssl.h
libssl-dev: /usr/include/openssl/ssl.h
libwolfssl-dev: /usr/include/cyassl/openssl/ssl.h
libwolfssl-dev: /usr/include/wolfssl/openssl/ssl.h
python3-pycparser: /usr/share/python3-pycparser/fake_libc_include/openssl/ssl.h

從查詢結果中的軟體包名稱跟提供的檔案路徑複雜度可推測應該是 libssl-dev 這個軟體包提供我們需要的 OpenSSL 開發用檔案

附註: 以下提供幾種同樣可以找到正確的提供 OpenSSL 軟體開發用檔案軟體包(libssl-dev)的可能作法:

  • 編輯 APT 軟體包管理器的軟體來源清單啟用來源碼軟體包source package的軟體來源(deb-src),然後使用 apt showsrc 命令查詢 openssl 這個來源碼軟體包到底會建構出哪些看起來是提供軟體開發用檔案的二進位軟體包binary package

    ​​​​# sed \
    ​​​​    --in-place \
    ​​​​    --expression='s/^# deb-src/deb-src/' \
    ​​​​    /etc/apt/sources.list
    ​​​​# apt update
    
    ​​​​    ...stripped...
    
    ​​​​$ apt showsrc openssl
    ​​​​Package: openssl
    ​​​​Format: 3.0 (quilt)
    ​​​​Binary: openssl, libssl3, libcrypto3-udeb, libssl3-udeb, libssl-dev, libssl-doc
    
    ​​​​    ...stripped...
    
    
  • 前面有提過「APT 軟體包管理器會將軟體的不同組成元件 分開打包成獨立的軟體包」這點,觀察 openssl 軟體包的文字描述可以得知此軟體包提供「Secure Sockets Layer(SSL) 工具組toolkit」的「加密學相關的cryptographic 工具程式utility」,那有沒有軟體包有提供「Secure Sockets Layer(SSL) 工具組」的「軟體開發用檔案」呢?直接用 Secure Sockets Layer toolkit 當作搜尋關鍵字找找看就可以發現 libssl-dev 應該是我們需要的軟體包:

    ​​​​$ apt search 'Secure Sockets Layer toolkit'
    ​​​​Sorting... Done
    ​​​​Full Text Search... Done
    ​​​​libssl-dev/jammy-updates,jammy-security 3.0.2-0ubuntu1.12 amd64
    ​​​​  Secure Sockets Layer toolkit - development files(軟體開發用檔案)
    
    ​​​​libssl-doc/jammy-updates,jammy-security 3.0.2-0ubuntu1.12 all
    ​​​​  Secure Sockets Layer toolkit - development documentation(軟體開發用說明文件)
    
    ​​​​libssl3/jammy-updates,jammy-security,now 3.0.2-0ubuntu1.12 amd64 [installed]
    ​​​​  Secure Sockets Layer toolkit - shared libraries(共享程式庫)
    
    ​​​​openssl/jammy-updates,jammy-security,now 3.0.2-0ubuntu1.12 amd64 [installed,automatic]
    ​​​​  Secure Sockets Layer toolkit - cryptographic utility(加密學相關的工具程式)
    
  • 使用 ldd "$(which openssl)" 命令查看 openssl 程式的會於執行時期載入的可能跟 OpenSSL 有關的共享程式庫名稱(libssl)(版本號去掉),然後用 apt search --names-only libssl 命令尋找該程式庫名稱開頭 -dev 結尾的提供軟體開發用檔案的軟體包:

    ​​​​$ ldd "$(which openssl)"
    ​​​​        linux-vdso.so.1 (0x00007ffcade07000)
    ​​​​        libssl.so.3 => /lib/x86_64-linux-gnu/libssl.so.3 (0x00007f02c13cd000)
    ​​​​        libcrypto.so.3 => /lib/x86_64-linux-gnu/libcrypto.so.3 (0x00007f02c0e00000)
    ​​​​        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f02c0a00000)
    ​​​​        /lib64/ld-linux-x86-64.so.2 (0x00007f02c156d000)
    ​​​​$ apt search --names-only '^libssl.*-dev$'
    ​​​​Sorting... Done
    ​​​​Full Text Search... Done
    ​​​​libssl-dev/jammy-updates,jammy-security 3.0.2-0ubuntu1.12 amd64
    ​​​​  Secure Sockets Layer toolkit - development files
    
    ​​​​    ...stripped...
    
  • 編輯 APT 軟體包管理器的軟體來源清單啟用來源碼軟體包source package的軟體來源(deb-src),然後使用 apt showsrc 命令查詢 NGINX 等確定有使用 OpenSSL 程式庫的軟體的建構依賴軟體build dependencies的軟體包有哪個看起來像是跟 OpenSSL 程式庫比較有關的,提供軟體開發用檔案的軟體包

    ​​​​# sed \
    ​​​​    --in-place \
    ​​​​    --expression='s/^# deb-src/deb-src/' \
    ​​​​    /etc/apt/sources.list
    ​​​​# apt update
    
    ​​​​    ...stripped...
    ​​​​    
    ​​​​$ apt showsrc nginx
    ​​​​Package: nginx
    
    ​​​​    ...stripped...
    
    ​​​​Build-Depends: debhelper-compat (= 13), dpkg-dev (>= 1.15.5), libexpat-dev, libgd-dev, libgeoip-dev, libhiredis-dev, libmaxminddb-dev, libmhash-dev, libpam0g-dev, libpcre3-dev, libperl-dev, libssl-dev, libxslt1-dev, po-debconf, quilt, zlib1g-dev
    
    ​​​​    ...stripped...
    
    
  • 當然傳統的直接找 Google/StackOverflow/AskUbuntu/ChatGPT/Google Gemini 問也可能可以找到解答

以 root 身份 執行下列命令補上依賴軟體的安裝:

apt install libssl-dev

然後再一次執行 NGINX 的軟體建構配置程式,現在它抱怨缺少 zlib 程式庫:

nginx-1.25.3 $ ./configure --with-http_ssl_module

    ...stripped...

checking for OpenSSL library ... found
checking for zlib library ... not found

./configure: error: the HTTP gzip module requires the zlib library.
You can either disable the module by using --without-http_gzip_module
option, or install the zlib library into the system, or build the zlib library
statically from the source with nginx by using --with-zlib=<path> option.

用同樣方式去尋找提供 zlib 程式庫的軟體開發用檔案軟體包,推定應該是 zlib1g-dev 這個軟體包沒錯:

$ apt search --names-only '^libzlib.*-dev$'
Sorting... Done
Full Text Search... Done

$ apt search --names-only 'zlib.*-dev$'
Sorting... Done
Full Text Search... Done

    ...stripped...

zlib1g-dev/jammy-updates,jammy-security 1:1.2.11.dfsg-2ubuntu9.2 amd64
  compression library - development

附註: 跟 OpenSSL 程式庫一樣,提供 zlib 程式庫軟體開發用檔案的軟體包一樣不太遵守 Debian 系 GNU/Linux 作業系統的軟體開發用檔案軟體包的命名慣例,這些軟體就只能多花點時間確認了(攤手)

以 root 身份 執行下列命令補上依賴軟體的安裝:

apt install zlib1g-dev

然後再一次執行 NGINX 的軟體建構配置程式,出現下列輸出代表 NGINX 的軟體建構已經順利完成配置了:

$ ./configure --with-http_ssl_module

    ...stripped...

creating objs/Makefile

Configuration summary
  + using system PCRE2 library
  + using system OpenSSL library
  + using system zlib library

  nginx path prefix: "/usr/local/nginx"
  nginx binary file: "/usr/local/nginx/sbin/nginx"
  nginx modules path: "/usr/local/nginx/modules"
  nginx configuration prefix: "/usr/local/nginx/conf"
  nginx configuration file: "/usr/local/nginx/conf/nginx.conf"
  nginx pid file: "/usr/local/nginx/logs/nginx.pid"
  nginx error log file: "/usr/local/nginx/logs/error.log"
  nginx http access log file: "/usr/local/nginx/logs/access.log"

    ...stripped...

留意 NGINX 安裝的:

  • OpenSSL 支援有啟用
  • 軟體安裝前綴目錄位於 /usr/local/nginx(一般來說應該是要安裝到 /usr/local,此為 NGINX 軟體的特例)
  • 可執行(二進制)檔安裝目錄位於 軟體安裝前綴目錄/sbin 目錄(第 12 行)
  • 服務配置前綴目錄位於 軟體安裝前綴目錄/conf 目錄(一般來說應該要是 軟體安裝前綴目錄/etc/服務識別名稱 這個目錄,此為 NGINX 軟體的特例)
  • 主服務配置檔案config file位於 服務配置前綴目錄/nginx.conf
  • 服務主進程識別編號檔process id(PID) file位於 軟體安裝前綴目錄/logs/nginx.pid(一般來說應該要安裝到 軟體安裝前綴目錄/run/服務識別名稱.pid,此為 NGINX 軟體的特例)
  • 服務的錯誤運行紀錄檔error log位於 軟體安裝前綴目錄/logs/error.log(一般來說應該要安裝到 軟體安裝前綴目錄/var/log/服務識別名稱/error.log 目錄下,此為 NGINX 軟體的特例)
  • 服務的預設站台訪問紀錄擋access log位於 安裝前綴目錄/logs/access.log(一般來說應該要安裝到 軟體安裝前綴目錄/var/log/服務識別名稱/access.log,此為 NGINX 軟體的特例)

且 NGINX 軟體來源碼目錄中出現可用於建構 NGINX 軟體的 Makefile 檔案:

nginx-1.25.3 $ ls
CHANGES     LICENSE   README  conf       contrib  man   src
CHANGES.ru  Makefile  auto    configure  html     objs

建構 NGINX 軟體

因為 NGINX 軟體建構配置程式產生了 Makefile 文件,我們可以推定該軟體使用的軟體建構自動化工具build-automation utilityGNU Make以 root 身份 執行下列命令進行安裝:

apt install make

然後我們可以執行下列命令進行 NGINX 軟體的軟體建構:

make_opts=(
    # 開啟平行建構(parallel build)功能,將將平行運行的 sub-make 工作數量限制在
    # 主機的中央處理器總處理器執行緒數以減少軟體建構所需時間
    --jobs="$(nproc)"
)
make "${make_opts[@]}"

如果看到類似這樣的輸出且命令的結束狀態代碼exit status code為零則代表軟體建構順利結束:

nginx-1.25.3 $ make "${make_opts[@]}"
make -f objs/Makefile
make[1]: Entering directory '/nginx-1.25.3'
cc -c -pipe  -O -W -Wall -Wpointer-arith -Wno-unused-parameter -Werror -g  -I src/core -I src/event -I src/event/modules -I src/event/quic -I src/os/unix -I objs \
        -o objs/src/core/nginx.o \
        src/core/nginx.c

    ...stripped...

objs/src/http/modules/ngx_http_upstream_zone_module.o \
objs/ngx_modules.o \
-lcrypt -lpcre2-8 -lssl -lcrypto -lz \
-Wl,-E
make[1]: Leaving directory '/nginx-1.25.3'

nginx-1.25.3 $ echo $?
0

附註:

  • 您可以在 make 命令前加上 time 命令以測量軟體建構的所需時間:

    ​​​​$ time make "${make_opts[@]}"
    
    ​​​​    ...stripped...
    
    ​​​​make[1]: Leaving directory '/nginx-1.25.3'
    
    ​​​​real    0m2.841s
    ​​​​user    0m31.412s
    ​​​​sys     0m6.163s
    

    這邊的軟體建構所需參考時間為 2.841 秒,沒有開啟平行建構的所需時間則為 15.772 秒(Framework 13 AMD 7040 系列(Ryzen 7 7840U)(總執行緒數:16)、使用 performance 性能層級),隨著要建構的軟體的大小規模增長軟體建構的所需時間也會隨之增加,超過半小時甚至是長達一整天或更久也是有可能的所以能開啟平行建構的話建議開啟

    有些情況下會發生平行建構報錯但是單執行緒建構可以正常完成的狀況,這通常是軟體的軟體建構自動化實現有缺陷所造成

  • GNU Make 預設會跳過已經完成且源碼檔案未異動的軟體建構工作項目,如果要進行重新建構您需要執行 clean 這個偽 make 目標phony make target來清理所有先前 Make 產生的的建構(中間)產物:

    ​​​​make clean
    

    注意此操作也會將 Makefile 文件移除,需要重新執行軟體建構配置程序來重新生成那個檔案

安裝 NGINX 軟體

以 root 身份執行 Makefile 提供的 install 偽 make 目標即可將建構好的 NGINX 軟體安裝到系統中:

make install

附註: 因為一般使用者沒有權限在 /usr/local 系統目錄寫入檔案,所以命令才需要以 root 身份執行,如果於 NGINX 軟體建構配置時期有指定將軟體安裝到一般使用者可寫入的目錄中是不需要這樣做的

驗證 NGINX 安裝

使用 less 純文字資料分頁器審閱 服務配置前綴目錄 中的 nginx.conf 服務主配置檔可以發現 NGINX 服務預設會在 80 通訊埠號提供一個服務 軟體安裝前綴目錄/html 目錄下網頁的靜態 HTTP 服務:


    ...stripped...

http {

    ...stripped...

    server {
        listen       80;
        server_name  localhost;

    ...stripped...

        location / {
            root   html;
            index  index.html index.htm;
        }
    }
}

接下來您可以以 root 身份執行下列命令將 NGINX 服務啟動並作為幕後服務daemon運行:

/usr/local/nginx/sbin/nginx
$ pgrep --list-full nginx
26776 nginx: master process /usr/local/nginx/sbin/nginx
26777 nginx: worker process     

附註:

  • 因為下列原因 NGINX 服務主程式才需要以 root 身份執行:
    • 一般使用者沒有權限運行監聽 <1024 監聽埠號的服務(除非服務主程式有被賦予 CAP_NET_BIND_SERVICE Linux 能力capability
    • 一般使用者沒有權限寫入位於系統目錄中的 NGINX 進程識別編號檔目錄以及運行紀錄目錄
  • NGINX 為多進程的運作模型,一個服務實體會創建一個主進程master process(負責管理工人進程等)跟至少一個的工人進程worker process(負責實際處理服務請求)

使用 curl HTTP 客戶端軟體訪問本地主機localhost(於此情境指 Docker 容器自己)的 80 HTTP 服務常規通訊埠應該可以獲得類似下面的 NGINX 範例站台網頁內容:

$ curl http://localhost
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

於宿主機使用 Web 瀏覽器訪問本地主機的的 80 HTTP 服務常規通訊埠應該也可以看到相同頁面:

使用 Mozilla Firefox Web 瀏覽器訪問容器中的 NGINX HTTP 服務站台示意圖

接下來我們來測試看看我們安裝的 NGINX 是否支援 HTTPS 服務,查看 NGINX 服務的主配置檔可以看到 NGINX 有一個預設註解掉的 HTTPS 站台範例配置:


    ...stripped...

http {

    ...stripped...

    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}
}

由於 Ubuntu 22.04 的 Docker 容器映像並無提供任何的純文字文件編輯器,我們以 root 身份執行下列命令安裝使用上最簡單的 GNU nano 編輯器軟體

apt install nano

然後我們可以以 root 身份執行下列命令編輯 NGINX 的服務主配置檔將該 HTTPS 服務配置取消註解:

nano /usr/local/nginx/conf/nginx.conf

附註: GNU nano 編輯器會在操作界面的下側列舉可使用的常見操作,各操作前面的 插入記號caret-字元 序列代表該操作需要以 Ctrl+字元 的組合鍵觸發:

GNU nano 的操作提示面板示意圖

例如內容編輯完成後可以按下 Ctrl+X 組合鍵存檔結束編輯

留意該站台配置會參照一個叫做 cert.pem 的 X.509 證書文件以及一個叫做 cert.key 的 RSA 私鑰文件:


    ...stripped...

http {

    ...stripped...

    # HTTPS server
    #
    server {
        listen       443 ssl;
        server_name  localhost;

        ssl_certificate      cert.pem;
        ssl_certificate_key  cert.key;

        ssl_session_cache    shared:SSL:1m;
        ssl_session_timeout  5m;

        ssl_ciphers  HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers  on;

        location / {
            root   html;
            index  index.html index.htm;
        }
    }
}

因為我們修改了配置,我們以 root 身份執行下列命令檢查 NGINX 配置的正確性:

/usr/local/nginx/sbin/nginx -t

附註: 需要以 root 身份執行的原因是因為 NGINX 安裝到系統目錄中以一般使用者身份執行會有部份資源存取不了的問題,如果 NGINX 是安裝到使用者自己的目錄下就沒這個問題

會發現 NGINX 抱怨無法存取到 cert.pem 這個 HTTPS 服務會使用到的 X.509 證書文件:

# /usr/local/nginx/sbin/nginx -t
nginx: [emerg] cannot load certificate "/usr/local/nginx/conf/cert.pem": BIO_new_file() failed (SSL: error:80000002:system library::No such file or directory:calling fopen(/usr/local/nginx/conf/cert.pem, r) error:10000080:BIO routines::no such file)

使用 ls 命令查看會發現 NGINX 配置目錄下並沒有該 SSL/TLS 證書文件(cert.pem)跟私鑰文件(cert.key),我們可以執行下列命令自行簽發self-sign一個給 localhost 站台使用的 X.509 證書來用:

# 因為私鑰洩漏有資訊安全疑慮所以將預設的群組(group)跟其他人(other)
# 的 Unix 檔案訪問權限設定遮罩掉
umask 077

# 產生一個 2048 位元大小的 RSA 私鑰
openssl genrsa 2048 > cert.key

# 使用前面產生的 RSA 私鑰產生一個給「localhost」這個 Common Name 實體
# 使用的 PKCS#10 憑證簽發請求(CSR)
openssl_req_opts=(
    # 產生新的憑證簽發請求
    -new
    
    # 指定要使用的私鑰文件
    -key cert.key
    
    # 設定新的憑證簽發請求的主體名稱(subject name)
    -subj '/CN=localhost'
)
openssl req "${openssl_req_opts[@]}" > cert.csr

# 使用前面產生的 PKCS#10 憑證簽發請求自行簽發一個新 X.509 憑證
openssl_x509_opts=(
    # 指定輸入文件為一憑證簽發請求
    -req
    
    # 指定輸入的憑證簽發請求文件
    -in cert.csr
    
    # 指定要簽發的 X.509 憑證的效期為一個月(30 天)
    -days 30
    
    # 指定用於簽發 X.509 憑證使用的憑證簽發者(certificate issuer)私鑰
    # 因為是自簽憑證我們使用同一個私鑰文件即可
    -signkey cert.key
    
    # 指定要簽發的 X.509 憑證文件路徑
    -out cert.pem
)
openssl x509 "${openssl_x509_opts[@]}"

然後以 root 身份執行下列命令將產生出來的證書跟私鑰文件安裝到 NGINX 服務配置目錄:

# 通用的 install 命令選項
install_opts_common=(
    # 設定擁有者為 root
    --owner root
    
    # 設定擁有群組為 root
    --group root
    
    # 顯示冗餘輸出
    --verbose
)
# 安裝 X.509 證書專用的 install 命令選項
install_opts_cert=(
    "${install_opts_common[@]}"

    # 設定 Unix 檔案訪問權限為 rw-r--r--
    --mode 644
)
# 安裝 RSA 私鑰文件專用的 install 命令選項
install_opts_key=(
    "${install_opts_common[@]}"
    
    # 設定 Unix 檔案訪問權限為 rw-------
    --mode 600
)
install "${install_opts_cert[@]}" cert.pem /usr/local/nginx/conf/cert.pem
install "${install_opts_key[@]}" cert.key /usr/local/nginx/conf/cert.key

再次檢查 NGINX 配置的正確性應該就能過了:

# /usr/local/nginx/sbin/nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful

然後以 root 身份執行下列命令重新載入 NGINX 的服務配置:

/usr/local/nginx/sbin/nginx -s reload

附註: 此命令實際的作用為自先前提到的 NGINX 服務主進程master process進程識別編號檔pid file讀取 NGINX 服務主進程的進程識別編號process ID(PID),然後對該進程發送 SIGHUP 訊號,更多資訊可以參閱 Controlling nginx 這個官方文件

順利的話您應可以執行下列命令訪問到 NGINX 的 HTTPS 站台:

curl_opts=(
    # 略過 SSL/TLS 憑證驗證,非由公認信任之憑證簽發機構(certificate authority(CA))
    # 簽發的憑證是沒辦法過驗證的
    --insecure
)
curl "${curl_opts[@]}" https://localhost

於宿主機使用 Web 瀏覽器(本文以 Mozilla Firefox Web 瀏覽器應用軟體為例)訪問本地主機的 https://localhost 頁面則會先跳出 SSL/TLS 驗證警告畫面(同前所述因為憑證是我們自己簽發的不被 Web 瀏覽器/作業系統信任:

Mozilla Firefox Web 瀏覽器的「本網站可能有安全性風險」警告畫面截圖

點擊下方的「進階…」按鈕再點擊「接受風險並繼續」按鈕就可以讓 Web 瀏覽器記住此安全例外設定:

Mozilla Firefox Web 瀏覽器的「本網站可能有安全性風險」警告畫面設定安全例外操作示意圖

接下來應該就可以看到同一個 NGINX 範例站台頁面:

使用 Mozilla Firefox Web 瀏覽器訪問容器中的 NGINX HTTPS 服務站台示意圖

至此我們就可以確定我們的建構的 NGINX 服務是正常運作且有滿足我們所有要求的

操作演示

以下提供實際進行操作的終端機畫面錄影,僅供參考:

asciinema 操作過程錄影

參考資料

以下說明進行本練習所需要的相關參考資料:

Select a repo