Context

After reviewing my previous posts (eg, this one) I caught myself on thinking that I tend to reveil issues in nodes configuration, which might be kind of impolite. So, in order to avoid being “an assh*le who is always highlighting the problems”, I decided to write a guide about public endpoints setup.

Plan

I assume you have already running node, it is hosted on a server with public IP.

The roadmap is the following:

  • Enable APIs
  • Setup DNS
  • Setup Nginx: upstreams
  • Setup Nginx: TLS

Enable APIs

Cosmos based validating nodes usually provide several APIs:

  • REST
  • RPC
  • gRPC

Enable REST API

This endpoint is controlled by config/app.toml configuration file, API Configuration section:

$ grep -B 1 -A 12 "API Configuration"  ./config/app.toml
###############################################################################
###                           API Configuration                             ###
###############################################################################

[api]

# Enable defines if the API server should be enabled.
enable = true

# Swagger defines if swagger documentation should automatically be registered.
swagger = true

# Address defines the API server to listen on.
address = "tcp://localhost:1317"

Make sure the following parameter is set to true: enable. You can also enable swagger, it provides documentation for the API and ability to perform requests from the browser UI. If you host several nodes on the same server, you need to use different ports in order to avoid conflicts - just replace 1317 in address parameter to whatever you want.

After configuration change, restart you node and check API is working:

$ sudo systemctl restart <your node service>
$ curl -I localhost:1317 # from the node terminal
HTTP/1.1 200 OK
X-Server-Time: 1716600371
Date: Sat, 25 May 2024 01:26:11 GMT
Content-Length: 860
Content-Type: text/html; charset=utf-8

HTTP 200 OK indicates everything is fine.

RPC

RPC is managed by ./config/config.toml configuration file, RPC Server Configuration Options section:

$ grep -B 1 -A 6 "RPC Server Configuration Options" ./config/config.toml
#######################################################
###       RPC Server Configuration Options          ###
#######################################################
[rpc]

# TCP or UNIX socket address for the RPC server to listen on
laddr = "tcp://127.0.0.1:26657"

Again, if you want to use custom port, just replace 26657 with proper value.

After any configuration chanhe you should restart your node:

$ sudo systemctl restart <your node service>

To check RPC endpoint run:

$ curl 127.0.0.1:26657 # from the node terminal
<html><body><br>Available endpoints:<br><br>Endpoints that require arguments:
...

gRPC

Need to work with ./config/app.toml again:

$ grep -B 1 -A 10 "gRPC Configuration" ./config/app.toml 
###############################################################################
###                           gRPC Configuration                            ###
###############################################################################

[grpc]

# Enable defines if the gRPC server should be enabled.
enable = true

# Address defines the gRPC server address to bind to.
address = "localhost:9090"

Make sure it is enabled and change the port if needed.

Now we have REST, RPC and gRPC endpoints enabled.

You might notice that they all are bound to localhost, so it is not possible to access them via public node ip. We will fix it later, during Nginx setup.

Setup DNS

In order you node was accessible via cool human readable address, DNS should be setup. It is a very generic topic, so you can learn more about the service at wiki. There are also many articles about DNS setup:

It is convenient to use different subdomains for different networks and APIs:

Setup Nginx: upstreams

Nginx is a very powerfull web server, I prefer to proxy all incoming traffic with it. First, need to install it:

$ sudo apt install -y nginx

The main configuration file is /etc/nginx/nginx.conf. In the http section one can see:

	##
	# Virtual Host Configs
	##

	include /etc/nginx/conf.d/*.conf;
	include /etc/nginx/sites-enabled/*;
}

It means one can have several configuration files inside /etc/nginx/conf.d/ and /etc/nginx/sites-enabled/, they will be included. We will use it to create config files per domain. Assuming the following subdomains are pointing to your node’s ip: api.galactica-t.example.com, rpc.galactica-t.example.com and grpc.galactica-t.example.com:

sudo tee /etc/nginx/conf.d/galactica-t.example.com > /dev/null << EOF

upstream api-galactica {
    server localhost:1317;
}

server {
    listen              80;
    server_name         api.galactica-t.example.com;
    keepalive_timeout   70;

    location / {
        proxy_pass           http://api-galactica$request_uri;
        proxy_set_header     Host $host;
    }
}

upstream rpc-galactica {
    server localhost:26657;
}

server {
    listen              80;
    server_name         rpc.galactica-t.example.com;
    keepalive_timeout   70;

    location / {
        proxy_pass           http://rpc-galactica$request_uri;
        proxy_set_header     Host $host;
    }

    location /websocket {
       proxy_pass http://rpc-galactica;
       proxy_http_version 1.1;
       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection "upgrade";
    }
}

upstream grpc-galactica {
    server localhost:9090;
}

server {
    listen              8080;

    location / {
    	grpc_pass grpc://grpc-galactica;
    }
}
EOF

Since now public endpoints should be available without TLS: http://api.galactica-t.example.com, http://rpc.galactica-t.example.com and rpc.galactica-t.example.com:8080:

curl -I http://api.galactica-t.example.com # should return 200 OK
curl http://rpc.galactica-t.example.com/status | jq . # should show json
grpcurl -plaintext grpc.galactica-t.example.com:8080 list # should show list of methods
wscat --connect ws://rpc.galactica-t.example.com/websocket \
-x { "jsonrpc": "2.0", "method": "status", "id": 1 } # should show json

Setup TLS

The last step of the setup is TLS. In order to enable it we need a valid certificate. There is a letsencrypt project which can issue a free certificate. This article is amazing guide how to setup Nginx with letsencrypt certificate. Certbot will also update config files, one should make sure they are fine (assuming certificate was issued for all domains and located in /etc/letsencrypt/live/api.galactica-t.example.com/ folder):

$ cat /etc/nginx/conf.d/galactica-t.example.com

upstream api-galactica {
    server localhost:1317;
}

server {
    listen              443 ssl http2;
    server_name         api.galactica-t.example.com;
    keepalive_timeout   70;

    ssl_certificate     /etc/letsencrypt/live/api.galactica-t.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.galactica-t.example.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot

#    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
#    ssl_ciphers         HIGH:!aNULL:!MD5;
    #...
    # Redirect non-https traffic to https
    if ($scheme != "https") {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    location / {
        proxy_pass           http://api-galactica$request_uri;
        proxy_set_header     Host $host;
    }
}

upstream rpc-galactica {
    server localhost:26657;
}

server {
    listen              443 ssl http2;
    server_name         rpc.galactica-t.example.com;
    keepalive_timeout   70;

    ssl_certificate     /etc/letsencrypt/live/api.galactica-t.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.galactica-t.example.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot

#    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
#    ssl_ciphers         HIGH:!aNULL:!MD5;
    #...
    # Redirect non-https traffic to https
    if ($scheme != "https") {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    location / {
        proxy_pass           http://rpc-galactica$request_uri;
        proxy_set_header     Host $host;
    }

    location /websocket {
       proxy_pass http://rpc-galactica;
       proxy_http_version 1.1;
       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection "upgrade";
    }
}

upstream grpc-galactica {
    server localhost:9090;
}

server {
    listen              9443 ssl http2;

    ssl_certificate     /etc/letsencrypt/live/api.galactica-t.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.galactica-t.example.com/privkey.pem;

    location / {
        grpc_pass grpc://grpc-galactica;
    }
}

Since now endpoints should be available with TLS:

curl -I https://api.galactica-t.example.com # should return 200 OK
curl https://rpc.galactica-t.example.com/status | jq . # should show json
grpcurl grpc.galactica-t.example.com:9443 list # should show list of methods
wscat --connect wss://rpc.galactica-t.example.com/websocket \
-x { "jsonrpc": "2.0", "method": "status", "id": 1 } # should show json

That’s it. REST, RPC (including websocket) and gRPC endpoints are working over TLS.