Installing Connector

Installing and running an ILP connector is not very difficult, especially if you have some basic knowledge about Linux. The installation takes about 1 hour. All you need is a server or virtual machine preferable with an own external IP address. You can even run the node on your favorite cloud provider such as AWS, Digital Ocean, etc.

Preface

Running a ILP connector, or any server, requires some basic knowledge. In addtion its probebly best *not* to have the ILP connector running on the same server as any other services (webserver, codius, etc) and keep it as lean as possible, not only to keep your ILP node stable, but also for security reasons as you might have a nice amount or XRP stalled on the node with its secret key. Our goal with this installation is not only to install just another ILP connect, but make sure its even PCI DSS compliant (which requires proper logging, firewall, antivirus and restricted access)

This manual assumes you have enough Linux skills. Also be sure to cycle thru all the steps Securing connector and Setting up peering. If needed visit the Troubleshooting page first, before asking questions while the answer is already within your reach. If you see errors in this documentation, please let us know so we can correct it.

This documentation is based on a Ubuntu 16.04 system. If you run another OS, most steps will be similair, however a few steps might not exactly match.

Requirements

For the ILP to operate there are a few requirements :

  • A server with at least 1 vCPU and 2 GB memory that is online 24/7 with 99,7% up-time preferably
  • A dedicated IP address, or enough skills for forward a bunch of ports thru your NAT router
  • a DNS name, like btp.mydomain.tld pointing to the IP address of the server (or the external IP of your NAT router)
  • If you are using NAT, forward ports 80, 443, 3000-3999
  • A dedicated XRP wallet for each ILP node you want to operate
  • If you run a private ILP connector : At least 40 XRP on funds
  • If you run a public ILP connector: At least 200 XRP on funds

Note: It’s considered best practice to monitor your ILP connect and its funds. It all funds are locked, endpoints might be stalled until funds free up. This could prevent your connected endpoints from operating the service (e.g. Codius) correctly. Running a public node with less then 200 XRP is unwise

Scripted installation

The scripted installation is the most easy way, however will result in a default installation which you might want to tweak here and there for security reasons.

wget -q -O ilp-install.sh https://raw.githubusercontent.com/xrp-community/ilp-connector-install/master/ilp-install.sh
chmod +x ilp-install.sh
./ilp-install.sh
npm install -g lev --unsafe-perm

Manual installation

Installing it manually takes a little longer, but it allows you to somewhat understand the installation setup. If you are trying to learn, its likely the best way to go.

Preinstallation:

Install an Ubuntu 16.04 LTS server, this documentation was build for Ubuntu and might not work copy/paste style under other Linux systems. However the commands  are somewhat self explanatory. The server has to have a domain name setup in advance with your DNS, and you need to think of a unique ILP address (something like g.myname).

If you are looking for a CentOS installation, the post from Stefan Thoman might be a beter starting point. It only covers the installation part, so you will still need to follow the other steps outlined on the ILP Exchange website.

You can install it any way you like, however one thing *must* be done correctly: The FQDN name you want to use for the ILP connect (e.g. btp1.mydomain.tld) has to be correctly set in the OS. You can check this by running hostname –fqdn that would return that name. You can configure this by:

Example for /etc/hosts:

127.0.0.1 localhost
127.0.1.1 btp1.mydomain.tld btp1

# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

Example for /etc/hostname:

btp1

After changing is, just reboot the machine. Then check it:

[email protected]:~# hostname --fqdn
btp1.mydomain.tld
Adding DNS Records

In order to run Codius, we need a primary hostname for our Codius host and we need any subdomains to point to our host also. For example, if your domain is example.com, you need to point codius.example.com to your Codius host, including any subdomains like xyz.codius.example.com.

To achieve that, we create two A records:

codius.example.com.    300     IN      A       203.0.113.1
*.codius.example.com.  300     IN      A       203.0.113.1

Replace codius.example.com with your Codius hostname and 203.0.113.1with the IP address of your Codius host.

Make sure you can ping your Codius host under its new hostname:

$ ping -c 1 codius.example.com
PING codius.example.com (203.0.113.1) 56(84) bytes of data.
64 bytes from 203.0.113.1 (203.0.113.1): icmp_seq=1 ttl=48 time=4.13 ms
--- codius.example.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 4.131/4.131/4.131/0.000 ms

And make sure it can be reached for any arbitrary subdomain as well:

$ ping -c 1 foobar.codius.example.com
PING foobar.codius.example.com (203.0.113.1) 56(84) bytes of data.
64 bytes from 203.0.113.1 (203.0.113.1): icmp_seq=1 ttl=48 time=3.72 m
--- foobar.codius.example.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 3.722/3.722/3.722/0.000 ms

Note that in both cases, the hostname resolved to our Codius host’s IP address, 203.0.113.1.

Now that our host is reachable, we’re ready to request a TLS certificate.

Steps as ‘root’ user:

Installation of packages:

add-apt-repository ppa:certbot/certbot

echo 'deb https://deb.nodesource.com/node_8.x xenial main' > /etc/apt/sources.list.d/nodesource.list
curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | sudo apt-key add -

apt-get update && apt-get dist-upgrade -y

apt-get install -y build-essential git httpie software-properties-common fail2ban nginx nodejs certbot ntp

npm install pm2 -g
npm install -g lev --unsafe-perm
npm install -g moneyd-gui --unsafe-perm

adduser connector

Prepare letsencrypt special webroot and DH file

openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
mkdir -p /var/lib/letsencrypt/.well-known
chgrp www-data /var/lib/letsencrypt
chmod g+s /var/lib/letsencrypt
rm -f /etc/nginx/sites-enabled/default

Replace default configuration files

rm -f /etc/nginx/snippets/letsencrypt.conf
cat <<EOT >> /etc/nginx/snippets/letsencrypt.conf
location ^~ /.well-known/acme-challenge/ {
  allow all;
  root /var/lib/letsencrypt/;
  default_type "text/plain";
  try_files \$uri =404;
}
EOT
rm -f /etc/nginx/snippets/ssl.conf
cat <<EOT >> /etc/nginx/snippets/ssl.conf
ssl_dhparam /etc/ssl/certs/dhparam.pem;

ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;

ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK';
ssl_prefer_server_ciphers on;

ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 30s;
EOT
rm -f /etc/nginx/snippets/security.conf
cat <<EOT >> /etc/nginx/snippets/security.conf
server_tokens off;
add_header Strict-Transport-Security "max-age=15768000; includeSubdomains; preload" always;
add_header X-Frame-Options "sameorigin" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Permitted-Cross-Domain-Policies "master-only" always;
add_header Set-Cookie "HttpOnly;Secure" always;
add_header Cache-Control "no-cache, no-store, must-revalidate" always;
add_header Pragma "no-cache" always;
add_header Expires "-1" always;
add_header X-Powered-By "ILP Connector on IX" always;
add_header X-Pingback "No pingback allowed" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; img-src 'self'; style-src 'self'; font-src 'self'; frame-src 'self'; object-src 'none'; report-uri https://`hostname --fqdn`/csp_report;" always;
EOT
rm -f /etc/nginx/sites-available/`hostname --fqdn`.conf
rm -f /etc/nginx/sites-enabled/`hostname --fqdn`.conf

cat <<EOT >> /etc/nginx/sites-available/`hostname --fqdn`.conf
server {
  listen 80;
  server_name `hostname --fqdn`;

  include snippets/letsencrypt.conf;
  include snippets/security.conf;
}
EOT

ln -s /etc/nginx/sites-available/`hostname --fqdn`.conf /etc/nginx/sites-enabled/`hostname --fqdn`.conf

/etc/init.d/nginx reload

Now we are ready to request our certificate. Just run this command and follow the prompts. Note, the following command requires you to add a VALID e-mail address (!). Only continue with the next step if your certificate request was successfully  completed.

certbot certonly --agree-tos --email [email protected] --webroot -w /var/lib/letsencrypt/ -d `hostname --fqdn`

Certbot will:

  • Ask you for your email address
  • Ask you to agree to the Terms of Service for Let’s Encrypt
  • Ask you if you want to get spam important messages from EFF
  • Ask you if you’re ok with your IP being logged publicly
  • Ask you to add a TXT record for _acme-challenge.codius.example.com
  • Ask you to add a second TXT record for _acme-challenge.codius.example.com
    If using AWS, simply add the two records as separate lines as described here.

Important: You need to add both records as separate TXT records. When you query TXT _acme-challenge.codius.example.com, you should see something like this:

$ dig TXT _acme-challenge.codius.example.com
;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 64965
;; flags: qr rd ra ; QUERY: 1, ANSWER: 2, AUTHORITY: 2, ADDITIONAL: 4
;; QUESTION SECTION:
;; _acme-challenge.codius.example.com. IN      TXT
;; ANSWER SECTION:
_acme-challenge.codius.example.com.    300     IN      TXT     "QwHjEBqK2RBhk5XyjriHPmjf2h2Ijettgy4BpwdVNlY"
_acme-challenge.codius.example.com.    300     IN      TXT     "YOMfcUWwPsW5hs2vl5AE/CRPg5m5BH7ORjEaUJReK4U"

If you did everything correctly, you should get a message like:

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/codius.example.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/codius.example.com/privkey.pem
   Your cert will expire on 2018-09-04. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - If you like Certbot, please consider supporting our work by:
   Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
   Donating to EFF:                  https://eff.org/donate-le

If you got this far, give yourself a pat on the back. The tough part is over! The renewal is a manual process that has to be repeated ever 90 days, else you will get SSL errors.

Add the SSL onto NGinx:

cat <<EOT >> /etc/nginx/sites-available/`hostname --fqdn`.conf
server {
  listen 443 ssl http2;
  server_name `hostname --fqdn`;

  ssl_certificate /etc/letsencrypt/live/`hostname --fqdn`/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/`hostname --fqdn`/privkey.pem;
  ssl_trusted_certificate /etc/letsencrypt/live/`hostname --fqdn`/chain.pem;

  include snippets/ssl.conf;
  include snippets/security.conf;

  location / {
    proxy_pass http://127.0.0.1:7443;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_connect_timeout 300;
    proxy_send_timeout 300;
    proxy_read_timeout 300;
    send_timeout 300;
    proxy_http_version 1.1;
    proxy_set_header Upgrade \$http_upgrade;
    proxy_set_header Connection "Upgrade";
  }
}
EOT

Note: Add anothe ‘allow x.x.x.x’ line with your trusted IP (not shared IP!)

cat <<EOT >> /etc/nginx/sites-available/gui.`hostname --fqdn`.conf
server {
  listen 1443 ssl;
  server_name `hostname --fqdn`;

  ssl_certificate /etc/letsencrypt/live/`hostname --fqdn`/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/`hostname --fqdn`/privkey.pem;
  ssl_trusted_certificate /etc/letsencrypt/live/`hostname --fqdn`/chain.pem;

  include snippets/ssl.conf;
  include snippets/security.conf;

  location / {
    allow 127.0.0.1;
    deny all;
   
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://d3js.org https://use.fontawesome.com; img-src 'self'; style-src 'self' 'unsafe-inline' https://maxcdn.bootstrapcdn.com; font-src 'self'; frame-src 'self'; object-src 'none'; report-uri https://`hostname --fqdn`/csp_report;" always;

    proxy_pass http://127.0.0.1:7770;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_connect_timeout 300;
    proxy_send_timeout 300;
    proxy_read_timeout 300;
    send_timeout 300;
  }
}
EOT
rm -f /etc/nginx/nginx.conf

cat <<EOT >> /etc/nginx/nginx.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid;

events {
  worker_connections 768;
  # multi_accept on;
}

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

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

  access_log /var/log/nginx/access.log;
  error_log /var/log/nginx/error.log;

  gzip on;
  gzip_disable "msie6";

  application/xml application/xml+rss text/javascript;

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

  include snippets/ssl.conf;
  include snippets/security.conf;
}
EOT

 

Steps as ‘connector’ user:
cd ~
mkdir app
mkdir data
git clone https://github.com/interledgerjs/tf-connector.git tf-connector
git clone https://github.com/interledgerjs/ilp-connector.git ~/app
cd ~/app
npm install --json
npm install ilp-plugin-xrp-paychan ilp-plugin-mini-accounts ilp-store-simpledb ilp-plugin-xrp-asym-server
wget -O ~/app/launch.config.js.default https://raw.githubusercontent.com/xrp-community/ilp-connector-docker/master/app/launch.config.js

Edit the ~/app/launch.config.js and change the element settings YOUR_HOT_WALLET_RIPPLE_ADDRESSYOUR_HOT_WALLET_RIPPLE_SECRET and MY_ILP_ADDRESS:

rm -f ~/app/launch.config.js

cat <<EOT >> ~/app/launch.config.js 
'use strict';

const path = require('path');

const address = 'YOUR_HOT_WALLET_RIPPLE_ADDRESS';
const secret = 'YOUR_HOT_WALLET_RIPPLE_SECRET';

const ilspServer = {
    relation: 'child',
    plugin: 'ilp-plugin-xrp-asym-server',
    assetCode: 'XRP',
    assetScale: 6,
    options: {
        port: 7443,
        xrpServer: 'wss://s2.ripple.com',
        address,
        secret
    }
}

const miniAccounts = {
    relation: 'child',
    plugin: 'ilp-plugin-mini-accounts',
    assetCode: 'XRP',
    assetScale: 9,
    options: {
        port: 7768
    }
};

const connectorApp = {
    name: 'connector',
    env: {
        DEBUG: 'ilp*,connector*',
        CONNECTOR_ILP_ADDRESS: 'MY_ILP_ADDRESS',
        CONNECTOR_ENV: 'production',
        CONNECTOR_BACKEND: 'one-to-one',
        CONNECTOR_ADMIN_API: true,
        CONNECTOR_ADMIN_API_PORT: 7769,
        CONNECTOR_SPREAD: '0',
        CONNECTOR_STORE_PATH: '/home/connector/data',
        CONNECTOR_ACCOUNTS: JSON.stringify({
            local: miniAccounts,
            ilsp: ilspServer
        })
    },
    script: path.resolve(__dirname, 'src/index.js')
};

module.exports = { apps: [ connectorApp ] };
EOT

 

 

Starting it up

When you following the above steps you have to follow the next two steps ‘Securing Connector‘ and ‘Setting up peering‘ before you can actually start the daemons. Once you have completed those steps you can run the following commands as user ‘connector’ (do NOT run them as root!)

cd ~/app

pm2 start moneyd-gui
pm2 start launch.config.js

Then check their running state with:

pm2 status

should output something like:
[email protected]:~/app$ pm2 status
┌────────────┬────┬──────┬───────┬────────┬─────────┬────────┬──────┬────────────┬───────────┬──────────┐
│ App name │ id │ mode │ pid │ status │ restart │ uptime │ cpu │ mem │ user │ watching │
├────────────┼────┼──────┼───────┼────────┼─────────┼────────┼──────┼────────────┼───────────┼──────────┤
│ connector │ 1 │ fork │ 23515 │ online │ 114 │ 22h │ 0.3% │ 100.3 MB │ connector │ disabled │
│ moneyd-gui │ 0 │ fork │ 9525 │ online │ 0 │ 47h │ 0% │ 81.0 MB │ connector │ disabled │
└────────────┴────┴──────┴───────┴────────┴─────────┴────────┴──────┴────────────┴───────────┴──────────┘
Use `pm2 show <id|name>` to get more details about an app

Also if you need to reload your connect configuration, you can do so with the following command:

pm2 reload launch.config.js --update-env