Skip to content

Commit

Permalink
Closes #379
Browse files Browse the repository at this point in the history
  • Loading branch information
subnetmarco committed Jul 10, 2015
1 parent 5e94856 commit ac5504b
Show file tree
Hide file tree
Showing 13 changed files with 964 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .luacheckrc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ files["kong/vendor/resty_http.lua"] = {
unused = false
}

files["kong/vendor/resty-lrucache/lib/resty/"] = {
global = false,
unused = false
}

files["spec/"] = {
globals = {"describe", "it", "before_each", "setup", "after_each", "teardown", "stub", "mock", "spy", "finally", "pending"}
}
7 changes: 7 additions & 0 deletions kong-0.4.0-1.rockspec
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ dependencies = {
"lua-cjson ~> 2.1.0-1",
"ansicolors ~> 1.0.2-3",
"lbase64 ~> 20120820-1",
"lua-resty-iputils ~> 0.1.0-1",

"luasocket ~> 2.0.2-5",
"lrexlib-pcre ~> 2.7.2-1",
Expand All @@ -39,6 +40,7 @@ build = {
["lapp"] = "kong/vendor/lapp.lua",
["ngx.ssl"] = "kong/vendor/ssl.lua",
["resty_http"] = "kong/vendor/resty_http.lua",
["resty.lrucache"] = "kong/vendor/resty-lrucache/lib/resty/lrucache.lua",

["kong.constants"] = "kong/constants.lua",

Expand Down Expand Up @@ -160,6 +162,11 @@ build = {
["kong.plugins.ssl.ssl_util"] = "kong/plugins/ssl/ssl_util.lua",
["kong.plugins.ssl.schema"] = "kong/plugins/ssl/schema.lua",

["kong.plugins.ip-restriction.handler"] = "kong/plugins/ip-restriction/handler.lua",
["kong.plugins.ip-restriction.init_worker"] = "kong/plugins/ip-restriction/init_worker.lua",
["kong.plugins.ip-restriction.access"] = "kong/plugins/ip-restriction/access.lua",
["kong.plugins.ip-restriction.schema"] = "kong/plugins/ip-restriction/schema.lua",

["kong.api.app"] = "kong/api/app.lua",
["kong.api.crud_helpers"] = "kong/api/crud_helpers.lua",
["kong.api.routes.kong"] = "kong/api/routes/kong.lua",
Expand Down
1 change: 1 addition & 0 deletions kong.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ plugins_available:
- response_transformer
- requestsizelimiting
- analytics
- ip-restriction

## The Kong working directory
## (Make sure you have read and write permissions)
Expand Down
2 changes: 1 addition & 1 deletion kong/dao/cassandra/base_dao.lua
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ end
-- the other should be preserved. Of course this only applies in partial update.
local function fix_tables(t, old_t, schema)
for k, v in pairs(schema.fields) do
if v.schema then
if t[k] ~= nil and v.schema then
local s = type(v.schema) == "function" and v.schema(t) or v.schema
for s_k, s_v in pairs(s.fields) do
if not t[k][s_k] and old_t[k] then
Expand Down
2 changes: 1 addition & 1 deletion kong/dao/schemas_validation.lua
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ function _M.validate_entity(t, schema, options)
is_valid_type = bool == "true" or bool == "false"
t[column] = bool == "true"
elseif v.type == "array" then
t[column] = stringy.split(t[column], ",")
t[column] = stringy.strip(t[column]) == "" and {} or stringy.split(t[column], ",") -- Handling empty arrays
for arr_k, arr_v in ipairs(t[column]) do
t[column][arr_k] = stringy.strip(arr_v)
end
Expand Down
30 changes: 30 additions & 0 deletions kong/plugins/ip-restriction/access.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
local iputils = require "resty.iputils"
local responses = require "kong.tools.responses"
local utils = require "kong.tools.utils"

local _M = {}

function _M.execute(conf)
local block = false

if conf.blacklist and utils.table_size(conf.blacklist) > 0 then
if iputils.ip_in_cidrs(ngx.var.remote_addr, conf._blacklist_cache) then
block = true
end
end

if conf.whitelist and utils.table_size(conf.whitelist) > 0 then
if iputils.ip_in_cidrs(ngx.var.remote_addr, conf._whitelist_cache) then
block = false
else
block = true
end
end

if block then
ngx.ctx.stop_phases = true -- interrupt other phases of this request
return responses.send_HTTP_FORBIDDEN("Your IP address is not allowed")
end
end

return _M
23 changes: 23 additions & 0 deletions kong/plugins/ip-restriction/handler.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
local BasePlugin = require "kong.plugins.base_plugin"
local init_worker = require "kong.plugins.ip-restriction.init_worker"
local access = require "kong.plugins.ip-restriction.access"

local IpRestrictionHandler = BasePlugin:extend()

function IpRestrictionHandler:new()
IpRestrictionHandler.super.new(self, "ip-restriction")
end

function IpRestrictionHandler:init_worker()
IpRestrictionHandler.super.init_worker(self)
init_worker.execute()
end

function IpRestrictionHandler:access(conf)
IpRestrictionHandler.super.access(self)
access.execute(conf)
end

IpRestrictionHandler.PRIORITY = 990

return IpRestrictionHandler
9 changes: 9 additions & 0 deletions kong/plugins/ip-restriction/init_worker.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
local iputils = require "resty.iputils"

local _M = {}

function _M.execute()
iputils.enable_lrucache()
end

return _M
26 changes: 26 additions & 0 deletions kong/plugins/ip-restriction/schema.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
local iputils = require "resty.iputils"

local function validate_ips(v, t, column)
local new_fields
if v and type(v) == "table" then
for _, ip in ipairs(v) do
local _, err = iputils.parse_cidr(ip)
if type(err) == "string" then -- It's an error only if the second variable is a string
return false, err
end
end
new_fields = { ["_"..column.."_cache"] = iputils.parse_cidrs(v) }
end
return true, nil, new_fields
end

return {
fields = {
whitelist = { type = "array", func = validate_ips },
blacklist = { type = "array", func = validate_ips },

-- Internal use
_whitelist_cache = { type = "array" },
_blacklist_cache = { type = "array" }
}
}
229 changes: 229 additions & 0 deletions kong/vendor/resty-lrucache/lib/resty/lrucache.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
-- Copyright (C) Yichun Zhang (agentzh)


local ffi = require "ffi"
local ffi_new = ffi.new
local ffi_sizeof = ffi.sizeof
local ffi_cast = ffi.cast
local ffi_fill = ffi.fill
local ngx_now = ngx.now
local uintptr_t = ffi.typeof("uintptr_t")
local setmetatable = setmetatable
local tonumber = tonumber


-- queue data types
--
-- this queue is a double-ended queue and the first node
-- is reserved for the queue itself.
-- the implementation is mostly borrowed from nginx's ngx_queue_t data
-- structure.

ffi.cdef[[
typedef struct lrucache_queue_s lrucache_queue_t;
struct lrucache_queue_s {
double expire; /* in seconds */
lrucache_queue_t *prev;
lrucache_queue_t *next;
};
]]

local queue_arr_type = ffi.typeof("lrucache_queue_t[?]")
local queue_ptr_type = ffi.typeof("lrucache_queue_t*")
local queue_type = ffi.typeof("lrucache_queue_t")
local NULL = ffi.null


-- queue utility functions

local function queue_insert_tail(h, x)
local last = h[0].prev
x.prev = last
last.next = x
x.next = h
h[0].prev = x
end


local function queue_init(size)
if not size then
size = 0
end
local q = ffi_new(queue_arr_type, size + 1)
ffi_fill(q, ffi_sizeof(queue_type, size + 1), 0)

if size == 0 then
q[0].prev = q
q[0].next = q

else
local prev = q[0]
for i = 1, size do
local e = q[i]
prev.next = e
e.prev = prev
prev = e
end

local last = q[size]
last.next = q
q[0].prev = last
end

return q
end


local function queue_is_empty(q)
-- print("q: ", tostring(q), "q.prev: ", tostring(q), ": ", q == q.prev)
return q == q[0].prev
end


local function queue_remove(x)
local prev = x.prev
local next = x.next

next.prev = prev
prev.next = next

-- for debugging purpose only:
x.prev = NULL
x.next = NULL
end


local function queue_insert_head(h, x)
x.next = h[0].next
x.next.prev = x
x.prev = h
h[0].next = x
end


local function queue_last(h)
return h[0].prev
end


local function queue_head(h)
return h[0].next
end


-- true module stuffs

local _M = {
_VERSION = '0.04'
}
local mt = { __index = _M }


local function ptr2num(ptr)
return tonumber(ffi_cast(uintptr_t, ptr))
end


function _M.new(size)
if size < 1 then
return nil, "size too small"
end

local self = {
keys = {},
hasht = {},
free_queue = queue_init(size),
cache_queue = queue_init(),
key2node = {},
node2key = {},
}
return setmetatable(self, mt)
end


function _M.get(self, key)
local hasht = self.hasht
local val = hasht[key]
if not val then
return nil
end

local node = self.key2node[key]

-- print(key, ": moving node ", tostring(node), " to cache queue head")
local cache_queue = self.cache_queue
queue_remove(node)
queue_insert_head(cache_queue, node)

if node.expire >= 0 and node.expire < ngx_now() then
-- print("expired: ", node.expire, " > ", ngx_now())
return nil, val
end
return val
end


function _M.delete(self, key)
self.hasht[key] = nil

local key2node = self.key2node
local node = key2node[key]

if not node then
return false
end

key2node[key] = nil
self.node2key[ptr2num(node)] = nil

queue_remove(node)
queue_insert_tail(self.free_queue, node)
return true
end


function _M.set(self, key, value, ttl)
local hasht = self.hasht
hasht[key] = value

local key2node = self.key2node
local node = key2node[key]
if not node then
local free_queue = self.free_queue
local node2key = self.node2key

if queue_is_empty(free_queue) then
-- evict the least recently used key
-- assert(not queue_is_empty(self.cache_queue))
node = queue_last(self.cache_queue)

local oldkey = node2key[ptr2num(node)]
-- print(key, ": evicting oldkey: ", oldkey, ", oldnode: ",
-- tostring(node))
if oldkey then
hasht[oldkey] = nil
key2node[oldkey] = nil
end

else
-- take a free queue node
node = queue_head(free_queue)
-- print(key, ": get a new free node: ", tostring(node))
end

node2key[ptr2num(node)] = key
key2node[key] = node
end

queue_remove(node)
queue_insert_head(self.cache_queue, node)

if ttl then
node.expire = ngx_now() + ttl
else
node.expire = -1
end
end


return _M
Loading

0 comments on commit ac5504b

Please sign in to comment.