Build docker image and push to registry.bitdeals.org / main-build-job (push) Successful in 3m0s
The sed pass that configures the daemon punished anyone setting a strong
password: '&' in a replacement means the whole match, so a password
containing one was silently rewritten into something else, and the '|'
delimiter made sed exit with 'unknown option to s'. Every setting rode in
one invocation and the script had no set -e, so that failure applied none
of them -- not apipassword, not apienabled, not apiinterface -- and the
daemon came up on whatever it had before without saying so.
esc() now escapes backslash, '&' and the delimiter via printf. The
expressions are anchored to the start of the line and name their key in the
replacement instead of using \1, so no backreference is involved and
nothing in another section can match. keys.dat also holds privsigningkey
and privencryptionkey for this node's Bitmessage identities; editing in
place leaves every byte outside [bitmessagesettings] untouched, which is
why this stays a line edit rather than a parse-and-rewrite.
The clients needed the other half of this: credentials go into an XML-RPC
URL, where '@' splits the userinfo and '#' truncates the rest, so a strong
password wrote correctly and still failed to connect. Both now
percent-encode. A '%' in the password remains unusable -- it breaks
PyBitmessage's own config reader and every API call returns 500.
Also here, all found while making the above safe:
- set -eu, with the keys.dat chown guarded. A misconfiguration now stops
the container instead of passing unnoticed.
- The seed-address retry loop used bash brace expansion under CMD ["sh"],
where /bin/sh is dash and {1..4} is a literal, so it ran once, not four
times.
- apt-get update shared a layer with nothing, letting a cached update feed
install months-stale package lists.
- HEALTHCHECK gained a start period; the startup VACUUM takes tens of
seconds on a large messages.dat and the container reported unhealthy for
all of it.
- Removed the AppImage systemd unit, AppArmor profile and updater script.
Nothing referenced them -- not the image, the deployment or the ansible
roles -- and the updater fetched a binary with no signature or checksum
check.
83 lines
3.1 KiB
Docker
83 lines
3.1 KiB
Docker
# A container for PyBitmessage daemon
|
|
FROM ubuntu:bionic
|
|
|
|
SHELL ["/bin/bash", "-exo", "pipefail", "-c"]
|
|
|
|
# Install dependencies. update and install share a layer on purpose: split
|
|
# across two, a cached update feeds install package lists that may be months
|
|
# stale, and the install then fails or pulls something unintended.
|
|
RUN apt-get update \
|
|
&& apt-get install -yq --no-install-suggests --no-install-recommends \
|
|
build-essential libcap-dev libssl-dev \
|
|
python-all-dev python-msgpack python-pip python-setuptools \
|
|
git
|
|
|
|
## Do not use cache when building next layers of the image.
|
|
ARG NOCACHE=0
|
|
|
|
WORKDIR /root/PyBitmessage
|
|
RUN git clone https://github.com/Bitmessage/PyBitmessage .
|
|
|
|
# Install
|
|
RUN pip2 install jsonrpclib .
|
|
|
|
# Raise the SQL-thread startup timeout from the stock 60 s.
|
|
#
|
|
# PyBitmessage kills the daemon outright if the SQL thread is not ready within
|
|
# sql_timeout seconds (class_objectProcessor.py -> os._exit(1)). The startup
|
|
# VACUUM of a messages.dat that has grown to a few hundred MB does not fit in
|
|
# 60 s, and since the process dies mid-VACUUM lastvacuumtime is never updated,
|
|
# so every later start retries the same doomed VACUUM and the node never comes
|
|
# back. Measured: 26 s for a 264 MB database on an idle host, and the last
|
|
# start that did survive used 36 s of the 60.
|
|
#
|
|
# The greps are load-bearing: the clone above is unpinned, so if upstream ever
|
|
# moves or renames the constant, a silent no-op sed would ship an image that
|
|
# looks fixed and is not. Fail the build instead. The .pyc is refreshed because
|
|
# at runtime /usr/local is root-owned while the daemon runs as bitmessage, so a
|
|
# stale one can only be recompiled to memory on every start.
|
|
RUN f=/usr/local/lib/python2.7/dist-packages/pybitmessage/helper_sql.py \
|
|
&& grep -q '^sql_timeout = 60$' "$f" \
|
|
&& sed -i 's/^sql_timeout = 60$/sql_timeout = 600/' "$f" \
|
|
&& grep -q '^sql_timeout = 600$' "$f" \
|
|
&& rm -f "${f}c" \
|
|
&& python -c "import py_compile; py_compile.compile('$f')"
|
|
|
|
FROM ubuntu:bionic
|
|
|
|
EXPOSE 8442/tcp
|
|
|
|
ENV USER_UID=2000
|
|
ENV USER_GID=2000
|
|
ENV HOME=/home/bitmessage
|
|
ENV BITMESSAGE_HOME=${HOME}
|
|
|
|
COPY --from=0 /usr/local/ /usr/local/
|
|
COPY ./docker/healthy_check.py /usr/local/bin/
|
|
COPY ./docker/seed_addr_gen.py /usr/local/bin/
|
|
COPY ./docker/run.sh /usr/local/bin/
|
|
|
|
# Install dependencies
|
|
RUN apt-get update \
|
|
&& apt-get install -yq --no-install-suggests --no-install-recommends python-setuptools moreutils gosu \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Create a user
|
|
RUN addgroup --gid $USER_GID bitmessage ;\
|
|
useradd --uid $USER_UID --gid $USER_GID --skel /dev/null --create-home --home-dir $HOME bitmessage
|
|
|
|
WORKDIR ${HOME}
|
|
|
|
# Generate default config
|
|
RUN su bitmessage -c "pybitmessage -t"
|
|
|
|
CMD ["sh", "/usr/local/bin/run.sh"]
|
|
|
|
## Check PyBitmessage active network connections.
|
|
## The start period covers the startup VACUUM of messages.dat, which takes tens
|
|
## of seconds once the database reaches a few hundred MB; without it the
|
|
## container reports unhealthy for that whole window on every restart.
|
|
HEALTHCHECK --retries=0 --interval=15s --start-period=180s \
|
|
CMD ["python", "/usr/local/bin/healthy_check.py"]
|
|
|