diff --git a/apisix/cli/ngx_tpl.lua b/apisix/cli/ngx_tpl.lua index 27dbf2847a1b..ab8407b572ec 100644 --- a/apisix/cli/ngx_tpl.lua +++ b/apisix/cli/ngx_tpl.lua @@ -653,6 +653,10 @@ http { } {% if ssl.enable then %} + ssl_client_hello_by_lua_block { + apisix.http_ssl_client_hello_phase() + } + ssl_certificate_by_lua_block { apisix.http_ssl_phase() } diff --git a/apisix/init.lua b/apisix/init.lua index 5552934903e3..86b68cf62208 100644 --- a/apisix/init.lua +++ b/apisix/init.lua @@ -178,11 +178,31 @@ end function _M.http_ssl_phase() + local ok, err = router.router_ssl.set(ngx.ctx.matched_ssl) + if not ok then + if err then + core.log.error("failed to fetch ssl config: ", err) + end + ngx_exit(-1) + end +end + + +function _M.http_ssl_client_hello_phase() + local sni, err = apisix_ssl.server_name(true) + if not sni or type(sni) ~= "string" then + local advise = "please check if the client requests via IP or uses an outdated " .. + "protocol. If you need to report an issue, " .. + "provide a packet capture file of the TLS handshake." + core.log.error("failed to find SNI: " .. (err or advise)) + ngx_exit(-1) + end + local ngx_ctx = ngx.ctx local api_ctx = core.tablepool.fetch("api_ctx", 0, 32) ngx_ctx.api_ctx = api_ctx - local ok, err = router.router_ssl.match_and_set(api_ctx) + local ok, err = router.router_ssl.match_and_set(api_ctx, true, sni) ngx_ctx.matched_ssl = api_ctx.matched_ssl core.tablepool.release("api_ctx", api_ctx) @@ -192,6 +212,13 @@ function _M.http_ssl_phase() if err then core.log.error("failed to fetch ssl config: ", err) end + core.log.error("failed to match any SSL certificate by SNI: ", sni) + ngx_exit(-1) + end + + ok, err = apisix_ssl.set_protocols_by_clienthello(ngx_ctx.matched_ssl.value.ssl_protocols) + if not ok then + core.log.error("failed to set ssl protocols: ", err) ngx_exit(-1) end end diff --git a/apisix/schema_def.lua b/apisix/schema_def.lua index f1d4d97efe5d..01e0649e0a91 100644 --- a/apisix/schema_def.lua +++ b/apisix/schema_def.lua @@ -790,6 +790,15 @@ _M.ssl = { enum = {1, 0}, default = 1 }, + ssl_protocols = { + description = "set ssl protocols", + type = "array", + maxItems = 3, + uniqueItems = true, + items = { + enum = {"TLSv1.1", "TLSv1.2", "TLSv1.3"} + }, + }, validity_end = timestamp_def, validity_start = timestamp_def, create_time = timestamp_def, diff --git a/apisix/ssl.lua b/apisix/ssl.lua index 8bcdec0ffa90..e14c92fc3c41 100644 --- a/apisix/ssl.lua +++ b/apisix/ssl.lua @@ -14,9 +14,11 @@ -- See the License for the specific language governing permissions and -- limitations under the License. -- -local core = require("apisix.core") -local ngx_ssl = require("ngx.ssl") -local secret = require("apisix.secret") +local core = require("apisix.core") +local secret = require("apisix.secret") +local ngx_ssl = require("ngx.ssl") +local ngx_ssl_client = require("ngx.ssl.clienthello") + local ngx_encode_base64 = ngx.encode_base64 local ngx_decode_base64 = ngx.decode_base64 local aes = require("resty.aes") @@ -38,8 +40,13 @@ local pkey_cache = core.lrucache.new { local _M = {} -function _M.server_name() - local sni, err = ngx_ssl.server_name() +function _M.server_name(clienthello) + local sni, err + if clienthello then + sni, err = ngx_ssl_client.get_client_hello_server_name() + else + sni, err = ngx_ssl.server_name() + end if err then return nil, err end @@ -57,6 +64,14 @@ function _M.server_name() end +function _M.set_protocols_by_clienthello(ssl_protocols) + if ssl_protocols then + return ngx_ssl_client.set_protocols(ssl_protocols) + end + return true +end + + local function init_iv_tbl(ivs) local _aes_128_cbc_with_iv_tbl = core.table.new(2, 0) local type_ivs = type(ivs) diff --git a/apisix/ssl/router/radixtree_sni.lua b/apisix/ssl/router/radixtree_sni.lua index f3e249167b30..b6824852e0b3 100644 --- a/apisix/ssl/router/radixtree_sni.lua +++ b/apisix/ssl/router/radixtree_sni.lua @@ -206,13 +206,35 @@ function _M.match_and_set(api_ctx, match_only, alt_sni) end end - local matched_ssl = api_ctx.matched_ssl - core.log.info("debug - matched: ", core.json.delay_encode(matched_ssl, true)) + core.log.info("debug - matched: ", core.json.delay_encode(api_ctx.matched_ssl, true)) if match_only then return true end + ok, err = _M.set(api_ctx.matched_ssl, sni) + if not ok then + return false, err + end + + return true +end + + +function _M.set(matched_ssl, sni) + if not matched_ssl then + return false, "failed to match ssl certificate" + end + local ok, err + if not sni then + sni, err = apisix_ssl.server_name() + if type(sni) ~= "string" then + local advise = "please check if the client requests via IP or uses an outdated " .. + "protocol. If you need to report an issue, " .. + "provide a packet capture file of the TLS handshake." + return false, "failed to find SNI: " .. (err or advise) + end + end ngx_ssl.clear_certs() local new_ssl_value = secret.fetch_secrets(matched_ssl.value) or matched_ssl.value diff --git a/ci/common.sh b/ci/common.sh index ce34bd1d0ca7..2840b7d8a711 100644 --- a/ci/common.sh +++ b/ci/common.sh @@ -21,6 +21,7 @@ export_or_prefix() { export OPENRESTY_PREFIX="/usr/local/openresty-debug" export APISIX_MAIN="https://raw.githubusercontent.com/apache/incubator-apisix/master/rockspec/apisix-master-0.rockspec" export PATH=$OPENRESTY_PREFIX/nginx/sbin:$OPENRESTY_PREFIX/luajit/bin:$OPENRESTY_PREFIX/bin:$PATH + export OPENSSL111_BIN=$OPENRESTY_PREFIX/openssl111/bin/openssl } create_lua_deps() { diff --git a/ci/linux-install-openresty.sh b/ci/linux-install-openresty.sh index 3bc2d18ab35e..8d4b6f87224a 100755 --- a/ci/linux-install-openresty.sh +++ b/ci/linux-install-openresty.sh @@ -38,6 +38,13 @@ OPENSSL3_PREFIX=${OPENSSL3_PREFIX-/home/runner} SSL_LIB_VERSION=${SSL_LIB_VERSION-openssl} if [ "$OPENRESTY_VERSION" == "source" ]; then + export openssl_prefix=/usr/local/openresty/openssl111 + export zlib_prefix=/usr/local/openresty/zlib + export pcre_prefix=/usr/local/openresty/pcre + + export cc_opt="-DNGX_LUA_ABORT_AT_PANIC -I${zlib_prefix}/include -I${pcre_prefix}/include -I${openssl_prefix}/include" + export ld_opt="-L${zlib_prefix}/lib -L${pcre_prefix}/lib -L${openssl_prefix}/lib -Wl,-rpath,${zlib_prefix}/lib:${pcre_prefix}/lib:${openssl_prefix}/lib" + if [ "$COMPILE_OPENSSL3" == "yes" ]; then apt install -y build-essential git clone https://github.com/openssl/openssl @@ -72,7 +79,8 @@ if [ "$OPENRESTY_VERSION" == "source" ]; then chmod +x build-apisix-base.sh ./build-apisix-base.sh latest - sudo apt-get install openresty-openssl111-debug-dev + sudo apt-get install -y openresty-openssl111 openresty-openssl111-debug-dev libldap2-dev openresty-pcre openresty-zlib + exit 0 fi diff --git a/docs/en/latest/admin-api.md b/docs/en/latest/admin-api.md index 88add7798c91..3071a3e00f86 100644 --- a/docs/en/latest/admin-api.md +++ b/docs/en/latest/admin-api.md @@ -1194,6 +1194,7 @@ SSL resource request address: /apisix/admin/ssls/{id} | update_time | False | Auxiliary | Epoch timestamp (in seconds) of the updated time. If missing, this field will be populated automatically. | 1602883670 | | type | False | Auxiliary | Identifies the type of certificate, default `server`. | `client` Indicates that the certificate is a client certificate, which is used when APISIX accesses the upstream; `server` Indicates that the certificate is a server-side certificate, which is used by APISIX when verifying client requests. | | status | False | Auxiliary | Enables the current SSL. Set to `1` (enabled) by default. | `1` to enable, `0` to disable | +| ssl_protocols | False | An array of ssl protocols | It is used to control the SSL/TLS protocol version used between servers and clients. See [SSL Protocol](./ssl-protocol.md) for more examples. | `["TLSv1.2", "TLSv2.3"]` | Example Configuration: diff --git a/docs/en/latest/config.json b/docs/en/latest/config.json index e564dc7d0cbf..ee172c33290c 100644 --- a/docs/en/latest/config.json +++ b/docs/en/latest/config.json @@ -367,6 +367,10 @@ { "type": "doc", "id": "profile" + }, + { + "type": "doc", + "id": "ssl-protocol" } ] }, diff --git a/docs/en/latest/ssl-protocol.md b/docs/en/latest/ssl-protocol.md new file mode 100644 index 000000000000..53dbcfbc752b --- /dev/null +++ b/docs/en/latest/ssl-protocol.md @@ -0,0 +1,343 @@ +--- +title: SSL Protocol +--- + + + +`APISIX` supports set TLS protocol and also supports dynamically specifying different TLS protocol versions for each [SNI](https://en.wikipedia.org/wiki/Server_Name_Indication). + +**For security reasons, the encryption suite used by default in `APISIX` does not support TLSv1.1 and lower versions.** +**If you need to enable the TLSv1.1 protocol, please add the encryption suite supported by the TLSv1.1 protocol to the configuration item `apisix.ssl.ssl_ciphers` in `config.yaml`.** + +## ssl_protocols Configuration + +### Static Configuration + +The `ssl_protocols` parameter in the static configuration `config.yaml` applies to the entire APISIX, but cannot be dynamically modified. It only takes effect when the matching SSL resource does not set `ssl_protocols`. + +```yaml +apisix: + ssl: + ssl_protocols: TLSv1.2 TLSv1.3 # default TLSv1.2 TLSv1.3 +``` + +### Dynamic Configuration + +Use the `ssl_protocols` field in the `ssl` resource to dynamically specify different TLS protocol versions for each SNI. + +Specify the `test.com` domain uses the TLSv1.2 and TLSv1.3: + +```bash +{ + "cert": "$cert", + "key": "$key", + "snis": ["test.com"], + "ssl_protocols": [ + "TLSv1.2", + "TLSv1.3" + ] +} +``` + +### Notes + +- Dynamic configuration has a higher priority than static configuration. When the `ssl_protocols` configuration item in the ssl resource is not empty, the static configuration will be overridden. +- The static configuration applies to the entire APISIX and requires a reload of APISIX to take effect. +- Dynamic configuration can control the TLS protocol version of each SNI in a fine-grained manner and can be dynamically modified, which is more flexible than static configuration. + +## Examples + +### How to specify the TLSv1.1 protocol + +While newer products utilize higher security-level TLS protocol versions, there are still legacy clients that rely on the lower-level TLSv1.1 protocol. However, enabling TLSv1.1 for new products presents potential security risks. In order to maintain the security of the API, it is crucial to have the ability to seamlessly switch between different protocol versions based on specific requirements and circumstances. +For example, consider two domain names: `test.com`, utilized by legacy clients requiring TLSv1.1 configuration, and `test2.com`, associated with new products that support TLSv1.2 and TLSv1.3 protocols. + +1. `config.yaml` configuration. + +```yaml +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 +``` + +2. Specify the TLSv1.1 protocol version for the test.com domain. + +```bash +curl http://127.0.0.1:9180/apisix/admin/ssls/1 \ +-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "cert" : "'"$(cat server.crt)"'", + "key": "'"$(cat server.key)"'", + "snis": ["test.com"], + "ssl_protocols": [ + "TLSv1.1" + ] +}' +``` + +3. Create an SSL object for test.com without specifying the TLS protocol version, which will use the static configuration by default. + +```bash +curl http://127.0.0.1:9180/apisix/admin/ssls/1 \ +-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "cert" : "'"$(cat server2.crt)"'", + "key": "'"$(cat server2.key)"'", + "snis": ["test2.com"] +}' +``` + +4. Access Verification + +Failed, accessed test.com with TLSv1.3: + +```shell +$ 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 +``` + +Successfully, accessed test.com with TLSv1.1: + +```shell +$ 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 +``` + +Successfully, accessed test2.com with TLSv1.3: + +```shell +$ 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 +``` + +Failed, accessed test2.com with TLSv1.1: + +```shell +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 +``` + +### Certificates are associated with multiple domains, but different TLS protocols are used between domains + +Sometimes, we may encounter a situation where a certificate is associated with multiple domains, but they need to use different TLS protocols to ensure security. For example, the test.com domain needs to use the TLSv1.2 protocol, while the test2.com domain needs to use the TLSv1.3 protocol. In this case, we cannot simply create an SSL object for all domains, but need to create an SSL object for each domain separately and specify the appropriate protocol version. This way, we can perform the correct SSL handshake and encrypted communication based on different domains and protocol versions. The example is as follows: + +1. Create an SSL object for test.com using the certificate and specify the TLSv1.2 protocol. + +```bash +curl http://127.0.0.1:9180/apisix/admin/ssls/1 \ +-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "cert" : "'"$(cat server.crt)"'", + "key": "'"$(cat server.key)"'", + "snis": ["test.com"], + "ssl_protocols": [ + "TLSv1.2" + ] +}' +``` + +2. Use the same certificate as test.com to create an SSL object for test2.com and specify the TLSv1.3 protocol. + +```bash +curl http://127.0.0.1:9180/apisix/admin/ssls/2 \ +-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "cert" : "'"$(cat server.crt)"'", + "key": "'"$(cat server.key)"'", + "snis": ["test2.com"], + "ssl_protocols": [ + "TLSv1.3" + ] +}' +``` + +3. Access verification + +Successfully, accessed test.com with TLSv1.2: + +```shell +$ 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: C=AU; ST=Some-State; O=Internet Widgits Pty Ltd; CN=test.com +* start date: Jul 20 15:50:08 2023 GMT +* expire date: Jul 17 15:50:08 2033 GMT +* issuer: C=AU; ST=Some-State; O=Internet Widgits Pty Ltd; CN=test.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: len=0 +* Using Stream ID: 1 (easy handle 0x5608905ee2e0) +> HEAD / HTTP/2 +> Host: test.com:9443 +> user-agent: curl/7.74.0 +> accept: */* + +``` + +Failed, accessed test.com with TLSv1.3: + +```shell +$ 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 + +``` + +Successfully, accessed test2.com with TLSv1.3: + +```shell +$ 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: C=AU; ST=Some-State; O=Internet Widgits Pty Ltd; CN=test2.com +* start date: Jul 20 16:05:47 2023 GMT +* expire date: Jul 17 16:05:47 2033 GMT +* issuer: C=AU; ST=Some-State; O=Internet Widgits Pty Ltd; CN=test2.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: len=0 +* 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 +``` + +Failed, accessed test2.com with TLSv1.2: + +```shell +$ 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 +``` diff --git a/docs/zh/latest/admin-api.md b/docs/zh/latest/admin-api.md index 18efb19c566f..ab2625d94598 100644 --- a/docs/zh/latest/admin-api.md +++ b/docs/zh/latest/admin-api.md @@ -1202,6 +1202,7 @@ SSL 资源请求地址:/apisix/admin/ssls/{id} | update_time | 否 | 辅助 | epoch 时间戳,单位为秒。如果不指定则自动创建。 | 1602883670 | | type | 否 | 辅助 | 标识证书的类型,默认值为 `server`。 | `client` 表示证书是客户端证书,APISIX 访问上游时使用;`server` 表示证书是服务端证书,APISIX 验证客户端请求时使用。 | | status | 否 | 辅助 | 当设置为 `1` 时,启用此 SSL,默认值为 `1`。 | `1` 表示启用,`0` 表示禁用 | +| ssl_protocols | 否 | tls 协议字符串数组 | 用于控制服务器与客户端之间使用的 SSL/TLS 协议版本。更多的配置示例,请参考[SSL 协议](./ssl-protocol.md)。 | | SSL 对象 JSON 配置示例: diff --git a/docs/zh/latest/config.json b/docs/zh/latest/config.json index 8e13d3e1e058..7ac2791cad6c 100644 --- a/docs/zh/latest/config.json +++ b/docs/zh/latest/config.json @@ -301,6 +301,10 @@ { "type": "doc", "id": "profile" + }, + { + "type": "doc", + "id": "ssl-protocol" } ] }, diff --git a/docs/zh/latest/ssl-protocol.md b/docs/zh/latest/ssl-protocol.md new file mode 100644 index 000000000000..ee886a621c08 --- /dev/null +++ b/docs/zh/latest/ssl-protocol.md @@ -0,0 +1,343 @@ +--- +title: SSL 协议 +--- + + + +`APISIX` 支持,还支持动态的为每一个 SNI 指定不同的 TLS 协议版本。 + +**为了安全考虑,APISIX 默认使用的加密套件不支持 TLSv1.1 以及更低的版本。** +**如果你需要启用 TLSv1.1 协议,请在 config.yaml 的配置项 apisix.ssl.ssl_ciphers 增加 TLSv1.1 协议所支持的加密套件。** + +## ssl_protocols 配置 + +### 静态配置 + +静态配置中 config.yaml 的 ssl_protocols 参数会作用于 APISIX 全局,但是不能动态修改,仅当匹配的 SSL 资源未设置 `ssl_protocols`,静态配置才会生效。 + +```yaml +apisix: + ssl: + ssl_protocols: TLSv1.2 TLSv1.3 # default TLSv1.2 TLSv1.3 +``` + +### 动态配置 + +使用 ssl 资源中 ssl_protocols 字段动态的为每一个 SNI 指定不同的 TLS 协议版本。 + +指定 test.com 域名使用 TLSv1.2 TLSv1.3 协议版本: + +```bash +{ + "cert": "$cert", + "key": "$key", + "snis": ["test.com"], + "ssl_protocols": [ + "TLSv1.2", + "TLSv1.3" + ] +} +``` + +### 注意事项 + +- 动态配置优先级比静态配置更高,当 ssl 资源配置项 ssl_protocols 不为空时 静态配置将会被覆盖。 +- 静态配置作用于全局需要重启 apisix 才能生效。 +- 动态配置可细粒度的控制每个 SNI 的 TLS 协议版本,并且能够动态修改,相比于静态配置更加灵活。 + +## 使用示例 + +### 如何指定 TLSv1.1 协议 + +存在一些老旧的客户端,仍然采用较低级别的 TLSv1.1 协议版本,而新的产品则使用较高安全级别的 TLS 协议版本。如果让新产品支持 TLSv1.1 可能会带来一些安全隐患。为了保证 API 的安全性,我们需要在协议版本之间进行灵活转换。 +例如:test.com 是老旧客户端所使用的域名,需要将其配置为 TLSv1.1 而 test2.com 属于新产品,同时支持了 TLSv1.2,TLSv1.3 协议。 + +1. config.yaml 配置。 + +```yaml +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 +``` + +2. 为 test.com 域名指定 TLSv1.1 协议版本。 + +```bash +curl http://127.0.0.1:9180/apisix/admin/ssls/1 \ +-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "cert" : "'"$(cat server.crt)"'", + "key": "'"$(cat server.key)"'", + "snis": ["test.com"], + "ssl_protocols": [ + "TLSv1.1" + ] +}' +``` + +3. 为 test.com 创建 SSL 对象,未指定 TLS 协议版本,将默认使用静态配置。 + +```bash +curl http://127.0.0.1:9180/apisix/admin/ssls/1 \ +-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "cert" : "'"$(cat server2.crt)"'", + "key": "'"$(cat server2.key)"'", + "snis": ["test2.com"] +}' +``` + +4. 访问验证 + +使用 TLSv1.3 访问 test.com 失败: + +```shell +$ 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 成功: + +```shell +$ 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 成功: + +```shell +$ 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.3 访问 test2.com 失败: + +```shell +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 +``` + +### 证书关联多个域名,但域名之间使用不同的 TLS 协议 + +有时候,我们可能会遇到这样一种情况,即一个证书关联了多个域名,但是它们需要使用不同的 TLS 协议来保证安全性。例如 test.com 域名需要使用 TlSv1.2 协议,而 test2.com 域名则需要使用 TLSv1.3 协议。在这种情况下,我们不能简单地为所有的域名创建一个 SSL 对象,而是需要为每个域名单独创建一个 SSL 对象,并指定相应的协议版本。这样,我们就可以根据不同的域名和协议版本来进行正确的 SSL 握手和加密通信。示例如下: + +1. 使用证书为 test.com 创建 ssl 对象,并指定 TLSv1.2 协议。 + +```bash +curl http://127.0.0.1:9180/apisix/admin/ssls/1 \ +-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -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 协议。 + +```bash +curl http://127.0.0.1:9180/apisix/admin/ssls/2 \ +-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "cert" : "'"$(cat server.crt)"'", + "key": "'"$(cat server.key)"'", + "snis": ["test2.com"], + "ssl_protocols": [ + "TLSv1.3" + ] +}' +``` + +3. 访问验证 + +使用 TLSv1.2 访问 test.com 成功: + +```shell +$ 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: C=AU; ST=Some-State; O=Internet Widgits Pty Ltd; CN=test.com +* start date: Jul 20 15:50:08 2023 GMT +* expire date: Jul 17 15:50:08 2033 GMT +* issuer: C=AU; ST=Some-State; O=Internet Widgits Pty Ltd; CN=test.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: len=0 +* 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 失败: + +```shell +$ 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 成功: + +```shell +$ 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: C=AU; ST=Some-State; O=Internet Widgits Pty Ltd; CN=test2.com +* start date: Jul 20 16:05:47 2023 GMT +* expire date: Jul 17 16:05:47 2033 GMT +* issuer: C=AU; ST=Some-State; O=Internet Widgits Pty Ltd; CN=test2.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: len=0 +* 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 失败: + +```shell +$ 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 +``` diff --git a/t/APISIX.pm b/t/APISIX.pm index 92e58a7ba265..0738f3ecffd7 100644 --- a/t/APISIX.pm +++ b/t/APISIX.pm @@ -267,6 +267,7 @@ env ENABLE_ETCD_AUTH; env APISIX_PROFILE; env PATH; # for searching external plugin runner's binary env TEST_NGINX_HTML_DIR; +env OPENSSL111_BIN; _EOC_ @@ -712,6 +713,12 @@ _EOC_ ssl_certificate_key cert/apisix.key; lua_ssl_trusted_certificate cert/apisix.crt; + ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3; + + ssl_client_hello_by_lua_block { + apisix.http_ssl_client_hello_phase() + } + ssl_certificate_by_lua_block { apisix.http_ssl_phase() } diff --git a/t/admin/ssl5.t b/t/admin/ssl5.t new file mode 100644 index 000000000000..c9bd7b162133 --- /dev/null +++ b/t/admin/ssl5.t @@ -0,0 +1,86 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +use t::APISIX 'no_plan'; + +no_root_location(); + +run_tests; + +__DATA__ + +=== TEST 1: Not supported set TLSv1.0 for ssl_protocols +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + local t = require("lib.test_admin") + + local ssl_cert = t.read_file("t/certs/apisix.crt") + local ssl_key = t.read_file("t/certs/apisix.key") + local data = {cert = ssl_cert, key = ssl_key, sni = "test.com", ssl_protocols = {"TLSv1.0", "TLSv1.2"}} + + local code, body = t.test('/apisix/admin/ssls/1', + ngx.HTTP_PUT, + core.json.encode(data), + [[{ + "key": "/apisix/ssls/1" + }]] + ) + + ngx.status = code + ngx.print(body) + } + } +--- request +GET /t +--- error_code: 400 +--- response_body +{"error_msg":"invalid configuration: property \"ssl_protocols\" validation failed: failed to validate item 1: matches none of the enum values"} + + + +=== TEST 2: The default value for the ssl_protocols is null +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + local t = require("lib.test_admin") + + local ssl_cert = t.read_file("t/certs/apisix.crt") + local ssl_key = t.read_file("t/certs/apisix.key") + local data = {cert = ssl_cert, key = ssl_key, sni = "test.com"} + + local code, body = t.test('/apisix/admin/ssls/1', + ngx.HTTP_PUT, + core.json.encode(data), + [[{ + "value": { + "sni": "test.com", + "ssl_protocols": null, + }, + "key": "/apisix/ssls/1" + }]] + ) + + ngx.status = code + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed diff --git a/t/node/ssl-protocols.t b/t/node/ssl-protocols.t new file mode 100644 index 000000000000..76fba7ff06ea --- /dev/null +++ b/t/node/ssl-protocols.t @@ -0,0 +1,298 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +use t::APISIX; + +repeat_each(1); +log_level('info'); +no_root_location(); +no_shuffle(); + +my $openssl_bin = $ENV{OPENSSL111_BIN}; +if (! -x $openssl_bin) { + $ENV{OPENSSL111_BIN} = '/usr/local/openresty/openssl111/bin/openssl'; + if (! -x $ENV{OPENSSL111_BIN}) { + plan(skip_all => "openssl111 not installed"); + } +} + +plan('no_plan'); + +add_block_preprocessor(sub { + my ($block) = @_; + + my $yaml_config = $block->yaml_config // <<_EOC_; +deployment: + role: traditional + role_traditional: + config_provider: etcd + admin: + admin_key: null +apisix: + node_listen: 1984 + proxy_mode: http&stream + stream_proxy: + tcp: + - 9100 + enable_resolv_search_opt: false + ssl: + ssl_protocols: TLSv1.1 TLSv1.2 TLSv1.3 + 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 +_EOC_ + + $block->set_value("yaml_config", $yaml_config); +}); + +run_tests(); + +__DATA__ + +=== TEST 1: set route +--- config +location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uris": ["/hello", "/world"] + }]] + ) + if code >= 300 then + ngx.status = code + ngx.say(message) + return + end + ngx.say(body) + } +} +--- request +GET /t +--- response_body +passed + + + +=== TEST 2: create ssl for test.com (unset ssl_protocols) +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + local t = require("lib.test_admin") + + local ssl_cert = t.read_file("t/certs/apisix.crt") + local ssl_key = t.read_file("t/certs/apisix.key") + local data = {cert = ssl_cert, key = ssl_key, sni = "test.com"} + + local code, body = t.test('/apisix/admin/ssls/1', + ngx.HTTP_PUT, + core.json.encode(data), + [[{ + "value": { + "sni": "test.com", + "ssl_protocols": null, + }, + "key": "/apisix/ssls/1" + }]] + ) + + ngx.status = code + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 3: Successfully, access test.com with TLSv1.3 +--- exec +echo -n "Q" | $OPENSSL111_BIN s_client -connect 127.0.0.1:1994 -servername test.com -tls1_3 2>&1 | cat +--- response_body eval +qr/Server certificate/ + + + +=== TEST 4: Successfully, access test.com with TLSv1.2 +--- exec +curl -k -v --tls-max 1.2 --tlsv1.2 --resolve "test.com:1994:127.0.0.1" https://test.com:1994/hello 2>&1 | cat +--- response_body eval +qr/TLSv1\.2 \(IN\), TLS handshake, Server hello(?s).*hello world/ + + + +=== TEST 5: Successfully, access test.com with TLSv1.1 +--- exec +echo -n "Q" | $OPENSSL111_BIN s_client -connect 127.0.0.1:1994 -servername test.com -tls1_1 2>&1 | cat +--- response_body eval +qr/Server certificate/ + + + +=== TEST 6: set TLSv1.2 and TLSv1.3 for test.com +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + local t = require("lib.test_admin") + + local ssl_cert = t.read_file("t/certs/apisix.crt") + local ssl_key = t.read_file("t/certs/apisix.key") + local data = {cert = ssl_cert, key = ssl_key, sni = "test.com", ssl_protocols = {"TLSv1.2", "TLSv1.3"}} + + local code, body = t.test('/apisix/admin/ssls/1', + ngx.HTTP_PUT, + core.json.encode(data), + [[{ + "value": { + "sni": "test.com", + "ssl_protocols": ["TLSv1.2", "TLSv1.3"], + }, + "key": "/apisix/ssls/1" + }]] + ) + + ngx.status = code + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 7: Set TLSv1.3 for the test2.com +--- config +location /t { + content_by_lua_block { + local core = require("apisix.core") + local t = require("lib.test_admin") + + local ssl_cert = t.read_file("t/certs/test2.crt") + local ssl_key = t.read_file("t/certs/test2.key") + local data = {cert = ssl_cert, key = ssl_key, sni = "test2.com", ssl_protocols = {"TLSv1.3"}} + + local code, body = t.test('/apisix/admin/ssls/2', + ngx.HTTP_PUT, + core.json.encode(data), + [[{ + "value": { + "sni": "test2.com" + }, + "key": "/apisix/ssls/2" + }]] + ) + + ngx.status = code + ngx.say(body) + } +} +--- response_body +passed +--- request +GET /t + + + +=== TEST 8: Successfully, access test.com with TLSv1.3 +--- exec +echo -n "Q" | $OPENSSL111_BIN s_client -connect 127.0.0.1:1994 -servername test.com -tls1_3 2>&1 | cat +--- response_body eval +qr/Server certificate/ + + + +=== TEST 9: Successfully, access test.com with TLSv1.2 +--- exec +curl -k -v --tls-max 1.2 --tlsv1.2 --resolve "test.com:1994:127.0.0.1" https://test.com:1994/hello 2>&1 | cat +--- response_body eval +qr/TLSv1\.2 \(IN\), TLS handshake, Server hello(?s).*hello world/ + + + +=== TEST 10: Successfully, access test2.com with TLSv1.3 +--- exec +echo -n "Q" | $OPENSSL111_BIN s_client -connect 127.0.0.1:1994 -servername test2.com -tls1_3 2>&1 | cat +--- response_body eval +qr/Server certificate/ + + + +=== TEST 11: Failed, access test2.com with TLSv1.2 +--- exec +curl -k -v --tls-max 1.2 --tlsv1.2 --resolve "test2.com:1994:127.0.0.1" https://test2.com:1994/hello 2>&1 | cat +--- response_body eval +qr/TLSv1\.2 \(IN\), TLS alert/ + + + +=== TEST 12: set TLSv1.1 for test.com +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + local t = require("lib.test_admin") + + local ssl_cert = t.read_file("t/certs/apisix.crt") + local ssl_key = t.read_file("t/certs/apisix.key") + local data = {cert = ssl_cert, key = ssl_key, sni = "test.com", ssl_protocols = {"TLSv1.1"}} + + local code, body = t.test('/apisix/admin/ssls/1', + ngx.HTTP_PUT, + core.json.encode(data), + [[{ + "value": { + "sni": "test.com", + "ssl_protocols": ["TLSv1.1"], + }, + "key": "/apisix/ssls/1" + }]] + ) + + ngx.status = code + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 13: Successfully, access test.com with TLSv1.1 +--- exec +echo -n "Q" | $OPENSSL111_BIN s_client -connect 127.0.0.1:1994 -servername test.com -tls1_1 2>&1 | cat +--- response_body eval +qr/Server certificate/ + + + +=== TEST 14: Failed, access test.com with TLSv1.3 +--- exec +echo -n "Q" | $OPENSSL111_BIN s_client -connect 127.0.0.1:1994 -servername test.com -tls1_3 2>&1 | cat +--- response_body eval +qr/tlsv1 alert/ diff --git a/t/router/radixtree-sni.t b/t/router/radixtree-sni.t index 9a28e3d78f78..781bf28d9b95 100644 --- a/t/router/radixtree-sni.t +++ b/t/router/radixtree-sni.t @@ -202,7 +202,7 @@ location /t { connected: 1 failed to do SSL handshake: handshake failed --- error_log -failed to find any SSL certificate by SNI +failed to match any SSL certificate by SNI diff --git a/t/router/radixtree-sni2.t b/t/router/radixtree-sni2.t index c64a20aae344..c761c9043d45 100644 --- a/t/router/radixtree-sni2.t +++ b/t/router/radixtree-sni2.t @@ -405,7 +405,7 @@ location /t { --- response_body failed to do SSL handshake: handshake failed --- error_log -failed to fetch ssl config: failed to find SNI: please check if the client requests via IP or uses an outdated protocol +failed to find SNI: please check if the client requests via IP or uses an outdated protocol --- no_error_log [alert]