OpenResty之ngx.ssl

本文介绍了一个Lua模块,提供了API来控制NGINX下游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()
上下文:任意

转载于:https://www.cnblogs.com/jimodetiantang/p/9260698.html

<think>嗯,用户遇到了编译OpenResty时出现的链接错误,具体是未定义的引用`SSL_get_client_random`。首先,我需要理解这个错误的原因。 看起来问题出在链接阶段,编译器找不到`SSL_get_client_random`函数的实现。这个函数应该是OpenSSL库的一部分。可能的情况是使用的OpenSSL版本不兼容或者编译时链接的库路径不正确。 用户提供的编译命令中有多个`-L`选项指向luajit的库路径,但可能缺少OpenSSL的库路径。另外,检查是否链接了正确的OpenSSL库文件(比如`-lssl -lcrypto`),这些是否指向了正确版本的OpenSSL。例如,如果系统安装了多个OpenSSL版本,可能需要指定具体路径。 另外,`SSL_get_client_random`函数是在哪个版本的OpenSSL中引入的?查阅资料发现,这个函数在OpenSSL 1.1.1及以上版本中存在。如果用户使用的OpenSSL版本过旧,可能没有这个函数,导致链接错误。因此,需要确认系统中安装的OpenSSL版本是否符合要求。 还有,用户是否在编译时正确配置了OpenSSL的路径?比如在OpenResty的configure步骤中,是否通过`--with-openssl`指定了正确的源码目录或安装路径。如果OpenSSL安装在非标准位置,可能需要添加相应的`-I`和`-L`选项。 此外,检查编译命令中的链接顺序是否正确。有时候库的顺序会影响解析符号的过程,可能需要将`-lssl`和`-lcrypto`放在依赖它们的对象文件之后。但用户的命令中已经包含了这些选项,可能需要确认顺序是否正确。 可能的解决方案包括:1. 确保安装了正确版本的OpenSSL(1.1.1或更新);2. 在编译时通过`-L`指定OpenSSL库的正确路径;3. 检查configure参数是否正确;4. 确认没有库路径或版本冲突。 另外,用户可能在自定义构建时没有正确指向OpenSSL的位置,或者构建环境变量如`LD_LIBRARY_PATH`没有设置正确。或者,在编译OpenResty时,需要指定OpenSSL的静态库而非动态库,反之亦然。 总结下来,用户需要检查OpenSSL的版本和安装路径,确保编译时正确链接,并调整configure参数以包含必要的库路径和版本。</think>在编译OpenResty时出现的链接错误是由于缺少OpenSSL相关符号`SSL_get_client_random`导致的。以下分步骤分析和解决该问题: --- ### **1. 错误原因分析** 错误信息表明: ```bash 对‘SSL_get_client_random’未定义的引用 ``` 该函数属于OpenSSL库,且首次出现在 **OpenSSL 1.1.1** 及以上版本。可能原因: - 系统安装的OpenSSL版本过低(<1.1.1) - 编译时未正确链接OpenSSL库路径 - OpenResty配置未指向正确的OpenSSL版本 --- ### **2. 验证OpenSSL版本** 执行以下命令检查OpenSSL版本: ```bash openssl version ``` 若输出为`OpenSSL 1.1.1`或更高版本,则版本符合要求。否则需升级OpenSSL。 --- ### **3. 解决方案** #### **情况1:系统OpenSSL版本过低** 需手动安装OpenSSL 1.1.1+,例如: ```bash # 下载源码 wget https://www.openssl.org/source/openssl-1.1.1w.tar.gz tar -xzf openssl-1.1.1w.tar.gz cd openssl-1.1.1w # 编译安装到自定义目录(如/opt/openssl-1.1.1w) ./config --prefix=/opt/openssl-1.1.1w --openssldir=/opt/openssl-1.1.1w make && make install ``` --- #### **情况2:配置OpenResty时未正确指定OpenSSL路径** 在编译OpenResty时,需通过`--with-openssl`参数指定OpenSSL源码路径: ```bash ./configure \ --prefix=/usr/local/openresty \ --with-openssl=/path/to/openssl-1.1.1w \ --with-pcre \ --with-pcre-jit \ # 其他参数... ``` --- #### **情况3:修复链接器路径** 在您的原始编译命令中,缺少OpenSSL库路径。需添加: ```bash -L/path/to/openssl/lib -I/path/to/openssl/include ``` 例如: ```bash gcc ... \ -L/opt/openssl-1.1.1w/lib \ -I/opt/openssl-1.1.1w/include \ -lssl -lcrypto \ ... ``` --- ### **4. 完整修复流程** 1. **安装OpenSSL 1.1.1+**(参考步骤3) 2. **重新配置OpenResty**: ```bash ./configure \ --prefix=/usr/local/openresty \ --with-openssl=/path/to/openssl-1.1.1w \ --with-pcre \ --with-pcre-jit \ # 其他必要参数 ``` 3. **重新编译**: ```bash make clean && make -j$(nproc) && make install ``` --- ### **5. 补充说明** - 如果使用动态库,确保运行时链接路径正确(通过`LD_LIBRARY_PATH`或`ldconfig`) - 检查是否有多版本OpenSSL冲突(使用`ldd /path/to/nginx`验证链接的库) 通过上述步骤应能解决`SSL_get_client_random`未定义引用的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值