Build docker image and push to registry.bitdeals.org / main-build-job (push) Successful in 1m50s
A private Bitmessage contour cannot be assembled by letting the nodes find each other. The sybil check in connectionpool refuses a candidate whose /16 is already among the outbound connections, and every container of a compose project shares one /16 -- so each node keeps a single outbound connection to a randomly chosen peer, and the contour splits into components on some runs and not on others. Three variables make the topology explicit instead: BITMESSAGE_TRUSTED_PEER trustedpeer = host:port BITMESSAGE_SEND_OUTGOING sendoutgoingconnections = True/False BITMESSAGE_KNOWN_NODES host:port,... -> knownnodes.dat With them a star is one line of config per node: the hub takes SEND_OUTGOING=False and only accepts, the spokes take TRUSTED_PEER=<hub>:8444. Details worth knowing: - trustedpeer is absent from the stock keys.dat, so a substitution alone would be a silent no-op. The key is added the same way maxtotalconnections is, and it is added even when the value is empty -- that is how a node that was pinned before can be unpinned. Its anchors stop at "=" rather than "= ", because an empty value leaves no trailing space to match. - knownnodes.dat is rewritten on every start, not only when missing. "Only when missing" would never have fired: the image ships one, built by the `pybitmessage -t` run in the Dockerfile, and a named volume inherits it. Seeding it is also what stops the DNS bootstrap -- deserialising any peer that is neither a DEFAULT_NODE nor "self" raises knownNodesActual, and startBootstrappers only runs while that flag is down. - Both peer variables are validated here. PyBitmessage does check trustedpeer, but with a sys.exit() from a constructor in the network thread, which reads as a container that died for no stated reason.
133 lines
7.3 KiB
Markdown
133 lines
7.3 KiB
Markdown
# Intro
|
|
|
|
> Русская версия: [README.ru-RU.md](README.ru-RU.md)
|
|
|
|
[PyBitmessage](https://bitmessage.org/) is a client of the Bitmessages P2P communication protocol used to send encrypted messages to another person or to many subscribers.
|
|
|
|
PyBitmessage client running as a daemon in docker container with XML-RPC API enabled.
|
|
|
|
This repository covers the docker deployment only.
|
|
|
|
# Usage
|
|
|
|
The container generates a Bitmessage Deterministic Addresses based on a `BITMESSAGE_SEED_PHRASE` variable.
|
|
|
|
Here are some example snippets to help you get started creating a container.
|
|
|
|
The container has two ports and they are not interchangeable. **8442** is the
|
|
XML-RPC API: it controls the daemon completely and has no TLS, so set your own
|
|
credentials and keep it on loopback. **8444** is the Bitmessage P2P port:
|
|
publish it to let other nodes connect in, leave it unpublished to stay
|
|
outbound-only. The daemon listens on both inside the container either way.
|
|
|
|
## docker-compose
|
|
|
|
```yaml
|
|
services:
|
|
pybitmessage:
|
|
build:
|
|
context: https://git.bitdeals.org/private/bitmessage.git
|
|
dockerfile: ./docker/Dockerfile
|
|
image: registry.bitdeals.org/bitmessage
|
|
environment:
|
|
- BITMESSAGE_API_USER=CHANGE_ME
|
|
- BITMESSAGE_API_PASSWORD=CHANGE_ME
|
|
- BITMESSAGE_SEED_PHRASE=bitmessage_seed_phrase
|
|
- BITMESSAGE_SEED_ADDRESSES=1
|
|
- BITMESSAGE_TTL=172800
|
|
- BITMESSAGE_STOPRESENDINGAFTERXDAYS=60
|
|
- BITMESSAGE_MAXTOTALCONNECTIONS=40
|
|
ports:
|
|
- 127.0.0.1:8442:8442 # API — loopback only
|
|
- 8444:8444 # P2P — omit this line to stay outbound-only
|
|
volumes:
|
|
- bitmessage:/home/bitmessage
|
|
|
|
volumes:
|
|
bitmessage:
|
|
```
|
|
|
|
## docker cli
|
|
|
|
```sh
|
|
docker run -d \
|
|
-e BITMESSAGE_API_USER=CHANGE_ME \
|
|
-e BITMESSAGE_API_PASSWORD=CHANGE_ME \
|
|
-e BITMESSAGE_SEED_PHRASE=bitmessage_seed_phrase \
|
|
-e BITMESSAGE_SEED_ADDRESSES=1 \
|
|
-e BITMESSAGE_TTL=172800 \
|
|
-e BITMESSAGE_STOPRESENDINGAFTERXDAYS=60 \
|
|
-e BITMESSAGE_MAXTOTALCONNECTIONS=40 \
|
|
-p 127.0.0.1:8442:8442 \
|
|
-p 8444:8444 \
|
|
-v bitmessage:/home/bitmessage \
|
|
registry.bitdeals.org/bitmessage
|
|
```
|
|
|
|
# Parameters
|
|
|
|
Container images are configured using parameters passed at runtime.
|
|
|
|
|Parameter|Function|
|
|
|:--------|:-------|
|
|
|-p 127.0.0.1:8442|API port. The daemon always binds `0.0.0.0` inside the container, so what you publish decides who reaches it|
|
|
|-p 8444|Bitmessage P2P port. Optional: without it the node still connects out to peers, it just cannot be connected to. Must be published as `8444:8444` — see Notes|
|
|
|-v /home/bitmessage|Data directory: `keys.dat` (identity, settings) and `messages.dat`. Without it the node is a new node after every update|
|
|
|-e BITMESSAGE_API_USER|XML-RPC API user. Default: `bitmessage_api_user` — change it|
|
|
|-e BITMESSAGE_API_PASSWORD|XML-RPC API password. Default: `bitmessage_api_password` — change it, see Notes|
|
|
|-e BITMESSAGE_SEED_PHRASE|Create Deterministic Addresses password. Default: regenerated on every start, giving different addresses each time. Only used when `BITMESSAGE_SEED_ADDRESSES` is above `0`|
|
|
|-e BITMESSAGE_SEED_ADDRESSES|Number of Deterministic Addresses to generate. Default: `0`|
|
|
|-e BITMESSAGE_TTL|The expiration of newly send messages, in seconds. Default: `172800`|
|
|
|-e BITMESSAGE_STOPRESENDINGAFTERXDAYS|Stop resending unreceived message after X days. Default: `30`|
|
|
|-e BITMESSAGE_APIVARIANT|provides xml or json-RPC API. Default: `legacy`|
|
|
|-e BITMESSAGE_MAXTOTALCONNECTIONS|Cap on all connections at once, inbound and outbound together (`maxoutboundconnections` is 8, so this minus 8 is the inbound headroom). Default: `200`, the PyBitmessage stock value — lower it when the P2P port is published|
|
|
|-e BITMESSAGE_TRUSTED_PEER|`host:port` of the one peer this node may connect out to; it dials nothing else. Default: empty — the node chooses its own peers. See Notes|
|
|
|-e BITMESSAGE_SEND_OUTGOING|Whether the node dials out at all, `True` or `False`. Default: `True`. `False` gives a node that only accepts inbound connections — the hub of a private contour|
|
|
|-e BITMESSAGE_KNOWN_NODES|Comma-separated `host:port` list, written into `knownnodes.dat` on every start in place of whatever was there. Default: empty — the file is left as it is. Also switches the DNS bootstrap off, see Notes|
|
|
|
|
# Notes
|
|
|
|
- `%` cannot be used in the API password: it breaks PyBitmessage's own config
|
|
reader, and every API call then returns `500` while `keys.dat` looks correct.
|
|
Other characters are fine, the container escapes them.
|
|
- Clients must percent-encode the credentials — they go into
|
|
`http://user:password@host:port/`, where `@`, `#`, `/` and `:` change how the
|
|
URL parses. The scripts in this image do; yours has to as well.
|
|
- The container turns healthy once the daemon has a network connection, which
|
|
on a new node takes a few minutes. The start period also covers the startup
|
|
`VACUUM` of `messages.dat`.
|
|
- **The P2P port must be published as `8444:8444`.** The daemon tells peers the
|
|
port from its own config (`port` in `keys.dat`), not the port you mapped it
|
|
to, so `8555:8444` advertises a port nobody can reach. A different host port
|
|
needs `extport` in `keys.dat`, which this container does not template.
|
|
- **Your own address is never configured.** The version message carries a
|
|
hardcoded `127.0.0.1` that every peer discards in favour of the IP it sees on
|
|
the socket, and it takes the port from that same message. So a node with 8444
|
|
published is found by the network on its own, as soon as it connects out —
|
|
there is no host IP or DNS name to set anywhere. The one exception is a Tor
|
|
hidden service, which needs an explicit `onionhostname`.
|
|
- **A private contour needs its peers pinned, and a star to pin them into.**
|
|
PyBitmessage refuses a candidate whose network group (the /16 for IPv4) is
|
|
already represented among its outbound connections. Every container of one
|
|
compose project lives in a single /16, so each node keeps exactly one outbound
|
|
connection, to a peer it picked at random — which as often as not leaves the
|
|
contour split into components. Give one node `BITMESSAGE_SEND_OUTGOING=False`
|
|
so it becomes a hub that only accepts (the check looks at outbound connections
|
|
only, so inbound are not capped by it), point the rest at it with
|
|
`BITMESSAGE_TRUSTED_PEER=<hub-ip>:8444`, and objects travel spoke → hub →
|
|
spokes. Use IP addresses, not service names: the sybil check parses the host
|
|
as an IP, and the `addr` exchange between nodes carries IPs anyway.
|
|
- **`BITMESSAGE_KNOWN_NODES` is what keeps a private contour private.** A node
|
|
whose `knownnodes.dat` names a peer outside PyBitmessage's built-in default
|
|
list stops asking `bootstrap8080.bitmessage.org` for more; without it even a
|
|
pinned node resolves the public bootstrap host on every start. The file is
|
|
rewritten on each start, so the variable, not the container's history, is what
|
|
the node believes on boot.
|
|
- **Publishing 8444 is a deliberate security trade-off.** The daemon runs on
|
|
Python 2 and already parses untrusted data from its outbound peers, so an open
|
|
port does not create that exposure — it changes *who* may connect, *when*, and
|
|
*how many*. The pre-handshake parser becomes reachable by anyone, and the
|
|
practical risk is resource exhaustion rather than code execution. Keep
|
|
`BITMESSAGE_MAXTOTALCONNECTIONS` low and put memory and CPU limits on the
|
|
container.
|