diff --git a/1.14.5/bullseye/build.sh b/1.14.5/bullseye/build.sh index 5fb7161..a7bc520 100755 --- a/1.14.5/bullseye/build.sh +++ b/1.14.5/bullseye/build.sh @@ -1,3 +1,3 @@ #!/bin/bash -docker build --no-cache --build-arg "USER=$USERNAME" --build-arg APP_UID=$APP_UID --build-arg APP_GID=$APP_GID -t xanimo/1.14.5-dogecoin:modify-chown . \ No newline at end of file +docker buildx build --no-cache --build-arg "USER=$USERNAME" --build-arg "APP_UID=$APP_UID" --build-arg "APP_GID=$APP_GID" -t xanimo/1.14.5-dogecoin:modify-chown . --load \ No newline at end of file diff --git a/1.14.5/bullseye/testme.sh b/1.14.5/bullseye/testme.sh index 1970a47..8b73691 100755 --- a/1.14.5/bullseye/testme.sh +++ b/1.14.5/bullseye/testme.sh @@ -2,7 +2,7 @@ OLD_UID=$APP_UID OLD_GID=$APP_GID -. ./set.sh $RANDOM $RANDOM +. ./set.sh ${1:-1337} ${2:-69420} . ./build.sh . ./run.sh sleep 5 diff --git a/1.14.6/bullseye/Dockerfile b/1.14.6/bullseye/Dockerfile new file mode 100644 index 0000000..b12d157 --- /dev/null +++ b/1.14.6/bullseye/Dockerfile @@ -0,0 +1,110 @@ +FROM debian:bullseye-slim AS verify + +WORKDIR /verify + +# github repository locations +ARG REPO_GITIAN_BUILDER=https://github.com/devrandom/gitian-builder.git +ARG REPO_GITIAN_SIGS=https://github.com/dogecoin/gitian.sigs.git +ARG REPO_DOGECOIN_CORE=https://github.com/dogecoin/dogecoin.git + +# Specify release variables +ARG RLS_VERSION=1.14.6 +ARG RLS_OS=linux +ARG RLS_LIB=gnu +ARG RLS_ARCH= + +# static derived variables +ARG SIG_PATH=${RLS_VERSION}-${RLS_OS} +ARG DESCRIPTOR_PATH=dogecoin/contrib/gitian-descriptors/gitian-${RLS_OS}.yml +ARG RLS_LOCATION=https://github.com/dogecoin/dogecoin/releases/download/v${RLS_VERSION} + +# install system requirements +RUN apt-get update && apt-get install --no-install-recommends -y \ + wget \ + git \ + ruby \ + gpg \ + gpg-agent \ + && rm -rf /var/lib/apt/lists/* + +# fetch tools and setup signers +RUN bash -o pipefail \ + && git clone --depth 1 ${REPO_GITIAN_BUILDER} gitian \ + && git clone --depth 1 ${REPO_GITIAN_SIGS} sigs \ + && git clone --depth 1 -b v${RLS_VERSION} ${REPO_DOGECOIN_CORE} dogecoin \ + && find dogecoin/contrib/gitian-keys -name "*.pgp" |xargs -n 1 gpg --import + +# determine architecture, download release binary +# and verify against random OK signer and pinned shasums +RUN set -ex && ARCHITECTURE=$(dpkg --print-architecture) \ + && if [ "${ARCHITECTURE}" = "amd64" ]; then RLS_ARCH=x86_64 ; fi \ + && if [ "${ARCHITECTURE}" = "arm64" ]; then RLS_ARCH=aarch64; fi \ + && if [ "${ARCHITECTURE}" = "armhf" ]; then RLS_ARCH=arm && RLS_LIB=gnueabihf; fi \ + && if [ "${ARCHITECTURE}" = "i386" ]; then RLS_ARCH=i686-pc; fi \ + && if [ "${RLS_ARCH}" = "" ]; then echo "Could not determine architecture" >&2; exit 1; fi \ + && RLS_FILE_NAME=dogecoin-${RLS_VERSION}-${RLS_ARCH}-${RLS_OS}-${RLS_LIB}.tar.gz \ + && wget ${RLS_LOCATION}/${RLS_FILE_NAME} \ + && wget ${RLS_LOCATION}/SHA256SUMS.asc \ + && gitian/bin/gverify --no-markup -d sigs -r ${SIG_PATH} ${DESCRIPTOR_PATH} \ + | grep OK | shuf -n 1 | sed s/:.*// > random_signer.txt \ + && grep ${RLS_FILE_NAME} sigs/${SIG_PATH}/$(cat random_signer.txt)/*assert | sha256sum -c \ + && grep ${RLS_FILE_NAME} SHA256SUMS.asc | sha256sum -c \ + && tar -zxvf ${RLS_FILE_NAME} \ + dogecoin-${RLS_VERSION}/bin/dogecoind \ + dogecoin-${RLS_VERSION}/bin/dogecoin-cli \ + dogecoin-${RLS_VERSION}/bin/dogecoin-tx \ + --strip-components=1 + +COPY entrypoint.py /entrypoint.py + +FROM debian:bullseye-slim AS final + +ARG USER= +ENV USER=${USER} +ARG APP_UID= +ENV APP_UID=${APP_UID} +ARG APP_GID= +ENV APP_GID=${APP_GID} +ENV DATADIR=/home/${USER}/.dogecoin + +# RUN echo ${USER} ${APP_UID} ${APP_GID} && exit 1 + +RUN echo ${USER} ${APP_UID} ${APP_GID} \ + && groupadd -g ${APP_GID} ${USER} \ + && useradd -m --uid ${APP_UID} ${USER} -g ${APP_GID} \ + && mkdir -p ${DATADIR} \ + && chgrp -R ${APP_GID} ${DATADIR} \ + && chown -R ${APP_UID}:${APP_GID} ${DATADIR} \ + && chmod -R 1007 ${DATADIR} + +WORKDIR /home/${USER} + +# Copy the downloaded binaries into the container system from the verify stage. +COPY --from=verify \ + /verify/bin/dogecoind \ + /verify/bin/dogecoin-cli \ + /verify/bin/dogecoin-tx /usr/local/bin/ + +COPY /entrypoint.py /usr/local/bin/ + +# Set permissions on copied files +RUN chmod g+x /usr/local/bin/* + +# P2P network (mainnet, testnet & regnet respectively) +EXPOSE 22556 44556 18444 + +# RPC interface (mainnet, testnet & regnet respectively) +EXPOSE 22555 44555 18332 + +VOLUME ["${DATADIR}"] + +# Dependencies install +RUN apt-get update && apt-get install --no-install-recommends -y \ + python3 \ + && rm -rf /var/lib/apt/lists/* + +USER ${APP_UID}:${APP_GID} + +ENTRYPOINT ["entrypoint.py"] + +CMD ["dogecoind"] diff --git a/1.14.6/bullseye/PLATFORMS b/1.14.6/bullseye/PLATFORMS new file mode 100644 index 0000000..743dafe --- /dev/null +++ b/1.14.6/bullseye/PLATFORMS @@ -0,0 +1,4 @@ +linux/amd64 +linux/arm64 +linux/arm/v7 +linux/386 diff --git a/1.14.6/bullseye/build.sh b/1.14.6/bullseye/build.sh new file mode 100755 index 0000000..7e2ffbe --- /dev/null +++ b/1.14.6/bullseye/build.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker buildx build --no-cache --build-arg "USER=$USERNAME" --build-arg "APP_UID=$APP_UID" --build-arg "APP_GID=$APP_GID" -t xanimo/dogecoin . --load \ No newline at end of file diff --git a/1.14.6/bullseye/entrypoint.py b/1.14.6/bullseye/entrypoint.py new file mode 100755 index 0000000..79b7460 --- /dev/null +++ b/1.14.6/bullseye/entrypoint.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python3 +""" + Docker entrypoint for Dogecoin Core +""" +import argparse +import os +# import pwd +import shutil +import sys +import subprocess + +CLI_EXECUTABLES = [ + "dogecoind", + "dogecoin-cli", + "dogecoin-tx", + ] + +def execute(executable, args): + """ + Run container command with execve(2). Use manually execve + to run the process as same pid and avoid to fork a child. + """ + executable_path = shutil.which(executable) + + if executable_path is None: + print(f"{sys.argv[0]}: {executable} not found.", file=sys.stderr) + return 1 + + #Prepare execve args & launch container command + execve_args = [executable_path] + args + return os.execve(executable_path, execve_args, os.environ) + +def get_help(command_arguments): + """Call any dogecoin executable help menu, retrieve its options""" + #Prepare menu call & grep command to pipe in a shell + menu_command = " ".join(command_arguments) + grep_command = "grep -E '^ -[a-z]+'" + + #Return a list of raw options of `-help` output + return subprocess.check_output( + f"{menu_command} | {grep_command}", + shell=True + ).decode("utf8").splitlines() + +def executable_options(executable): + """ + Retrieve available options of a dogecoin executable using help menu. + + Call executable with `-help` flag and parse output to detect available + Dogecoin Core options. + """ + command_arguments = [executable, "-help"] + + #`-help-debug` display extra flag in help menu for dogecoind & qt + if executable == "dogecoind": + command_arguments.append("-help-debug") + + help_options = get_help(command_arguments) + + #Clean raw option from the menu, keeping only variable name. + #For example, convert ` -rpcpassword=` in `rpcpassword`. + options = [] + for option_entry in help_options: + cleaned_option = option_entry.strip().split("=")[0] + cleaned_option = cleaned_option.replace("-", "", 1) + options.append(cleaned_option) + + return options + +def create_datadir(): + """ + Create data directory used by dogecoin daemon. + Create manually the directory while root at container creation, + root rights needed to create folder with host volume. + """ + #Try to get datadir from argv + parser = argparse.ArgumentParser(add_help=False) + parser.add_argument("-datadir", "--datadir") + argv, _ = parser.parse_known_args() + + #Try to get datadir from environment + datadir = argv.datadir or os.environ.get("DATADIR") + print(datadir) + print(argv.datadir) + os.makedirs(datadir, exist_ok=True) + + app_uid = os.environ["APP_UID"] + app_gid = os.environ["APP_GID"] + user = os.environ["USER"] + subprocess.run(["chmod", "-R", "1700", datadir], check=True) + subprocess.run(["chown", "-R", f"{app_uid}:{app_gid}", f"/home/{user}/.dogecoin"], check=True) + +def convert_env(executable): + """ + Convert existing environment variables into command line arguments, + remove it from the environment. + + Options from executable man pages are searched in the environment, + converting options in upper case and convert "-" to "_". + + Exemple: + -rpcuser is RPCUSER + -help-debug is HELP_DEBUG + + Environment variables can be used with an empty value if the + corresponding option do not expect a value. + """ + man_options = executable_options(executable) + option_to_env = lambda opt_value : opt_value.upper().replace("-", "_") + + cli_arguments = [] + for option in man_options: + env_option = os.environ.pop(option_to_env(option), None) + + if env_option is not None: + cli_option = "-" + option + cli_option += "=" + env_option if env_option else "" + cli_arguments.append(cli_option) + + return cli_arguments + +def run_executable(executable, executable_args): + """ + Run selected dogecoin executable with arguments from environment and + command line. Switch manually from root rights needed at startup + to unprivileged user. + + Manually execve + setuid/setgid to run process as pid 1, + to manage a single process in a container & more predictive + signal handling. + """ + if executable == "dogecoind": + executable_args.append("-printtoconsole") + + #Switch process from root to user. + #Equivalent to use gosu or su-exec + # user_info = pwd.getpwnam(os.environ['USER']) + # os.setgid(user_info.pw_gid) + # os.setuid(user_info.pw_uid) + + #Run container command + return execute(executable, executable_args) + +def main(): + """ + Main routine + """ + + if sys.argv[1].startswith("-"): + executable = "dogecoind" + else: + executable = sys.argv.pop(1) + + #Container running arbitrary commands unrelated to dogecoin + if executable not in CLI_EXECUTABLES: + return execute(executable, sys.argv[1:]) + + create_datadir() + + executable_args = convert_env(executable) + executable_args += sys.argv[1:] + + return run_executable(executable, executable_args) + +if __name__ == "__main__": + sys.exit(main()) diff --git a/1.14.6/bullseye/gid.sh b/1.14.6/bullseye/gid.sh new file mode 100755 index 0000000..d6a4cca --- /dev/null +++ b/1.14.6/bullseye/gid.sh @@ -0,0 +1,3 @@ +#!/bin/bash +# this file is called by . ./set.sh $RANDOM $RANDOM to generate a random UID for the container to run on. +export APP_GID="$1" \ No newline at end of file diff --git a/1.14.6/bullseye/run.sh b/1.14.6/bullseye/run.sh new file mode 100755 index 0000000..035dc48 --- /dev/null +++ b/1.14.6/bullseye/run.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker run -d -e APP_UID=$APP_UID -e APP_GID=$APP_GID -u $APP_UID:$APP_GID -it --rm --name dogecoin xanimo/dogecoin diff --git a/1.14.6/bullseye/set.sh b/1.14.6/bullseye/set.sh new file mode 100755 index 0000000..a94ff2a --- /dev/null +++ b/1.14.6/bullseye/set.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +. ./uid.sh $1 +. ./gid.sh $2 \ No newline at end of file diff --git a/1.14.6/bullseye/testme.sh b/1.14.6/bullseye/testme.sh new file mode 100755 index 0000000..76081ab --- /dev/null +++ b/1.14.6/bullseye/testme.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +OLD_UID=$APP_UID +OLD_GID=$APP_GID +. ./set.sh ${1:-1337} ${2:-69420} +. ./build.sh +. ./run.sh +sleep 5 +docker exec -it dogecoin dogecoin-cli getnetworkinfo +docker stop dogecoin +echo "APP_UID:APP_GID=$APP_UID:$APP_GID (old or non-existent)" +echo "APP_UID:APP_GID=$OLD_UID:$OLD_GID (new)" \ No newline at end of file diff --git a/1.14.6/bullseye/uid.sh b/1.14.6/bullseye/uid.sh new file mode 100755 index 0000000..bfccb35 --- /dev/null +++ b/1.14.6/bullseye/uid.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +export APP_UID="$1" \ No newline at end of file