Skip to content

Commit 0b78019

Browse files
committed
Simplify deployment scripts and add sensitive-data checks
1 parent 0d6c229 commit 0b78019

17 files changed

Lines changed: 789 additions & 581 deletions

File tree

.github/workflows/npm-test.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ jobs:
2121
node-version: 24
2222
package-manager-cache: false
2323

24+
- name: Scan tracked files for sensitive data
25+
run: node scripts/lint-sensitive-data.js
26+
2427
- name: Install native build dependencies
2528
run: |
2629
sudo apt-get update

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ Run the installer as `root`:
5757

5858
```bash
5959
curl -L https://raw.githubusercontent.com/MoneroOcean/nodejs-pool/master/deployment/deploy.bash | \
60-
WWW_DNS=pool.example.com API_DNS=api.pool.example.com CF_DNS_API_TOKEN="Cloudflare API Token" CERTBOT_EMAIL=ops@example.com bash -x
60+
WWW_DNS=pool.example.com API_DNS=api.pool.example.com CF_DNS_API_TOKEN="Cloudflare API Token" CERTBOT_EMAIL=ops@example.com bash
6161
```
6262

6363
`WWW_DNS`, `API_DNS`, and `CERTBOT_EMAIL` default to the MoneroOcean production values if omitted. Set them explicitly for any non-production install.
@@ -96,16 +96,18 @@ For a leaf-only install:
9696

9797
```bash
9898
curl -L https://raw.githubusercontent.com/MoneroOcean/nodejs-pool/master/deployment/leaf.bash \
99-
| TARI_WALLET_PAYMENT_ADDRESS=<your_tari_wallet_address> bash -x
99+
| TARI_WALLET_PAYMENT_ADDRESS=<your_tari_wallet_address> bash
100100
```
101101

102102
`TARI_WALLET_PAYMENT_ADDRESS` is required so Tari merge-mining rewards are paid to a wallet you control.
103-
Set `MONERO_RELEASE_TAG` or `TARI_RELEASE_TAG` before `bash -x` to use a different reviewed release tag on leaf nodes.
103+
Set `MONERO_RELEASE_TAG` or `TARI_RELEASE_TAG` before `bash` to use a different reviewed release tag on leaf nodes.
104104
Leaf installs retain 10,000 Tari blocks by default; override this with `TARI_PRUNING_HORIZON` when more history is required.
105105
The installer disables root/password SSH access and enables an SSH Fail2ban jail. Set `SSH_FAIL2BAN_IGNORE_IPS` to a whitespace-separated list of trusted management addresses or CIDRs that must never be banned; SSH itself remains reachable from any address allowed by UFW.
106106
Site-specific trust settings can be stored in `/etc/moneroocean/leaf.conf`, which must be a root-owned regular file without group/other write permissions. Keep it outside the repository and use shell assignments such as `SSH_FAIL2BAN_IGNORE_IPS="..."` and `POOL_TRUSTED_SOURCE_IPV4S="..."`; the latter is a comma-separated IPv4 list. Override the path with `LEAF_CONFIG_FILE` when needed.
107107
Fresh daemon synchronization can take hours. `MONERO_SYNC_TIMEOUT_SECONDS` and `TARI_SYNC_TIMEOUT_SECONDS` default to 172,800 seconds and accept `0` for no timeout.
108108

109+
The installers load `deployment/common.bash` from a local checkout when available. In the documented curl-pipe form they fetch the reviewed helper over HTTPS into a private temporary file; they do not read deployment files from the caller's working directory. Installer output intentionally avoids shell tracing so credentials and other environment values are not echoed.
110+
109111
After install, update the leaf config so it points at the main pool infrastructure, then start the `pool` module on that node.
110112

111113
### Docker And Optional Multi-Coin Stack

config_example.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@
1313
"host": "127.0.0.1",
1414
"database": "pool",
1515
"user": "pool",
16-
"password": "98erhfiuehw987fh23d"
16+
"password": "CHANGEME"
1717
}
1818
}

deployment/base.sql

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
CREATE DATABASE pool;
2-
CREATE USER pool@`127.0.0.1` IDENTIFIED WITH mysql_native_password BY '98erhfiuehw987fh23d';
3-
CREATE USER pool@localhost IDENTIFIED WITH mysql_native_password BY '98erhfiuehw987fh23d';
4-
GRANT ALL ON pool.* TO pool@`127.0.0.1`;
5-
GRANT ALL ON pool.* TO pool@localhost;
6-
FLUSH PRIVILEGES;
2+
-- deployment/deploy.bash provisions database users with a generated password.
73
USE pool;
84
ALTER DATABASE pool DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
95
CREATE TABLE `balance` (

deployment/common.bash

Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
#!/bin/bash
2+
3+
# Source-only helpers shared by the deploy and leaf installers.
4+
# Keep this file free of top-level work: both entrypoints load it before their
5+
# own validation and installation steps.
6+
MONEROOCEAN_COMMON_API_VERSION=1
7+
8+
is_test_mode() {
9+
[ "${POOL_DEPLOY_TEST_MODE:-0}" = "1" ]
10+
}
11+
12+
retry_command() {
13+
local attempt
14+
for attempt in 1 2 3 4 5; do
15+
if "$@"; then
16+
return 0
17+
fi
18+
[ "$attempt" -eq 5 ] || sleep $((attempt * 5))
19+
done
20+
return 1
21+
}
22+
23+
install_node_dependencies() {
24+
if [ -f package-lock.json ] || [ -f npm-shrinkwrap.json ]; then
25+
retry_command npm ci "$@"
26+
else
27+
retry_command npm install "$@"
28+
fi
29+
}
30+
31+
configure_journald_retention() {
32+
install -d -m 755 /etc/systemd/journald.conf.d
33+
cat >/etc/systemd/journald.conf.d/90-moneroocean-retention.conf <<'EOF'
34+
[Journal]
35+
SystemMaxUse=100M
36+
SystemKeepFree=1G
37+
SystemMaxFileSize=10M
38+
EOF
39+
}
40+
41+
configure_needrestart_pm2_guard() {
42+
install -d -m 755 /etc/needrestart/conf.d
43+
rm -f /etc/needrestart/conf.d/moneroocean-critical.conf
44+
cat >/etc/needrestart/conf.d/moneroocean-pm2.conf <<'EOF'
45+
# Keep unattended package maintenance from restarting the pool process manager.
46+
# Restart PM2 deliberately during a maintenance window to load updated libraries.
47+
$nrconf{override_rc}->{qr(^pm2-user\.service$)} = 0;
48+
EOF
49+
}
50+
51+
clone_repo_once() {
52+
local repo="$1"
53+
local dest="$2"
54+
if [ -d "$dest/.git" ]; then
55+
return 0
56+
fi
57+
retry_command git clone "$repo" "$dest"
58+
}
59+
60+
configure_overcommit() {
61+
install -d -m 755 /etc/sysctl.d
62+
cat >/etc/sysctl.d/90-monero-overcommit.conf <<'EOF'
63+
vm.overcommit_memory = 2
64+
vm.overcommit_ratio = 150
65+
EOF
66+
if ! sysctl -p /etc/sysctl.d/90-monero-overcommit.conf; then
67+
if is_test_mode; then
68+
echo "Skipping active overcommit sysctl apply in test mode"
69+
return 0
70+
fi
71+
return 1
72+
fi
73+
}
74+
75+
configure_pool_conntrack() {
76+
install -d -m 755 /etc/modules-load.d /etc/sysctl.d
77+
printf 'nf_conntrack\n' >/etc/modules-load.d/moneroocean-conntrack.conf
78+
cat >/etc/sysctl.d/92-moneroocean-conntrack.conf <<EOF
79+
# Leave headroom for daemon RPC and management traffic during miner reconnect bursts.
80+
net.netfilter.nf_conntrack_max = $POOL_CONNTRACK_MAX
81+
EOF
82+
if is_test_mode; then
83+
echo "Skipping active conntrack module load and sysctl apply in test mode"
84+
return 0
85+
fi
86+
modprobe nf_conntrack
87+
sysctl -p /etc/sysctl.d/92-moneroocean-conntrack.conf
88+
if [ "$(sysctl -n net.netfilter.nf_conntrack_max)" != "$POOL_CONNTRACK_MAX" ]; then
89+
echo "nf_conntrack_max did not apply: expected $POOL_CONNTRACK_MAX, got $(sysctl -n net.netfilter.nf_conntrack_max)" >&2
90+
return 1
91+
fi
92+
}
93+
94+
configure_pool_health_guard() {
95+
local guard_dir=/usr/local/libexec/moneroocean
96+
install -d -o root -g root -m 755 "$guard_dir"
97+
install -o root -g root -m 755 /home/user/nodejs-pool/pool_health_guard.sh "$guard_dir/pool-health-guard"
98+
install -o root -g root -m 644 /home/user/nodejs-pool/deployment/pool-health-guard.service /lib/systemd/system/pool-health-guard.service
99+
install -o root -g root -m 644 /home/user/nodejs-pool/deployment/pool-health-guard.timer /lib/systemd/system/pool-health-guard.timer
100+
systemctl daemon-reload
101+
systemctl enable pool-health-guard.timer
102+
if ! is_test_mode; then
103+
systemctl restart pool-health-guard.timer
104+
fi
105+
}
106+
107+
configure_swap() {
108+
if awk 'NR > 1 {found = 1} END {exit found ? 0 : 1}' /proc/swaps; then
109+
return 0
110+
fi
111+
if grep -Eq '^[^#]+[[:space:]]+[^[:space:]]+[[:space:]]+swap[[:space:]]' /etc/fstab; then
112+
swapon -a
113+
return 0
114+
fi
115+
if [ ! -f /swapfile ] || [ "$(stat -c %s /swapfile 2>/dev/null || echo 0)" -lt 1073741824 ]; then
116+
rm -f /swapfile
117+
fallocate -l 1G /swapfile || dd if=/dev/zero of=/swapfile bs=1M count=1024
118+
fi
119+
chmod 600 /swapfile
120+
if ! awk 'NR > 1 && $1 == "/swapfile" {found = 1} END {exit found ? 0 : 1}' /proc/swaps; then
121+
mkswap -f /swapfile
122+
chmod 600 /swapfile
123+
if [ "$(awk 'NR > 1 {total += $3} END {print total + 0}' /proc/swaps)" -eq 0 ]; then
124+
if ! swapon /swapfile; then
125+
if is_test_mode; then
126+
echo "Skipping active swap enable in test mode"
127+
else
128+
return 1
129+
fi
130+
fi
131+
fi
132+
fi
133+
if ! grep -Eq '^[^#]*[[:space:]]/swapfile[[:space:]]' /etc/fstab; then
134+
echo " /swapfile none swap sw 0 0" >>/etc/fstab
135+
fi
136+
}
137+
138+
default_tari_memory_high() {
139+
local mem_kb
140+
mem_kb="$(awk '/MemTotal:/ {print $2}' /proc/meminfo)"
141+
if [ "$mem_kb" -ge $((30 * 1024 * 1024)) ]; then
142+
echo 18G
143+
else
144+
echo 12G
145+
fi
146+
}
147+
148+
validate_systemd_memory_limit() {
149+
local value="$1"
150+
local name="$2"
151+
if [[ ! "$value" =~ ^(infinity|max|[0-9]+([.][0-9]+)?[KMGTPE]?)$ ]]; then
152+
echo "Invalid $name value: $value" >&2
153+
exit 1
154+
fi
155+
}
156+
157+
rpc_synced() {
158+
local url="$1"
159+
local method="$2"
160+
local response
161+
response="$(curl -fsS -H 'Content-Type: application/json' --data "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"$method\",\"params\":{}}" "$url")" || return 1
162+
printf '%s' "$response" | python3 -c '
163+
import json
164+
import sys
165+
166+
method = sys.argv[1]
167+
payload = json.load(sys.stdin)
168+
result = payload.get("result") or {}
169+
if method == "get_info":
170+
sys.exit(0 if result.get("status") == "OK" and result.get("synchronized") is True and result.get("busy_syncing") is not True else 1)
171+
if method == "GetTipInfo":
172+
metadata = result.get("metadata") or {}
173+
synced = result.get("initial_sync_achieved")
174+
height = int(metadata.get("best_block_height") or 0)
175+
sys.exit(0 if synced is True and height > 0 else 1)
176+
sys.exit(1)
177+
' "$method"
178+
}
179+
180+
ensure_rust_toolchain() {
181+
if [ -s "$HOME/.cargo/env" ]; then
182+
. "$HOME/.cargo/env"
183+
fi
184+
if ! command -v cargo >/dev/null 2>&1; then
185+
retry_command bash -lc 'set -o pipefail; curl --proto "=https" --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain stable'
186+
. "$HOME/.cargo/env"
187+
fi
188+
retry_command rustup update stable
189+
}
190+
191+
checkout_repo_ref() {
192+
local repo="$1"
193+
local dest="$2"
194+
local ref="$3"
195+
if [ -e "$dest" ] && [ ! -d "$dest/.git" ]; then
196+
mv "$dest" "$dest.pre-source.$(date +%Y%m%d%H%M%S)"
197+
fi
198+
clone_repo_once "$repo" "$dest"
199+
cd "$dest"
200+
retry_command git fetch --tags origin
201+
git checkout --force "$ref"
202+
}
203+
204+
ensure_tari_user() {
205+
id -u "$TARI_USER" >/dev/null 2>&1 || useradd -m -d "$TARI_HOME" -s /bin/sh "$TARI_USER"
206+
install -d -m 755 -o "$TARI_USER" -g "$TARI_USER" "$TARI_HOME"
207+
}
208+
209+
configure_monero_hugepages() {
210+
local gid
211+
groupadd --system "$HUGEPAGES_GROUP" 2>/dev/null || true
212+
usermod -a -G "$HUGEPAGES_GROUP" monerodaemon
213+
gid="$(getent group "$HUGEPAGES_GROUP" | cut -d: -f3)"
214+
test -n "$gid"
215+
install -d -m 755 /etc/sysctl.d
216+
cat >/etc/sysctl.d/91-moneroocean-hugepages.conf <<EOF
217+
vm.nr_hugepages = $MONERO_RANDOMX_HUGEPAGES
218+
vm.hugetlb_shm_group = $gid
219+
EOF
220+
echo 1 >/proc/sys/vm/compact_memory 2>/dev/null || true
221+
if ! sysctl -p /etc/sysctl.d/91-moneroocean-hugepages.conf; then
222+
if is_test_mode; then
223+
echo "Skipping active hugepage sysctl apply in test mode"
224+
return 0
225+
fi
226+
return 1
227+
fi
228+
if [ "$(sysctl -n vm.nr_hugepages)" -lt "$MONERO_RANDOMX_HUGEPAGES" ]; then
229+
echo "Warning: requested $MONERO_RANDOMX_HUGEPAGES hugepages but only $(sysctl -n vm.nr_hugepages) are available until reboot or more memory compaction"
230+
fi
231+
}
232+
233+
write_monero_service() {
234+
local block_notify_arg=""
235+
if [ "${1:-}" = "enable-block-notify" ]; then
236+
block_notify_arg=" --block-notify '/bin/bash /home/user/nodejs-pool/block_notify.sh'"
237+
fi
238+
cat >/lib/systemd/system/monero.service <<EOF
239+
[Unit]
240+
Description=Monero Daemon
241+
After=network.target
242+
243+
[Service]
244+
Environment=MALLOC_ARENA_MAX=2
245+
SupplementaryGroups=$HUGEPAGES_GROUP
246+
LimitMEMLOCK=infinity
247+
ExecStart=/usr/local/src/monero/build/release/bin/monerod --rpc-bind-ip=127.0.0.1 --rpc-bind-port=18083 --hide-my-port --prune-blockchain --enable-dns-blocklist --no-zmq --out-peers 64 --non-interactive --log-level '$MONERO_LOG_CATEGORIES'$block_notify_arg
248+
Restart=always
249+
User=monerodaemon
250+
Nice=10
251+
CPUQuota=400%
252+
253+
[Install]
254+
WantedBy=multi-user.target
255+
EOF
256+
}
257+
258+
write_tari_service() {
259+
cat >/lib/systemd/system/xtm.service <<EOF
260+
[Unit]
261+
Description=Tari Daemon
262+
After=network.target
263+
264+
[Service]
265+
# Tari SubmitBlock JSON bodies can exceed grpc-json-proxy's 1 MiB default when
266+
# the block carries a large proof body.
267+
ExecStart=/bin/bash -c "(sleep 2; /usr/bin/node /usr/local/src/grpc-json-proxy/grpc-json-proxy.js /usr/local/src/grpc-json-proxy/base_node.proto 18146 18142 --max-body-bytes 16777216) & (sleep 2; /usr/bin/node /usr/local/src/grpc-json-proxy/grpc-json-proxy.js /usr/local/src/grpc-json-proxy/base_node.proto 18148 18142 --max-body-bytes 16777216) & /usr/local/src/tari/target/release/minotari_node --non-interactive-mode --watch status --disable-splash-screen"
268+
Restart=always
269+
User=$TARI_USER
270+
Environment=HOME=$TARI_HOME
271+
Nice=10
272+
CPUQuota=400%
273+
MemoryHigh=$TARI_MEMORY_HIGH
274+
MemorySwapMax=$TARI_MEMORY_SWAP_MAX
275+
276+
[Install]
277+
WantedBy=multi-user.target
278+
EOF
279+
}
280+
281+
write_tari_merge_mining_service() {
282+
local dependencies="$1"
283+
cat >/lib/systemd/system/xtm_mm.service <<EOF
284+
[Unit]
285+
Description=Tari Merge Mining Daemon
286+
After=network.target $dependencies
287+
PartOf=$dependencies
288+
289+
[Service]
290+
ExecStart=/usr/local/src/tari/target/release/minotari_merge_mining_proxy --non-interactive-mode
291+
Restart=always
292+
RestartSec=3s
293+
StartLimitBurst=0
294+
User=$TARI_USER
295+
Environment=HOME=$TARI_HOME
296+
Nice=10
297+
CPUQuota=400%
298+
MemoryHigh=$TARI_MM_MEMORY_HIGH
299+
MemorySwapMax=$TARI_MM_MEMORY_SWAP_MAX
300+
301+
[Install]
302+
WantedBy=multi-user.target
303+
EOF
304+
}

0 commit comments

Comments
 (0)