diff --git a/confs/global/init-lua.conf b/confs/global/init-lua.conf index d765985..e6f035e 100644 --- a/confs/global/init-lua.conf +++ b/confs/global/init-lua.conf @@ -3,6 +3,7 @@ init_by_lua_block { local dataloader = require "dataloader" local logger = require "logger" local cjson = require "cjson" +local remoteapi = require "remoteapi" local use_redis = {% if USE_REDIS == "yes" %}true{% else %}false{% endif +%} @@ -12,6 +13,8 @@ local use_tor_exit_nodes = {% if has_value("BLOCK_TOR_EXIT_NODE", "yes") %}true{ local use_user_agents = {% if has_value("BLOCK_USER_AGENT", "yes") %}true{% else %}false{% endif +%} local use_referrers = {% if has_value("BLOCK_REFERRER", "yes") %}true{% else %}false{% endif +%} +local use_remote_api = {% if has_value("USE_REMOTE_API", "yes") %}true{% else %}false{% endif +%} + if not use_redis then if use_proxies then dataloader.load_ip("/etc/nginx/proxies.list", ngx.shared.proxies_data) @@ -72,4 +75,44 @@ for dir in p:lines() do end p:close() +-- Remote API +if use_remote_api then + + -- Save server + ngx.shared.remote_api:set("server", "{{ REMOTE_API_SERVER }}", 0) + + -- Save version + local f = io.open("/opt/bunkerized-nginx/VERSION", "r") + ngx.shared.remote_api:set("version", f:read("*all"), 0) + f:close() + + -- Save and ask a machine ID if needed + local f = io.open("/opt/bunkerized-nginx/cache/machine.id", "rw") + if f == nil then + local res, id = remoteapi.register() + if not res then + logger.log(ngx.ERR, "REMOTE API", "Can't register to the remote API") + else + logger.log(ngx.ERR, "REMOTE API", "Successfully registered to the remote API") + f:write(data) + ngx.shared.remote_api:set("id", data, 0) + end + else + logger.log(ngx.ERR, "REMOTE API", "*NOT AN ERROR* Using existing machine ID from cache") + id = f:read("*all") + end + f:close() + + -- Test the machine ID + if id ~= nil then + local res, pong = remoteapi.ping() + if not res or pong ~= "pong" then + logger.log(ngx.ERR, "REMOTE API", "Ping failed, the remote server may be down or your machine ID is invalid") + else + logger.log(ngx.ERR, "REMOTE API", "*NOT AN ERROR* Ping successful") + end + end + +end + } diff --git a/confs/global/nginx.conf b/confs/global/nginx.conf index 20d8548..84dfbcb 100644 --- a/confs/global/nginx.conf +++ b/confs/global/nginx.conf @@ -91,6 +91,7 @@ http { {% if has_value("USE_BAD_BEHAVIOR", "yes") %}lua_shared_dict behavior_ban 10m;{% endif +%} {% if has_value("USE_BAD_BEHAVIOR", "yes") %}lua_shared_dict behavior_count 10m;{% endif +%} lua_shared_dict plugins_data 10m; + {% if has_value("USE_REMOTE_API", "yes") %}lua_shared_dict remote_api 1m;{% endif +%} # shared memory zone for limit_req {% if has_value("USE_LIMIT_REQ", "yes") %}limit_req_zone $binary_remote_addr$uri zone=limit:{{ LIMIT_REQ_CACHE }} rate={{ LIMIT_REQ_RATE }};{% endif +%} diff --git a/confs/site/log-lua.conf b/confs/site/log-lua.conf index 33a5bc7..470d655 100644 --- a/confs/site/log-lua.conf +++ b/confs/site/log-lua.conf @@ -1,5 +1,7 @@ log_by_lua_block { +local logger = require "logger" + -- bad behavior local use_bad_behavior = {% if USE_BAD_BEHAVIOR == "yes" %}true{% else %}false{% endif +%} local behavior = require "behavior" @@ -12,4 +14,20 @@ if use_bad_behavior then behavior.count(bad_behavior_status_codes, bad_behavior_threshold, bad_behavior_count_time, bad_behavior_ban_time) end +-- remote API +local use_remote_api = {% if USE_REMOTE_API == "yes" %}true{% else %}false{% endif +%} +local remoteapi = require "remoteapi" + +if use_remote_api then + if ngx.status == ngx.HTTP_FORBIDDEN then + -- TODO check if IP is global + good reason + local res, data = remoteapi.ip(ngx.var.remote_addr, "other") + if res then + logger.log(ngx.NOTICE, "REMOTE API", "Successfully reported ip " .. ngx.var.remote_addr) + else + logger.log(ngx.ERR, "REMOTE API", "Error while reporting ip " .. ngx.var.remote_addr .. " : " .. data) + end + end +end + } diff --git a/helpers/install.sh b/helpers/install.sh index 9c0e6aa..3e86f8f 100755 --- a/helpers/install.sh +++ b/helpers/install.sh @@ -749,6 +749,10 @@ fi echo "[*] Copy bunkerized-nginx" do_and_check_cmd cp /tmp/bunkerized-nginx/helpers/bunkerized-nginx /usr/local/bin +# Copy VERSION +echo "[*] Copy VERSION" +do_and_check_cmd cp /tmp/bunkerized-nginx/VERSION /opt/bunkerized-nginx + # Replace old nginx.service file if [ "$OS" != "alpine" ] ; then do_and_check_cmd mv /lib/systemd/system/nginx.service /lib/systemd/system/nginx.service.bak diff --git a/lua/remoteapi.lua b/lua/remoteapi.lua new file mode 100644 index 0000000..164571c --- /dev/null +++ b/lua/remoteapi.lua @@ -0,0 +1,81 @@ +local M = {} +local http = require "resty.http" +local cjson = require "cjson" +local logger = require "logger" + +function M.send(method, url, data) + local httpc, err = http.new() + if not httpc then + logger.log(ngx.ERR, "REMOTE API", "Can't instantiate HTTP object : " .. err) + return false, nil, nil + end + local res, err = httpc:request_uri(ngx.shared.remote_api:get("server") .. url, { + method = method, + body = cjson.encode(data), + headers = { + ["Content-Type"] = "application/json", + ["User-Agent"] = "bunkerized-nginx/" .. data["version"] + } + }) + if not res then + logger.log(ngx.ERR, "REMOTE API", "Can't send HTTP request : " .. err) + return false, nil, nil + end + if res.status ~= 200 then + logger.log(ngx.WARN, "REMOTE API", "Received status " .. res.status .. " from API : " .. res.body) + end + return true, res.status, cjson.decode(res.body)["data"] +end + +function M.gen_data(use_id, data) + local all_data = {} + if use_id then + all_data["id"] = ngx.shared.remote_api:get("id") + end + all_data["version"] = ngx.shared.remote_api:get("version") + for k, v in pairs(data) do + all_data[k] = v + end + return all_data +end + +function M.register() + local request = {} + local res, status, data = M.send("POST", "/register", M.gen_data(false, request)) + if res and status == 200 then + return true, data + end + return false, data +end + +function M.ping() + local request = {} + local res, status, data = M.send("GET", "/ping", M.gen_data(true, request)) + if res and status == 200 then + return true, data + end + return false, data +end + +function M.ip(ip, reason) + local request = { + ["ip"] = ip, + ["reason"] = reason + } + local res, status, data = M.send("POST", "/ip", M.gen_data(true, request)) + if res and status == 200 then + return true, data + end + return false, data +end + +function M.db() + local request = {} + local res, status, data = M.send("GET", "/db", M.gen_data(true, request)) + if res and status == 200 then + return true, data + end + return false, data +end + +return M diff --git a/settings.json b/settings.json index fbb4e5c..e2570ec 100644 --- a/settings.json +++ b/settings.json @@ -1274,6 +1274,24 @@ "regex": "^([A-Za-z0-9\\-\\.\\_]+|.{0})$", "type": "text" } + { + "context": "multisite", + "default": "no", + "env": "USE_REMOTE_API", + "id": "use-remote-api", + "label": "Use a remote service for enhanced security", + "regex": "^(yes|no)$", + "type": "checkbox" + }, + { + "context": "global", + "default": "", + "env": "REMOTE_API_SERVER", + "id": "remote-api-server", + "label": "The URL of the remote service", + "regex": "^.*$", + "type": "text" + } ] }, "nginx": {