cap total connections, and document the P2P port 8444
Build docker image and push to registry.bitdeals.org / main-build-job (push) Successful in 2m12s

The daemon has always listened on 8444 and nothing published it, so every
node built from this image was outbound-only. Publishing it is now a
documented choice rather than an omission -- including the two things that
are not obvious from the config: peers are told the port from `port` in
keys.dat rather than the one you mapped it to (so only 8444:8444 works),
and the node's own address is never configured at all, because every peer
replaces the hardcoded 127.0.0.1 in the version message with the IP it
sees on the socket.

An open port needs a brake, hence BITMESSAGE_MAXTOTALCONNECTIONS. It
defaults to the PyBitmessage stock 200, so nothing changes for existing
users of the image. The key is inserted when the stock config lacks it
instead of trusting the substitution: the PyBitmessage clone is unpinned,
and a silent no-op would ship a node that looks capped and is not.

The API port in the example compose moves to loopback, which is what the
README already prescribed.
This commit is contained in:
bitdeals git user
2026-08-02 14:48:01 +00:00
parent 335ee52c0d
commit 2701c57200
5 changed files with 96 additions and 7 deletions
+29 -3
View File
@@ -14,8 +14,11 @@ The container generates a Bitmessage Deterministic Addresses based on a `BITMESS
Here are some example snippets to help you get started creating a container. Here are some example snippets to help you get started creating a container.
The XML-RPC API controls the daemon completely and has no TLS. Set your own The container has two ports and they are not interchangeable. **8442** is the
credentials and keep the port on loopback. 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 ## docker-compose
@@ -33,8 +36,10 @@ services:
- BITMESSAGE_SEED_ADDRESSES=1 - BITMESSAGE_SEED_ADDRESSES=1
- BITMESSAGE_TTL=172800 - BITMESSAGE_TTL=172800
- BITMESSAGE_STOPRESENDINGAFTERXDAYS=60 - BITMESSAGE_STOPRESENDINGAFTERXDAYS=60
- BITMESSAGE_MAXTOTALCONNECTIONS=40
ports: ports:
- 127.0.0.1:8442:8442 - 127.0.0.1:8442:8442 # API — loopback only
- 8444:8444 # P2P — omit this line to stay outbound-only
volumes: volumes:
- bitmessage:/home/bitmessage - bitmessage:/home/bitmessage
@@ -52,7 +57,9 @@ docker run -d \
-e BITMESSAGE_SEED_ADDRESSES=1 \ -e BITMESSAGE_SEED_ADDRESSES=1 \
-e BITMESSAGE_TTL=172800 \ -e BITMESSAGE_TTL=172800 \
-e BITMESSAGE_STOPRESENDINGAFTERXDAYS=60 \ -e BITMESSAGE_STOPRESENDINGAFTERXDAYS=60 \
-e BITMESSAGE_MAXTOTALCONNECTIONS=40 \
-p 127.0.0.1:8442:8442 \ -p 127.0.0.1:8442:8442 \
-p 8444:8444 \
-v bitmessage:/home/bitmessage \ -v bitmessage:/home/bitmessage \
registry.bitdeals.org/bitmessage registry.bitdeals.org/bitmessage
``` ```
@@ -64,6 +71,7 @@ Container images are configured using parameters passed at runtime.
|Parameter|Function| |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 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| |-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_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_API_PASSWORD|XML-RPC API password. Default: `bitmessage_api_password` — change it, see Notes|
@@ -72,6 +80,7 @@ Container images are configured using parameters passed at runtime.
|-e BITMESSAGE_TTL|The expiration of newly send messages, in seconds. Default: `172800`| |-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_STOPRESENDINGAFTERXDAYS|Stop resending unreceived message after X days. Default: `30`|
|-e BITMESSAGE_APIVARIANT|provides xml or json-RPC API. Default: `legacy`| |-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|
# Notes # Notes
@@ -84,3 +93,20 @@ Container images are configured using parameters passed at runtime.
- The container turns healthy once the daemon has a network connection, which - 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 on a new node takes a few minutes. The start period also covers the startup
`VACUUM` of `messages.dat`. `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`.
- **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.
+30 -3
View File
@@ -14,8 +14,11 @@
Ниже — примеры, с которых удобно начать создание контейнера. Ниже — примеры, с которых удобно начать создание контейнера.
XML-RPC API полностью управляет демоном и не имеет TLS. Задайте свои учётные У контейнера два порта, и они не взаимозаменяемы. **8442** — XML-RPC API: он
данные и оставьте порт на loopback. полностью управляет демоном и не имеет TLS, поэтому задайте свои учётные данные
и оставьте его на loopback. **8444** — P2P-порт Bitmessage: опубликуйте его,
чтобы к узлу могли подключаться другие, или не публикуйте, и тогда узел работает
только на исходящих. Внутри контейнера демон слушает оба в любом случае.
## docker-compose ## docker-compose
@@ -33,8 +36,10 @@ services:
- BITMESSAGE_SEED_ADDRESSES=1 - BITMESSAGE_SEED_ADDRESSES=1
- BITMESSAGE_TTL=172800 - BITMESSAGE_TTL=172800
- BITMESSAGE_STOPRESENDINGAFTERXDAYS=60 - BITMESSAGE_STOPRESENDINGAFTERXDAYS=60
- BITMESSAGE_MAXTOTALCONNECTIONS=40
ports: ports:
- 127.0.0.1:8442:8442 - 127.0.0.1:8442:8442 # API — только loopback
- 8444:8444 # P2P — уберите строку, чтобы остаться на исходящих
volumes: volumes:
- bitmessage:/home/bitmessage - bitmessage:/home/bitmessage
@@ -52,7 +57,9 @@ docker run -d \
-e BITMESSAGE_SEED_ADDRESSES=1 \ -e BITMESSAGE_SEED_ADDRESSES=1 \
-e BITMESSAGE_TTL=172800 \ -e BITMESSAGE_TTL=172800 \
-e BITMESSAGE_STOPRESENDINGAFTERXDAYS=60 \ -e BITMESSAGE_STOPRESENDINGAFTERXDAYS=60 \
-e BITMESSAGE_MAXTOTALCONNECTIONS=40 \
-p 127.0.0.1:8442:8442 \ -p 127.0.0.1:8442:8442 \
-p 8444:8444 \
-v bitmessage:/home/bitmessage \ -v bitmessage:/home/bitmessage \
registry.bitdeals.org/bitmessage registry.bitdeals.org/bitmessage
``` ```
@@ -64,6 +71,7 @@ docker run -d \
|Параметр|Назначение| |Параметр|Назначение|
|:--------|:-------| |:--------|:-------|
|-p 127.0.0.1:8442|Порт API. Внутри контейнера демон всегда слушает `0.0.0.0`, поэтому доступность определяет то, что опубликовано| |-p 127.0.0.1:8442|Порт API. Внутри контейнера демон всегда слушает `0.0.0.0`, поэтому доступность определяет то, что опубликовано|
|-p 8444|P2P-порт Bitmessage. Необязательный: без него узел всё равно подключается к пирам сам, просто к нему подключиться нельзя. Публиковать только как `8444:8444` — см. «Замечания»|
|-v /home/bitmessage|Каталог данных: `keys.dat` (личность, настройки) и `messages.dat`. Без него после каждого обновления это новый узел| |-v /home/bitmessage|Каталог данных: `keys.dat` (личность, настройки) и `messages.dat`. Без него после каждого обновления это новый узел|
|-e BITMESSAGE_API_USER|Пользователь XML-RPC API. По умолчанию: `bitmessage_api_user` — измените| |-e BITMESSAGE_API_USER|Пользователь XML-RPC API. По умолчанию: `bitmessage_api_user` — измените|
|-e BITMESSAGE_API_PASSWORD|Пароль XML-RPC API. По умолчанию: `bitmessage_api_password` — измените, см. «Замечания»| |-e BITMESSAGE_API_PASSWORD|Пароль XML-RPC API. По умолчанию: `bitmessage_api_password` — измените, см. «Замечания»|
@@ -72,6 +80,7 @@ docker run -d \
|-e BITMESSAGE_TTL|Срок жизни вновь отправляемых сообщений, в секундах. По умолчанию: `172800`| |-e BITMESSAGE_TTL|Срок жизни вновь отправляемых сообщений, в секундах. По умолчанию: `172800`|
|-e BITMESSAGE_STOPRESENDINGAFTERXDAYS|Прекратить повторную отправку недоставленного сообщения через X дней. По умолчанию: `30`| |-e BITMESSAGE_STOPRESENDINGAFTERXDAYS|Прекратить повторную отправку недоставленного сообщения через X дней. По умолчанию: `30`|
|-e BITMESSAGE_APIVARIANT|Предоставляемый API: xml или json-RPC. По умолчанию: `legacy`| |-e BITMESSAGE_APIVARIANT|Предоставляемый API: xml или json-RPC. По умолчанию: `legacy`|
|-e BITMESSAGE_MAXTOTALCONNECTIONS|Предел одновременных соединений, входящих и исходящих вместе (`maxoutboundconnections` равен 8, то есть это значение минус 8 — запас на входящие). По умолчанию: `200`, штатное значение PyBitmessage — снижайте, если P2P-порт опубликован|
# Замечания # Замечания
@@ -84,3 +93,21 @@ docker run -d \
- Контейнер становится здоровым, когда у демона появилось сетевое соединение, - Контейнер становится здоровым, когда у демона появилось сетевое соединение,
— на новом узле это несколько минут. Начальный период ожидания заодно — на новом узле это несколько минут. Начальный период ожидания заодно
покрывает стартовый `VACUUM` файла `messages.dat`. покрывает стартовый `VACUUM` файла `messages.dat`.
- **P2P-порт публикуется только как `8444:8444`.** Пирам демон сообщает порт из
собственной конфигурации (`port` в `keys.dat`), а не тот, в который вы его
отобразили, поэтому `8555:8444` объявляет сети порт, где никого нет. Для
другого порта на хосте нужен `extport` в `keys.dat`, а его этот контейнер не
подставляет.
- **Свой адрес нигде не указывается.** В version-сообщении демон шлёт
захардкоженный `127.0.0.1`, который каждый пир отбрасывает и берёт вместо него
IP, увиденный на сокете, а порт — из того же сообщения. Поэтому узел с
опубликованным 8444 сеть находит сама, как только он подключится наружу:
ни IP, ни DNS-имя хоста задавать негде. Единственное исключение — скрытый
сервис Tor, которому нужен явный `onionhostname`.
- **Публикация 8444 — осознанный компромисс по безопасности.** Демон работает на
Python 2 и уже разбирает недоверенные данные от своих исходящих пиров, так что
открытый порт эту поверхность не создаёт — он меняет то, *кто* может
подключиться, *когда* и *сколько их*. Разбор до рукопожатия становится доступен
кому угодно, а реальный риск — исчерпание ресурсов, а не выполнение кода.
Держите `BITMESSAGE_MAXTOTALCONNECTIONS` низким и ограничьте контейнер по
памяти и CPU.
+8 -1
View File
@@ -11,8 +11,15 @@ services:
- BITMESSAGE_SEED_ADDRESSES=1 - BITMESSAGE_SEED_ADDRESSES=1
- BITMESSAGE_TTL=172800 - BITMESSAGE_TTL=172800
- BITMESSAGE_STOPRESENDINGAFTERXDAYS=60 - BITMESSAGE_STOPRESENDINGAFTERXDAYS=60
- BITMESSAGE_MAXTOTALCONNECTIONS=40
ports: ports:
- 8442:8442 # The API controls the daemon completely and has no TLS, so it never
# leaves loopback -- the README has said so all along, this file did not.
- 127.0.0.1:8442:8442
# P2P. Only 8444:8444 works — the daemon announces its own configured
# port to peers, not the one you mapped it to. Drop this line to run
# outbound-only.
- 8444:8444
volumes: volumes:
- bitmessage:/home/bitmessage - bitmessage:/home/bitmessage
volumes: volumes:
+4
View File
@@ -45,7 +45,11 @@ RUN f=/usr/local/lib/python2.7/dist-packages/pybitmessage/helper_sql.py \
FROM ubuntu:bionic FROM ubuntu:bionic
# 8442 is the XML-RPC API (keep it on loopback), 8444 the Bitmessage P2P port.
# The daemon listens on both regardless; publishing 8444 is what makes the node
# reachable for inbound peers.
EXPOSE 8442/tcp EXPOSE 8442/tcp
EXPOSE 8444/tcp
ENV USER_UID=2000 ENV USER_UID=2000
ENV USER_GID=2000 ENV USER_GID=2000
+25
View File
@@ -9,6 +9,18 @@ export BITMESSAGE_API_PORT="${BITMESSAGE_API_PORT:-8442}"
export BITMESSAGE_TTL="${BITMESSAGE_TTL:-172800}" export BITMESSAGE_TTL="${BITMESSAGE_TTL:-172800}"
export BITMESSAGE_STOPRESENDINGAFTERXDAYS="${BITMESSAGE_STOPRESENDINGAFTERXDAYS:-30}" export BITMESSAGE_STOPRESENDINGAFTERXDAYS="${BITMESSAGE_STOPRESENDINGAFTERXDAYS:-30}"
export BITMESSAGE_APIVARIANT="${BITMESSAGE_APIVARIANT:-legacy}" export BITMESSAGE_APIVARIANT="${BITMESSAGE_APIVARIANT:-legacy}"
export BITMESSAGE_MAXTOTALCONNECTIONS="${BITMESSAGE_MAXTOTALCONNECTIONS:-200}"
# Reject anything but a plain number: this value is written into keys.dat, and
# unlike the credentials below it has no business containing characters that
# esc() would have to neutralise. A typo here would otherwise land in the config
# as a key the daemon silently ignores.
case "$BITMESSAGE_MAXTOTALCONNECTIONS" in
'' | *[!0-9]*)
echo "BITMESSAGE_MAXTOTALCONNECTIONS must be a positive integer" >&2
exit 1
;;
esac
if [ -z "${BITMESSAGE_SEED_PHRASE:-}" ] if [ -z "${BITMESSAGE_SEED_PHRASE:-}" ]
then then
@@ -32,6 +44,18 @@ then
chmod 600 keys.dat chmod 600 keys.dat
fi fi
# maxtotalconnections is the only brake on a node whose P2P port (8444) is
# published: it caps inbound sockets at the total minus maxoutboundconnections.
# The substitution below is a no-op when the key is missing, which would ship a
# node that looks capped and is not -- and the PyBitmessage clone in the
# Dockerfile is unpinned, so the stock config is whatever upstream generates
# today. Add the key rather than trust the substitution alone; line 1 is the
# [bitmessagesettings] header the daemon reads it from.
if ! grep -q "^maxtotalconnections = " keys.dat
then
gosu bitmessage sed -i "1a maxtotalconnections = $BITMESSAGE_MAXTOTALCONNECTIONS" keys.dat
fi
# Set config values. Every expression is anchored to the start of the line and # Set config values. Every expression is anchored to the start of the line and
# names its key in the replacement, so no backreference is involved and nothing # names its key in the replacement, so no backreference is involved and nothing
# in another section can match. With set -e a failure here now stops the # in another section can match. With set -e a failure here now stops the
@@ -46,6 +70,7 @@ gosu bitmessage sed -i \
-e "s|^apienabled = .*|apienabled = True|" \ -e "s|^apienabled = .*|apienabled = True|" \
-e "s|^ttl = .*|ttl = $(esc "$BITMESSAGE_TTL")|" \ -e "s|^ttl = .*|ttl = $(esc "$BITMESSAGE_TTL")|" \
-e "s|^stopresendingafterxdays = .*|stopresendingafterxdays = $(esc "$BITMESSAGE_STOPRESENDINGAFTERXDAYS")|" \ -e "s|^stopresendingafterxdays = .*|stopresendingafterxdays = $(esc "$BITMESSAGE_STOPRESENDINGAFTERXDAYS")|" \
-e "s|^maxtotalconnections = .*|maxtotalconnections = $BITMESSAGE_MAXTOTALCONNECTIONS|" \
-e "s|^udp = .*|udp = False|" keys.dat -e "s|^udp = .*|udp = False|" keys.dat
# generate address from seed # generate address from seed