Ensure the security of your smart contracts

How to Set Up Your Own Forta/Erigon Node

Author: Evgeny Pleskach
Security researcher at MixBytes
Intro
Forta is a real-time detection network for the security & operational monitoring of blockchain activity.

As a decentralized monitoring network, Forta instantly detects threats and anomalies on DeFi, NFT, governance, bridges, and other Web3 systems. It helps protocols and investors to react quickly to neutralize threats and prevent or minimize loss of funds by giving timely and relevant alerts about the security and health of owned or dependent systems.

Forta has a decentralized network of scanner nodes; anybody can participate in this network and run their own scanner node.

In this article, we want to describe all the necessary steps to run a scanner node with some security recommendations and best practices.

Official documentation: https://docs.forta.network/en/latest/scanner-quickstart/
Buying a server for Forta
According to the documentation, the requirements are below:
  • 64-bit Linux distribution
  • CPU with 4+ cores
  • 16GB RAM
  • Connection to Internet
  • Docker v20.10+
  • 100GB SSD (in addition to full node requirements)
  • Recommended: Full node (any chain)

You can choose any cloud, for example, DigitalOcean with the following parameters is selected:
  • 4 vCPUs
  • 32GB RAM
  • 100GB Disk
  • AMS
Basic server setup for Forta
Users setup

# login as a root user and create a new one
adduser bastion
passwd bastion

# install editors on the system
apt install mc nano -y

# give root permissions to the user, add a line to the file
# open /etc/sudoers via mcedit
mcedit /etc/sudoers

# add this line to the file and close file
bastion ALL=(ALL:ALL) NOPASSWD:ALL

# go to user
su - bastion

# create a directory and a file to store
mkdir .ssh && nano .ssh/authorized_keys
# a public ssh key should be here

# change permissions to the directory and file
chown -R bastion:bastion /home/bastion/.ssh/ && chmod 700 /home/bastion/.ssh/ && chmod 600 /home/bastion/.ssh/authorized_keys

# disable root user remotely
sudo mcedit /etc/ssh/sshd_config 
# add line to the file /etc/ssh/sshd_config
PermitRooLogin no

# restart ssh service 
sudo systemctl restart sshd
Basic server protection for Forta
In the previous step, we disabled the root user remotely, and we need to configure a firewall to access the server on port 22.
Here is an example of the steps to take in DigitalOcean cloud:

1. Go to the admin panel: https://cloud.digitalocean.com/
2. Find your server and go to its settings.
3. Find the Firewall section and go to it (there is no firewall by default, you need to create it and add it to the desired server).
4. Change the rules.
4.1. We recommend to allow all ports to the admin IP address and prohibit everything except ports for Forta operation.
5. Add the server and wait for the rules to apply.
Installation of components to work with Forta
To produce correct timestamps on the alerts and avoid authorization problems at the time of publishing alerts, you must ensure at all times that the system time is correct. If the system time is not correct, your node will fail to publish alerts and may generate no rewards as a result.

We suggest using systemd-timesyncd which is widely available and sufficient as a time synchronization daemon. After started, it will periodically synchronize the system time in background.

To enable systemd-timesyncd and check the result, you can do as follows:

$ sudo systemctl enable systemd-timesyncd
$ sudo systemctl start systemd-timesyncd
$ timedatectl status
               Local time: Tue 2022-01-01 17:00:00 -03
           Universal time: Tue 2022-01-01 20:00:00 UTC
                 RTC time: Tue 2022-01-01 20:00:00
                Time zone: America/Argentina/Buenos_Aires (-03, -0300)
System clock synchronized: yes
              NTP service: active  <------------------- (it worked)
          RTC in local TZ: no
Install Docker (at least v20.10)
Official guide: https://docs.docker.com/engine/install/

We must add network params for working docker.
Add a file called daemon.json to your /etc/docker directory with the following contents:

{
   "default-address-pools": [
        {
            "base":"172.17.0.0/12",
            "size":16
        },
        {
            "base":"192.168.0.0/16",
            "size":20
        },
        {
            "base":"10.99.0.0/16",
            "size":24
        }
    ]
}
To apply changes, we must run:

systemctl restart docker
Forta installation
The Forta scan node software is available for popular 64-bit Linux distributions using official Forta repositories. Package installation methods are verifiable (auto-verified during installation) and help you install required dependencies.

$ sudo curl https://dist.forta.network/pgp.public -o /usr/share/keyrings/forta-keyring.asc -s
$ echo 'deb [signed-by=/usr/share/keyrings/forta-keyring.asc] https://dist.forta.network/repositories/apt stable main' | sudo tee -a /etc/apt/sources.list.d/forta.list
$ sudo apt-get update
$ sudo apt-get install forta
Forta setup
Initialization creates a private key that will sign the alerts from your scan node. You must set the FORTA_PASSPHRASE environment variable or provide the --passphrase flag to the init command.

Initialize Forta using the forta init command:

$ forta init --passphrase <your_passphrase>
This command generates a config directory, a private key and outputs your address.
Example output:

Scanner address: 0xAAA8C491232cB65a65FBf7F36b71220B3E695AAA
Successfully initialized at /yourname/.forta
Configure systemd
If Forta ever stops running, it must be restarted. If you used a package installation method, there is a Forta systemd service that can be enabled and overridden with your passphrase and config directory environment variables.

Please, do not modify the original forta.service file and instead prefer the override recommended here. This is needed because the original file will be replaced next time you update Forta through yum or apt.

To override the systemd service environment, you can set the variables in /etc/systemd/system/forta.service.d/env.conf like:

[Service]
Environment="FORTA_DIR=<your_forta_config_dir>"
Environment="FORTA_PASSPHRASE=<your_forta_passphrase>"
making edits to the configuration

mcedit .forta/config.yml

chainId: 1

scan:
  jsonRpc:
    #  you can read more about setting up your own erigon node below
    url: http://your-node-erigon:8545

trace:
  jsonRpc:
    url: http://your-node-erigon:8545

# Defaulting to scan node url because it is not set - the best option when running a node
# jsonRpcProxy:
#   jsonRpc:
#     url: http://your-node-erigon:8545
After you need to add the process to autoload and service start.

sudo systemctl daemon-reload
sudo systemctl enable forta
sudo systemctl start forta
Forta check

forta status
docker ps
example output

root@forta-prod:~# forta status
forta.container.forta-inspector.summary
⬤ ok

forta.container.forta-json-rpc.summary
⬤ ok

forta.container.forta-scanner.summary
⬤ ok: at block 15933557.

forta.container.forta-supervisor.summary
⬤ ok: all 6 service containers are running.

forta.container.forta-updater.summary
⬤ ok
You can find out your scan node address with command

forta account address
Register Scan Node
Your scan node has an Ethereum address that makes two main features possible:
  • Receiving detection bots to run
  • Asserting an authority on the outputted alerts

While this address remains as the main identity, it must be owned by a different wallet. After registration, the scan node is minted as an NFT (ERC721) and transferred to this owner. You can check the smart contract documentation here:
https://docs.forta.network/en/latest/smart-contracts/

In the future, the owner wallet will allow you to disable your scan node remotely and avoid slashing while you do maintenance (for a short period) or when you decide to shut down your node entirely. Right now, forta disable and forta enable commands are available to you to do the same using the scan node private key.

To register your node to the registry contract, you can run

forta register --owner-address <address>
Make sure you have set the chainId in your config.yml correctly before executing this. Your scan node can be registered only once and to scan a specific chain. The owner wallet address needs to be a different address than your scan node address.

About stacking you can read here: https://docs.forta.network/en/latest/stake-on-scan-node/

Check forta working:

https://api.forta.network/stats/sla/scanner/YOUR_NODE_ADDRESS
Buying server for Erigon:
https://github.com/ledgerwatch/erigon#system-requirements

We recommend to buy a server meeting the requiremens in the same way in any cloud.
For example, on hetzner:

  • Dedicated Root Server AX101
  • AMD Ryzen 9 5950X 16 Cores (Zen3)
  • 128 GB DDR4 ECC RAM
  • 2 x 3.84 TB NVMe SSD Datacenter Edition (Software-RAID 1)
  • 1 Gbit/s bandwidth
Basic server setup for Erigon
Similar to the article for Forta: Basic server setup for Forta
Basic server protection for Erigon
Similar to the article for Forta: Basic server protection for Forta
Setting components to work with Erigon

apt install git make supervisor build-essential software-properties-common

# installing prysm
mkdir ethereum
cd ethereum/
mkdir consensus
cd consensus/
mkdir prysm
cd prysm/
curl https://raw.githubusercontent.com/prysmaticlabs/prysm/master/prysm.sh --output prysm.sh && chmod +x prysm.sh

mcedit /root/.bashrc. 
# add a line, mandtory indication of the prysm version and close the file
export USE_PRYSM_VERSION=v3.1.1

# generate a JWT token
cd ethereum/consensus/prysm/
./prysm.sh beacon-chain generate-auth-secret

# create a manifest to launch the prysm
cd /etc/supervisor/conf.d/
mcedit prysm.conf
# add to prysm.conf file
---
[program:beacon_eth]
command=/root/ethereum/consensus/prysm/prysm.sh beacon-chain --execution-endpoint=http://localhost:8551 --jwt-secret=/root/ethereum/consensus/prysm/jwt.hex --verbosity=debug
user=root
numprocs=1
autostart=true
autorestart=true
stderr_logfile=/var/log/prysm.log
---
# close file and call next command
systemctl restart supervisor

# check that the service has started
supervisorctl

# example of command output
supervisorctl
beacon_eth                       RUNNING   pid 383637, uptime 30 days, 5:52:02
supervisor>
Go installation is performed as per documentation: https://go.dev/doc/install

setting the time on the server:

systemctl enable systemd-timesyncd
sudo systemctl start systemd-timesyncd
timedatectl status
Erigon installation

git clone --recurse-submodules https://github.com/ledgerwatch/erigon.git
cd erigon
git checkout alpha
make erigon
make rpcdaemon
Erigon setup
Setting up services:

# create the service
mcedit /etc/systemd/system/erigon.service
---
[Unit]
Description=Erigon Node
After=network.target network-online.target
Wants=network-online.target

[Service]
WorkingDirectory=/root/erigon/
ExecStart=/root/erigon/build/bin/erigon --datadir=/erigon --private.api.addr=localhost:9090 --metrics --metrics.addr=localhost --metrics.port=6060 --http=false --authrpc.jwtsecret /root/ethereum/consensus/prysm/jwt.hex
User=root
Restart=always
RestartSec=5s

# Output to syslog
StandardOutput=syslog
StandardError=syslog
#Change this to find app logs in /var/log/syslog
SyslogIdentifier=erigon

[Install]
WantedBy=multi-user.target
---

# create the second service
mcedit /etc/systemd/system/erigon-rpc.service
---
[Unit]
Description=Erigon RPC Daemon

[Service]

WorkingDirectory=/root/erigon/
ExecStart=/root/erigon/build/bin/rpcdaemon --datadir=/erigon --private.api.addr=localhost:9090 --http.vhosts '*' --http.port 8545 --http.addr 0.0.0.0 --http.corsdomain '*' --http.api=eth,erigon,web3,net,debug,trace
User=root
Restart=always
RestartSec=5s

# Output to syslog
StandardOutput=syslog
StandardError=syslog
#Change this to find app logs in /var/log/syslog
SyslogIdentifier=erigon-rpc

[Install]
WantedBy=multi-user.target
---

sudo systemctl daemon-reload
sudo systemctl enable erigon
sudo systemctl enable erigon-rpc
sudo systemctl start erigon
sudo systemctl start erigon-rpc
Erigon check

curl http://localhost:3500/eth/v1/node/syncing
# how to check logs 
journalctl -f -u erigon
journalctl -f -u erigon-rpc
tail -f /var/log/prysm.log
Server monitoring for Forta/Erigon
We recommend to run on two different servers.
Installing prometheus on the server:

mkdir -p /etc/prometheus
sudo mkdir -p /var/lib/prometheus
# download prometheus archive
wget https://github.com/prometheus/prometheus/releases/download/v2.31.0/prometheus-2.31.0.linux-amd64.tar.gz

# unpack archive
tar -xvf prometheus-2.31.0.linux-amd64.tar.gz

# moving binary files to the system directories
cd prometheus-2.31.0.linux-amd64/
sudo mv prometheus promtool /usr/local/bin/
sudo mv consoles/ console_libraries/ /etc/prometheus/
sudo mv prometheus.yml /etc/prometheus/prometheus.yml

# prometheus version check
prometheus --version

# create the prometheus service
sudo groupadd --system prometheus
sudo useradd -s /sbin/nologin --system -g prometheus prometheus
sudo chown -R prometheus:prometheus /etc/prometheus/ /var/lib/prometheus/
sudo chmod -R 775 /etc/prometheus/ /var/lib/prometheus/
# create the service
mcedit /etc/systemd/system/prometheus.service
----
[Unit]
Description=Prometheus
Wants=network-online.target
After=network-online.target

[Service]
User=prometheus
Group=prometheus
Restart=always
Type=simple
ExecStart=/usr/local/bin/prometheus \
    --config.file=/etc/prometheus/prometheus.yml \
    --storage.tsdb.path=/var/lib/prometheus/ \
    --web.console.templates=/etc/prometheus/consoles \
    --web.console.libraries=/etc/prometheus/console_libraries \
    --web.listen-address=0.0.0.0:9090

[Install]
WantedBy=multi-user.target
----

# launch the service, add to startup, check the service status
sudo systemctl start prometheus
sudo systemctl enable prometheus
sudo systemctl status prometheus

# check availability in browser
http://IP:9090

# add parameters to configuration file
mcedit /etc/prometheus/prometheus.yml

# add to targets (local node-exporter)
scrape_configs:
  - job_name: "prometheus"
    static_configs:
      - targets: ["localhost:9090"]

  - job_name: 'servers'
    static_configs:
    - targets: ['127.0.0.1:9100']

sudo systemctl restart prometheus
Installing Grаfana locally

# download dependencies

sudo apt-get install -y gnupg2 curl software-properties-common

# install Grafana
curl https://packages.grafana.com/gpg.key | sudo apt-key add -
sudo add-apt-repository "deb https://packages.grafana.com/oss/deb stable main"
apt-get -y install grafana

# add to startup and check the service status
systemctl enable --now grafana-server
systemctl status grafana-server.service

# check availability in browser
http://IP:3000

# default authorization is admin/admin

add prometheus http://localhost:9090 to datasource

# import dashboard with id=10180
Installing node-exporter locally

# create a user to run the service
useradd --no-create-home --shell /bin/false node_exporter

# download the archive for further work
wget https://github.com/prometheus/node_exporter/releases/download/v1.3.1/node_exporter-1.3.1.linux-amd64.tar.gz

# unpack the archive
tar xvf node_exporter-1.3.1.linux-amd64.tar.gz

# copy the binary file to the system, issue the necessary rights
cp node_exporter-1.3.1.linux-amd64/node_exporter /usr/local/bin
chown node_exporter:node_exporter /usr/local/bin/node_exporter
mkdir -p /prometheus/metrics
chown node_exporter:node_exporter /prometheus/metrics

# create the service
mcedit /lib/systemd/system/node_exporter.service
---
[Unit]
Description=Prometheus
Wants=network-online.target
After=network-online.target

[Service]
Type=simple
User=node_exporter
Group=node_exporter
ExecStart=/usr/local/bin/node_exporter --collector.textfile.directory=/prometheus/metrics --web.listen-address=0.0.0.0:9100
Restart=always
RestartSec=10s
NotifyAccess=all

[Install]
WantedBy=multi-user.target
---
systemctl enable node_exporter
systemctl start node_exporter
systemctl status node_exporter

# check availability in browser
http://IP:9100
Additional for Erigon:

# add to file /etc/prometheus/prometheus.yml

mcedit /etc/prometheus/prometheus.yml

  - job_name: erigon
    metrics_path: /debug/metrics/prometheus
    static_configs:
      - targets:
        - "127.0.0.1:6060"
Import dashboard to Grafana:
Download https://github.com/ledgerwatch/erigon/blob/devel/cmd/prometheus/dashboards/erigon.json and set the downloaded file.
Other posts