OpenResty之ngx.ssl


翻譯自: ngx.ssl - Lua API for controlling NGINX downstream SSL handshakes

1. 概要

# 注意:如果你使用的是 OpenResty 1.9.7.2+,則不需要該行
lua_package_path "/path/to/lua-resty-core/lib/?.lua;;";

server {
    listen 443 ssl;
    server_name test.com;
    
    # useless placeholders: just to shut up NGINX configuration
    # loder errors:
    ssl_certificate /path/to/fallback.crt;
    ssl_certificate_key /path/to/fallback.key;
    
    ssl_certificate_by_lua_block {
        local ssl = require "ngx.ssl"
        
        -- clear the fallback certificates and private keys
        -- set by the ssl_certificate and ssl_certificate_key
        -- directives above:
        local ok, err = ssl.clear_certs()
        if not ok then
            ngx.log(ngx.ERR, "failed to clear existing (fallback) certificates")
            return ngx.exit(ngx.ERROR)
        end
        
        -- assuming the user already the my_load_certificate_chain()
        -- herself.
        local pem_cert_chain = assert(my_load_certifiate_chain())
        
        local der_cert_chain, err = ssl.cert_pem_to_der(pem_cert_chain)
        if not der_cert_chain then
            ngx.log(ngx.ERR, "failed to convert certificate chain ",
                    "from PEM to DER: ", err)
            return ngx.exit(ngx.ERROR)
        end
        
        local ok, err = ssl.set_der_cert(der_cert_chain)
        if not ok then
            ngx.log(ngx.ERR, "failed to set DER cert: ", err)
            return ngx.exit(ngx.ERROR)
        end
        
        -- assuming the user already defined the my_load_private_key()
        -- function herself.
        local pem_pkey = assert(my_load_private_key())
        
        local der_pkey, err = ssl.priv_key_pem_to_der(pem_pkey)
        if not der_pkey then
            ngx.log(ngx.ERR, "failed to convert private key ",
                    "from PEM to DER: ", err)
            return ngx.exit(ngx.ERROR)
        end
        
        local ok, err = ssl.ser_der_priv_key(der_pkey)
        if not ok then
            ngx.log(ngx.ERR, "failed to set DER private key: ", err)
            return ngx.exit(ngx.ERROR)
        end
    }
    
    location / {
        root html;
    }
}

2. 描述

該 Lua 模塊提供 API 函數來控制類似 ssl_certificate_by_lua*(ngx_lua 模塊) 等上下文的 SSL 握手過程。

OpenSSL 允許我們動態地設置證書和私鑰,因此期望可以在建立連接前才設置證書和私鑰,這樣,可以結合 SNI,針對不同的請求域名動態設置不同的證書和私鑰,而無需事先把可能用到的證書和私鑰都准備好。該 lua 模塊提供的 API 以在 ssl_certificate_by_lua* 指令的上下文中支持此種情況。

在 Lua 中加載 ngx.ssl 模塊,因如下:

local ssl = require "ngx.ssl"

3. 方法

3.1 clear_certs

語法:ok, err = ssl.clear_certs()
上下文:ssl_certificate_by_lua*
  • 清除在當前 SSL 連接中設置的任何已經存在的 SSL 證書和私鑰。
  • 成功返回 true,失敗返回一個 nil 值並且通過字符串描述錯誤。

3.2 cert_pem_to_der

語法:der_cert_chain, err = ssl.cert_pem_to_der(pem_cert_chain)
上下文:任意
  • 將 PEM 格式的 SSL 證書鏈數據轉換為 DER 格式(轉換后的 der 格式數據將用於 set_der_cert 函數)。
  • 失敗,則返回 nil 值,並且通過字符串描述錯誤。
  • openssl 命令行工具可能無法正確將 SSL 證書鏈從 PEM 格式轉換為 DER,因此總是使用該 Lua 函數進行轉換。你可以總是使用類似 lua-resty-lrucache 或者 ngx_lua API(如 lua_shared_dict)等庫來緩存 DER 格式的結果。
  • 該函數可以在任何上下文中使用。

3.3 set_der_cert

語法:ok, err = ssl.set_der_cert(der_cert_chain)
上下文:ssl_certificate_by_lua*
  • 為當前的 SSL 連接設置 DER 格式的 SSL 證書鏈數據。注意,該 DER 數據是直接保存在 Lua 字符串參數中的。不需要外部文件支持。
  • 若成功返回 true,否則返回 nil 並且通過字符串描述錯誤。
  • 注意,SSL 證書鏈通常以 PEM 格式編碼,因此首先需要使用 cert_perm_to_der 函數進行轉換。

3.4 priv_key_pem_to_der

語法:der_priv_key, err = ssl.priv_key_perm_to_der(perm_priv_key)
上下文:任意
  • 將 PEM 格式的 SSL 私鑰數據轉換為 DER 格式(用於 set_der_priv_key 函數)。
  • 失敗,返回 nil 並通過字符串描述錯誤。
  • 可選地,你可以使用 openssl 命令行工具脫機下執行 PEM 轉換為 DER,如下:
openssl rsa -in key.pem -outform DER -out key.der
  • 該函數可以在任意上下文中調用。

3.5 set_der_priv_key

語法:ok, err = ssl.set_der_priv_key(der_priv_key)
上下文:ssl_certificate_by_lua*
  • 為當前 SSL 連接設置 DER 格式的私鑰。
  • 成功返回 true,失敗返回 nil 並通過字符串描述錯誤。
  • 通常,私鑰是以 PEM 格式編碼的。可以使用 priv_key_perm_to_der 函數或者使用 openssl 命令行工具脫機將 PEM 轉換為 DER,如下:
openssl rsa -in key.pem -outform DER -out key.der

3.6 server_name

語法:name, err = ssl.server_name()
上下文:任意
  • 返回由客戶端設置的 TLS SNI(Server Name Indication) 名。若客戶端沒有設置則返回 nil。
  • 失敗,返回 nil 並通過字符串描述錯誤。
  • 通常我們使用 SNI 名作為域名(如 www.openresty.org)來標識當前的 web 站點,同時為當該網站加載相應的 SSL 證書鏈和私鑰。
  • 注意,並不是所有的 https 客戶端都設置 SNI 名,因此當從客戶端握手請求中缺失 SNI 名時,我們可以使用客戶端訪問的服務器 IP 地址來標識該網站。有關更多詳細信息,參閱 raw_server_addr 方法。
  • 可以在下游 https 的任何上下文中調用該函數。

3.7 raw_server_addr

語法:addr_data, addr_type, err = ssl.raw_server_addr()
上下文:任意
  • 返回當前 SSL 連接中客戶端實際訪問的原始服務器地址。
  • 前兩個返回值分別表示地址數據和地址類型的字符串,根據地址類型值對地址值進行不同的解釋:
    • unix: 地址數據是 UNIX 域套接字的文件路徑。
    • inet:地址數據是 4 字節長的二進制 IPv4 地址。
    • inet6:地址數據時 16 字節長的二進制 IPv6 地址。
  • 失敗返回 nil,並通過字符串描述錯誤。
  • 以下代碼顯示如何將 UNIX 域套接字和 IPv4 地址打印為人類可讀的字符串:
local ssl = require "ngx.ssl"
local byte = string.byte

local addr, addrtyp, err = ssl.raw_server_addr()
if not addr then
    ngx.log(ngx.ERR, "failed to fetch raw server addr: ", err)
    return
end

if addrtyp == "inet" then  -- IPv4
    ip = string.format("%d.%d.%d.%d", byte(addr, 1), byte(addr, 2),
                       byte(addr, 3), byte(addr, 4))
    print("Using IPv4 address: ", ip)

elseif addrtyp == "unix" then  -- UNIX
    print("Using unix socket file ", addr)

else  -- IPv6
    -- leave as an exercise for the readers
end
  • 可以在下游 https 的任何上下文中調用該函數。

3.8 raw_client_addr

語法:addr_data, addr_type, err = ssl.raw_client_addr()
上下文:任意


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM