NGINX 作為廣泛使用的 Web 伺服器和反向代理,其模組化設計允許開發者透過第三方模組擴充套件其功能。本文將介紹如何使用 njs、Lua 和 Perl 模組增強 NGINX 的處理能力,並探討如何利用 Consul 和 consul-template 實作 NGINX 組態的動態更新。njs 模組允許在 NGINX 中執行 JavaScript 程式碼,實作更精細的請求和回應處理,例如解析 JWT。Lua 模組提供輕量級的指令碼執行環境,適用於快速原型開發和簡單邏輯處理。Perl 模組則適合處理更複雜的任務。此外,Ansible 和 Chef 等組態管理工具可以簡化 NGINX 的安裝和組態流程,提高自動化程度。最後,Consul 和 consul-template 的結合,讓 NGINX 組態能隨著服務註冊資訊動態變化,無需手動修改組態檔案和重啟服務,大幅提升 Web 服務的彈性和可維護性。
使用NGINX JavaScript模組(njs)擴充套件NGINX功能
問題描述
需要在NGINX中執行自定義邏輯來處理請求或回應。
解決方案
透過安裝NGINX JavaScript(njs)模組來啟用JavaScript功能。以下步驟假設您已為Linux發行版增加了官方的NGINX儲存函式庫,如第1章所示。
安裝njs模組
使用APT套件管理器安裝NGINX Open Source的njs模組:
$ apt install nginx-module-njs使用APT套件管理器安裝NGINX Plus的njs模組:
$ apt install nginx-plus-module-njs使用YUM套件管理器安裝NGINX Open Source的njs模組:
$ yum install nginx-module-njs使用YUM套件管理器安裝NGINX Plus的njs模組:
$ yum install nginx-plus-module-njs
設定njs模組
建立JavaScript檔案存放目錄(如果尚未建立):
$ mkdir -p /etc/nginx/njs建立JavaScript檔案
/etc/nginx/njs/jwt.js,內容如下:function jwt(data) { var parts = data.split('.').slice(0,2) .map(v=>Buffer.from(v, 'base64url').toString()) .map(JSON.parse); return { headers:parts[0], payload: parts[1] }; } function jwt_payload_subject(r) { return jwt(r.headersIn.Authorization.slice(7)).payload.sub; } function jwt_payload_issuer(r) { return jwt(r.headersIn.Authorization.slice(7)).payload.iss; } export default {jwt_payload_subject, jwt_payload_issuer}內容解密:
jwt函式負責解碼JSON Web Tokens(JWTs)。它將JWT分割成header和payload兩部分,並進行base64url解碼和JSON解析。jwt_payload_subject和jwt_payload_issuer函式利用jwt函式來提取JWT中的subject和issuer欄位。.slice(7)用於移除Authorization頭部值的前七個字元(通常是“Bearer ”),以便直接傳遞JWT給jwt函式。
在NGINX主設定檔中載入njs模組並匯入JavaScript檔案:
load_module /etc/nginx/modules/ngx_http_js_module.so; http { js_path "/etc/nginx/njs/"; js_import main from jwt.js; js_set $jwt_payload_subject main.jwt_payload_subject; js_set $jwt_payload_issuer main.jwt_payload_issuer; ... }內容解密:
load_module指令用於動態載入njs模組。js_path定義了JavaScript檔案的存放路徑。js_import匯入了之前建立的jwt.js檔案,並命名為main。js_set指令將NGINX變數與JavaScript函式的傳回值關聯起來。
定義一個伺服器區塊來傳回由JavaScript設定的變數:
server { listen 80 default_server; listen [::]:80 default_server; server_name _; location / { return 200 "$jwt_payload_subject $jwt_payload_issuer"; } }內容解密:
- 該伺服器組態監聽80埠,並傳回客戶端透過
Authorization頭部提供的JWT中的subject和issuer值。
- 該伺服器組態監聽80埠,並傳回客戶端透過
驗證
- 使用一個已知的JWT向伺服器傳送請求,以驗證JavaScript程式碼是否正確執行並傳回預期值。
使用Lua和Perl擴充套件NGINX的功能
NGINX是一個高度可擴充套件的伺服器,可以透過多種語言模組進行擴充套件,包括Lua和Perl。這些模組允許開發者透過指令碼語言增強NGINX的功能,從而實作更複雜的邏輯和處理。
Lua模組的使用
Lua是一種輕量級的指令碼語言,NGINX的Lua模組允許開發者使用Lua語言編寫指令碼,以擴充套件NGINX的功能。下面是一個使用Lua模組的例子:
default_type text/html;
content_by_lua_block {
ngx.say("hello, world")
}
內容解密:
default_type text/html;:設定預設的Content-Type為text/html,這樣NGINX就會將回應的內容當作HTML處理。content_by_lua_block:這是一個NGINX的指令,用於執行Lua程式碼塊。ngx.say("hello, world"):ngx.say是Lua模組提供的一個函式,用於輸出字串到客戶端。這裡輸出的是"hello, world"。
Perl模組的使用
Perl是一種成熟的指令碼語言,NGINX的Perl模組允許開發者使用Perl語言編寫指令碼,以擴充套件NGINX的功能。下面是一個使用Perl模組的例子:
load_module modules/ngx_http_perl_module.so;
events {}
http {
perl_set $app_endpoint 'sub { return $ENV{"APP_DNS_ENDPOINT"}; }';
server {
listen 8080;
location / {
proxy_pass http://$app_endpoint
}
}
}
內容解密:
load_module modules/ngx_http_perl_module.so;:載入Perl模組。perl_set:這是一個NGINX的指令,用於設定一個變數的值,這個值是透過執行Perl程式碼獲得的。$app_endpoint:這是一個NGINX變數,它的值是透過執行Perl程式碼sub { return $ENV{"APP_DNS_ENDPOINT"}; }獲得的,這段程式碼傳回環境變數APP_DNS_ENDPOINT的值。proxy_pass http://$app_endpoint:將請求代理到$app_endpoint指定的地址。
使用Ansible安裝和組態NGINX
Ansible是一種流行的組態管理工具,可以用來安裝和組態NGINX。下面是一個使用Ansible安裝和組態NGINX的例子:
---
- hosts: all
collections:
- nginxinc.nginx_core
tasks:
- name: Install NGINX
include_role:
name: nginx
- name: Configure NGINX
ansible.builtin.include_role:
name: nginx_config
vars:
nginx_config_http_template_enable: true
nginx_config_http_template:
- template_file: http/default.conf.j2
deployment_location: /etc/nginx/conf.d/default.conf
config:
servers:
- core:
listen:
- port: 80
server_name: localhost
log:
access:
- path: /var/log/nginx/access.log
format: main
sub_filter:
sub_filters:
- string: server_hostname
replacement: $hostname
once: false
locations:
- location: /
core:
root: /usr/share/nginx/html
index: index.html
nginx_config_html_demo_template_enable: true
nginx_config_html_demo_template:
- template_file: www/index.html.j2
deployment_location: /usr/share/nginx/html/index.html
web_server_name: Ansible NGINX collection
內容解密:
hosts: all:指定Ansible要操作的主機。collections: nginxinc.nginx_core:使用NGINX官方提供的Ansible集合。include_role: name: nginx:使用nginx角色來安裝NGINX。nginx_config角色用於組態NGINX,包括設定伺服器區塊、位置區塊等。
使用Chef安裝和組態NGINX
Chef是一種流行的組態管理工具,可以用來安裝和組態NGINX。下面是一個使用Chef安裝和組態NGINX的例子:
nginx_install 'nginx' do
source 'repo'
end
nginx_config 'nginx' do
default_site_enabled true
keepalive_timeout 65
worker_processes 'auto'
action :create
notifies :reload, 'nginx_service[nginx]', :delayed
end
內容解密:
nginx_install 'nginx' do source 'repo' end:從F5維護的倉函式庫安裝NGINX。nginx_config 'nginx' do ... end:組態NGINX的基本設定,如預設站點是否啟用、keepalive超時時間、worker程式數等。
使用NGINX進行基本認證組態
問題描述
需要在應用程式或內容前增加HTTP基本認證來進行保護。
解決方案
首先,需要生成一個特定格式的檔案,其中密碼需要被加密或雜湊處理,支援的格式包括:
# 註解
使用者名稱1:密碼1
使用者名稱2:密碼2:註解
使用者名稱3:密碼3
使用者名稱是第一欄位,密碼是第二欄位,中間以冒號分隔。第三欄位是可選的,用於新增對使用者的註解。NGINX支援多種密碼格式,其中一種是使用crypt()函式加密的密碼,可以透過openssl passwd命令列工具生成。
生成加密密碼
使用openssl命令生成加密密碼:
$ openssl passwd MyPassword1234
輸出的字串可以被NGINX用於密碼檔案中。
組態NGINX基本認證
在NGINX組態中使用auth_basic和auth_basic_user_file指令來啟用基本認證:
location / {
auth_basic "私人網站";
auth_basic_user_file conf.d/passwd;
}
auth_basic指令可以放置在http、server或location上下文中,它接受一個字串引數,該引數會在使用者未認證時顯示在基本認證彈窗中。auth_basic_user_file指令指定了存放使用者帳號密碼的檔案路徑。
測試組態
使用curl命令加上-u或--user引數來測試組態:
$ curl --user myuser:MyPassword1234 https://localhost
詳細說明
有多種方法可以生成基本認證的密碼,並且支援不同的格式,提供不同程度的安全性。除了openssl命令外,Apache的htpasswd命令也可以用來生成密碼。兩者都可以生成NGINX支援的apr1演算法密碼。密碼也可以是加鹽的MD5格式。
程式碼解說
NGINX基本認證組態範例
location / {
auth_basic "私人網站";
auth_basic_user_file conf.d/passwd;
}
內容解密:
location /:此指令定義了基本認證將被套用的URL路徑。在此例中,所有請求到根目錄(/)及以下路徑都會受到基本認證保護。auth_basic "私人網站";:啟用基本認證,並在瀏覽器彈出的認證視窗中顯示「私人網站」。auth_basic_user_file conf.d/passwd;:指定存放使用者名稱和密碼的檔案位置。NGINX會讀取此檔案來驗證使用者的憑證。
實務經驗與技術深度探討
在實際應用中,基本認證是一種快速且簡單的保護機制,但它有其限制,如傳輸過程中的安全性問題等。因此,在生產環境中,可能需要考慮更安全的認證方式,如使用HTTPS加密傳輸,或者結合其他驗證機制來提升安全性。
相關資源
- NGINX官方檔案關於基本認證的說明
- 使用NGINX Plus進行JWT驗證的
使用Consul和consul-template動態組態NGINX
問題描述
需要在環境變化時自動更新NGINX組態。
解決方案
使用Consul和consul-template工具來實作NGINX組態的動態更新。
Consul範本範例
upstream backend { {{range service "app.backend"}}
server {{.Address}};{{end}}
}
這個範本會遍歷Consul中標記為app.backend的服務節點,並為每個節點生成一個server指令。
執行consul-template
$ consul-template -consul-addr consul.example.internal -template \
./upstream.template:/etc/nginx/conf.d/upstream.conf:"nginx -s reload"
這條命令指示consul-template守護程式連線到指定的Consul叢集,使用本地的upstream.template範本檔案來生成/etc/nginx/conf.d/upstream.conf組態檔案,並在組態檔案變更後重新載入NGINX。
詳細說明
Consul是一個強大的服務發現和組態儲存工具。它提供了RESTful API介面,並允許透過DNS介面查詢節點資訊。consul-template工具可以根據Consul中的變化動態地重新範本化組態檔案,使得NGINX組態能夠隨著環境變化而動態調整。
程式碼解說
Consul範本範例程式碼分析
upstream backend { {{range service "app.backend"}}
server {{.Address}};{{end}}
}
內容解密:
upstream backend { ... }:定義了一個名為backend的上游伺服器群組。{{range service "app.backend"}}:遍歷Consul中註冊為app.backend服務的所有節點。server {{.Address}};:為每個服務節點生成一個server指令,其中.Address代表節點的IP地址。{{end}}:結束遍歷迴圈。
透過這種方式,NGINX的上游伺服器組態能夠根據Consul中的服務註冊資訊動態變化,無需手動修改組態檔案。