# eddie.thgttg.com
an ethereum full node built with rubberneck.
`thgttg.com` is a domain referencing douglas adam's well known work. hostnames are chosen from the character list of that plot. in this case, ***e**ddie* shares his initial with ***e**thereum*.
## system spec
hp proliant dl380 g7, 2u rack mount
- cpu: 2 × intel xeon e5620 @ 2.4ghz
- ram: 16gb
- hdd (os): 8 × 146gb, 15k rpm sas, hw raid 1+0 for 512gb total
- ssd (data): a 4tb wd ssd that i had laying around is mounted as the `/data` partition for the blockchain data dirs
- os: fedora server 38 (vanilla en-us)
in hindsight, and with the benefit of the [node dashboard](https://marvin.thgttg.com/grafana/d/rYdddlPWk/node-exporter-full?orgId=1&refresh=10s), it's obvious this hardware is maxed out running geth/prysm. next time i spin up an eth node, it'll be on beefier hardware.
build references:
- https://geth.ethereum.org/docs/getting-started/installing-geth
- https://geth.ethereum.org/docs/monitoring/metrics
- https://docs.prylabs.network/docs/install/install-with-script
- https://docs.prylabs.network/docs/prysm-usage/parameters
- https://docs.prylabs.network/docs/prysm-usage/checkpoint-sync
- https://docs.prylabs.network/docs/troubleshooting/issues-errors
## server and network init
- create a dns entry for eddie.thgttg.com as a cname record pointing to the dynamic dns entry for the rack location
```bash
#!/usr/bin/env bash
curl \
--request POST \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer $(pass cloudflare/thgttg.com/dns-api-key)" \
--data '{
"type": "CNAME",
"name": "eddie.thgttg.com",
"content": "mp.thgttg.com"
}' \
https://api.cloudflare.com/client/v4/zones/80ac5f73f4e147bb0079d96e1497bf07/dns_records
```
- manually configure the site router to
- assign persistent lan ip `192.168.0.194` to `eddie.thgttg.com`
- port forward
- inbound tcp on wan port `2204` to lan `192.168.0.194:22`
- inbound udp on wan port `12000` to lan `192.168.0.194:12000`
- inbound tcp on wan port `13000` to lan `192.168.0.194:13000`
- inbound tcp+udp on wan port `30303` to lan `192.168.0.194:30303`
- configure site nginx to:
- reverse proxy eddie.thgttg.com:80 to 192.168.0.194:80
- reverse proxy eddie.thgttg.com:443 to 192.168.0.194:443
- enable ssh access on first boot
```bash
#!/usr/bin/env bash
mkdir -p ~/.ssh
curl -Lo ~/.ssh/authorized_keys https://github.com/grenade.keys
```
- set hostname
```bash
#!/usr/bin/env bash
sudo hostnamectl set-hostname eddie.thgttg.com
```
- open firewall ports (handled in rubberneck but documented here because it took a bit of trial and error to get all the ports)
```bash
#!/usr/bin/env bash
# see: https://docs.prylabs.network/docs/prysm-usage/p2p-host-ip
for port_proto in {{3500,6060,8545,9100,13000}/tcp,12000/udp,30303/{tcp,udp}}; do
sudo firewall-cmd --zone=$(sudo firewall-cmd --get-default-zone) --add-port=${port_proto} --permanent
done
sudo firewall-cmd --reload
sudo firewall-cmd --list-all
```
- reset chain state. i got errors in the prysm journal (`journalctl -fu prysm.service`), including:
> Sep 01 08:07:48 eddie.thgttg.com prysm.sh[404877]: could not get ancestor state: slot 7223583 not in db due to checkpoint sync: cannot retrieve data for slot
which suggests a corrupt chain database which [requires](https://docs.prylabs.network/docs/troubleshooting/issues-errors) clearing out the data directory and restarting the chain.
```bash
#!/usr/bin/env bash
sudo systemctl stop geth.service
sudo systemctl stop prysm.service
sudo rm -rf /data/ethereum/{beaconchaindata,geth,geth.ipc,keystore}
sudo systemctl start geth.service
sudo systemctl start prysm.service
```
- rubberneck automation handles the rest: https://github.com/grenade/rubberneck/compare/7c2f943..d5b4b09
- geth/prysm user (eddie) and data directory (`/data/ethereum`) creation and permissions
- geth binary install
- geth service install, enable and start
- prysm script/binary install
- prysm service install, enable and start
- services keep-alive/hard-restart
- os security updates
# marvin.thgttg.com
a prometheus and grafana server.
***m**arvin* shares his initial with ***m**etrics*.
## server and network init
- create a dns entry for marvin.thgttg.com as an A record pointing to the static public ip for the hetzner instance
```bash
#!/usr/bin/env bash
curl \
--request POST \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer $(pass cloudflare/thgttg.com/dns-api-key)" \
--data '{
"type": "A",
"name": "marvin.thgttg.com",
"content": "95.216.75.94"
}' \
https://api.cloudflare.com/client/v4/zones/80ac5f73f4e147bb0079d96e1497bf07/dns_records
# flush the local dns cache
sudo resolvectl flush-caches
```
- (re)image instance with clean partitions and a vanilla os install
- set hostname
- configure software raid
- partition drives (boot, lvm)
- create logical volumes on lvm:
- swap (16gb)
- / (64gb)
- /var/lib (512gb)
- /var/log (32gb)
- install os
- reboot
- remove rescue system host key
```bash=
#!/usr/bin/env bash
# see https://github.com/hetzneronline/installimage/blob/master/get_options.sh
fqdn=marvin.thgttg.com
ssh-keygen -R ${fqdn}
ssh -o StrictHostKeyChecking=accept-new root@${fqdn} \
/root/.oldroot/nfs/install/installimage \
-a \
-n ${fqdn} \
-r yes \
-l 1 \
-d sda,sdb \
-p /boot:ext3:1024M,lvm:vg0:all \
-v vg0:swap:swap:swap:16G,vg0:root:/:ext4:64G,vg0:lib:/var/lib:ext4:512G,vg0:log:/var/log:ext4:32G \
-i /root/images/CentOS-90-stream-amd64-base.tar.gz \
-G yes \
-K https://github.com/grenade.keys
ssh root@${fqdn} reboot
ssh-keygen -R ${fqdn}
sleep 300
ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=1 root@${fqdn} dnf update -y
```
- create superuser
```bash=
#!/usr/bin/env bash
user=grenade
fqdn=marvin.thgttg.com
ssh root@${fqdn} "
getent passwd ${user} &> /dev/null || useradd --create-home --shell /bin/bash --user-group --groups wheel,systemd-journal ${user};
test -d /home/${user}/.ssh || sudo -H -u ${user} mkdir /home/${user}/.ssh;
chmod 700 /home/${user}/.ssh;
sudo -H -u ${user} bash -c 'curl -Lo /home/${user}/.ssh/authorized_keys https://github.com/grenade.keys';
chmod 644 /home/${user}/.ssh/authorized_keys;
echo '${user} ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/${user};
chmod 0440 /etc/sudoers.d/${user};
"
```
- rubberneck automation handles the rest: https://github.com/grenade/rubberneck/compare/d5b4b09..4c404a4
- prometheus binary install
- prometheus configuration
- prometheus service install, enable and start
- grafana install
- grafana configuration
- grafana service enable and start
- nginx install
- nginx configuration
- reverse proxy prometheus over tls
- reverse proxy grafana over tls
- certbot install
- obtain tls cert, configured for auto-renewals
- services keep-alive/hard-restart
- os security updates
- grafana manual config (in the [grafana ui](https://marvin.thgttg.com/grafana))
- enable github oauth in grafana config
- create (local) prometheus datasource
- import dashboards:
- [geth overview](https://grafana.com/grafana/dashboards/14053-geth-overview)
- [node exporter full](https://grafana.com/grafana/dashboards/1860-node-exporter-full)
# outputs from this excercise
- geth/prysm full node on private datacenter lan bare metal with nat and tls proxy for access to public endpoints: eddie.thgttg.com
- prometheus/grafana server on public (hetzner robot) bare metal: marvin.thgttg.com
- grafana dashboards (login with github. you must be a member of a github org shortlist):
- [geth overview](https://marvin.thgttg.com/grafana/d/FPpjH6Hik/geth-overview?orgId=1&refresh=10s)
- [node exporter full](https://marvin.thgttg.com/grafana/d/rYdddlPWk/node-exporter-full?orgId=1&var-DS_PROMETHEUS=default&var-job=thgttg.com%20-%20node&var-node=eddie.thgttg.com:443&var-diskdevices=%5Ba-z%5D%2B%7Cnvme%5B0-9%5D%2Bn%5B0-9%5D%2B%7Cmmcblk%5B0-9%5D%2B)