mirror of
https://github.com/logos-storage/logos-storage-nim.git
synced 2026-06-28 05:19:28 +00:00
399 lines
9.9 KiB
Markdown
399 lines
9.9 KiB
Markdown
# Logos Storage Node Machine Setup Recipe
|
|
|
|
This recipe describes how to prepare a fresh machine to run a headless Logos Storage node. It is written for a human operator or an AI agent that can SSH into the target machine and execute administrative commands.
|
|
|
|
The first section is system-independent. The second section gives the concrete Ubuntu 24.04 setup used for the Linode test node.
|
|
|
|
## Goal
|
|
|
|
Set up a machine that:
|
|
|
|
- Runs Logos Storage as a non-root service user.
|
|
- Keeps the REST API bound to localhost only.
|
|
- Exposes only the P2P and discovery ports publicly.
|
|
- Can be administered over SSH using key-based login.
|
|
- Starts the node automatically on boot using the host service manager.
|
|
|
|
## Target Runtime Shape
|
|
|
|
| Purpose | Default Used Here | Protocol | Exposure |
|
|
|---|---:|---|---|
|
|
| SSH | `22` | TCP | Restricted to operator IP |
|
|
| P2P listen | `8070` | TCP | Public |
|
|
| Discovery | `8090` | UDP | Public |
|
|
| REST API | `8080` | TCP | Localhost only |
|
|
|
|
The storage node command should look conceptually like:
|
|
|
|
```bash
|
|
storage \
|
|
--data-dir=<data-dir> \
|
|
--listen-port=8070 \
|
|
--disc-port=8090
|
|
```
|
|
|
|
Do not pass `--api-bindaddr=0.0.0.0` unless there is a deliberate reason to expose the API. The default API bind address is `127.0.0.1`.
|
|
|
|
## Generic Setup Steps
|
|
|
|
1. Provision a machine.
|
|
|
|
Choose a machine with enough resources for the intended workload. For running the node, a small machine can work. For compiling on the machine, prefer at least 2 vCPU and 2-4 GB RAM. A 1 vCPU / 1 GB RAM machine can build, but slowly and may need extra swap.
|
|
|
|
2. Configure firewall rules.
|
|
|
|
Inbound rules:
|
|
|
|
| Purpose | Protocol | Port | Source |
|
|
|---|---:|---:|---|
|
|
| SSH | TCP | `22` | Operator IP only, if possible |
|
|
| Storage P2P | TCP | `8070` | Public IPv4/IPv6 as needed |
|
|
| Storage Discovery | UDP | `8090` | Public IPv4/IPv6 as needed |
|
|
|
|
Do not open `8080/TCP` publicly. Access the API over SSH tunneling.
|
|
|
|
3. Create a non-root service user.
|
|
|
|
Create a dedicated user, for example `storage`. The node process should run as this user, not as `root`.
|
|
|
|
4. Configure SSH access.
|
|
|
|
Install the operator public SSH key for the service/admin user. Verify login works before disabling root login. Then harden SSH:
|
|
|
|
```text
|
|
PermitRootLogin no
|
|
PasswordAuthentication no
|
|
PubkeyAuthentication yes
|
|
```
|
|
|
|
5. Install build/runtime prerequisites.
|
|
|
|
Install a C/C++ toolchain, `git`, `cmake`, `curl`, `make`, `bash`, and runtime libraries required by the build. If building from source, install Nim `2.2.10`, preferably through `choosenim`.
|
|
|
|
6. Clone and build Logos Storage.
|
|
|
|
Clone `https://github.com/logos-storage/logos-storage-nim.git`, initialize submodules, and build:
|
|
|
|
```bash
|
|
make update
|
|
make NIMFLAGS="-d:disableMarchNative"
|
|
```
|
|
|
|
Use low parallelism on small machines:
|
|
|
|
```bash
|
|
make -j1 NIMFLAGS="-d:disableMarchNative"
|
|
```
|
|
|
|
`-d:disableMarchNative` is recommended for Linux amd64 release/test artifacts to avoid leaking build-machine CPU choices into the binary and to avoid known GCC/secp256k1 x86_64 asm failures.
|
|
|
|
7. Install the binary.
|
|
|
|
Install the built binary somewhere stable, for example:
|
|
|
|
```text
|
|
/opt/logos-storage/storage
|
|
```
|
|
|
|
Keep source checkout and runtime data separate.
|
|
|
|
8. Create the data directory.
|
|
|
|
Use a dedicated persistent data directory owned by the service user, for example:
|
|
|
|
```text
|
|
/var/lib/logos-storage
|
|
```
|
|
|
|
9. Create a service.
|
|
|
|
Use the platform service manager to run the node as the non-root service user, restart on failure, and start on boot.
|
|
|
|
10. Verify.
|
|
|
|
Verify:
|
|
|
|
- Service is running.
|
|
- TCP `8070` listens on all addresses.
|
|
- UDP `8090` listens on all addresses.
|
|
- TCP `8080` listens only on `127.0.0.1`.
|
|
- `GET http://127.0.0.1:8080/api/storage/v1/peerid` returns a peer ID locally.
|
|
- External TCP connectivity to `8070` works.
|
|
|
|
## Ubuntu 24.04 Example
|
|
|
|
This example assumes:
|
|
|
|
- Ubuntu 24.04 LTS.
|
|
- Initial SSH access as `root`.
|
|
- Desired user: `storage`.
|
|
- Public IP: replace `SERVER_IP` with the host IP.
|
|
- Repository branch: `master`.
|
|
|
|
### 1. Update The System
|
|
|
|
```bash
|
|
apt-get update
|
|
DEBIAN_FRONTEND=noninteractive apt-get -y upgrade
|
|
DEBIAN_FRONTEND=noninteractive apt-get -y full-upgrade
|
|
reboot
|
|
```
|
|
|
|
Reconnect after reboot and verify:
|
|
|
|
```bash
|
|
apt list --upgradable
|
|
test ! -f /var/run/reboot-required
|
|
```
|
|
|
|
### 2. Create The `storage` User
|
|
|
|
```bash
|
|
adduser --disabled-password --gecos "" storage
|
|
usermod -aG sudo storage
|
|
install -d -m 700 -o storage -g storage /home/storage/.ssh
|
|
install -m 600 -o storage -g storage /root/.ssh/authorized_keys /home/storage/.ssh/authorized_keys
|
|
```
|
|
|
|
Optional passwordless sudo for bootstrap/admin work:
|
|
|
|
```bash
|
|
printf 'storage ALL=(ALL) NOPASSWD:ALL\n' > /etc/sudoers.d/90-storage-user
|
|
chmod 440 /etc/sudoers.d/90-storage-user
|
|
visudo -cf /etc/sudoers.d/90-storage-user
|
|
```
|
|
|
|
From the operator machine, verify:
|
|
|
|
```bash
|
|
ssh storage@SERVER_IP 'whoami; id; sudo -n true'
|
|
```
|
|
|
|
### 3. Harden SSH
|
|
|
|
Create a drop-in:
|
|
|
|
```bash
|
|
install -d -m 755 /etc/ssh/sshd_config.d
|
|
cat > /etc/ssh/sshd_config.d/99-logos-storage-hardening.conf <<'EOF'
|
|
PermitRootLogin no
|
|
PasswordAuthentication no
|
|
PubkeyAuthentication yes
|
|
EOF
|
|
sshd -t
|
|
systemctl restart ssh || systemctl restart sshd
|
|
```
|
|
|
|
Verify from the operator machine:
|
|
|
|
```bash
|
|
ssh storage@SERVER_IP 'true'
|
|
ssh -o BatchMode=yes root@SERVER_IP 'true'
|
|
```
|
|
|
|
The root command should fail.
|
|
|
|
### 4. Install Packages
|
|
|
|
```bash
|
|
sudo apt-get update
|
|
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
|
build-essential \
|
|
cmake \
|
|
git \
|
|
curl \
|
|
make \
|
|
bash \
|
|
lcov \
|
|
libgomp1 \
|
|
jq \
|
|
ca-certificates \
|
|
pkg-config
|
|
```
|
|
|
|
### 5. Add Swap On Small Machines
|
|
|
|
On very small machines, for example 1 GB RAM, add a swap file before building:
|
|
|
|
```bash
|
|
if [ ! -f /swapfile ]; then
|
|
sudo fallocate -l 2G /swapfile || sudo dd if=/dev/zero of=/swapfile bs=1M count=2048
|
|
sudo chmod 600 /swapfile
|
|
sudo mkswap /swapfile
|
|
sudo swapon /swapfile
|
|
fi
|
|
grep -q '^/swapfile ' /etc/fstab || echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
|
|
free -h
|
|
```
|
|
|
|
### 6. Install Nim 2.2.10 With `choosenim`
|
|
|
|
Run as the `storage` user:
|
|
|
|
```bash
|
|
curl https://nim-lang.org/choosenim/init.sh -sSf | sh -s -- -y
|
|
export PATH="$HOME/.nimble/bin:$PATH"
|
|
choosenim 2.2.10
|
|
nim --version
|
|
```
|
|
|
|
Persist PATH:
|
|
|
|
```bash
|
|
grep -qxF 'export PATH=$HOME/.nimble/bin:$PATH' ~/.profile || \
|
|
printf '\nexport PATH=$HOME/.nimble/bin:$PATH\n' >> ~/.profile
|
|
```
|
|
|
|
### 7. Clone And Build
|
|
|
|
Run as the `storage` user:
|
|
|
|
```bash
|
|
git clone https://github.com/logos-storage/logos-storage-nim.git ~/logos-storage-nim
|
|
cd ~/logos-storage-nim
|
|
make -j1 update
|
|
make -j1 NIMFLAGS="-d:disableMarchNative"
|
|
./build/storage --help >/dev/null
|
|
```
|
|
|
|
On larger machines, use higher parallelism, for example:
|
|
|
|
```bash
|
|
make -j"$(nproc)" update
|
|
make -j"$(nproc)" NIMFLAGS="-d:disableMarchNative"
|
|
```
|
|
|
|
### 8. Install Binary And Data Directory
|
|
|
|
```bash
|
|
sudo install -d -m 755 -o root -g root /opt/logos-storage
|
|
sudo install -m 755 -o root -g root ~/logos-storage-nim/build/storage /opt/logos-storage/storage
|
|
sudo install -d -m 700 -o storage -g storage /var/lib/logos-storage
|
|
/opt/logos-storage/storage --help >/dev/null
|
|
```
|
|
|
|
### 9. Create `systemd` Service
|
|
|
|
```bash
|
|
sudo tee /etc/systemd/system/logos-storage.service >/dev/null <<'EOF'
|
|
[Unit]
|
|
Description=Logos Storage Node
|
|
After=network-online.target
|
|
Wants=network-online.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=storage
|
|
Group=storage
|
|
WorkingDirectory=/var/lib/logos-storage
|
|
ExecStart=/opt/logos-storage/storage --data-dir=/var/lib/logos-storage --listen-port=8070 --disc-port=8090
|
|
Restart=on-failure
|
|
RestartSec=10
|
|
LimitNOFILE=1048576
|
|
NoNewPrivileges=true
|
|
PrivateTmp=true
|
|
ProtectSystem=full
|
|
ReadWritePaths=/var/lib/logos-storage
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable --now logos-storage.service
|
|
```
|
|
|
|
### 10. Verify Service And Ports
|
|
|
|
```bash
|
|
systemctl --no-pager --full status logos-storage.service
|
|
ss -lntup | grep -E ':(8070|8080|8090)\b'
|
|
curl -fsS http://127.0.0.1:8080/api/storage/v1/peerid
|
|
curl -fsS http://127.0.0.1:8080/api/storage/v1/spr
|
|
journalctl -u logos-storage.service --no-pager -n 80
|
|
```
|
|
|
|
Expected bindings:
|
|
|
|
```text
|
|
udp 0.0.0.0:8090
|
|
tcp 127.0.0.1:8080
|
|
tcp 0.0.0.0:8070
|
|
```
|
|
|
|
From the operator machine, verify public TCP reachability:
|
|
|
|
```bash
|
|
timeout 10 bash -c 'cat < /dev/null > /dev/tcp/SERVER_IP/8070' && echo open
|
|
```
|
|
|
|
UDP reachability is harder to verify with a simple shell one-liner. Confirm the firewall rule exists and inspect node logs for discovery startup and advertised `/udp/8090` addresses.
|
|
|
|
## API Access From Operator Machine
|
|
|
|
Keep the API private and tunnel over SSH:
|
|
|
|
```bash
|
|
ssh -N -L 127.0.0.1:18080:127.0.0.1:8080 storage@SERVER_IP
|
|
```
|
|
|
|
Then use:
|
|
|
|
```bash
|
|
curl http://127.0.0.1:18080/api/storage/v1/peerid
|
|
```
|
|
|
|
In this repository, `scripts/storage-test.sh` manages this tunnel automatically for commands targeting `remote`.
|
|
|
|
## Operational Commands
|
|
|
|
```bash
|
|
sudo systemctl start logos-storage.service
|
|
sudo systemctl stop logos-storage.service
|
|
sudo systemctl restart logos-storage.service
|
|
systemctl status logos-storage.service
|
|
journalctl -u logos-storage.service -f
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### SSH Hangs During Build
|
|
|
|
Symptoms:
|
|
|
|
```text
|
|
Connection timed out during banner exchange
|
|
```
|
|
|
|
Likely cause: small VM overloaded by compilation. Use `make -j1`, add swap, or build on a larger machine and copy the binary.
|
|
|
|
### REST API Is Unreachable Externally
|
|
|
|
This is expected. The REST API should bind to `127.0.0.1:8080`. Use SSH tunneling.
|
|
|
|
### Node Has No Peers
|
|
|
|
Check:
|
|
|
|
- Firewall allows `8070/TCP` and `8090/UDP`.
|
|
- Service logs show public IP in advertised addresses.
|
|
- The machine has public internet access.
|
|
- The network preset is the intended one, for example `logos.test`.
|
|
|
|
### Data Directory Permissions Warning
|
|
|
|
The node may correct permissions on startup. Prefer setting the directory explicitly:
|
|
|
|
```bash
|
|
sudo chmod 700 /var/lib/logos-storage
|
|
sudo chown storage:storage /var/lib/logos-storage
|
|
```
|
|
|
|
## Security Notes
|
|
|
|
- Do not run the node as `root`.
|
|
- Do not expose API port `8080` publicly.
|
|
- Restrict SSH source IPs in the cloud firewall.
|
|
- Keep outbound traffic allowed unless there is a specific network policy.
|
|
- Treat the node private key in the data directory as sensitive.
|