diff --git a/README.md b/README.md index 923bbcf..99cb016 100644 --- a/README.md +++ b/README.md @@ -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. -The XML-RPC API controls the daemon completely and has no TLS. Set your own -credentials and keep the port on loopback. +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 @@ -33,8 +36,10 @@ services: - BITMESSAGE_SEED_ADDRESSES=1 - BITMESSAGE_TTL=172800 - BITMESSAGE_STOPRESENDINGAFTERXDAYS=60 + - BITMESSAGE_MAXTOTALCONNECTIONS=40 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: - bitmessage:/home/bitmessage @@ -52,7 +57,9 @@ docker run -d \ -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 ``` @@ -64,6 +71,7 @@ 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| @@ -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_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| # 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 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`. +- **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. diff --git a/README.ru-RU.md b/README.ru-RU.md index 7291fcf..91a1c26 100644 --- a/README.ru-RU.md +++ b/README.ru-RU.md @@ -14,8 +14,11 @@ Ниже — примеры, с которых удобно начать создание контейнера. -XML-RPC API полностью управляет демоном и не имеет TLS. Задайте свои учётные -данные и оставьте порт на loopback. +У контейнера два порта, и они не взаимозаменяемы. **8442** — XML-RPC API: он +полностью управляет демоном и не имеет TLS, поэтому задайте свои учётные данные +и оставьте его на loopback. **8444** — P2P-порт Bitmessage: опубликуйте его, +чтобы к узлу могли подключаться другие, или не публикуйте, и тогда узел работает +только на исходящих. Внутри контейнера демон слушает оба в любом случае. ## docker-compose @@ -33,8 +36,10 @@ services: - BITMESSAGE_SEED_ADDRESSES=1 - BITMESSAGE_TTL=172800 - BITMESSAGE_STOPRESENDINGAFTERXDAYS=60 + - BITMESSAGE_MAXTOTALCONNECTIONS=40 ports: - - 127.0.0.1:8442:8442 + - 127.0.0.1:8442:8442 # API — только loopback + - 8444:8444 # P2P — уберите строку, чтобы остаться на исходящих volumes: - bitmessage:/home/bitmessage @@ -52,7 +57,9 @@ docker run -d \ -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 ``` @@ -64,6 +71,7 @@ docker run -d \ |Параметр|Назначение| |:--------|:-------| |-p 127.0.0.1:8442|Порт API. Внутри контейнера демон всегда слушает `0.0.0.0`, поэтому доступность определяет то, что опубликовано| +|-p 8444|P2P-порт Bitmessage. Необязательный: без него узел всё равно подключается к пирам сам, просто к нему подключиться нельзя. Публиковать только как `8444:8444` — см. «Замечания»| |-v /home/bitmessage|Каталог данных: `keys.dat` (личность, настройки) и `messages.dat`. Без него после каждого обновления это новый узел| |-e BITMESSAGE_API_USER|Пользователь XML-RPC API. По умолчанию: `bitmessage_api_user` — измените| |-e BITMESSAGE_API_PASSWORD|Пароль XML-RPC API. По умолчанию: `bitmessage_api_password` — измените, см. «Замечания»| @@ -72,6 +80,7 @@ docker run -d \ |-e BITMESSAGE_TTL|Срок жизни вновь отправляемых сообщений, в секундах. По умолчанию: `172800`| |-e BITMESSAGE_STOPRESENDINGAFTERXDAYS|Прекратить повторную отправку недоставленного сообщения через X дней. По умолчанию: `30`| |-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`. +- **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. diff --git a/docker-compose.yml b/docker-compose.yml index 5f49620..c1f9256 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,8 +11,15 @@ services: - BITMESSAGE_SEED_ADDRESSES=1 - BITMESSAGE_TTL=172800 - BITMESSAGE_STOPRESENDINGAFTERXDAYS=60 + - BITMESSAGE_MAXTOTALCONNECTIONS=40 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: - bitmessage:/home/bitmessage volumes: diff --git a/docker/Dockerfile b/docker/Dockerfile index 33ba654..87f973d 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -45,7 +45,11 @@ RUN f=/usr/local/lib/python2.7/dist-packages/pybitmessage/helper_sql.py \ 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 8444/tcp ENV USER_UID=2000 ENV USER_GID=2000 diff --git a/docker/run.sh b/docker/run.sh index 363872e..7dacc0e 100644 --- a/docker/run.sh +++ b/docker/run.sh @@ -9,6 +9,18 @@ export BITMESSAGE_API_PORT="${BITMESSAGE_API_PORT:-8442}" export BITMESSAGE_TTL="${BITMESSAGE_TTL:-172800}" export BITMESSAGE_STOPRESENDINGAFTERXDAYS="${BITMESSAGE_STOPRESENDINGAFTERXDAYS:-30}" 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:-}" ] then @@ -32,6 +44,18 @@ then chmod 600 keys.dat 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 # 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 @@ -46,6 +70,7 @@ gosu bitmessage sed -i \ -e "s|^apienabled = .*|apienabled = True|" \ -e "s|^ttl = .*|ttl = $(esc "$BITMESSAGE_TTL")|" \ -e "s|^stopresendingafterxdays = .*|stopresendingafterxdays = $(esc "$BITMESSAGE_STOPRESENDINGAFTERXDAYS")|" \ + -e "s|^maxtotalconnections = .*|maxtotalconnections = $BITMESSAGE_MAXTOTALCONNECTIONS|" \ -e "s|^udp = .*|udp = False|" keys.dat # generate address from seed