Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support host level dynamic setting of tls protocol version #9903

Merged
merged 14 commits into from
Aug 4, 2023
Merged
4 changes: 4 additions & 0 deletions apisix/cli/ngx_tpl.lua
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,10 @@ http {
}

{% if ssl.enable then %}
ssl_client_hello_by_lua_block {
apisix.http_ssl_protocols_phase()
}

ssl_certificate_by_lua_block {
apisix.http_ssl_phase()
}
Expand Down
29 changes: 27 additions & 2 deletions apisix/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -178,11 +178,30 @@ 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

AlinsRan marked this conversation as resolved.
Show resolved Hide resolved
function _M.http_ssl_protocols_phase()
AlinsRan marked this conversation as resolved.
Show resolved Hide resolved
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)
Expand All @@ -192,10 +211,16 @@ 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
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
monkeyDluffy6017 marked this conversation as resolved.
Show resolved Hide resolved

local function stash_ngx_ctx()
local ref = ctxdump.stash_ngx_ctx()
Expand Down
9 changes: 9 additions & 0 deletions apisix/schema_def.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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"}
AlinsRan marked this conversation as resolved.
Show resolved Hide resolved
},
},
validity_end = timestamp_def,
validity_start = timestamp_def,
create_time = timestamp_def,
Expand Down
25 changes: 20 additions & 5 deletions apisix/ssl.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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
Expand All @@ -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
AlinsRan marked this conversation as resolved.
Show resolved Hide resolved


local function init_iv_tbl(ivs)
local _aes_128_cbc_with_iv_tbl = core.table.new(2, 0)
local type_ivs = type(ivs)
Expand Down
26 changes: 24 additions & 2 deletions apisix/ssl/router/radixtree_sni.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unused var ok here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line 242 has.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, err = _M.set_cert_and_key(sni, new_ssl_value)

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
Expand Down
1 change: 1 addition & 0 deletions ci/common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
7 changes: 7 additions & 0 deletions ci/linux-install-openresty.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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=$OPENRESTY_PREFIX/openssl111
export zlib_prefix=$OPENRESTY_PREFIX/zlib
export pcre_prefix=$OPENRESTY_PREFIX/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
Expand Down
1 change: 1 addition & 0 deletions docs/en/latest/admin-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
4 changes: 4 additions & 0 deletions docs/en/latest/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,10 @@
{
"type": "doc",
"id": "profile"
},
{
"type": "doc",
"id": "ssl-protocol"
}
]
},
Expand Down
Loading