scw.lua 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. require "apache2"
  2. -- local redis = require "redis"
  3. local hiredis = require "hiredis"
  4. local regex = require "rex_pcre"
  5. local mime = require "mime"
  6. local cipher = require "openssl".cipher
  7. local pretty = require 'pl.pretty'
  8. -- --------------------------------------------------------------------------
  9. -- try to connect to redis
  10. --
  11. local redis_conn = nil
  12. function _decrypt(message_and_iv, key)
  13. if message_and_iv:len() < 32 then
  14. return ""
  15. end
  16. iv, _ = message_and_iv:sub(1, 16)
  17. message, _ = message_and_iv:sub(17, -1)
  18. local c = cipher.new("aes-256-cbc")
  19. c:decrypt(key, iv)
  20. cleartext, err = c:final(message)
  21. if err ~= nil then
  22. return ""
  23. end
  24. return cleartext
  25. end
  26. function scw(r)
  27. local start_time = r:clock()
  28. local has_redis = false
  29. local ignore = os.getenv("SCW_IGNORE")
  30. if ignore == nil or ignore == "" then
  31. r:err("SCW_IGNORE is not set!")
  32. end
  33. local uri = string.match(r.the_request, "^%w+%s+(.+)%s+HTTP")
  34. if ignore ~= nil and regex.match(uri, ignore) then
  35. return apache2.DECLINED
  36. end
  37. -- --------------------------------------------------------------------------
  38. -- Does the user have an encrypted scw cookie that proves he is human?
  39. --
  40. local cookie_name = os.getenv("SCW_COOKIE")
  41. if cookie_name == nil or cookie_name == "" then
  42. r:err("SCW_COOKIE is not set!")
  43. end
  44. local cookie_key = r:base64_decode(os.getenv("SCW_KEY"))
  45. if cookie_key == nil or cookie_key == "" then
  46. r:err("SCW_KEY is not set!")
  47. end
  48. local human_cookie = r:getcookie(cookie_name)
  49. local xff = tostring(r.headers_in["X-Forwarded-For"])
  50. local rip = tostring(r.headers_in["X-Real-IP"])
  51. if human_cookie then
  52. human_cookie = r:base64_decode(r:unescape(human_cookie))
  53. end
  54. local is_human = false
  55. if human_cookie ~= nil and cookie_key:len() == 32 then
  56. local cookie_data = _decrypt(human_cookie, cookie_key)
  57. is_human = string.gsub(cookie_data, "scw|(.-)|(%d+)$", function (ip, exp)
  58. if (ip == r.useragent_ip or ip == xff or ip == rip) and r:clock() <= tonumber(exp) then
  59. return true
  60. end
  61. return false
  62. end)
  63. if is_human then
  64. return apache2.DECLINED
  65. end
  66. end
  67. -- --------------------------------------------------------------------------
  68. -- check for blacklist status
  69. --
  70. local sucess = false
  71. local err = ""
  72. if redis_conn ~= nil then
  73. success, err = pcall(function() redis_conn:command("PING") end)
  74. if redis_conn == nil then
  75. r:info("redis_conn turned nil after ping")
  76. end
  77. end
  78. if success then
  79. r:info("redis ping was successful")
  80. has_redis = true
  81. else
  82. -- io.stderr:write("reconnecting to redis\n")
  83. local redis_host = os.getenv("SCW_REDIS_HOST")
  84. if redis_host == nil or redis_host == "" then
  85. r:err("SCW_REDIS_HOST is not set!")
  86. end
  87. local redis_port = os.getenv("SCW_REDIS_PORT")
  88. if redis_port == nil or redis_port == "" then
  89. r:err("SCW_REDIS_PORT is not set!")
  90. end
  91. r:info(string.format("connecting to redis %s:%s", redis_host, redis_port))
  92. success, err = pcall(function() redis_conn = hiredis.connect(redis_host, redis_port) end)
  93. if success then
  94. r:info("created redis connection to " .. redis_host .. ":" .. redis_port)
  95. has_redis = true
  96. end
  97. end
  98. local captcha_url = os.getenv("SCW_CAPTCHA_URL")
  99. if captcha_url == nil or captcha_url == "" then
  100. r:err("SCW_CAPTCHA_URL is not set!")
  101. end
  102. if has_redis and captcha_url ~= "" then
  103. -- the client ip
  104. if redis_conn == nil then
  105. r:info("redis is suddenly nil!")
  106. end
  107. local v = redis_conn:command("GET", "bl:" .. r.useragent_ip)
  108. -- if v == nil then
  109. -- r:info("v is nil!")
  110. -- end
  111. -- the X-Forwarded-For IP
  112. if v == nil and xff ~= "" then
  113. v = redis_conn:command("GET", "bl:" .. xff)
  114. end
  115. -- the X-Real-IP IP
  116. if v == nil and rip ~= "" then
  117. v = redis_conn:command("GET", "bl:" .. rip)
  118. end
  119. if tostring(v) ~= "" and tostring(v) ~= "NIL" then -- and h == nil then
  120. local rprotocol = "http"
  121. if r.is_https then
  122. rprotocol = "https"
  123. end
  124. local rport = ""
  125. if (r.is_https and r.port ~= 443) or (r.is_https ~= true and r.port ~= 80) then
  126. rport = string.format(":%d", r.port)
  127. end
  128. local referer = string.format("%s://%s%s%s", rprotocol, r.hostname, rport, r.unparsed_uri)
  129. r.headers_out["location"] = string.format(captcha_url, r.useragent_ip, r:escape(referer))
  130. --[[
  131. r.headers_in["X-SCW-IP"] = v
  132. r.handler = "proxy-server"
  133. r.proxyreq = apache2.PROXYREQ_REVERSE
  134. r.filename = string.format("proxy:http://captcha:8080/?src=%s&r=%s", r.useragent_ip, r.unparsed_uri)
  135. --]]
  136. return apache2.HTTP_MOVED_TEMPORARILY
  137. end
  138. end
  139. return apache2.DECLINED
  140. end