1
0
mirror of https://github.com/bigchaindb/bigchaindb.git synced 2024-06-17 10:03:20 +02:00

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,33 @@
## Deploying the BigchainDB Web Proxy on a Kubernetes Cluster
### Configure the Web Proxy
* Fill in the configuration details for the proxy in the
`nginx-https-web-proxy-conf.yaml` file.
* Use the command below to create the appropriate ConfigMap and Secret:
```
kubectl apply -f nginx-https-web-proxy-conf.yaml
```
### Start the Kubernetes Service for BigchainDB Web Proxy
* Use the command below to start the Kubernetes Service:
```
kubectl apply -f nginx-https-web-proxy-svc.yaml
```
* This will give you a public IP address tied to an Azure LB.
* Map this to an available domain of your choice on the Azure portal (or use
any other DNS service provider!)
### Start the Kubernetes Deployment for BigchainDB Web Proxy
* Use the command below to start the Kubernetes Deployment:
```
kubectl apply -f nginx-https-web-proxy-dep.yaml
```

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

View File

@ -0,0 +1,60 @@
# All secret data should be base64 encoded before embedding them in the Secret.
# Short strings can be encoded using, e.g.
# echo "secret string" | base64 -w 0 > secret.string.b64
# Files (e.g. certificates) can be encoded using, e.g.
# cat cert.pem | base64 -w 0 > cert.pem.b64
# then copy the contents of cert.pem.b64 (for example) below.
# Ref: https://kubernetes.io/docs/concepts/configuration/secret/
# Unused values can be set to ""
apiVersion: v1
kind: Secret
metadata:
name: proxy-credentials
namespace: default
type: Opaque
data:
# app_id is the app id that the proxy adds to requests going to the backend
app_id: "<b64 encoded app_id>"
# app_key is the app key that the proxy adds to requests going to the backend
app_key: "<b64 encoded app_key>"
---
apiVersion: v1
kind: Secret
metadata:
name: proxy-https-certs
namespace: default
type: Opaque
data:
# Base64-encoded HTTPS private key
cert.key: "<b64 encoded HTTPS private key>"
# Base64-encoded HTTPS certificate chain
# starting with your primary SSL cert (e.g. your_domain.crt)
# followed by all intermediate certs.
# If cert is from DigiCert, download "Best format for nginx".
cert.pem: "<b64 encoded HTTPS certificate chain"
---
## Note: data values do NOT have to be base64-encoded for ConfigMap.
# proxy-vars is common environment variables for the Web Proxy.
apiVersion: v1
kind: ConfigMap
metadata:
name: proxy-vars
namespace: default
data:
# proxy-fqdn is the DNS name registered for your HTTPS certificate.
proxy-fqdn: "proxy-bdb.example.com"
# proxy-frontend-port is the port number on which this web proxy's services
# are available to external clients.
proxy-frontend-port: "4443"
# expected-http-referer is the expected regex expression of the Referer
# header in the HTTP requests to the proxy.
# The default below accepts the referrer value to be *.bigchaindb.com
expected-http-referer: "^https://(.*)bigchaindb\\.com/(.*)"
# expected-http-origin is the expected regex expression of the Origin
# header in the HTTP requests to the proxy.
# The default below accepts the origin value to be *.bigchaindb.com
expected-http-origin: "^https://(.*)bigchaindb\\.com"

View File

@ -0,0 +1,92 @@
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: web-proxy-instance-0-dep
spec:
replicas: 1
template:
metadata:
labels:
app: web-proxy-instance-0-dep
spec:
terminationGracePeriodSeconds: 10
containers:
- name: web-proxy
image: bigchaindb/nginx-https-web-proxy:0.10
imagePullPolicy: Always
env:
- name: PROXY_FQDN
valueFrom:
configMapKeyRef:
name: proxy-vars
key: proxy-fqdn
- name: PROXY_FRONTEND_PORT
valueFrom:
configMapKeyRef:
name: proxy-vars
key: proxy-frontend-port
- name: PROXY_EXPECTED_REFERER_HEADER
valueFrom:
configMapKeyRef:
name: proxy-vars
key: expected-http-referer
- name: PROXY_EXPECTED_ORIGIN_HEADER
valueFrom:
configMapKeyRef:
name: proxy-vars
key: expected-http-origin
- name: OPENRESTY_BACKEND_HOST
valueFrom:
configMapKeyRef:
name: vars
key: ngx-openresty-instance-name
- name: OPENRESTY_BACKEND_PORT
valueFrom:
configMapKeyRef:
name: vars
key: openresty-backend-port
- name: DNS_SERVER
valueFrom:
configMapKeyRef:
name: vars
key: cluster-dns-server-ip
- name: HEALTH_CHECK_PORT
valueFrom:
configMapKeyRef:
name: vars
key: cluster-health-check-port
- name: BIGCHAINDB_BACKEND_HOST
valueFrom:
configMapKeyRef:
name: vars
key: ngx-bdb-instance-name
- name: BIGCHAINDB_API_PORT
valueFrom:
configMapKeyRef:
name: vars
key: bigchaindb-api-port
ports:
- containerPort: <port where the proxy is listening for requests>
protocol: TCP
resources:
limits:
cpu: 200m
memory: 256Mi
volumeMounts:
- name: proxy-https-certs
mountPath: /etc/nginx/ssl/
readOnly: true
- name: proxy-credentials
mountPath: /etc/nginx/proxy/credentials/
readOnly: true
# TODO probes!!??
restartPolicy: Always
volumes:
- name: proxy-https-certs
secret:
secretName: proxy-https-certs
defaultMode: 0400
- name: proxy-credentials
secret:
secretName: proxy-credentials
defaultMode: 0400

View File

@ -0,0 +1,16 @@
apiVersion: v1
kind: Service
metadata:
name: web-proxy-instance-0
namespace: default
labels:
name: web-proxy-instance-0
spec:
selector:
app: web-proxy-instance-0-dep
ports:
- port: <port where the proxy is listening for requests>
targetPort: <port where the proxy is listening for requests>
name: public-web-proxy-port
protocol: TCP
type: LoadBalancer