拓冰建站拓冰建站
首页 / 资讯中心 / 正文

APISIX SSL 协议版本配置指南:按 SNI 动态控制 TLS 协议

APISIX SSL 协议版本配置指南按 SNI 动态控制 TLS 协议【免费下载链接】apisixThe Cloud-Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/ap/apisix本文以 Apache APISIX云原生 API 网关的 SSL/TLS 协议版本配置为主题系统讲解如何在config.yaml中做全局静态配置以及如何通过 SSL 资源Admin API为每个 SNIServer Name Indication域名动态指定TLSv1.1 / TLSv1.2 / TLSv1.3协议版本。读完本文你将掌握静态与动态两种配置方式的完整写法、优先级规则、底层实现原理并能用 curl 快速验证配置是否生效从而在兼容老旧客户端与保障 API 安全之间取得平衡。概述APISIX 的 TLS 协议支持方式APISIX支持 TLS 协议还支持动态的为每一个 SNI 指定不同的 TLS 协议版本。这意味着在全局层面可以通过静态配置config.yaml中apisix.ssl.ssl_protocols设定网关默认支持的 TLS 版本集合在域名层面可以通过 Admin API 为 SSL 资源设置ssl_protocols字段按 SNI 精确控制每个域名的 TLS 版本。为了安全考虑APISIX 默认使用的加密套件不支持 TLSv1.1 以及更低的版本。如果你需要启用 TLSv1.1 协议请在 config.yaml 的配置项 apisix.ssl.ssl_ciphers 增加 TLSv1.1 协议所支持的加密套件。需要特别说明的是APISIX 的 SSL 资源 schema 只允许TLSv1.1、TLSv1.2、TLSv1.3三种协议版本见下文源码分析TLSv1.0及更早版本不会被接受这从根上保证了默认安全基线。静态配置config.yaml 中的 ssl_protocols静态配置中 config.yaml 的ssl_protocols参数会作用于 APISIX 全局但是不能动态修改仅当匹配的 SSL 资源未设置ssl_protocols静态配置才会生效。apisix: ssl: ssl_protocols: TLSv1.2 TLSv1.3 # default TLSv1.2 TLSv1.3从配置默认值看APISIX 出厂即只开放TLSv1.2 TLSv1.3。在 conf/config.yaml.example 中同样可以看到该默认配置项ssl_protocols: TLSv1.2 TLSv1.3 # TLS versions supported.而 apisix/cli/config.lua 中内置的默认值也是ssl_protocols TLSv1.2 TLSv1.3。该配置属于静态配置最终会被渲染进 nginx.conf。在 apisix/cli/ngx_tpl.lua 的 nginx 配置模板中可以看到ssl_protocols {* ssl.ssl_protocols *}; ssl_ciphers {* ssl.ssl_ciphers *};即 APISIX 启动时用apisix.ssl.ssl_protocols的值替换模板占位符生成真实的ssl_protocols指令写入 nginx 配置。因此修改该值后需要重启 APISIX重新生成并加载 nginx.conf才能生效。动态配置SSL 资源中的 ssl_protocols使用 ssl 资源中ssl_protocols字段动态的为每一个 SNI 指定不同的 TLS 协议版本。指定 test.com 域名使用 TLSv1.2 TLSv1.3 协议版本{ cert: $cert, key: $key, snis: [test.com], ssl_protocols: [ TLSv1.2, TLSv1.3 ] }与静态配置不同动态配置通过 Admin API 实时下发无需重启即可生效且可以做到按 SNI 细分同一个网关实例上test.com走 TLSv1.2/1.3test2.com走 TLSv1.3互不干扰。动态配置的 schema 校验从源码看SSL 资源的ssl_protocols字段定义在 apisix/schema_def.luassl_protocols { description set ssl protocols, type array, maxItems 3, uniqueItems true, items { enum {TLSv1.1, TLSv1.2, TLSv1.3} }, },这段 schema 意味着取值只能是TLSv1.1、TLSv1.2、TLSv1.3三者之一数组元素不能重复uniqueItems true最多 3 个元素maxItems 3。任何超出该枚举的值都会被 Admin API 直接拒绝。这一点有测试用例佐证在 t/admin/ssl5.t 中尝试为ssl_protocols设置TLSv1.0时返回的校验错误为invalid configuration: property ssl_protocols validation failed: failed to validate item 1: matches none of the enum values动态配置的底层生效机制动态配置之所以能够“按 SNI 生效”是因为 APISIX 在 TLS 握手的ClientHello 阶段ssl_certificate_by_lua_block就会完成 SSL 资源的匹配与协议设置。核心调用链如下apisix/init.lua 中通过apisix_ssl.server_name(true)从 ClientHello 中提取 SNI用提取到的 SNI 执行router.router_ssl.match_and_set(api_ctx, true, sni)匹配出对应的 SSL 资源调用apisix_ssl.set_protocols_by_clienthello(ngx_ctx.matched_ssl.value.ssl_protocols)设置协议版本apisix/ssl.lua 中的_M.set_protocols_by_clienthello最终调用 OpenResty 的ngx_ssl_client.set_protocols(ssl_protocols)完成底层设置function _M.set_protocols_by_clienthello(ssl_protocols) if ssl_protocols then return ngx_ssl_client.set_protocols(ssl_protocols) end return true end也就是说ssl_protocols为空的 SSL 资源会返回true不额外设置从而回落到全局静态配置只有显式设置了ssl_protocols的 SSL 资源才会在握手早期覆盖默认协议集合。注意事项两种配置的优先级动态配置优先级比静态配置更高当 ssl 资源配置项ssl_protocols不为空时静态配置将会被覆盖。静态配置作用于全局需要重启 apisix 才能生效。动态配置可细粒度的控制每个 SNI 的 TLS 协议版本并且能够动态修改相比于静态配置更加灵活。测试用例 t/node/ssl-protocols.t 也验证了这一点当 SSL 资源不设置ssl_protocols时Admin API 返回的值中ssl_protocols为null此时客户端用 TLSv1.1 / TLSv1.2 / TLSv1.3 访问均成功——因为测试环境在 config.yaml 中静态配置了ssl_protocols: TLSv1.1 TLSv1.2 TLSv1.3。使用示例一如何指定 TLSv1.1 协议存在一些老旧的客户端仍然采用较低级别的 TLSv1.1 协议版本而新的产品则使用较高安全级别的 TLS 协议版本。如果让新产品支持 TLSv1.1 可能会带来一些安全隐患。为了保证 API 的安全性我们需要在协议版本之间进行灵活转换。例如test.com是老旧客户端所使用的域名需要将其配置为 TLSv1.1而test2.com属于新产品同时支持 TLSv1.2、TLSv1.3 协议。第 1 步config.yaml 配置全局收紧仅允许 TLSv1.3。apisix: ssl: ssl_protocols: TLSv1.3 # ssl_ciphers is for reference only ssl_ciphers: ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA注意由于 APISIX 默认加密套件不支持 TLSv1.1此处通过ssl_ciphers补充了 TLSv1.1 所需的套件如ECDHE-RSA-AES256-SHA等。该示例同时说明要让静态配置的ssl_protocols真正包含 TLSv1.1必须同步配置匹配的加密套件否则握手仍会失败。修改 config.yaml 后需重启 APISIX 使静态配置生效。第 2 步为 test.com 域名指定 TLSv1.1 协议版本动态覆盖全局配置。:::note您可以这样从config.yaml中获取admin_key并存入环境变量admin_key$(yq .deployment.admin.admin_key[0].key conf/config.yaml | sed s///g):::curl http://127.0.0.1:9180/apisix/admin/ssls/1 \ -H X-API-KEY: $admin_key -X PUT -d { cert : $(cat server.crt), key: $(cat server.key), snis: [test.com], ssl_protocols: [ TLSv1.1 ] }第 3 步为 test2.com 创建 SSL 对象未指定 TLS 协议版本将默认使用静态配置。curl http://127.0.0.1:9180/apisix/admin/ssls/1 \ -H X-API-KEY: $admin_key -X PUT -d { cert : $(cat server2.crt), key: $(cat server2.key), snis: [test2.com] }第 4 步访问验证。使用 TLSv1.3 访问 test.com 失败因为该域名被动态限制为 TLSv1.1$ curl --tls-max 1.3 --tlsv1.3 https://test.com:9443 -v -k -I * Trying 127.0.0.1:9443... * Connected to test.com (127.0.0.1) port 9443 (#0) * ALPN, offering h2 * ALPN, offering http/1.1 * successfully set certificate verify locations: * CAfile: /etc/ssl/certs/ca-certificates.crt * CApath: /etc/ssl/certs * TLSv1.3 (OUT), TLS handshake, Client hello (1): * TLSv1.3 (IN), TLS alert, protocol version (582): * error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version * Closing connection 0 curl: (35) error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version使用 TLSv1.1 访问 test.com 成功$ curl --tls-max 1.1 --tlsv1.1 https://test.com:9443 -v -k -I * Trying 127.0.0.1:9443... * Connected to test.com (127.0.0.1) port 9443 (#0) * ALPN, offering h2 * ALPN, offering http/1.1 * successfully set certificate verify locations: * CAfile: /etc/ssl/certs/ca-certificates.crt * CApath: /etc/ssl/certs * TLSv1.1 (OUT), TLS handshake, Client hello (1): * TLSv1.1 (IN), TLS handshake, Server hello (2): * TLSv1.1 (IN), TLS handshake, Certificate (11): * TLSv1.1 (IN), TLS handshake, Server key exchange (12): * TLSv1.1 (IN), TLS handshake, Server finished (14): * TLSv1.1 (OUT), TLS handshake, Client key exchange (16): * TLSv1.1 (OUT), TLS change cipher, Change cipher spec (1): * TLSv1.1 (OUT), TLS handshake, Finished (20): * TLSv1.1 (IN), TLS handshake, Finished (20): * SSL connection using TLSv1.1 / ECDHE-RSA-AES256-SHA使用 TLSv1.3 访问 test2.com 成功未设置 ssl_protocols回落到静态配置 TLSv1.3$ curl --tls-max 1.3 --tlsv1.3 https://test2.com:9443 -v -k -I * Trying 127.0.0.1:9443... * Connected to test2.com (127.0.0.1) port 9443 (#0) * ALPN, offering h2 * ALPN, offering http/1.1 * successfully set certificate verify locations: * CAfile: /etc/ssl/certs/ca-certificates.crt * CApath: /etc/ssl/certs * TLSv1.3 (OUT), TLS handshake, Client hello (1): * TLSv1.3 (IN), TLS handshake, Server hello (2): * TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8): * TLSv1.3 (IN), TLS handshake, Certificate (11): * TLSv1.3 (IN), TLS handshake, CERT verify (15): * TLSv1.3 (IN), TLS handshake, Finished (20): * TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1): * TLSv1.3 (OUT), TLS handshake, Finished (20): * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384使用 TLSv1.1 访问 test2.com 失败curl --tls-max 1.1 --tlsv1.1 https://test2.com:9443 -v -k -I * Trying 127.0.0.1:9443... * Connected to test2.com (127.0.0.1) port 9443 (#0) * ALPN, offering h2 * ALPN, offering http/1.1 * successfully set certificate verify locations: * CAfile: /etc/ssl/certs/ca-certificates.crt * CApath: /etc/ssl/certs * TLSv1.1 (OUT), TLS handshake, Client hello (1): * TLSv1.1 (IN), TLS alert, protocol version (582): * error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version * Closing connection 0 curl: (35) error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version说明--tlsv1.x为 curl 的协议版本限定参数某些新版本 curl如 8.x已将--tlsv1.1之类的独立选项移除此时可以改用--tls-max 1.1配合--tlsv1或直接使用 openssl s_client如openssl s_client -connect test.com:9443 -servername test.com -tls1_1做等价验证判定逻辑不变期望的协议版本握手成功被禁止的协议版本返回tlsv1 alert protocol version。使用示例二证书关联多个域名但域名之间使用不同的 TLS 协议有时候我们可能会遇到这样一种情况即一个证书关联了多个域名但是它们需要使用不同的 TLS 协议来保证安全性。例如test.com域名需要使用 TLSv1.2 协议而test2.com域名则需要使用 TLSv1.3 协议。在这种情况下我们不能简单地为所有的域名创建一个 SSL 对象而是需要为每个域名单独创建一个 SSL 对象并指定相应的协议版本。这样我们就可以根据不同的域名和协议版本来进行正确的 SSL 握手和加密通信。示例如下第 1 步使用证书为 test.com 创建 ssl 对象并指定 TLSv1.2 协议。curl http://127.0.0.1:9180/apisix/admin/ssls/1 \ -H X-API-KEY: $admin_key -X PUT -d { cert : $(cat server.crt), key: $(cat server.key), snis: [test.com], ssl_protocols: [ TLSv1.2 ] }第 2 步使用与 test.com 同一证书为 test2.com 创建 ssl 对象并指定 TLSv1.3 协议。curl http://127.0.0.1:9180/apisix/admin/ssls/2 \ -H X-API-KEY: $admin_key -X PUT -d { cert : $(cat server.crt), key: $(cat server.key), snis: [test2.com], ssl_protocols: [ TLSv1.3 ] }第 3 步访问验证。使用 TLSv1.2 访问 test.com 成功注意握手完成后 ALPN 协商到了 h2说明 HTTP/2 在协议匹配成功后正常工作$ curl --tls-max 1.2 --tlsv1.2 https://test.com:9443 -v -k -I * Trying 127.0.0.1:9443... * Connected to test.com (127.0.0.1) port 9443 (#0) * ALPN, offering h2 * ALPN, offering http/1.1 * successfully set certificate verify locations: * CAfile: /etc/ssl/certs/ca-certificates.crt * CApath: /etc/ssl/certs * TLSv1.2 (OUT), TLS handshake, Client hello (1): * TLSv1.2 (IN), TLS handshake, Server hello (2): * TLSv1.2 (IN), TLS handshake, Certificate (11): * TLSv1.2 (IN), TLS handshake, Server key exchange (12): * TLSv1.2 (IN), TLS handshake, Server finished (14): * TLSv1.2 (OUT), TLS handshake, Client key exchange (16): * TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1): * TLSv1.2 (OUT), TLS handshake, Finished (20): * TLSv1.2 (IN), TLS handshake, Finished (20): * SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256 * ALPN, server accepted to use h2 * Server certificate: * subject: CAU; STSome-State; OInternet Widgits Pty Ltd; CNtest.com * start date: Jul 20 15:50:08 2023 GMT * expire date: Jul 17 15:50:08 2033 GMT * issuer: CAU; STSome-State; OInternet Widgits Pty Ltd; CNtest.com * SSL certificate verify result: EE certificate key too weak (66), continuing anyway. * Using HTTP2, server supports multi-use * Connection state changed (HTTP/2 confirmed) * Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len0 * Using Stream ID: 1 (easy handle 0x5608905ee2e0) HEAD / HTTP/2 Host: test.com:9443 user-agent: curl/7.74.0 accept: */*使用 TLSv1.3 协议访问 test.com 失败$ curl --tls-max 1.3 --tlsv1.3 https://test.com:9443 -v -k -I * Trying 127.0.0.1:9443... * Connected to test.com (127.0.0.1) port 9443 (#0) * ALPN, offering h2 * ALPN, offering http/1.1 * successfully set certificate verify locations: * CAfile: /etc/ssl/certs/ca-certificates.crt * CApath: /etc/ssl/certs * TLSv1.3 (OUT), TLS handshake, Client hello (1): * TLSv1.3 (IN), TLS alert, protocol version (582): * error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version * Closing connection 0 curl: (35) error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version使用 TLSv1.3 协议访问 test2.com 成功$ curl --tls-max 1.3 --tlsv1.3 https://test2.com:9443 -v -k -I * Trying 127.0.0.1:9443... * Connected to test2.com (127.0.0.1) port 9443 (#0) * ALPN, offering h2 * ALPN, offering http/1.1 * successfully set certificate verify locations: * CAfile: /etc/ssl/certs/ca-certificates.crt * CApath: /etc/ssl/certs * TLSv1.3 (OUT), TLS handshake, Client hello (1): * TLSv1.3 (IN), TLS handshake, Server hello (2): * TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8): * TLSv1.3 (IN), TLS handshake, Certificate (11): * TLSv1.3 (IN), TLS handshake, CERT verify (15): * TLSv1.3 (IN), TLS handshake, Finished (20): * TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1): * TLSv1.3 (OUT), TLS handshake, Finished (20): * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 * ALPN, server accepted to use h2 * Server certificate: * subject: CAU; STSome-State; OInternet Widgits Pty Ltd; CNtest2.com * start date: Jul 20 16:05:47 2023 GMT * expire date: Jul 17 16:05:47 2033 GMT * issuer: CAU; STSome-State; OInternet Widgits Pty Ltd; CNtest2.com * SSL certificate verify result: EE certificate key too weak (66), continuing anyway. * Using HTTP2, server supports multi-use * Connection state changed (HTTP/2 confirmed) * Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len0 * Using Stream ID: 1 (easy handle 0x55569cbe42e0) HEAD / HTTP/2 Host: test2.com:9443 user-agent: curl/7.74.0 accept: */* * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): * old SSL session ID is stale, removing使用 TLSv1.2 协议访问 test2.com 失败$ curl --tls-max 1.2 --tlsv1.2 https://test2.com:9443 -v -k -I * Trying 127.0.0.1:9443... * Connected to test2.com (127.0.0.1) port 9443 (#0) * ALPN, offering h2 * ALPN, offering http/1.1 * successfully set certificate verify locations: * CAfile: /etc/ssl/certs/ca-certificates.crt * CApath: /etc/ssl/certs * TLSv1.2 (OUT), TLS handshake, Client hello (1): * TLSv1.2 (IN), TLS alert, protocol version (582): * error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version * Closing connection 0 curl: (35) error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version深入原理ClientHello 阶段的协议下发流程动态按 SNI 设置 TLS 协议的关键在于 APISIX 在握手早期ssl_certificate_by_lua_block就介入处理。完整流程可以概括为客户端发起 TLS 握手ClientHello 中携带 SNI如test.comAPISIX 通过ngx_ssl_client.get_client_hello_server_name()提取 SNI见 apisix/ssl.lua 中的server_name函数未携带 SNI 时可回退到apisix.ssl.fallback_sni以 SNI 为键匹配 SSL 资源router_ssl.match_and_set若命中资源的ssl_protocols非空则调用ngx_ssl_client.set_protocols在握手阶段覆盖协议版本见 apisix/init.lua 与 apisix/ssl.lua之后继续正常的证书加载与握手流程。从实现上看静态配置作用于 nginx 全局指令ssl_protocols动态配置则是在握手早期通过 lua-resty 的 ClientHello API 做更细粒度的覆盖这正是“动态配置优先级更高”的底层原因。总结与安全建议对比维度静态配置config.yaml动态配置SSL 资源 ssl_protocols作用范围APISIX 全局单个 SNI / SSL 资源修改方式改配置文件后重启Admin API 实时下发生效时机重启后立即生效优先级低被动态配置覆盖高适用场景设定全局安全基线按域名差异化放行协议版本实战建议生产环境优先采用全局收紧 按需放开的策略config.yaml 只开放TLSv1.2 TLSv1.3确需兼容老旧客户端的域名再单独创建 SSL 资源用ssl_protocols: [TLSv1.1]做定向兼容启用 TLSv1.1 时务必同步在apisix.ssl.ssl_ciphers中补充 TLSv1.1 支持的加密套件否则即使协议放行握手依然会因无匹配套件而失败同一张证书关联多个域名、但各域名协议要求不同时应为每个域名单独创建 SSL 资源并分别指定ssl_protocols而不是共用一个 SSL 对象验证手段建议同时使用 curl--tls-max限定最大版本与 openssl s_client并通过握手输出中TLSv1.x (IN), TLS alert, protocol version或SSL connection using TLSv1.x / ...判断放行与拒绝结果。若希望进一步了解 SSL 资源证书、私钥、SNI、客户端 mTLS的完整配置能力可继续阅读 apisix/schema_def.lua 中 SSL schema 的其余字段定义以及 t/node/ssl-protocols.t 与 t/admin/ssl5.t 中的完整测试用例。【免费下载链接】apisixThe Cloud-Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/ap/apisix创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门