Skip to content

Instantly share code, notes, and snippets.

@vqiu
Forked from agix/Knocky.lua
Created October 28, 2018 13:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vqiu/d62b054bb40e31944ce33a34302e50ee to your computer and use it in GitHub Desktop.
Save vqiu/d62b054bb40e31944ce33a34302e50ee to your computer and use it in GitHub Desktop.
Nginx + redis + lua + authy script to allow access by IP with one touch
-- apt-get install nginx-extras # to get nginx with lua support
-- apt-get install luarocks
-- apt-get install lua-nginx-redis
-- luarocks install lua-requests
-- In your nginx config file add two lua dict outside server definition
--
-- lua_shared_dict ip_whitelist 1m;
-- lua_shared_dict ip_asklist 1m;
-- server {
--
-- Then in the location you want to protect
--
-- location / {
-- access_by_lua_file /etc/nginx/lua-scripts/knocky.lua;
-- proxy_pass http://localhost:5984;
-- }
--
local requests = require("requests")
local redis = require "nginx.redis";
local authy = "AAAAAAAAAAAAAAAAAAAAAAAAA"
local user = "XXXXXXX"
local service_name = "ZZZZZZZ"
local allowed_time = 60*15
local ipinfo = "BBBBBBBBBBBBBB"
local redis_host = "127.0.0.1"
local redis_port = 6379
local redis_db = 10
local redis_connection_timeout = 100
local red = redis:new();
local cache_ttl = 1
local ip = ngx.var.remote_addr
function askAuthy(approval_request)
local headers = {['X-Authy-API-Key'] = authy}
local raw_response = requests.get{"https://api.authy.com/onetouch/json/approval_requests/"..approval_request, headers = headers}
local response, error = raw_response.json()
if response.success then
if response.approval_request.status == "approved" then
red:setex("white_"..ip, allowed_time, true)
return true
else
ngx.log(ngx.ERR, approval_request.. " is "..response.approval_request.status)
end
else
ngx.log(ngx.ERR, "Error contacting authy : "..raw_response.text);
end
return false
end
function updateList(list_type)
local ip_list;
if list_type == "white" then
ip_list = ngx.shared.ip_whitelist;
else
ip_list = ngx.shared.ip_asklist;
end
local last_update_time = ip_list:get("last_update_time");
if last_update_time == nil or last_update_time < ( ngx.now() - cache_ttl ) then
local name_len = string.len(list_type) + 2;
local new_ip_list, err = red:keys(list_type .. "_*");
if err then
ngx.log(ngx.ERR, "Redis read error while retrieving ip_list: " .. err);
else
ip_list:flush_all();
for index, potential_ip in ipairs(new_ip_list) do
ip_list:set(string.sub(potential_ip, name_len), true);
end
ip_list:set("last_update_time", ngx.now());
end
end
return ip_list
end
red:set_timeout(redis_connection_timeout);
local ok, err = red:connect(redis_host, redis_port);
if not ok then
ngx.log(ngx.ERR, "Redis connection error: " .. err);
return ngx.exit(ngx.HTTP_NOT_FOUND);
end
local ok, err = red:select(redis_db)
if not ok then
ngx.log(ngx.ERR, "Redis select error: " .. err);
red:quit()
return ngx.exit(ngx.HTTP_NOT_FOUND);
end
local ip_whitelist = updateList("white")
if not(ip_whitelist:get(ip)) then
local ip_asklist = updateList("ask")
if not(ip_asklist:get(ip)) then
local query_parameters = {token = ipinfo}
local raw_response_ipinfo = requests.get{"https://ipinfo.io/"..ip, params = query_parameters, headers = {['Accept'] = 'application/json'}}
ngx.log(ngx.ERR, raw_response_ipinfo.text)
local response_ipinfo, error = raw_response_ipinfo.json()
local data = {message = "Would you open "..service_name.." for "..ip.." ("..response_ipinfo.city..", "..response_ipinfo.region..", "..response_ipinfo.country..")", seconds_to_expire = allowed_time}
local headers = {['Content-Type'] = 'application/json', ['X-Authy-API-Key'] = authy}
local raw_response = requests.post{"https://api.authy.com/onetouch/json/users/"..user.."/approval_requests", data = data, headers = headers}
local response, error = raw_response.json()
if response.success then
red:setex("ask_"..ip, allowed_time, response.approval_request.uuid)
local attempt = 0
local max_attempt = 10
while attempt < max_attempt do
os.execute("sleep 1")
attempt = attempt + 1
if askAuthy(response.approval_request.uuid) then
red:quit()
return
end
end
else
ngx.log(ngx.ERR, "Error contacting authy : "..raw_response.text);
end
else
local approval_request, err = red:get("ask_"..ip)
if err then
ngx.log(ngx.ERR, "Redis read error while retrieving asked_ip: " .. err);
else
if askAuthy(approval_request) then
red:quit()
return
end
end
end
red:quit()
return ngx.exit(ngx.HTTP_NOT_FOUND);
else
red:quit()
return
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment