require "apache2" local redis = require "redis" local regex = require "rex_pcre" local mime = require "mime" local cipher = require "openssl.cipher" -- -------------------------------------------------------------------------- -- try to connect to redis -- local redis_conn = nil function _decrypt(message_and_iv, key) if message_and_iv:len() < 32 then return "" end iv, _ = message_and_iv:sub(1, 16) message, _ = message_and_iv:sub(17, -1) local c = cipher.new("aes-256-cbc") c:decrypt(key, iv) cleartext, err = c:final(message) if err ~= nil then return "" end return cleartext end function scw(r) local start_time = r:clock() local has_redis = false local ignore = os.getenv("SCW_IGNORE") if ignore == nil or ignore == "" then r:err("SCW_IGNORE is not set!") end uri = string.match(r.the_request, "^%w+%s+(.+)%s+HTTP") if ignore ~= nil and regex.match(uri, ignore) then return apache2.DECLINED end -- -------------------------------------------------------------------------- -- Does the user have an encrypted scw cookie that proves he is human? -- local cookie_name = os.getenv("SCW_COOKIE") if cookie_name == nil or cookie_name == "" then r:err("SCW_COOKIE is not set!") end local cookie_key = r:base64_decode(os.getenv("SCW_KEY")) if cookie_key == nil or cookie_key == "" then r:err("SCW_KEY is not set!") end local human_cookie = r:getcookie(cookie_name) local xff = tostring(r.headers_in["X-Forwarded-For"]) local rip = tostring(r.headers_in["X-Real-IP"]) if human_cookie then human_cookie = r:base64_decode(r:unescape(human_cookie)) end local is_human = false if human_cookie ~= nil and cookie_key:len() == 32 then local cookie_data = _decrypt(human_cookie, cookie_key) is_human = string.gsub(cookie_data, "scw|(.-)|(%d+)$", function (ip, exp) if (ip == r.useragent_ip or ip == xff or ip == rip) and r:clock() <= tonumber(exp) then return true end return false end) if is_human then return apache2.DECLINED end end -- -------------------------------------------------------------------------- -- check for blacklist status -- if pcall(function() redis_conn:ping() end) then has_redis = true else -- io.stderr:write("reconnecting to redis\n") local redis_host = os.getenv("SCW_REDIS_HOST") if redis_host == nil or redis_host == "" then r:err("SCW_REDIS_HOST is not set!") end local redis_port = os.getenv("SCW_REDIS_PORT") if redis_port == nil or redis_port == "" then r:err("SCW_REDIS_PORT is not set!") end if pcall(function() redis_conn = redis.connect(redis_host, redis_port) end) then has_redis = true end end local captcha_url = os.getenv("SCW_CAPTCHA_URL") if captcha_url == nil or captcha_url == "" then r:err("SCW_CAPTCHA_URL is not set!") end if has_redis and captcha_url ~= "" then -- the client ip local v = redis_conn:get("bl:" .. r.useragent_ip) -- the X-Forwarded-For IP if v == nil then v = redis_conn:get("bl:" .. xff) end -- the X-Real-IP IP if v == nil then v = redis_conn:get("bl:" .. rip) end if v ~= nil then -- and h == nil then local rprotocol = "http" if r.is_https then rprotocol = "https" end local rport = "" if (r.is_https and r.port ~= 443) or (r.is_https ~= true and r.port ~= 80) then rport = string.format(":%d", r.port) end local referer = string.format("%s://%s%s%s", rprotocol, r.hostname, rport, r.unparsed_uri) r.headers_out["location"] = string.format(captcha_url, r.useragent_ip, r:escape(referer)) --[[ r.headers_in["X-SCW-IP"] = v r.handler = "proxy-server" r.proxyreq = apache2.PROXYREQ_REVERSE r.filename = string.format("proxy:http://captcha:8080/?src=%s&r=%s", r.useragent_ip, r.unparsed_uri) --]] return apache2.HTTP_MOVED_TEMPORARILY end end return apache2.DECLINED end