Created
November 10, 2015 17:38
-
-
Save marklkelly/657de217867885891494 to your computer and use it in GitHub Desktop.
PoC security proxy with OpenResty and Redis
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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