Skip to content

Commit

Permalink
fix(wasm): validate filters in all persistence modes (#11288)
Browse files Browse the repository at this point in the history
The primary purpose of this change is to ensure that each .filters[].name
is validated against available/installed filters in dbless mode (this was
only working in db mode prior).

I also added an additional entity check which will return a more descriptive
error when wasm is disabled or no filters are present (this is more useful
for dbless mode than anything).
  • Loading branch information
flrgh authored Jul 28, 2023
1 parent 1ec4d73 commit 33dbd4f
Show file tree
Hide file tree
Showing 15 changed files with 409 additions and 163 deletions.
1 change: 0 additions & 1 deletion kong-3.5.0-0.rockspec
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,6 @@ build = {
["kong.db.schema"] = "kong/db/schema/init.lua",
["kong.db.dao.keys"] = "kong/db/dao/keys.lua",
["kong.db.dao.key_sets"] = "kong/db/dao/key_sets.lua",
["kong.db.dao.filter_chains"] = "kong/db/dao/filter_chains.lua",
["kong.db.schema.entities.keys"] = "kong/db/schema/entities/keys.lua",
["kong.db.schema.entities.key_sets"] = "kong/db/schema/entities/key_sets.lua",
["kong.db.schema.entities.consumers"] = "kong/db/schema/entities/consumers.lua",
Expand Down
5 changes: 3 additions & 2 deletions kong/clustering/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ local pl_tablex = require("pl.tablex")
local clustering_utils = require("kong.clustering.utils")
local events = require("kong.clustering.events")
local clustering_tls = require("kong.clustering.tls")
local wasm = require("kong.runloop.wasm")


local assert = assert
Expand Down Expand Up @@ -102,8 +103,8 @@ function _M:init_worker()
end, plugins_list)

local filters = {}
if kong.db.filter_chains.filters then
for _, filter in ipairs(kong.db.filter_chains.filters) do
if wasm.enabled() and wasm.filters then
for _, filter in ipairs(wasm.filters) do
filters[filter.name] = { name = filter.name }
end
end
Expand Down
108 changes: 0 additions & 108 deletions kong/db/dao/filter_chains.lua

This file was deleted.

24 changes: 22 additions & 2 deletions kong/db/schema/entities/filter_chains.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
local typedefs = require "kong.db.schema.typedefs"
local wasm = require "kong.runloop.wasm"


---@class kong.db.schema.entities.filter_chain : table
---
Expand All @@ -20,10 +22,12 @@ local typedefs = require "kong.db.schema.typedefs"
---@field enabled boolean
---@field config string|table|nil


local filter = {
type = "record",
fields = {
{ name = { type = "string", required = true, }, },
{ name = { type = "string", required = true, one_of = wasm.filter_names,
err = "no such filter", }, },
{ config = { type = "string", required = false, }, },
{ enabled = { type = "boolean", default = true, required = true, }, },
},
Expand All @@ -37,7 +41,6 @@ return {
admin_api_name = "filter-chains",
generate_admin_api = true,
workspaceable = true,
dao = "kong.db.dao.filter_chains",
cache_key = { "route", "service" },

fields = {
Expand Down Expand Up @@ -65,5 +68,22 @@ return {
"route",
}
},

-- This check is for user experience and is not strictly necessary to
-- validate filter chain input.
--
-- The `one_of` check on `filters[].name` already covers validation, but in
-- the case where wasm is disabled or no filters are installed, this check
-- adds an additional entity-level error (e.g. "wasm support is not enabled"
-- or "no wasm filters are available").
--
-- All of the wasm API routes are disabled when wasm is also disabled, so
-- this primarily serves the dbless `/config` endpoint.
{ custom_entity_check = {
field_sources = { "filters" },
run_with_invalid_fields = true,
fn = wasm.status,
},
},
},
}
5 changes: 4 additions & 1 deletion kong/db/schema/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1221,7 +1221,10 @@ local function run_entity_check(self, name, input, arg, full_check, errors)
end

-- Don't run check if any of its fields has errors
if not all_ok and not checker.run_with_invalid_fields then
if not all_ok
and not checker.run_with_invalid_fields
and not arg.run_with_invalid_fields
then
return
end

Expand Down
1 change: 1 addition & 0 deletions kong/db/schema/metaschema.lua
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ local entity_checkers = {
{ field_sources = { type = "array", elements = { type = "string" } } },
{ fn = { type = "function" } },
{ run_with_missing_fields = { type = "boolean" } },
{ run_with_invalid_fields = { type = "boolean" } },
}
}
},
Expand Down
2 changes: 0 additions & 2 deletions kong/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -628,8 +628,6 @@ function Kong.init()
-- Load plugins as late as possible so that everything is set up
assert(db.plugins:load_plugin_schemas(config.loaded_plugins))

assert(db.filter_chains:load_filters(config.wasm_modules_parsed))

if is_stream_module then
stream_api.load_handlers()
end
Expand Down
117 changes: 101 additions & 16 deletions kong/runloop/wasm.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,28 @@
local _M = {}
local _M = {
-- these filter lookup tables are created once and then reset/re-used when
-- `wasm.init()` is called. This means other modules are permitted to stash
-- a reference to them, which helps to avoid several chicken/egg dependency
-- ordering issues.

---@type kong.configuration.wasm_filter[]
filters = {},

---@type table<string, kong.configuration.wasm_filter>
filters_by_name = {},

---@type string[]
filter_names = {},
}


--- This represents a wasm module discovered by the conf_loader in
--- `kong.configuration.wasm_filters_path`
---
---@class kong.configuration.wasm_filter
---
---@field name string
---@field path string


local utils = require "kong.tools.utils"
local dns = require "kong.tools.dns"
Expand Down Expand Up @@ -46,8 +70,13 @@ local TYPE_SERVICE = 0
local TYPE_ROUTE = 1
local TYPE_COMBINED = 2

local STATUS_DISABLED = "wasm support is not enabled"
local STATUS_NO_FILTERS = "no wasm filters are available"
local STATUS_ENABLED = "wasm support is enabled"

local ENABLED = false
local STATUS = STATUS_DISABLED


local hash_chain
do
Expand Down Expand Up @@ -513,33 +542,79 @@ local function get_filter_chain_for_request(route, service)
end


---@param filters kong.configuration.wasm_filter[]|nil
local function set_available_filters(filters)
clear_tab(_M.filters)
clear_tab(_M.filters_by_name)
clear_tab(_M.filter_names)

if filters then
for i, filter in ipairs(filters) do
_M.filters[i] = filter
_M.filters_by_name[filter.name] = filter
_M.filter_names[i] = filter.name
end
end
end


---@param reason string
local function disable(reason)
set_available_filters(nil)

_G.dns_client = nil

ENABLED = false
STATUS = reason or STATUS_DISABLED
end


local function enable(kong_config)
set_available_filters(kong_config.wasm_modules_parsed)

-- setup a DNS client for ngx_wasm_module
_G.dns_client = _G.dns_client or dns(kong_config)

proxy_wasm = proxy_wasm or require "resty.wasmx.proxy_wasm"
ATTACH_OPTS.isolation = proxy_wasm.isolations.FILTER

ENABLED = true
STATUS = STATUS_ENABLED
end


_M.get_version = get_version

_M.update_in_place = update_in_place

_M.set_state = set_state

function _M.enable(filters)
enable({
wasm = true,
wasm_modules_parsed = filters,
})
end

---@param kong_config table
function _M.init(kong_config)
if not kong_config.wasm then
return
end
_M.disable = disable

local modules = kong_config.wasm_modules_parsed
if not modules or #modules == 0 then
return
end

reports.add_immutable_value("wasm_cnt", #modules)
---@param kong_config table
function _M.init(kong_config)
if kong_config.wasm then
local filters = kong_config.wasm_modules_parsed

-- setup a DNS client for ngx_wasm_module
_G.dns_client = dns(kong_config)
if filters and #filters > 0 then
reports.add_immutable_value("wasm_cnt", #filters)
enable(kong_config)

proxy_wasm = require "resty.wasmx.proxy_wasm"
ENABLED = true
else
disable(STATUS_NO_FILTERS)
end

ATTACH_OPTS.isolation = proxy_wasm.isolations.FILTER
else
disable(STATUS_DISABLED)
end
end


Expand Down Expand Up @@ -653,4 +728,14 @@ function _M.enabled()
end


---@return boolean? ok
---@return string? error
function _M.status()
if not ENABLED then
return nil, STATUS
end

return true
end

return _M
Loading

1 comment on commit 33dbd4f

@khcp-gha-bot
Copy link

Choose a reason for hiding this comment

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

Bazel Build

Docker image available kong/kong:33dbd4f17c713ea0a2500f14901b0d30673bed28
Artifacts available https://github.com/Kong/kong/actions/runs/5695370992

Please sign in to comment.