Tobias von Dewitz 7 anni fa
commit
b7e6116686
9 ha cambiato i file con 384 aggiunte e 0 eliminazioni
  1. 4 0
      Dockerfile
  2. 57 0
      conf/default.conf
  3. 42 0
      conf/nginx.conf
  4. 10 0
      conf/scw.conf
  5. 40 0
      docker-compose.yml
  6. 71 0
      examples/encryption.go
  7. 1 0
      html/index.html
  8. 13 0
      ips.rb
  9. 146 0
      lua/scw.lua

+ 4 - 0
Dockerfile

@@ -0,0 +1,4 @@
+FROM openresty/openresty:alpine
+
+RUN apk add --no-cache lua5.1 lua-redis lua-socket lua-cjson lua-rex-pcre lua-ossl
+

+ 57 - 0
conf/default.conf

@@ -0,0 +1,57 @@
+lua_code_cache off;
+lua_package_path '/usr/share/lua/5.1/?.lua';
+lua_package_cpath '/usr/lib/lua/5.1/?.so';
+
+server {
+    listen       80;
+    server_name  localhost;
+
+    #charset koi8-r;
+    #access_log  /var/log/nginx/host.access.log  main;
+
+    error_log /dev/stderr info;
+
+    location / {
+        root   /usr/local/openresty/nginx/html;
+        index  index.html index.htm;
+        rewrite_by_lua_file /usr/local/openresty/scw/scw.lua;
+    }
+
+    location /lua {
+       content_by_lua_block {
+          ngx.header.content_type = "text/plain"
+          ngx.say("Hallo " .. ngx.val.uri .. "!")
+       }
+    }
+    #error_page  404              /404.html;
+
+    # redirect server error pages to the static page /50x.html
+    #
+    error_page   500 502 503 504  /50x.html;
+    location = /50x.html {
+        root   /usr/local/openresty/nginx/html;
+    }
+
+    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
+    #
+    #location ~ \.php$ {
+    #    proxy_pass   http://127.0.0.1;
+    #}
+
+    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
+    #
+    #location ~ \.php$ {
+    #    root           /usr/local/openresty/nginx/html;
+    #    fastcgi_pass   127.0.0.1:9000;
+    #    fastcgi_index  index.php;
+    #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
+    #    include        fastcgi_params;
+    #}
+
+    # deny access to .htaccess files, if Apache's document root
+    # concurs with nginx's one
+    #
+    #location ~ /\.ht {
+    #    deny  all;
+    #}
+}

+ 42 - 0
conf/nginx.conf

@@ -0,0 +1,42 @@
+#user  nobody;
+worker_processes  1;
+
+#error_log  logs/error.log;
+#error_log  logs/error.log  notice;
+#error_log  logs/error.log  info;
+
+#pid        logs/nginx.pid;
+
+env SCW_IGNORE;
+env SCW_KEY;
+env SCW_COOKIE;
+env SCW_CAPTCHA_URL;
+env SCW_REDIS_HOST;
+env SCW_REDIS_PORT;
+
+
+events {
+    worker_connections  1024;
+}
+
+
+http {
+    include       mime.types;
+    default_type  application/octet-stream;
+
+    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
+    #                  '$status $body_bytes_sent "$http_referer" '
+    #                  '"$http_user_agent" "$http_x_forwarded_for"';
+
+    #access_log  logs/access.log  main;
+
+    sendfile        on;
+    #tcp_nopush     on;
+
+    #keepalive_timeout  0;
+    keepalive_timeout  65;
+
+    #gzip  on;
+
+    include /etc/nginx/conf.d/*.conf;
+}

+ 10 - 0
conf/scw.conf

@@ -0,0 +1,10 @@
+server {
+  lua_code_cache off;
+
+  location /lua {
+    content_by_lua_block {
+      ngx.say("Hallo " .. ngx.var.arg_a)
+    }
+    # content_by_lua_file /usr/local/openresty/scw/scw.lua;
+  }
+}

+ 40 - 0
docker-compose.yml

@@ -0,0 +1,40 @@
+version: '3' 
+services:
+  redis:
+    image: redis:latest
+    volumes:
+     - /docker-filesystem/httpd/redis:/data
+    restart: always
+    ports:
+      - 8020:6379
+  captcha:
+    image: registry.scw.systems/captcha:1.2.5.1
+    restart: always
+    environment:
+      - COOKIE_NAME=hicludes
+      - COOKIE_KEY=DydmRdMMZWcRF91mNj/CWLPzzQQi5Rew0cBP0qiBUZI=
+      - BIND=0.0.0.0
+      - REDIS_HOST=redis
+      - REDIS_PORT=6379
+      - HUMAN_TTL=10m
+    ports:
+      - 8021:8080
+  nginx:
+    image: openresty-scw:latest
+    depends_on:
+      - redis
+    volumes:
+      - ./conf/default.conf:/etc/nginx/conf.d/default.conf
+      - ./conf/nginx.conf:/usr/local/openresty/nginx/conf/nginx.conf
+      - ./html:/usr/local/openresty/nginx/html
+      - ./lua:/usr/local/openresty/scw
+    ports:
+      - 8022:80
+    restart: always
+    environment:
+      - SCW_IGNORE=(^/assets/|\.(png|jpe?g|svg|gif|js|css)$$)
+      - SCW_KEY=DydmRdMMZWcRF91mNj/CWLPzzQQi5Rew0cBP0qiBUZI=
+      - SCW_COOKIE=hicludes
+      - SCW_CAPTCHA_URL=http://docker.scw.systems:8003/?src=%s&r=%s
+      - SCW_REDIS_HOST=redis
+      - SCW_REDIS_PORT=6379

+ 71 - 0
examples/encryption.go

@@ -0,0 +1,71 @@
+package main
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"crypto/rand"
+)
+
+func encrypt(plaintext, key []byte) ([]byte, error) {
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		return []byte{}, err
+	}
+
+	iv := make([]byte, aes.BlockSize)
+	rand.Read(iv)
+
+	plaintextPadded := pad(plaintext)
+	encrypted := make([]byte, len(plaintextPadded))
+
+	mode := cipher.NewCBCEncrypter(block, iv)
+	mode.CryptBlocks(encrypted, plaintextPadded)
+
+	return append(iv, encrypted...), nil
+}
+
+func decrypt(encryptedIVandData, key []byte) ([]byte, error) {
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		return []byte{}, err
+	}
+
+	iv := encryptedIVandData[0:aes.BlockSize]
+	encrypted := encryptedIVandData[aes.BlockSize:]
+
+	mode := cipher.NewCBCDecrypter(block, iv)
+
+	plaintextPadded := make([]byte, len(encrypted))
+	mode.CryptBlocks(plaintextPadded, encrypted)
+	plaintext := unpad(plaintextPadded)
+
+	return plaintext, nil
+}
+
+func unpad(in []byte) []byte {
+	if len(in) == 0 {
+		return nil
+	}
+
+	padding := in[len(in)-1]
+	if int(padding) > len(in) || padding > aes.BlockSize {
+		return nil
+	} else if padding == 0 {
+		return nil
+	}
+
+	for i := len(in) - 1; i > len(in)-int(padding)-1; i-- {
+		if in[i] != padding {
+			return nil
+		}
+	}
+	return in[:len(in)-int(padding)]
+}
+
+func pad(in []byte) []byte {
+	padding := aes.BlockSize - (len(in) % aes.BlockSize)
+	for i := 0; i < padding; i++ {
+		in = append(in, byte(padding))
+	}
+	return in
+}

+ 1 - 0
html/index.html

@@ -0,0 +1 @@
+INDEX

+ 13 - 0
ips.rb

@@ -0,0 +1,13 @@
+#!/usr/bin/env ruby
+#
+#
+
+require 'redis'
+
+redis = Redis.new(host: "127.0.0.1", port: 8002)
+
+1.upto(100000) do
+  ip = "%d.%d.%d.%d" % [rand(253)+1, rand(254), rand(254), rand(254)]
+
+  redis.set "bl:#{ip}", 1
+end

+ 146 - 0
lua/scw.lua

@@ -0,0 +1,146 @@
+local redis = require "redis"
+local regex = require "rex_pcre"
+local mime = require "mime"
+local cipher = require "openssl.cipher"
+
+local redis_conn = nil
+
+function scw()
+  -- local start_time = ngx.now()
+  local has_redis = false
+  local ignore = os.getenv("SCW_IGNORE")
+  local uri = ngx.var.uri
+  local req_uri = ngx.var.request_uri
+
+  ngx.log(ngx.INFO, "req_uri: " .. req_uri)
+
+  if ignore ~= nil and regex.match(uri, ignore) then
+    return -- do norhing if configured to ignore
+  end
+
+  -- --------------------------------------------------------------------------
+  -- Does the user have an encrypted scw cookie that proves he is human?
+  --
+  local cookie_name = "cookie_" .. tostring(os.getenv("SCW_COOKIE"))
+  if cookie_name == "cookie_" then
+    ngx.log(ngx.ERR, "SCW_COOKIE is not set!")
+    return
+  end
+
+  local scw_key = os.getenv("SCW_KEY")
+  if scw_key == nil or scw_key == "" then
+     ngx.log(ngx.ERR, "SCW_KEY is not set!")
+     return
+  end
+
+
+  local cookie_key = ngx.decode_base64(scw_key)
+  local human_cookie =  ngx.var[cookie_name]
+
+  local xff = tostring(ngx.var["http_x_forwarded_for"])
+  local rip = tostring(ngx.var["http_x_real_ip"])
+
+
+  if human_cookie then
+    human_cookie = ngx.decode_base64(ngx.unescape_uri(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)
+
+    local cookie_ip = string.gsub(cookie_data, "scw|(.-)|(%d+)$", "%1")
+    local cookie_expiry = string.gsub(cookie_data, "scw|(.-)|(%d+)$", "%2")
+
+    is_human =  cookie_ip ~= "" and (cookie_ip == ngx.var.remote_addr or cookie_ip == xff or cookie_ip == rip) and ngx.now() <= tonumber(cookie_expiry)
+
+    if is_human then
+      return 
+    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")
+    local redis_port = os.getenv("SCW_REDIS_PORT")
+
+    if redis_host == nil or redis_host == "" then
+      ngx.log(ngx.ERR, "SCW_REDIS_HOST is not set!")
+      return
+    end
+    if redis_port == nil or redis_port == "" then
+      ngx.log(ngx.ERR, "SCW_REDIS_PORT is not set!")
+      return
+    end
+
+    if pcall(function() redis_conn = redis.connect(redis_host, 6379) end) then
+      has_redis = true
+    end
+  end
+
+  local captcha_url = os.getenv("SCW_CAPTCHA_URL")
+  if captcha_url == nil or captcha_url == "" then
+    ngx.log(ngx.ERR, "SCW_CAPTCHA_URL is not set!")
+    return
+  end
+
+  if has_redis and captcha_url ~= "" then
+    -- the client ip
+    local v = redis_conn:get("bl:" .. ngx.var.remote_addr)
+
+    -- 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 ngx.var.scheme == "https" or ngx.var["http_x_forwarded_proto"] == "https" then
+        rprotocol = "https"
+      end
+
+      ngx.log(ngx.INFO, "port: " .. ngx.var.server_port)
+      local rport = ""
+      if (rprotocol == "https" and ngx.var.server_port ~= 443) or (rprotocol == "http" and ngx.var.server_port ~= 80) then
+        rport = string.format(":%d", ngx.var.server_port)
+      end
+
+      local referer = string.format("%s://%s%s%s", rprotocol, ngx.var.host, rport, ngx.var.uri)
+
+      return ngx.redirect(string.format(captcha_url, ngx.var.remote_addr, ngx.escape_uri(referer)))
+    end
+  end
+end
+
+
+
+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
+
+scw()