Improve LNbits provisioning script for security and configuration
Update the provisioning script to use Tailscale IP for Nginx binding, enable non-interactive admin key extraction, and provide clearer backend notes. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 418bf6f8-212b-4bb0-a7a5-8231a061da4e Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: 517a9f91-7fcb-4f89-8cec-333aac2de28b Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/9f85e954-647c-46a5-90a7-396e495a805a/418bf6f8-212b-4bb0-a7a5-8231a061da4e/Q83Uqvu Replit-Helium-Checkpoint-Created: true
This commit is contained in:
@@ -1,32 +1,34 @@
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# Hermes VPS — LNbits provisioning script
|
||||
# Target: Ubuntu 24.04 LTS, tested on 143.198.27.163
|
||||
# Target: Ubuntu 24.04 LTS (tested on root@143.198.27.163)
|
||||
#
|
||||
# Usage:
|
||||
# export DB_PASS="$(openssl rand -hex 20)"
|
||||
# export TAILSCALE_IP="$(tailscale ip -4)" # e.g. 100.x.x.x
|
||||
# bash scripts/hermes-lnbits/provision.sh
|
||||
#
|
||||
# Required environment variables (operator must supply):
|
||||
# DB_PASS — password for the lnbits PostgreSQL role (generate securely)
|
||||
# Required env vars (no defaults — operator must supply):
|
||||
# DB_PASS — password for the lnbits PostgreSQL role
|
||||
# TAILSCALE_IP — Tailscale IPv4 of this host (Nginx binds only to this)
|
||||
#
|
||||
# Optional environment variables:
|
||||
# LNBITS_VERSION (default: 0.12.12)
|
||||
# LNBITS_PORT (default: 5000)
|
||||
# LNBITS_DIR (default: /opt/lnbits)
|
||||
# SERVICE_USER (default: lnbits — dedicated non-root user)
|
||||
# Backend note:
|
||||
# FakeWallet is used rather than VoidWallet because FakeWallet settles
|
||||
# internal (self-payment) invoices in real-time, which is required for
|
||||
# the dev/test payment simulation loop. VoidWallet silently drops all
|
||||
# outbound payments and never fires invoice-settled events.
|
||||
#
|
||||
# After this script completes:
|
||||
# 1. Open http://<VPS_IP>:${LNBITS_PORT} in a browser
|
||||
# 2. Create a wallet and copy the admin key
|
||||
# 3. Set Replit secrets:
|
||||
# LNBITS_URL=http://<VPS_IP>:${LNBITS_PORT}
|
||||
# After this script:
|
||||
# 1. Extract the admin key non-interactively (see step below).
|
||||
# 2. Set Replit secrets:
|
||||
# LNBITS_URL=http://${TAILSCALE_IP}:5000
|
||||
# LNBITS_API_KEY=<admin-key>
|
||||
# 4. Restart the api-server workflow
|
||||
# 3. Restart the api-server workflow.
|
||||
# =============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
: "${DB_PASS:?DB_PASS must be set. Run: export DB_PASS=\$(openssl rand -hex 20)}"
|
||||
: "${TAILSCALE_IP:?TAILSCALE_IP must be set. Run: export TAILSCALE_IP=\$(tailscale ip -4)}"
|
||||
|
||||
LNBITS_VERSION="${LNBITS_VERSION:-0.12.12}"
|
||||
LNBITS_PORT="${LNBITS_PORT:-5000}"
|
||||
@@ -38,10 +40,11 @@ DB_USER=lnbits
|
||||
echo "==> Installing system deps"
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq python3.11 python3.11-venv python3-pip \
|
||||
postgresql postgresql-contrib nginx curl git openssl
|
||||
postgresql postgresql-contrib nginx curl git openssl jq
|
||||
|
||||
echo "==> Creating service user (non-root)"
|
||||
id -u "${SERVICE_USER}" &>/dev/null || useradd -r -m -d "${LNBITS_DIR}" -s /usr/sbin/nologin "${SERVICE_USER}"
|
||||
echo "==> Creating dedicated service user (non-root)"
|
||||
id -u "${SERVICE_USER}" &>/dev/null || \
|
||||
useradd -r -m -d "${LNBITS_DIR}" -s /usr/sbin/nologin "${SERVICE_USER}"
|
||||
|
||||
echo "==> Configuring PostgreSQL"
|
||||
systemctl enable --now postgresql
|
||||
@@ -51,13 +54,13 @@ sudo -u postgres psql -c "ALTER ROLE ${DB_USER} WITH PASSWORD '${DB_PASS}';"
|
||||
sudo -u postgres psql -tc "SELECT 1 FROM pg_database WHERE datname='${DB_NAME}'" \
|
||||
| grep -q 1 || sudo -u postgres createdb -O "${DB_USER}" "${DB_NAME}"
|
||||
|
||||
echo "==> Creating LNbits venv"
|
||||
echo "==> Creating LNbits venv at ${LNBITS_DIR}/.venv"
|
||||
mkdir -p "${LNBITS_DIR}/data"
|
||||
python3.11 -m venv "${LNBITS_DIR}/.venv"
|
||||
"${LNBITS_DIR}/.venv/bin/pip" install --quiet --upgrade pip
|
||||
"${LNBITS_DIR}/.venv/bin/pip" install --quiet "lnbits==${LNBITS_VERSION}" psycopg2-binary
|
||||
|
||||
echo "==> Writing env file (root-readable only)"
|
||||
echo "==> Writing ${LNBITS_DIR}/.env (mode 600, owned by ${SERVICE_USER})"
|
||||
cat > "${LNBITS_DIR}/.env" << ENVFILE
|
||||
LNBITS_BACKEND_WALLET_CLASS=FakeWallet
|
||||
HOST=0.0.0.0
|
||||
@@ -69,19 +72,18 @@ ENVFILE
|
||||
chmod 600 "${LNBITS_DIR}/.env"
|
||||
chown "${SERVICE_USER}:${SERVICE_USER}" "${LNBITS_DIR}/.env"
|
||||
|
||||
echo "==> Writing /opt/lnbits/run.sh"
|
||||
cat > "${LNBITS_DIR}/run.sh" << RUNSH
|
||||
echo "==> Writing ${LNBITS_DIR}/run.sh"
|
||||
cat > "${LNBITS_DIR}/run.sh" << 'RUNSH'
|
||||
#!/usr/bin/env bash
|
||||
set -a
|
||||
source "${LNBITS_DIR}/.env"
|
||||
source /opt/lnbits/.env
|
||||
set +a
|
||||
exec "${LNBITS_DIR}/.venv/bin/lnbits" --host 0.0.0.0 --port "${LNBITS_PORT}"
|
||||
exec /opt/lnbits/.venv/bin/lnbits --host 0.0.0.0 --port "${PORT}"
|
||||
RUNSH
|
||||
chmod 750 "${LNBITS_DIR}/run.sh"
|
||||
chown "${SERVICE_USER}:${SERVICE_USER}" "${LNBITS_DIR}/run.sh"
|
||||
chown -R "${SERVICE_USER}:${SERVICE_USER}" "${LNBITS_DIR}"
|
||||
|
||||
echo "==> Writing systemd unit"
|
||||
echo "==> Installing systemd unit (User=${SERVICE_USER}, hardened)"
|
||||
cat > /etc/systemd/system/lnbits.service << UNIT
|
||||
[Unit]
|
||||
Description=LNbits Lightning wallet server
|
||||
@@ -110,17 +112,16 @@ systemctl restart lnbits
|
||||
echo "==> Waiting for LNbits to start (20 s)..."
|
||||
sleep 20
|
||||
|
||||
echo "==> Switching backend to FakeWallet via SQL"
|
||||
# FakeWallet settles internal self-payments — required for dev/test payment simulation.
|
||||
# VoidWallet silently drops all payments and cannot settle invoices.
|
||||
echo "==> Ensuring FakeWallet is set in DB"
|
||||
sudo -u postgres psql "${DB_NAME}" -c \
|
||||
"INSERT INTO system_settings (id, value) VALUES ('lnbits_backend_wallet_class', '\"FakeWallet\"')
|
||||
ON CONFLICT (id) DO UPDATE SET value = EXCLUDED.value;" 2>/dev/null || true
|
||||
|
||||
echo "==> Writing Nginx reverse-proxy config"
|
||||
echo "==> Configuring Nginx (Tailscale-only listener on ${TAILSCALE_IP})"
|
||||
cat > /etc/nginx/sites-available/lnbits << NGINX
|
||||
# Bind only to the Tailscale interface — not exposed on public IP
|
||||
server {
|
||||
listen 80;
|
||||
listen ${TAILSCALE_IP}:80;
|
||||
server_name _;
|
||||
|
||||
location / {
|
||||
@@ -142,18 +143,27 @@ echo "==> Health check"
|
||||
if HEALTH=$(curl -sf "http://localhost:${LNBITS_PORT}/api/v1/health" 2>&1); then
|
||||
echo " LNbits health: ${HEALTH}"
|
||||
else
|
||||
echo " WARNING: health check failed — check: journalctl -u lnbits -n 100"
|
||||
journalctl -u lnbits -n 20 --no-pager
|
||||
echo " WARNING: health check failed"
|
||||
journalctl -u lnbits -n 30 --no-pager
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "==> Extracting admin key (non-interactive)"
|
||||
# LNbits >= 0.12 creates a superuser on first start. Query the DB directly.
|
||||
ADMIN_KEY=$(sudo -u postgres psql "${DB_NAME}" -tAc \
|
||||
"SELECT adminkey FROM wallets WHERE deleted = false LIMIT 1;" 2>/dev/null || true)
|
||||
if [[ -n "${ADMIN_KEY}" ]]; then
|
||||
echo " Admin key: ${ADMIN_KEY}"
|
||||
echo " Set in Replit: LNBITS_API_KEY=${ADMIN_KEY}"
|
||||
else
|
||||
echo " No wallet found yet — open LNbits UI to create one, then run:"
|
||||
echo " sudo -u postgres psql lnbits -c \"SELECT adminkey FROM wallets LIMIT 1;\""
|
||||
fi
|
||||
|
||||
systemctl status lnbits --no-pager | head -8
|
||||
|
||||
echo ""
|
||||
echo "==> DONE"
|
||||
echo " LNbits: http://$(curl -sf https://api.ipify.org 2>/dev/null || echo '<VPS_IP>'):${LNBITS_PORT}"
|
||||
echo " Next:"
|
||||
echo " 1. Open LNbits UI, create a wallet, copy admin key"
|
||||
echo " 2. Set Replit secrets:"
|
||||
echo " LNBITS_URL=http://<VPS_IP>:${LNBITS_PORT}"
|
||||
echo " LNBITS_API_KEY=<admin-key>"
|
||||
echo " 3. Restart api-server workflow"
|
||||
echo "==> DONE. Set these Replit secrets:"
|
||||
echo " LNBITS_URL=http://${TAILSCALE_IP}:80"
|
||||
echo " LNBITS_API_KEY=${ADMIN_KEY:-<run-key-extraction-above>}"
|
||||
echo " Then restart the api-server workflow."
|
||||
|
||||
Reference in New Issue
Block a user