Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apisix/schema_def.lua
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ local id_schema = {
}
}

local host_def_pat = "^\\*?[0-9a-zA-Z-._\\[\\]:]+$"
local host_def_pat = "^\\*$|^\\*?[0-9a-zA-Z-._\\[\\]:]+$"
local host_def = {
type = "string",
pattern = host_def_pat,
Expand Down
67 changes: 50 additions & 17 deletions apisix/ssl/router/radixtree_sni.lua
Original file line number Diff line number Diff line change
Expand Up @@ -55,24 +55,30 @@ local function create_router(ssl_items)
if type(ssl.value.snis) == "table" and #ssl.value.snis > 0 then
sni = core.table.new(0, #ssl.value.snis)
for _, s in ipairs(ssl.value.snis) do
j = j + 1
sni[j] = s:reverse()
if s ~= "*" then
Copy link
Member

Choose a reason for hiding this comment

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

to me, I think we can keep old code, it seems work fine too? all right?

Copy link
Contributor

Choose a reason for hiding this comment

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

The main point of contention lies in #12668 (comment), which determines whether we must handle the * case separately.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay i was wrong. Due to an initial mistake in my code I got that failure and falsely assumed it was because radixtree couldn't handle it. I have modified the PR. Apologies for mistake

j = j + 1
sni[j] = s:reverse()
end
Copy link
Contributor

Choose a reason for hiding this comment

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

Image

It is reasonable not to perform additional processing, as it does not cause any issues.
This is not necessary, as ("*"):reverse() functions correctly. Even if this extra check were added, it would only apply in extremely rare cases and would require explanation for maintainability. I do not believe it differs from normal sni case.

end
else
sni = ssl.value.sni:reverse()
if ssl.value.sni ~= "*" then
sni = ssl.value.sni:reverse()
end
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto

end

idx = idx + 1
route_items[idx] = {
paths = sni,
handler = function (api_ctx)
if not api_ctx then
return
if sni and (type(sni) == "table" and #sni > 0 or type(sni) == "string") then
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems to cover all existing scenarios. Is this necessary?

idx = idx + 1
route_items[idx] = {
paths = sni,
handler = function (api_ctx)
if not api_ctx then
return
end
api_ctx.matched_ssl = ssl
api_ctx.matched_sni = sni
end
api_ctx.matched_ssl = ssl
api_ctx.matched_sni = sni
end
}
}
end
end
end

Expand All @@ -89,7 +95,6 @@ local function create_router(ssl_items)
return router
end


local function set_pem_ssl_key(sni, cert, pkey)
local r = get_request()
if r == nil then
Expand Down Expand Up @@ -171,6 +176,33 @@ function _M.match_and_set(api_ctx, match_only, alt_sni)

local sni_rev = sni:reverse()
local ok = radixtree_router:dispatch(sni_rev, nil, api_ctx)

-- if no SSL matched, try to find a wildcard SSL
Copy link
Contributor

@bzp2010 bzp2010 Oct 14, 2025

Choose a reason for hiding this comment

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

Can't radix trees perform wildcard matching directly? Why do wildcard matches work fine in our HTTP routes (/test/*) and SSL SNI (*.example.com)?
In my view, there's no fundamental difference between *.example.com and *. If the former works correctly after reverse sort, the latter should also function out of the box. We shouldn't need to add any special logic for this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Please refer #12668 (comment)

Copy link
Contributor

@bzp2010 bzp2010 Oct 14, 2025

Choose a reason for hiding this comment

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

I saw your reply at #12668 (comment), but it doesn't resolve this concern. We should investigate the root cause.

if not ok then
for _, ssl in config_util.iterate_values(ssl_certificates.values) do
if ssl.value and ssl.value.type == "server" and
(ssl.value.status == nil or ssl.value.status == 1) then
local has_wildcard = false
if ssl.value.sni == "*" then
has_wildcard = true
elseif type(ssl.value.snis) == "table" then
for _, s in ipairs(ssl.value.snis) do
if s == "*" then
has_wildcard = true
break
end
end
end
if has_wildcard then
api_ctx.matched_ssl = ssl
api_ctx.matched_sni = "*"
ok = true
break
end
end
end
end

if not ok then
if not alt_sni then
-- it is expected that alternative SNI doesn't have a SSL certificate associated
Expand All @@ -180,8 +212,10 @@ function _M.match_and_set(api_ctx, match_only, alt_sni)
return false
end


if type(api_ctx.matched_sni) == "table" then
if api_ctx.matched_sni == "*" then
-- wildcard matches everything, no need for further validation
core.log.info("matched wildcard SSL for SNI: ", sni)
elseif type(api_ctx.matched_sni) == "table" then
local matched = false
for _, msni in ipairs(api_ctx.matched_sni) do
if sni_rev == msni or not str_find(sni_rev, ".", #msni) then
Expand Down Expand Up @@ -221,7 +255,6 @@ function _M.match_and_set(api_ctx, match_only, alt_sni)
return true
end


function _M.set(matched_ssl, sni)
if not matched_ssl then
return false, "failed to match ssl certificate"
Expand Down
Loading
Loading