BigchainDB Web Proxy to add headers to requests

Currently, the requests from public websites (like
`bigchaindb.com/getstarted` and tutorials.bigchaindb.com/crab) cannot have the
app_id and app_key required to access IPDB in the web page.

We pass such requests through a web proxy that adds the required headers
to any POST requests from `*.bigchaindb.com`.
This commit is contained in:
krish7919 (Krish)
2017-09-05 15:51:28 +02:00
committed by Krish
parent 5fbc3f4a6c
commit f07df50041
8 changed files with 426 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
FROM openresty/openresty:alpine
RUN apk update \
&& apk upgrade \
&& apk add bash
COPY nginx.conf.template /etc/nginx/nginx.conf
COPY nginx_entrypoint.bash /
EXPOSE 443
ENTRYPOINT ["/nginx_entrypoint.bash"]

View File

@@ -0,0 +1,5 @@
#!/bin/bash
docker build -t bigchaindb/nginx-https-web-proxy:0.10 .
docker push bigchaindb/nginx-https-web-proxy:0.10

View File

@@ -0,0 +1,138 @@
# Frontend Proxy server that:
# 1. Acts as the HTTPS proxy termination point.
# 2. Forwards BDB POST requests to OpenResty backend after appending the app_id
# and app_key headers.
# 3. Forwards BDB GET requests to BDB backend.
# 4. Does health check with LB.
worker_processes 4;
daemon off;
user nobody nogroup;
pid /tmp/nginx.pid;
error_log /dev/stderr;
events {
# Each worker handles up to 1024 connections. Increase this for heavy
# workloads.
worker_connections 1024;
accept_mutex on;
use epoll;
}
http {
access_log /dev/stdout combined buffer=16k flush=5s;
# Allow 2048 req/sec from the same IP address, and store the counters in a
# `zone` or shared memory location tagged as 'one'.
limit_req_zone $binary_remote_addr zone=one:10m rate=2048r/s;
# Enable logging when requests are being throttled.
limit_req_log_level notice;
# HTTP status code that is returned to the client; 429 is for TooManyRequests,
# ref. RFC 6585
limit_req_status 429;
# Limit requests from the same client, allow `burst` to 3072 r/s,
# `nodelay` or drop connection immediately in case it exceeds this
# threshold.
limit_req zone=one burst=3072 nodelay;
# `slowloris` attack mitigation settings.
client_body_timeout 30s;
client_header_timeout 10s;
# DNS resolver to use for all the backend names specified in this configuration.
resolver DNS_SERVER valid=30s ipv6=off;
keepalive_timeout 60s;
# The following map blocks enable lazy-binding to the backend at runtime,
# rather than binding as soon as NGINX starts.
map $remote_addr $bdb_backend {
default BIGCHAINDB_BACKEND_HOST;
}
map $remote_addr $openresty_backend {
default OPENRESTY_BACKEND_HOST;
}
# Frontend server for the external clients; acts as HTTPS termination point.
server {
listen PROXY_FRONTEND_PORT ssl;
server_name "PROXY_FQDN";
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/cert.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
underscores_in_headers on;
# No websocket support for web proxy
location /api/v1/streams/valid_transactions {
return 403 'Websockets are not supported in the web proxy';
}
# Forward other URL paths as per business logic/use case to BDB or
# OpenResty instance.
location / {
proxy_ignore_client_abort on;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# max client request body size: avg transaction size.
client_max_body_size 15k;
# Debug block for listing all the headers sent with the request
header_filter_by_lua_block {
local h = ngx.req.get_headers()
for k, v in pairs(h) do
ngx.log(ngx.ERR, "Header "..k..": "..v..";")
end
}
# check if the request originated from the required web page
# use referer header.
if ($http_referer !~ "PROXY_EXPECTED_REFERER_HEADER" ) {
return 403 'Unknown referer';
}
# check if the request has the expected origin header
if ($http_origin !~ "PROXY_EXPECTED_ORIGIN_HEADER" ) {
return 403 'Unknown origin';
}
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,app_key,app_id';
add_header 'Access-Control-Max-Age' 43200;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
# No auth for GETs, forward directly to BDB.
if ($request_method = GET) {
proxy_pass http://$bdb_backend:BIGCHAINDB_API_PORT;
}
# POST requests get forwarded to OpenResty instance; set the correct
# headers accordingly
proxy_set_header app_id "PROXY_APP_ID";
proxy_set_header app_key "PROXY_APP_KEY";
if ($request_method = POST ) {
proxy_pass http://$openresty_backend:OPENRESTY_BACKEND_PORT;
}
}
}
# Frontend server for the load balancer to respond to health checks.
server {
listen HEALTH_CHECK_PORT;
location = /health {
return 200;
}
}
}

View File

@@ -0,0 +1,74 @@
#!/bin/bash
set -euo pipefail
# Proxy vars
proxy_fqdn=`printenv PROXY_FQDN`
proxy_frontend_port=`printenv PROXY_FRONTEND_PORT`
proxy_app_id_file=/etc/nginx/proxy/credentials/app_id
proxy_app_key_file=/etc/nginx/proxy/credentials/app_key
proxy_app_id=`cat ${proxy_app_id_file}`
proxy_app_key=`cat ${proxy_app_key_file}`
proxy_expected_referer_header=`printenv PROXY_EXPECTED_REFERER_HEADER`
proxy_expected_origin_header=`printenv PROXY_EXPECTED_ORIGIN_HEADER`
# OpenResty vars
openresty_backend_host=`printenv OPENRESTY_BACKEND_HOST`
openresty_backend_port=`printenv OPENRESTY_BACKEND_PORT`
# NGINX vars
dns_server=`printenv DNS_SERVER`
health_check_port=`printenv HEALTH_CHECK_PORT`
# BigchainDB vars
bdb_backend_host=`printenv BIGCHAINDB_BACKEND_HOST`
bdb_api_port=`printenv BIGCHAINDB_API_PORT`
# sanity check
if [[ -z "${proxy_frontend_port:?PROXY_FRONTEND_PORT not specified. Exiting!}" || \
-z "${openresty_backend_port:?OPENRESTY_BACKEND_PORT not specified. Exiting!}" || \
-z "${openresty_backend_host:?OPENRESTY_BACKEND_HOST not specified. Exiting!}" || \
-z "${bdb_backend_host:?BIGCHAINDB_BACKEND_HOST not specified. Exiting!}" || \
-z "${bdb_api_port:?BIGCHAINDB_API_PORT not specified. Exiting!}" || \
-z "${dns_server:?DNS_SERVER not specified. Exiting!}" || \
-z "${health_check_port:?HEALTH_CHECK_PORT not specified. Exiting!}" || \
-z "${proxy_app_id:?PROXY_APP_ID not specified. Exiting!}" || \
-z "${proxy_app_key:?PROXY_APP_KEY not specified. Exiting!}" || \
-z "${proxy_expected_referer_header:?PROXY_EXPECTED_REFERER_HEADER not specified. Exiting!}" || \
-z "${proxy_expected_origin_header:?PROXY_EXPECTED_ORIGIN_HEADER not specified. Exiting!}" || \
-z "${proxy_fqdn:?PROXY_FQDN not specified. Exiting!}" ]]; then
exit 1
else
echo PROXY_FQDN="$proxy_fqdn"
echo PROXY_FRONTEND_PORT="$proxy_frontend_port"
echo PROXY_EXPECTED_REFERER_HEADER="$proxy_expected_referer_header"
echo PROXY_EXPECTED_ORIGIN_HEADER="$proxy_expected_origin_header"
echo DNS_SERVER="$dns_server"
echo HEALTH_CHECK_PORT="$health_check_port"
echo OPENRESTY_BACKEND_HOST="$openresty_backend_host"
echo OPENRESTY_BACKEND_PORT="$openresty_backend_port"
echo BIGCHAINDB_BACKEND_HOST="$bdb_backend_host"
echo BIGCHAINDB_API_PORT="$bdb_api_port"
fi
NGINX_CONF_FILE=/etc/nginx/nginx.conf
# configure the nginx.conf file with env variables
sed -i "s|PROXY_FQDN|${proxy_fqdn}|g" ${NGINX_CONF_FILE}
sed -i "s|PROXY_FRONTEND_PORT|${proxy_frontend_port}|g" ${NGINX_CONF_FILE}
sed -i "s|OPENRESTY_BACKEND_PORT|${openresty_backend_port}|g" ${NGINX_CONF_FILE}
sed -i "s|OPENRESTY_BACKEND_HOST|${openresty_backend_host}|g" ${NGINX_CONF_FILE}
sed -i "s|BIGCHAINDB_BACKEND_HOST|${bdb_backend_host}|g" ${NGINX_CONF_FILE}
sed -i "s|BIGCHAINDB_API_PORT|${bdb_api_port}|g" ${NGINX_CONF_FILE}
sed -i "s|DNS_SERVER|${dns_server}|g" ${NGINX_CONF_FILE}
sed -i "s|HEALTH_CHECK_PORT|${health_check_port}|g" ${NGINX_CONF_FILE}
sed -i "s|PROXY_APP_ID|${proxy_app_id}|g" ${NGINX_CONF_FILE}
sed -i "s|PROXY_APP_KEY|${proxy_app_key}|g" ${NGINX_CONF_FILE}
sed -i "s|PROXY_EXPECTED_REFERER_HEADER|${proxy_expected_referer_header}|g" ${NGINX_CONF_FILE}
sed -i "s|PROXY_EXPECTED_ORIGIN_HEADER|${proxy_expected_origin_header}|g" ${NGINX_CONF_FILE}
# start nginx
echo "INFO: starting nginx..."
exec nginx -c /etc/nginx/nginx.conf