diff --git a/.env.example b/.env.example index 9f7254d..f724e4b 100644 --- a/.env.example +++ b/.env.example @@ -6,7 +6,13 @@ PLM_RPC_PASSWORD=change-me #REPORT_SERVICES=tcp://electrum.example.com:50001 #ELECTRUMX_TCP_PORT=50001 -# SSL (port 50002): on by default, self-signed certificate generated once and kept in the electrumx-ssl volume. +# Data directory of the host: db/ (database) and ssl/ (certificate). Create it first: mkdir -p ~/.plm-electrumx/db ~/.plm-electrumx/ssl +#ELECTRUMX_DATADIR=~/.plm-electrumx +# owner of the data directory, if it is not user 1000 +#PLM_UID=1000 +#PLM_GID=1000 + +# SSL (port 50002): on by default, self-signed certificate generated once and kept in ssl/ of the data directory. #SSL_ENABLED=true # name (or IP) of the server, written in the generated certificate #SSL_HOSTNAME=electrum.example.com diff --git a/CLAUDE.md b/CLAUDE.md index f203214..553bee9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -22,7 +22,7 @@ DB_ENGINE=rocksdb DB_DIRECTORY=... DAEMON_URL=http://user:pass@host:port/ electr electrumx_rpc getinfo # talk to a running server via local RPC ``` -Docker: `docker compose up -d --build` (root `Dockerfile`, `docker-compose.yml`, `.env` from `.env.example`) runs the server on the external `palladium` network of the Palladium node container, daemon at `palladiumd:2332`. `docker-entrypoint.sh` sets up SSL: it uses `SSL_CERTFILE`/`SSL_KEYFILE` if given, else the self-signed certificate in the `electrumx-ssl` volume, generating it only if missing (never overwrite it: clients pin it), and sets `SERVICES`. +Docker: `docker compose up -d --build` (root `Dockerfile`, `docker-compose.yml`, `.env` from `.env.example`) runs the server on the external `palladium` network of the Palladium node container, daemon at `palladiumd:2332`. `docker-entrypoint.sh` sets up SSL: it uses `SSL_CERTFILE`/`SSL_KEYFILE` if given, else the self-signed certificate in `ssl/` of the data directory (`~/.plm-electrumx`, bind-mounted), generating it only if missing (never overwrite it: clients pin it), and sets `SERVICES`. Configuration is entirely via environment variables, parsed in `src/electrumx/server/env.py` (documented in `docs/environment.rst`). `DB_ENGINE` is mandatory in 2.0 (`leveldb` or `rocksdb`). Python >=3.10; free-threaded 3.14t is experimental. diff --git a/DEPLOY.md b/DEPLOY.md index d80f9ce..b198547 100644 --- a/DEPLOY.md +++ b/DEPLOY.md @@ -87,7 +87,7 @@ addresses and balances unencrypted. `docker-entrypoint.sh` (the entrypoint of the image) does this at every start: 1. If you gave your own certificate (`SSL_CERTFILE` and `SSL_KEYFILE`, see below), it uses that. -2. Otherwise it looks for `/var/lib/electrumx-ssl/server.crt` and `server.key`, in the `electrumx-ssl` volume. +2. Otherwise it looks for `server.crt` and `server.key` in `~/.plm-electrumx/ssl` (`/var/lib/electrumx-ssl` in the container). - **They exist: they are used as they are, never overwritten.** - **They do not exist (first start): a self-signed certificate valid 10 years is generated** for `SSL_HOSTNAME` and saved there. If only one of the two files exists, the container stops with an error instead of overwriting it. @@ -111,15 +111,15 @@ the wallet saves the certificate, and from then on it refuses a different one (a man-in-the-middle cannot take over later. The fields of the certificate (name, country, ...) are not checked by the client, only the expiry date. Consequences: -- **Keep the certificate.** It is in the `electrumx-ssl` volume, separate from the database on purpose: the database - can be deleted and rebuilt at any time (`docker volume rm _electrumx-db`), the certificate should not be. - `docker compose down -v` deletes **both** volumes, and after that a new certificate is generated and every client - that saved the old one refuses to connect. -- **Back it up** while the container runs: +- **Keep the certificate.** It is in `~/.plm-electrumx/ssl`, separate from the database (`~/.plm-electrumx/db`) on + purpose: the database can be deleted and rebuilt at any time, the certificate should not be. If `ssl/` is deleted, + a new certificate is generated and every client that saved the old one refuses to connect. +- **Back it up** by copying the folder (it contains the private key: keep it private): ```bash - docker compose cp electrumx:/var/lib/electrumx-ssl ./ssl-backup # contains the private key: keep it private + cp -a ~/.plm-electrumx/ssl /safe/place/plm-electrumx-ssl ``` - To restore it, put the two files somewhere on the host and use them as "your own certificate" below. + To restore it, copy the two files (`server.crt`, `server.key`) back into `~/.plm-electrumx/ssl` before the first + start, or use them as "your own certificate" below. - If it expires or is lost, clients that saved it must be told to forget it, or the server must get a new name. Generated certificates last 10 years, so expiry is rarely the problem. - Whether a wallet accepts a self-signed certificate depends on the wallet. Electrum does (trust on first use); @@ -173,14 +173,25 @@ Peer discovery is on by default (`PEER_DISCOVERY`, `PEER_ANNOUNCE`). `lib/coins. servers you know there (format: `'host s t'`, for example `'electrum.example.com s50002 t50001'`) and rebuild, or keep the server private with `PEER_DISCOVERY=off` / `PEER_DISCOVERY=self`. -## Resources and first start +## Data directory and first start + +Create the data directory as the user that runs Docker, before the first start (otherwise Docker creates it as root and +the container cannot write; the log then says `ERROR: ... is not writable`): + +```bash +mkdir -p ~/.plm-electrumx/db ~/.plm-electrumx/ssl +``` + +The container runs as uid/gid 1000 (the first user of most hosts). For another user set `PLM_UID` / `PLM_GID` in `.env`. +To keep the data elsewhere, set `ELECTRUMX_DATADIR` in `.env`. - On the first start ElectrumX builds its database from the node, from genesis. With the chain at about 542,000 blocks it took roughly a minute on a development machine, ending at about 230 MB of database and 0.5 GB of RAM. The database grows with the chain. - `CACHE_MB` in `.env` is the memory used for caching during the sync (default in `docker-compose.yml`: 1200). -- The database is the `electrumx-db` volume. It is derived from the node, so it can be rebuilt at any time: - `docker compose down -v` deletes it and the next start syncs again. A backup is optional. +- The data directory is `~/.plm-electrumx` (`ELECTRUMX_DATADIR`): `db/` is the database, `ssl/` the certificate. + The database is derived from the node, so it can be rebuilt at any time: stop the container, delete `db/` and start + again (it syncs again). A backup of it is optional; keep a backup of `ssl/`. - Disk: the node needs `txindex`, so budget for the node's own data too (see the node's README). ## Operation @@ -214,5 +225,6 @@ From another machine, `nc -zv 50001` must succeed if the server is m | `blockchain.outpoint.subscribe` or package broadcast return a daemon error | expected: Palladium Core is based on Bitcoin Core 0.19 and does not have these RPCs | | the server stays at an old height | the node is not synced; ElectrumX only follows the node | | clients cannot connect from outside | port not published/forwarded, firewall, or `SERVICES` does not list that protocol | -| container restarts at once, log says `ERROR: ... SSL_CERTFILE` | your own certificate is not mounted or not readable by uid 1000, only one of `SSL_CERTFILE`/`SSL_KEYFILE` is set, or only one of the generated files exists in the `electrumx-ssl` volume | -| a wallet refuses the server after it worked | the certificate changed (for example after `docker compose down -v`); the wallet saved the old one | +| container stops at once, log says `ERROR: ... is not writable` | the data directory belongs to another user (or to root, if Docker created it): `chown -R $(id -u):$(id -g) ~/.plm-electrumx`, or set `PLM_UID`/`PLM_GID` | +| container restarts at once, log says `ERROR: ... SSL_CERTFILE` | your own certificate is not mounted or not readable by uid 1000, only one of `SSL_CERTFILE`/`SSL_KEYFILE` is set, or only one of the generated files exists in `~/.plm-electrumx/ssl` | +| a wallet refuses the server after it worked | the certificate changed (for example `ssl/` was deleted); the wallet saved the old one | diff --git a/README.md b/README.md index 5fc37e9..91701eb 100644 --- a/README.md +++ b/README.md @@ -24,12 +24,15 @@ as `palladiumd:2332`. RPC and ZMQ of the node are not published on the host, so `server=1` and `txindex=1` (no `prune`). 2. `cp .env.example .env` and set `PLM_RPC_USER` / `PLM_RPC_PASSWORD` to the RPC credentials of that file (use a dedicated `rpcauth` user for anything public). -3. `docker compose up -d --build` +3. Create the data directory as your user: `mkdir -p ~/.plm-electrumx/db ~/.plm-electrumx/ssl` + (`ELECTRUMX_DATADIR` in `.env` changes it). The container runs as uid/gid 1000; for another user set + `PLM_UID` / `PLM_GID` in `.env`. +4. `docker compose up -d --build` The first start builds the database from the node from genesis, which takes a while; follow it with -`docker compose logs -f electrumx`. The database is the `electrumx-db` volume. Electrum clients connect to +`docker compose logs -f electrumx`. The data directory has `db/` (the database, rebuilt from the node if deleted) and `ssl/` (the certificate). Electrum clients connect to TCP port 50001 (`ELECTRUMX_TCP_PORT` in `.env`) and SSL port 50002 (`ELECTRUMX_SSL_PORT`). SSL is on by default: -a self-signed certificate is generated on the first start, kept in the `electrumx-ssl` volume and reused on every +a self-signed certificate is generated on the first start, kept in `~/.plm-electrumx/ssl` and reused on every later start (or use your own, see [DEPLOY.md](DEPLOY.md)). To announce a public server to the network, set `REPORT_SERVICES` in `.env`. Settings are in `.env` and `docker-compose.yml`; the image defaults are in the `Dockerfile`. Ports, firewall, SSL, RPC credentials and troubleshooting are in [DEPLOY.md](DEPLOY.md). diff --git a/docker-compose.yml b/docker-compose.yml index 5a05935..fa62995 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,16 +20,16 @@ services: ports: - "${ELECTRUMX_TCP_PORT:-50001}:50001" - "${ELECTRUMX_SSL_PORT:-50002}:50002" + # data directory of the host (like ~/.palladium for the node): db/ is the database (can be rebuilt from the node), + # ssl/ the certificate (keep it, clients pin it). Create it first, see README.md volumes: - - electrumx-db:/var/lib/electrumx - - electrumx-ssl:/var/lib/electrumx-ssl # the certificate: keep it, clients pin it + - ${ELECTRUMX_DATADIR:-~/.plm-electrumx}/db:/var/lib/electrumx + - ${ELECTRUMX_DATADIR:-~/.plm-electrumx}/ssl:/var/lib/electrumx-ssl + # owner of the data directory: PLM_UID/PLM_GID if it is not user 1000 + user: "${PLM_UID:-1000}:${PLM_GID:-1000}" stop_grace_period: 2m # lets the server flush the db on shutdown networks: [palladium] -volumes: - electrumx-db: - electrumx-ssl: - networks: palladium: name: palladium diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 9006e82..11a8232 100755 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -16,6 +16,13 @@ SSL_HOSTNAME="${SSL_HOSTNAME:-electrumx}" log() { echo "entrypoint: $*"; } +for dir in "$DB_DIRECTORY" "$SSL_DIR"; do + if [ ! -w "$dir" ]; then + log "ERROR: $dir is not writable by uid $(id -u). Create the data directory on the host as that user, or set PLM_UID/PLM_GID (see README.md)." >&2 + exit 1 + fi +done + case "$SSL_ENABLED" in true|TRUE|True|1|yes|on) if [ -n "${SSL_CERTFILE:-}" ] || [ -n "${SSL_KEYFILE:-}" ]; then