Liam Vu
Liam Vu's Blog

Liam Vu's Blog

Setup NGINX, Authentication and Discord Alert for Full Nodes

Setup NGINX, Authentication and Discord Alert for Full Nodes

Liam Vu's photo
Liam Vu

Published on Sep 21, 2021

3 min read

Full nodes are always essential parts for building dApps. We can choose paid options or even setup a node on AWS or GCP for unlimited rate limit of access. For self-deployed full nodes, we expect that they have fixed addresses. However, static IPs are very limited, for example AWS only allows 6 Elastic IPs per region. We need a gateway and alert system for all our nodes!

Install Nginx

I suggest to use a separate server to run Nginx, for example t2.medium on AWS with Ubuntu 20.04 LTS (remember the instance's IP address). Login server console using SSH and then update software packages:

sudo apt update
sudo apt-get upgrade -y
sudo reboot

Wait some minutes until the instance finishes to reboot. Then install Nginx:

sudo apt install nginx
systemctl status nginx

Point domain and generate SSL certificate

Using the instance's IP address from previous section and point your subdomain, e.g. fullnode, to this address: Screen Shot 2021-09-21 at 12.47.02 PM.png

If you have not SSL certificate yet, you can generate a new one with Cloudflare (remember to save public and private key):

Screen Shot 2021-09-21 at 1.30.57 PM.png

Setup Nginx configuration

Add a config file:

sudo touch /etc/nginx/conf.d/full-node.conf
sudo vi /etc/nginx/conf.d/full-node.conf

Update config as below

server {
    listen 443 ssl default_server;
    listen [::]:443 ssl default_server;

    server_name fullnode.hiliamtech.com;

    ssl_certificate /etc/nginx/conf.d/cert.pem;
    ssl_certificate_key /etc/nginx/conf.d/cert.key;

    auth_basic "protected full node";
    auth_basic_user_file /etc/nginx/conf.d/full-node-pass;

    location /eth-1 {
        proxy_pass http://33.33.33.33:8545/;
    }

    location /eth-2 {
        proxy_pass http://44.44.44.44:8545/;
    }

Save SSL private key as ssl_certificate_key /etc/nginx/conf.d/cert.key, and SSL public key as /etc/nginx/conf.d/cert.pem. This will enable SSL protection for your connection. To deal with authentication, I use basic auth. Generate user file with:

printf "{username}:$(openssl passwd -crypt {password})" > full-node-pass
sudo mv full-node-pass /etc/nginx/conf.d

For example: printf "fullnode:$(openssl passwd -crypt nodepas)" > full-node-pass. The basic auth will be enabled in Nginx with:

auth_basic "protected full node";
auth_basic_user_file /etc/nginx/conf.d/full-node-pass;

Test config file and restart Nginx:

sudo nginx -t
sudo systemctl restart nginx

Check connection from your local machine (require geth)

$ geth attach https://fullnode:nodepas@fullnode.hiliamtech.com/eth-1
Welcome to the Geth JavaScript console!
...
To exit, press ctrl-d
>

Setup monitoring service

Create project:

mkdir node-gateway
cd node-gateway
npm init -y
touch main.js

Install required dependencies:

npm install web3
npm install discord.js

Read this and create a Discord bot to get the bot token. Then, update main.js with:

const Web3 = require('web3');
const axios = require('axios');
const { Client, Intents } = require('discord.js');

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

const channelID = '884022210142936081'
const token = 'your.bot.discord.token'

const rpcs = [
    "https://fullnode:nodepas@fullnode.hiliamtech.com/eth-1",
    "https://fullnode:nodepas@fullnode.hiliamtech.com/eth-2"
]

const getReportFunc = async function () {
    const client = new Client({ intents: [Intents.FLAGS.GUILDS] });
    client.once('ready', () => {
        console.log('discord bot is ready!');
    });
    await client.login(token);
    const channel = await client.channels.fetch(channelID);

    return async function (rpc, isOk, lastStatus) {
        const paths = rpc.split('/');
        const name = paths[paths.length - 1];

        console.log("syncing:", name, isOk);
        if (lastStatus[name] !== isOk) {
            let msg = ":scream: **" + name + "** is down!"
            if (isOk === true) {
                msg = ":yum: **" + name + "** is up!"
            }
            await channel.send(msg);
        }

        lastStatus[name] = isOk;
        return lastStatus
    }
}

const start = async function () {
    let report = await getReportFunc();
    let lastStatus = {};

    while (true) {
        console.log('checking syncing status ...');

        for (let id in gethRPCs) {
            try {
                let web3 = new Web3(new Web3.providers.HttpProvider(gethRPCs[id]));
                const syncing = await web3.eth.isSyncing();
                lastStatus = await report(gethRPCs[id], syncing === false, lastStatus);
            } catch (e) {
                lastStatus = await report(gethRPCs[id], false, lastStatus);
            }
        }
    }
}

start();

Finally, start the program with npm start. The result should be:

image.png

Yay, that's all. Thank you for your reading.

 
Share this