From 59d11b47d386740be1655f9eb05239f65101f156 Mon Sep 17 00:00:00 2001 From: Aaron Fiore Date: Wed, 14 Aug 2024 16:43:23 -0700 Subject: [PATCH 1/5] client: Dockerfiles: add `proxychains-ng` package Needed for Tor support (and any potential future proxy support). --- client/Dockerfiles/finance/Dockerfile.archlinux.in | 1 + client/Dockerfiles/finance/Dockerfile.ubuntu.in | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/client/Dockerfiles/finance/Dockerfile.archlinux.in b/client/Dockerfiles/finance/Dockerfile.archlinux.in index 4dd04cd..c9c6c98 100644 --- a/client/Dockerfiles/finance/Dockerfile.archlinux.in +++ b/client/Dockerfiles/finance/Dockerfile.archlinux.in @@ -63,6 +63,7 @@ USER root RUN pacman -Syu \ bc \ composer \ + proxychains-ng \ --noconfirm --disable-download-timeout RUN sed -i \ diff --git a/client/Dockerfiles/finance/Dockerfile.ubuntu.in b/client/Dockerfiles/finance/Dockerfile.ubuntu.in index e1bbede..d812961 100644 --- a/client/Dockerfiles/finance/Dockerfile.ubuntu.in +++ b/client/Dockerfiles/finance/Dockerfile.ubuntu.in @@ -73,7 +73,8 @@ RUN apt-get install -y \ php \ php-bcmath \ php-curl \ - php-gmp + php-gmp \ + proxychains4 USER builder WORKDIR /usr/local/lib/php From 5b4bf6fbd091d6585939d54baa892761bf50dec1 Mon Sep 17 00:00:00 2001 From: Aaron Fiore Date: Wed, 14 Aug 2024 16:53:49 -0700 Subject: [PATCH 2/5] client: plugins: implement Tor plugin (tor.bash) - Creates, prepares and manages containerized Tor instance - Prepares a running docker-finance container to proxy (`proxychains-ng`) through the containerized Tor instance --- client/plugins/docker/tor.bash | 161 +++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100755 client/plugins/docker/tor.bash diff --git a/client/plugins/docker/tor.bash b/client/plugins/docker/tor.bash new file mode 100755 index 0000000..1a2f78f --- /dev/null +++ b/client/plugins/docker/tor.bash @@ -0,0 +1,161 @@ +#!/usr/bin/env bash + +# docker-finance | modern accounting for the power-user +# +# Copyright (C) 2024 Aaron Fiore (Founder, Evergreen Crypto LLC) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# +# "Libraries" +# + +[ -z "$DOCKER_FINANCE_CLIENT_REPO" ] && exit 1 +source "${DOCKER_FINANCE_CLIENT_REPO}/client/src/docker/lib/lib_docker.bash" + +[[ -z "$global_platform" || -z "$global_arg_delim_1" || -z "$global_user" || -z "$global_tag" ]] && lib_utils::die_fatal +instance="${global_platform}${global_arg_delim_1}${global_user}:${global_tag}" +lib_docker::docker "$instance" # NOTE: "constructor" only needed if calling library directly + +# +# Implementation +# + +declare -g tor_container="docker-finance_tor" + +# TODO: current implementation will clobber any other proxychains usage (i.e., other SOCKS proxy or overlay network (I2P, etc.)) +function tor::start() +{ + [[ -z "$global_container" || -z "$global_network" ]] && lib_utils::die_fatal + + # NOTE: proxychains.conf's [ProxyList] won't allow hostnames (or Docker network container name). + # So, to avoid conflicting IP address spaces, docker-finance will not hardcode address space. + # Ergo, a already-running container will be needed (sorry, lib_docker::run()) + if ! docker container inspect -f '{{.State.Running}}' "$global_container" &>/dev/null; then + lib_utils::die_fatal "docker-finance not running! Bring \`up\` container and try again." + fi + + local -r _torrc="/etc/tor/torrc" + + if docker container inspect -f '{{.State.Running}}' "$tor_container" &>/dev/null; then + lib_utils::print_error "${tor_container}: instance already running (consider \`restart\`)" + return 1 + else + docker run -it --rm --detach \ + --network "$global_network" \ + --name="${tor_container}" \ + --entrypoint=/bin/sh alpine:latest -c " + apk update \ + && apk add tor \ + && cp ${_torrc}.sample ${_torrc} \ + && su -s /usr/bin/tor tor" 1>/dev/null || lib_utils::die_fatal + fi && lib_utils::print_info "${tor_container}: container started" + + # Get Tor container's local IP + local _ip + _ip="$(docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $tor_container)" + lib_utils::print_info "${tor_container}: container IP '${_ip}'" + + # Need to wait for a working installation + lib_utils::print_warning "${tor_container}: waiting for Tor installation" + while ! docker exec "$tor_container" /bin/sh -c 'apk info -e tor' 1>/dev/null; do + sleep 1s + done && lib_utils::print_info "${tor_container}: Tor installation ready" + + # Update torrc with container's local IP (to avoid 0.0.0.0) + lib_utils::print_info "${tor_container}: updating $_torrc" + docker exec "$tor_container" \ + /bin/sh -c " + sed -i '/^SOCKSPort /d' ${_torrc} \ + && echo 'SOCKSPort ${_ip}:9050' >>${_torrc}" || lib_utils::die_fatal + + # Have Tor pickup changes + lib_utils::print_info "${tor_container}: restarting Tor with updated ${_torrc}" + docker exec "$tor_container" /bin/sh -c "pkill -HUP tor" || lib_utils::die_fatal + + # Set docker-finance's proxychains to point to Tor instance + local -r _proxychains="/etc/proxychains.conf" + lib_utils::print_info "${global_container}: updating $_proxychains" + docker exec -it --user root "$global_container" \ + /bin/bash -i -c " + sed -i \ + -e 's:^#quiet_mode:quiet_mode:' \ + -e 's:^# localnet 127.0.0.0/255.0.0.0:localnet 127.0.0.0/255.0.0.0:' \ + -e '/^socks/d' $_proxychains \ + && echo 'socks5 $_ip 9050' >>$_proxychains" || lib_utils::die_fatal + + # Test Tor connection + local -r _sleep="15s" + lib_utils::print_warning "${global_container}: testing connection (bootstrapping ~${_sleep})" + sleep "$_sleep" # Give time to bootstrap + # TODO: run timer to verify response (and fail if N times if no response) + docker exec -it "$global_container" \ + /bin/bash -i -c " + proxychains curl -s https://check.torproject.org 2>/dev/null \ + | grep -B3 'Your IP address appears to be' \ + | sed -e 's/^ //g' -e '\$ s/[^\\.0-9]//g' -e '/^\$/d' -e '2,3d'" || lib_utils::die_fatal +} + +function tor::stop() +{ + if ! docker container inspect -f '{{.State.Running}}' "$tor_container" &>/dev/null; then + lib_utils::print_error "${tor_container}: container not running" + return 1 + fi + lib_utils::print_warning "${tor_container}: stopping container" + docker container stop "$tor_container" &>/dev/null \ + && lib_utils::print_info "${tor_container}: container stopped" +} + +function tor::restart() +{ + tor::stop + tor::start +} + +function main() +{ + local -r _usage=" +\e[32mDescription:\e[0m + + Connect docker-finance container's \`fetch\` to The Onion Router. + +\e[32mUsage:\e[0m + + \e[37;2m# Client (host) command\e[0m + $ dfi $instance plugins repo${global_arg_delim_1}$(basename $0) <{start|up}|restart|{stop|down}> + + \e[37;2m# Container command (see \`fetch\` help for usage)\e[0m + $ dfi fetch help +" + + case "$1" in + start | up) + tor::start + ;; + stop | down) + tor::stop + ;; + restart) + tor::restart + ;; + *) + lib_utils::die_usage "$_usage" + ;; + esac +} + +main "$@" + +# vim: sw=2 sts=2 si ai et From 77a8b21893a3063c0bd0b9b68b496c10c07dc971 Mon Sep 17 00:00:00 2001 From: Aaron Fiore Date: Wed, 14 Aug 2024 17:10:17 -0700 Subject: [PATCH 3/5] container: lib_fetch: implement Tor support - Adds 'tor' option - Uses `proxychains-ng` - Expects docker-finance's Tor plugin --- .../src/finance/lib/internal/lib_fetch.bash | 57 +++++++++++++++++-- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/container/src/finance/lib/internal/lib_fetch.bash b/container/src/finance/lib/internal/lib_fetch.bash index 92eff72..94d0920 100644 --- a/container/src/finance/lib/internal/lib_fetch.bash +++ b/container/src/finance/lib/internal/lib_fetch.bash @@ -79,6 +79,10 @@ function lib_fetch::__parse_args() (block)chain${global_arg_delim_2} + Tor (proxy): + + tor${global_arg_delim_2}<{on|true}|{off|false}> (default 'off') + \e[32mExamples:\e[0m \e[37;2m#\e[0m @@ -136,6 +140,14 @@ function lib_fetch::__parse_args() \e[37;2m# Fetch specific blockchain/subaccount/address for metamask\e[0m $ $global_usage account${global_arg_delim_2}metamask chain${global_arg_delim_2}ethereum/phone:wallet-1/0x236ba53B56FEE4901cdac3170D17f827DF43E969 + + \e[37;2m# Fetch given blochchain-based subaccounts for current year, over the Tor network\e[0m + $ $global_usage account${global_arg_delim_2}metamask${global_arg_delim_3}ledger blockchain${global_arg_delim_2}ethereum${global_arg_delim_3}polygon${global_arg_delim_3}tezos${global_arg_delim_3}algorand tor${global_arg_delim_2}on + +\e[32mNotes:\e[0m + + - For all commands, you can proxy your fetch through Tor with the \`tor${global_arg_delim_2}on\` option + * IMPORTANT: client-side \`tor\` plugin must be started *prior* to fetch " # @@ -151,6 +163,7 @@ function lib_fetch::__parse_args() && [[ ! "$_arg" =~ ^account[s]?${global_arg_delim_2} ]] \ && [[ ! "$_arg" =~ ^(^block)?chain[s]?${global_arg_delim_2} ]] \ && [[ ! "$_arg" =~ ^year[s]?${global_arg_delim_2} ]] \ + && [[ ! "$_arg" =~ ^tor${global_arg_delim_2} ]] \ && lib_utils::die_usage "$_usage" done @@ -193,6 +206,11 @@ function lib_fetch::__parse_args() local _arg_year="${_arg:${_len}}" [ -z "$_arg_year" ] && lib_utils::die_usage "$_usage" fi + + if [[ "$_key" =~ ^tor$ ]]; then + local _arg_tor="${_arg:${_len}}" + [ -z "$_arg_tor" ] && lib_utils::die_usage "$_usage" + fi done # @@ -247,6 +265,14 @@ function lib_fetch::__parse_args() fi fi + # Arg: tor + if [ ! -z "$_arg_tor" ]; then + # Need a valid arg + if [[ -z "$_arg_all" && -z "$_arg_price" && -z "$_arg_account" ]]; then + lib_utils::die_usage "$_usage" + fi + fi + # # Test argument values, set globals # @@ -318,6 +344,12 @@ function lib_fetch::__parse_args() global_arg_year="$(date +%Y)" # Set default current declare -gr global_arg_year fi + + # Arg: tor + if [ ! -z "$_arg_tor" ]; then + [[ ! "$_arg_tor" =~ ^on$|^true$|^off$|^false$ ]] && lib_utils::die_usage "$_usage" + declare -gr global_arg_tor="$_arg_tor" + fi } function lib_fetch::__fetch() @@ -752,15 +784,30 @@ function lib_fetch::__fetch_exec() [ -z "$_subtype" ] && lib_utils::die_fatal local _dir="${DOCKER_FINANCE_CONTAINER_REPO}/src/finance/lib/internal/fetch" - local _ifs="$IFS" IFS=' ' - # TODO: remove no-limit after internal fetching writes per-paginated instead of per-fetch - local _php=("/usr/bin/php" "-d" "memory_limit=\"-1\"" "-d" "include_path=\"/usr/local/lib:${_dir}\"") - lib_utils::print_debug "${_php[@]}" + # + # Setup fetch + # - "${_php[@]}" "${_dir}/fetch.php" "$_type" "$_subtype" + local _exec=() + [[ "$global_arg_tor" =~ ^on$|^true$ ]] && _exec+=("proxychains") # Wrap with proxychains + + # Test for Tor plugin + if ! "${_exec[@]}" curl -s "https://check.torproject.org" &>/dev/null; then + lib_utils::die_fatal "Tor plugin not started (or needs restart)" + fi + + # TODO: remove no-limit after internal fetching writes per-paginated instead of per-fetch + _exec+=("/usr/bin/php" "-d" "memory_limit=\"-1\"" "-d" "include_path=\"/usr/local/lib:${_dir}\"") + lib_utils::print_debug "${_exec[@]}" + + # + # Execute fetch + # + + "${_exec[@]}" "${_dir}/fetch.php" "$_type" "$_subtype" IFS="$_ifs" } From c475dbdbc765368ba80658c53306a6b68b3f8daf Mon Sep 17 00:00:00 2001 From: Aaron Fiore Date: Wed, 14 Aug 2024 21:54:41 -0700 Subject: [PATCH 4/5] client: manifest: add `proxychains-ng` package --- client/docker-finance.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/docker-finance.yaml b/client/docker-finance.yaml index e0c4497..3baae77 100644 --- a/client/docker-finance.yaml +++ b/client/docker-finance.yaml @@ -42,6 +42,7 @@ container: - "hledger-iadd" - "hledger-ui" - "hledger-web" + - "proxychains-ng" - "python-pipx" - "stack" - "timew" @@ -90,6 +91,7 @@ container: - "hledger-ui" - "hledger-web" - "pipx" + - "proxychains4" - "timewarrior" - "vim" - "visidata" From 5e5908739089f3dd93c996a687d16130c1d36f6d Mon Sep 17 00:00:00 2001 From: Aaron Fiore Date: Thu, 15 Aug 2024 01:58:23 -0700 Subject: [PATCH 5/5] client: docker.bash: add Tor plugin to usage help --- client/src/docker/docker.bash | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/src/docker/docker.bash b/client/src/docker/docker.bash index c9bce8d..e64f1a5 100755 --- a/client/src/docker/docker.bash +++ b/client/src/docker/docker.bash @@ -124,6 +124,9 @@ function main() \e[37;2m# Print current version of 'docker-finance' and client/container ('finance') dependencies\e[0m $ dfi archlinux${global_arg_delim_1}${USER}:default version type${global_arg_delim_2}all + \e[37;2m# Print Tor plugin's usage help\e[0m + $ dfi archlinux${global_arg_delim_1}${USER}:default plugins repo${global_arg_delim_1}tor.bash help + \e[37;2m#\e[0m \e[37;2m# Dev-tools platform\e[0m \e[37;2m#\e[0m