Ubuntu 18.04 does not ship setpriv. The program exists in util-linux 2.31, but Debian only began installing it at 2.32, and this image is on bionic because PyBitmessage is Python 2. The build assertion added in the last commit caught it, which is what it is for: `/bin/sh: 1: setpriv: not found`, exit 127, no image pushed. drop_privs.py does the same work with what the image already has. It sets no_new_privs, drops the bounding set with PR_CAPBSET_DROP while CAP_SETPCAP is still held, then optionally becomes the daemon's user. Two shapes, both in run.sh: keep four capabilities and stay root, for the supervisor; keep none and become uid 2000, for the daemon and everything run on its behalf. It is better than setpriv would have been in one respect. The bounding set is walked up to the kernel's own cap_last_cap instead of a list of names, so a capability this image has never heard of goes too -- and the "-all" spelling that bionic's setpriv refuses under a newer kernel is not needed at all. Verified in a user namespace, in the arrangement run.sh uses: the outer drop leaves 00000000000001e0, the inner leaves every capability set at zero with no_new_privs set. The build assertion checks the same two things.
17 KiB
Общие сведения
English version: README.md
PyBitmessage — клиент P2P-протокола обмена сообщениями Bitmessage, служащий для отправки шифрованных сообщений как одному адресату, так и множеству подписчиков.
Клиент PyBitmessage, работающий демоном в docker-контейнере с включённым XML-RPC API.
Репозиторий описывает только развёртывание в docker.
Использование
Контейнер создаёт детерминированные адреса Bitmessage на основе переменной BITMESSAGE_SEED_PHRASE.
Ниже — примеры, с которых удобно начать создание контейнера.
У контейнера два порта, и они не взаимозаменяемы. 8442 — XML-RPC API: он полностью управляет демоном и не имеет TLS, поэтому задайте свои учётные данные и оставьте его на loopback. 8444 — P2P-порт Bitmessage: опубликуйте его, чтобы к узлу могли подключаться другие, или не публикуйте, и тогда узел работает только на исходящих. Внутри контейнера демон слушает оба в любом случае.
docker-compose
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
- 8444:8444 # P2P — уберите строку, чтобы остаться на исходящих
volumes:
- bitmessage:/home/bitmessage
volumes:
bitmessage:
docker cli
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
Параметры
Образы контейнера настраиваются параметрами, передаваемыми при запуске.
| Параметр | Назначение |
|---|---|
| -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 — измените, см. «Замечания» |
| -e BITMESSAGE_SEED_PHRASE | Парольная фраза для создания детерминированных адресов. По умолчанию: генерируется заново при каждом старте, то есть адреса каждый раз другие. Используется только при BITMESSAGE_SEED_ADDRESSES больше 0 |
| -e BITMESSAGE_SEED_ADDRESSES | Количество создаваемых детерминированных адресов. По умолчанию: 0 |
| -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-порт опубликован |
| -e BITMESSAGE_TRUSTED_PEER | host:port единственного пира, к которому узел подключается наружу; больше ни к кому. По умолчанию: пусто — узел выбирает пиров сам. См. «Замечания» |
| -e BITMESSAGE_SEND_OUTGOING | Подключается ли узел наружу вообще: True или False. По умолчанию: True. При False получается узел, который только принимает входящие, — центр приватного контура |
| -e BITMESSAGE_KNOWN_NODES | Список host:port через запятую; записывается в knownnodes.dat при каждом старте вместо того, что там было. По умолчанию: пусто — файл не трогается. Заодно выключает бутстрап по DNS, см. «Замечания» |
| -e BITMESSAGE_WATCHDOG | Перезапускать ли демона, потерявшего всех пиров: True или False. По умолчанию: True. См. «Замечания» |
| -e BITMESSAGE_WATCHDOG_PERIOD | Секунд между проверками числа пиров. По умолчанию: 60 |
| -e BITMESSAGE_WATCHDOG_AFTER | Сколько проверок подряд без пиров нужно, чтобы вмешаться. По умолчанию: 5 — то есть пять минут при периоде по умолчанию |
| -e BITMESSAGE_WATCHDOG_COOLDOWN | Минимальный промежуток между двумя перезапусками, в секундах. По умолчанию: 900. 0 снимает ограничение |
Замечания
-
%в пароле API использовать нельзя: он ломает собственный чтец конфигурации PyBitmessage, и любой вызов API отвечает500, притом чтоkeys.datвыглядит правильным. Остальные символы допустимы, контейнер их экранирует. -
Клиенты обязаны кодировать учётные данные — они попадают в
http://user:password@host:port/, где@,#,/и:меняют разбор URL. Скрипты этого образа кодируют, ваш клиент должен тоже. -
Контейнер становится здоровым, когда у демона появилось сетевое соединение, — на новом узле это несколько минут. Начальный период ожидания заодно покрывает стартовый
VACUUMфайлаmessages.dat. -
Демона без пиров перезапускаем, молчащего — нет. Демон, потерявший всех пиров, сам обратно дорогу не находит: он может простоять на нуле соединений сколько угодно, — а только что запущенный ищет пиров напористо и находит. Поэтому контейнер присматривает за собственным демоном: раз в
BITMESSAGE_WATCHDOG_PERIODспрашивает у API число соединений и послеBITMESSAGE_WATCHDOG_AFTERнулей подряд останавливает демона через его же API и поднимает заново. Контейнер при этом не пересоздаётся и никто снаружи не участвует, так что соседний узел сохраняет адреса и видит лишь моргнувший API.Демона, у которого API не отвечает вовсе, сторож не трогает намеренно. Это узел, застрявший на стартовом
VACUUM, которого он не может закончить; перезапуск его не лечит, а лишь обрывает следующийVACUUMна середине. Проверка здоровья показываетunhealthyв обоих случаях, но вмешиваться стоит только в первый, а во втором нужен человек.Задайте контейнеру
stop_grace_period: 90s(compose) или--stop-timeout 90(docker run). Корректное закрытиеmessages.datне укладывается в десять секунд, отпущенные Docker по умолчанию, — замерено около тринадцати на небольшой базе, и растёт вместе с файлом, — так что без этого демона убьёт SIGKILL на середине записи, ровно то, ради предотвращения чего супервизор и заведён. Сам образ это задать не может, только вызывающая сторона.BITMESSAGE_WATCHDOG=Falseвыключает правило о пирах. Супервизор остаётся в любом случае: он же корректно останавливает демона поdocker stopи поднимает того, кто умер сам, вместо контейнера, стоящего вокруг трупа. -
P2P-порт публикуется только как
8444:8444. Пирам демон сообщает порт из собственной конфигурации (portвkeys.dat), а не тот, в который вы его отобразили, поэтому8555:8444объявляет сети порт, где никого нет. Для другого порта на хосте нуженextportвkeys.dat, а его этот контейнер не подставляет. -
Свой адрес нигде не указывается. В version-сообщении демон шлёт захардкоженный
127.0.0.1, который каждый пир отбрасывает и берёт вместо него IP, увиденный на сокете, а порт — из того же сообщения. Поэтому узел с опубликованным 8444 сеть находит сама, как только он подключится наружу: ни IP, ни DNS-имя хоста задавать негде. Единственное исключение — скрытый сервис Tor, которому нужен явныйonionhostname. -
Приватному контуру нужны назначенные пиры и звезда, в которую их назначать. PyBitmessage отвергает кандидата, чья сетевая группа (для IPv4 — /16) уже представлена среди его исходящих соединений. Все контейнеры одного compose-проекта живут в одной /16, поэтому каждый узел удерживает ровно одно исходящее соединение — со случайно выбранным пиром, и контур чаще всего распадается на компоненты. Задайте одному узлу
BITMESSAGE_SEND_OUTGOING=False, и он станет хабом, который только принимает (проверка смотрит лишь на исходящие, входящие она не ограничивает), остальные направьте на него черезBITMESSAGE_TRUSTED_PEER=<ip-хаба>:8444— объекты пойдут спица → хаб → спицы. Адреса задавайте IP, а не именами сервисов: проверка разбирает хост как IP, да и обменaddrмежду узлами оперирует IP. -
BITMESSAGE_KNOWN_NODES— то, что делает приватный контур приватным. Узел, у которого вknownnodes.datесть пир не из встроенного списка PyBitmessage, перестаёт спрашивать адреса уbootstrap8080.bitmessage.org; без этого даже узел с назначенным пиром при каждом старте резолвит публичный бутстрап-хост. Файл переписывается на каждом старте, поэтому во что узел верит при загрузке, определяет переменная, а не история контейнера. -
Публикация 8444 — осознанный компромисс по безопасности. Демон работает на Python 2 и уже разбирает недоверенные данные от своих исходящих пиров, так что открытый порт эту поверхность не создаёт — он меняет то, кто может подключиться, когда и сколько их. Разбор до рукопожатия становится доступен кому угодно, а реальный риск — исчерпание ресурсов, а не выполнение кода. Держите
BITMESSAGE_MAXTOTALCONNECTIONSнизким и ограничьте контейнер по памяти и CPU. -
У демона нет собственных прав, а контейнер снимает с себя остальное.
run.shнужен root ровно один раз, на старте:keys.datможет приехать из bind-монтирования с чужим владельцем, поэтому сначала ему меняют владельца, ставят режим 600 и читают. Сразу после этого скрипт перезапускает сам себя с bounding set из четырёх capability —SETUIDиSETGID, чтобы запускать демона под его собственным пользователем,KILLдля запасной остановки иSETPCAP, чтобы снять остальные, — а каждый запуск демона идёт черезdrop_privs.py: uid 2000, все четыре набора capability пустые,no_new_privsвключён. Ни на одном файле образа нет бита setuid или setgid, так что захваченному демону не по чему подниматься.Три вещи может задать только вызывающая сторона, и все три стоит задать.
cap_drop: ALLс возвращённымиCHOWN,DAC_OVERRIDE,FOWNER,SETUID,SETGID,KILLиSETPCAP: эти семь нужны описанному выше старту, а сузить набор, с которым работают healthcheck иdocker exec, контейнер сам не может — только это.read_only: trueсtmpfsпод/tmpи/run: демон пишет только в свой домашний каталог. Иpids_limit— рядом с ограничениями по памяти и CPU, о которых просит предыдущее замечание.