Skip to content

Instantly share code, notes, and snippets.

@marklkelly
Created November 10, 2015 17:38
Show Gist options
  • Save marklkelly/657de217867885891494 to your computer and use it in GitHub Desktop.
Save marklkelly/657de217867885891494 to your computer and use it in GitHub Desktop.
PoC security proxy with OpenResty and Redis
function strsplit(inputstr, sep)
if sep == nil then
sep = "%s"
end
local t={} ; i=1
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
t[i] = str
i = i + 1
end
return t
end
function log(message)
ngx.log(ngx.ERR, message)
--ngx.say(message)
end
function get_config()
local config = {}
config["target_search_service"] = "searchg2.crownpeak.net"
config["redis_addr"] = "127.0.0.1"
config["redis_port"] = "6379"
config["redis_timeout"] = "1000"
config["access_any_ip"] = "0.0.0.0"
return config
end
function check_ip(remote_ip, allowed_ip)
log("Checking IP. Remote: " .. remote_ip .. " permitted: " .. allowed_ip)
if not (allowed_ip == "0.0.0.0") then
local ip_list = strsplit(allowed_ip, ",")
for k,ip in pairs(ip_list) do
log("Permitted IP: " .. ip)
if op == operation then
log("Matching Permitted IP found.")
return true
end
end
log("IP not permitted.")
ngx.exit(ngx.HTTP_FORBIDDEN)
end
end
function process_error(httpErrorCode, description)
ngx.log(ngx.ERR, description, err)
return ngx.exit(httpErrorCode)
end
-- Need to consider how much information we give away here.
function validate_request_params(request)
local isValid = true
if not request.index then
isValid = false
local message = "Index not specified"
log (message)
return message
end
end
function get_index_data(index_name, config)
local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(config.redis_timeout)
local ok, err = red:connect(config.redis_addr, config.redis_port)
if not ok then
log("REDIS: Failed to connect to redis: " .. err)
return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end
local index_entry, err = red:hmget(index_name, "private", "public", "ip_permitted", "ops_permitted")
if not index_entry then
log("REDIS: Failed to get redis key: " .. err)
return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end
-- Extract table values. This is really just to provide some meaningful labels to access each value.
local results = {}
results["private_key"] = index_entry[1]
results["public_key"] = index_entry[2]
results["ip_permitted"] = index_entry[3]
results["ops_permitted"] = index_entry[4]
return results
end
function is_permitted_operation(operation_list, operation)
if not (operation_list) then
log("no permitted operation list found." )
return true
end
log("operation list: " .. operation_list)
log("requested operation: " .. operation)
log("type(operation_list): " .. type(operation_list))
local ops = strsplit(operation_list, ",")
log("type(ops): " .. type(ops))
for k,op in pairs(ops) do
log("Permitted operation: " .. op)
if op == operation then
log("Matching Permitted operation found.")
return true
end
end
log("Matching Permitted operation not found.")
return false
end
function authorise_request(index_data, request)
-- Check for public access
if request.access_key == index_data.private_key then
-- Check operation
if not is_permitted_operation(index_data.ops_permitted, request.operation) then
log("FAILED: Private")
ngx.exit(ngx.HTTP_FORBIDDEN)
end
log("PASSED: Private ")
return config.target_search_service .. "/" .. request.index .. "/" .. request.operation .. "/?" .. request.args
elseif request.access_key == index_data.public_key then
if not (request.operation == "select") then
log("FAILED: Public")
ngx.exit(ngx.HTTP_FORBIDDEN)
end
log("PASSED: Public ")
return config.target_search_service .. "/" .. request.index .. "/select/?" .. request.args
else
log("FAILED: Key not recognised")
ngx.exit(ngx.HTTP_FORBIDDEN)
end
end
log("Lets do this!")
request = {}
request["index"] = ngx.var.index
request["access_key"] = ngx.var.key
request["operation"] = ngx.var.operation
request["remote_ip"] = ngx.var.remote_addr
request["args"] = ngx.var.args
-- Validate input.
local errors = validate_request_params(request)
if errors then
log(errors)
return ngx.exit(ngx.HTTP_BAD_REQUEST)
end
--Get configurataion, retireve data for requested index, and check for IP based restrictions.
config = get_config()
index_data = get_index_data(request.index, config)
check_ip(request.remote_ip, index_data.ip_permitted)
log("request_ip: " .. request.remote_ip)
log("index_data.ip_permitted: " .. index_data.ip_permitted)
log("access_any_ip value: " .. config.access_any_ip)
log("index_data.private_key: " .. index_data.private_key)
log("request_operation: " .. request.operation)
log("index: " .. request.index)
target = authorise_request(index_data, request)
if target then
log("target: " .. target)
ngx.var.target = target
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment