Skip to content

Nginx Proxy Server for JSONRPC Calls

Meri Herrera edited this page May 4, 2018 · 4 revisions

Openresty/Nginx Proxy for Node Servers

(tested on Ubuntu 16.04)

In this step by step guide we will assume you have an RSK Node up and running. Since this is for reference only the proxy will be installed on the same server with the RSK Node.

1. Update your server

apt-get -y update
apt-get -y upgrade

2. Install Nginx Service & necessary packages.

apt-get -y install gnupg-curl nginx-extras build-essential libpcre3-dev libssl-dev libgeoip-dev libpq-dev libxslt1-dev libgd2-xpm-dev

3. Download Openresty

wget -c https://openresty.org/download/openresty-1.11.2.2.tar.gz

4. Extract Openresty and configure/compile

tar zxvf openresty-1.11.2.2.tar.gz

cd openresty-1.11.2.2

./configure \
--sbin-path=/usr/sbin/nginx \
--conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log \
--http-client-body-temp-path=/var/lib/nginx/body \
--http-fastcgi-temp-path=/var/lib/nginx/fastcgi \
--http-log-path=/var/log/nginx/access.log \
--http-proxy-temp-path=/var/lib/nginx/proxy \
--http-scgi-temp-path=/var/lib/nginx/scgi \
--http-uwsgi-temp-path=/var/lib/nginx/uwsgi \
--lock-path=/var/lock/nginx.lock \
--pid-path=/var/run/nginx.pid \
--with-sha1=/usr/include/openssl \
--with-md5=/usr/include/openssl \
--with-http_stub_status_module \
--with-http_secure_link_module \
--with-luajit \
--with-pcre-jit \
--with-debug \
--with-http_auth_request_module \
--with-http_addition_module \
--with-http_gunzip_module \
--with-http_image_filter_module \
--with-http_dav_module \
--with-http_flv_module \
--with-http_geoip_module \
--with-http_gzip_static_module \
--with-http_realip_module \
--with-http_stub_status_module \
--with-http_ssl_module \
--with-http_sub_module \
--with-http_xslt_module \
--with-ipv6 \
--with-http_postgres_module

make

make install

apt-get -y autoclean

apt-get -y autoremove

5. Create and add the .lua file

(Place in /usr/local/openresty/nginx/jsonrpc-access.lua)

local cjson = require('cjson')

local function empty(s)
  return s == nil or s == ''
end

local function split(s)
  local res = {}
  local i = 1
  for v in string.gmatch(s, "([^,]+)") do
    res[i] = v
    i = i + 1
  end
  return res
end

local function contains(arr, val)
  for i, v in ipairs (arr) do
    if v == val then
      return true
    end
  end
  return false
end

-- parse conf
local blacklist, whitelist = nil
if not empty(ngx.var.jsonrpc_blacklist) then
  blacklist = split(ngx.var.jsonrpc_blacklist)
end
if not empty(ngx.var.jsonrpc_whitelist) then
  whitelist = split(ngx.var.jsonrpc_whitelist)
end

-- check conf
if blacklist ~= nil and whitelist ~= nil then
  ngx.log(ngx.ERR, 'invalid conf: jsonrpc_blacklist and jsonrpc_whitelist are both set')
  ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
  return
end

-- get request content
ngx.req.read_body()

-- try to parse the body as JSON
local success, body = pcall(cjson.decode, ngx.var.request_body);
if not success then
  ngx.log(ngx.ERR, 'invalid JSON request')
  ngx.exit(ngx.HTTP_BAD_REQUEST)
  return
end

local method = body['method']
local version = body['jsonrpc']

-- check we have a method and a version
if empty(method) or empty(version) then
  ngx.log(ngx.ERR, 'no method and/or jsonrpc attribute')
  ngx.exit(ngx.HTTP_BAD_REQUEST)
  return
end

-- check the version is supported
if version ~= "2.0" then
  ngx.log(ngx.ERR, 'jsonrpc version not supported: ' .. version)
  ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
  return
end

-- if whitelist is configured, check that the method is whitelisted
if whitelist ~= nil then
  if not contains(whitelist, method) then
    ngx.log(ngx.ERR, 'jsonrpc method is not whitelisted: ' .. method)
    ngx.exit(ngx.HTTP_FORBIDDEN)
    return
  end
end

-- if blacklist is configured, check that the method is not blacklisted
if blacklist ~= nil then
  if contains(blacklist, method) then
    ngx.log(ngx.ERR, 'jsonrpc method is blacklisted: ' .. method)
    ngx.exit(ngx.HTTP_FORBIDDEN)
    return
  end
end

return

6. Basic Nginx configuration file

(Located in /etc/nginx/nginx.conf)

user www-data;
worker_processes  5;
error_log  /var/log/nginx/error.log;
pid        /run/nginx.pid;

events {
  worker_connections  768;
}

http {
	server_tokens off;
	sendfile on;
	tcp_nopush on;
	tcp_nodelay on;
	keepalive_timeout 65;
	types_hash_max_size 2048;

	# server_names_hash_bucket_size 64;
	# server_name_in_redirect off;
	
	ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
	ssl_prefer_server_ciphers on;

	include /etc/nginx/mime.types;
	default_type application/octet-stream;

        include /etc/nginx/sites-enabled/*;
        include /etc/nginx/conf.d/*;
	
	gzip on;
	gzip_disable "msie6";

	# gzip_vary on;
	# gzip_proxied any;
	# gzip_comp_level 6;
	# gzip_buffers 16 8k;
	# gzip_http_version 1.1;
	# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

	log_format   main '$remote_addr - $remote_user [$time_local]  $status '
	'"$request" $body_bytes_sent "$http_referer" '
	'"$http_user_agent" "$http_x_forwarded_for"';
	access_log   /var/log/nginx/access.log;

}

7. Basic Site Configuration.

(Located in: /etc/nginx/sites-enabled/default)

server {
  listen       80;
  server_name  YOUR.SERVER.COM;
  root /var/www/html;
  index index.html;

  location / {
    set $jsonrpc_whitelist 'web3_clientVersion,eth_getTransactionByHash,eth_getTransactionReceipt,eth_sendRawTransaction';
    access_by_lua_file 'jsonrpc-access.lua';
    proxy_pass http://127.0.0.1:4444;
  }
}

8. Basic SSL Site Configuration.

(Located in: /etc/nginx/conf.d/ssl_site.conf)


server {
  listen      443 default_server ssl spdy;

  server_name  YOUR.SERVER.COM;

  # Certificate(s) and private key
  ssl_certificate /etc/nginx/ssl_certs/proxy_server.crt;
  ssl_certificate_key /etc/nginx/ssl_certs/proxy_server.key;

  # openssl dhparam 4096 -out /etc/ssl/dhparam.pem
  ssl_dhparam /etc/ssl/dhparam.pem;

  ssl_protocols TLSv1.2 TLSv1.1 TLSv1;
  ssl_prefer_server_ciphers on;
  ssl_ciphers EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA512:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:ECDH+AESGCM:ECDH+AES256:DH+AESGCM:DH+AES256:RSA+AESGCM:!aNULL:!eNULL:!LOW:!RC4:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS;

  ssl_session_cache shared:TLS:2m;

  # OCSP stapling
  ssl_stapling on;
  ssl_stapling_verify on;
  resolver 8.8.8.8;

  # Set HSTS to 365 days
  add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains';

    location / {
      more_clear_headers Server;
      header_filter_by_lua 'ngx.header["server"] = nil';
      set $jsonrpc_whitelist 'web3_clientVersion,eth_getTransactionByHash,eth_getTransactionReceipt,eth_sendRawTransaction';
      access_by_lua_file 'jsonrpc-access.lua';
      proxy_pass http://127.0.0.1:4444;
    }
  }

9. Generate your SSL Server Certificates.

In this example we will be creating and using Self-Signed certificates.

(Keep in mind you will need to request your valid certificates to an authorized provider).

Check for openssl package:

sudo apt-get install openssl
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/ssl_certs/proxy_server.key -out /etc/nginx/ssl_certs/proxy_server.crt

Fill out the prompts appropriately. The most important line is the one that requests the Common Name (e.g. server FQDN or YOUR name). You need to enter the domain name associated with your server or, more likely, your server's public IP address.

The entirety of the prompts will look something like this:


Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:New York
Locality Name (eg, city) []:New York City
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Bouncy Castles, Inc.
Organizational Unit Name (eg, section) []:Ministry of Water Slides
Common Name (e.g. server FQDN or YOUR name) []:server_IP_address
Email Address []:admin@your_domain.com
sudo openssl dhparam 4096 -out /etc/ssl/dhparam.pem
(!! This command will take a long time !!).

10. How to allow or deny methods.

Look for "set $jsonrpc_whitelist" in your sites configuration (files created on step 7 & 8) you will found something like:

"web3_clientVersion,eth_getTransactionByHash,eth_getTransactionReceipt,eth_sendRawTransaction"

Those 4 methods are the ones used on this example to allow a response from the server.

Add or remove methods as needed based on the list described below.

11. WHITE-BLACK LIST Methods

web3_clientVersion
web3_sha3
net_version
net_peerCount
net_listening
eth_protocolVersion
eth_syncing
eth_coinbase
eth_mining
eth_hashrate
eth_gasPrice
eth_accounts
eth_blockNumber
eth_getBalance
eth_getStorageAt
eth_getTransactionCount
eth_getBlockTransactionCountByHash
eth_getBlockTransactionCountByNumber
eth_getUncleCountByBlockHash
eth_getUncleCountByBlockNumber
eth_getCode
eth_sign
eth_sendTransaction
eth_sendRawTransaction
eth_call
eth_estimateGas
eth_getBlockByHash
eth_getBlockByNumber
eth_getTransactionByHash
eth_getTransactionByBlockHashAndIndex
eth_getTransactionByBlockNumberAndIndex
eth_getTransactionReceipt
eth_getUncleByBlockHashAndIndex
eth_getUncleByBlockNumberAndIndex
eth_getCompilers
eth_compileLLL
eth_compileSolidity
eth_compileSerpent
eth_newFilter
eth_newBlockFilter
eth_newPendingTransactionFilter
eth_uninstallFilter
eth_getFilterChanges
eth_getFilterLogs
eth_getLogs
eth_getWork
eth_submitWork
eth_submitHashrate
db_putString
db_getString
db_putHex
db_getHex
shh_post
shh_version
shh_newIdentity
shh_hasIdentity
shh_newGroup
shh_addToGroup
shh_newFilter
shh_uninstallFilter
shh_getFilterChanges
shh_getMessages

12. Necessary Changes in your environment.

  1. In files created on steps 7 and 8 look for:
  proxy_pass http://127.0.0.1:4444;

This should be pointing to your RSK Node, if its installed on a different server.

  1. In files created on steps 7 & 8 look for:
  server_name  YOUR.SERVER.COM;

This should match with your desired DNS CNAME/A Record for your Server SSL Certificate.

Clone this wiki locally