From c0db575ad882837e132b9a90495904163f07121e Mon Sep 17 00:00:00 2001 From: Thomas Leplus Date: Thu, 31 Oct 2024 19:11:26 +0545 Subject: [PATCH] Implement customizable torrc configuration (#141) --- .gitleaksignore | 1 + README.md | 44 ++++++- tor/Dockerfile | 12 +- tor/docker-compose.test.yml | 2 + tor/tor-wrapper.sh | 61 +++++++++ tor/torrc | 2 - tor/torrc.template | 252 ++++++++++++++++++++++++++++++++++++ 7 files changed, 366 insertions(+), 8 deletions(-) create mode 100644 .gitleaksignore create mode 100755 tor/tor-wrapper.sh delete mode 100644 tor/torrc create mode 100644 tor/torrc.template diff --git a/.gitleaksignore b/.gitleaksignore new file mode 100644 index 0000000..b57d671 --- /dev/null +++ b/.gitleaksignore @@ -0,0 +1 @@ +/github/workspace/tor/torrc.template:generic-api-key:59 \ No newline at end of file diff --git a/README.md b/README.md index 06d0803..37ff9c5 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ Run TOR conveniently from a docker container. [![Docker Pulls](https://img.shields.io/docker/pulls/leplusorg/tor)](https://hub.docker.com/r/leplusorg/tor) [![Docker Version](https://img.shields.io/docker/v/leplusorg/tor?sort=semver)](https://hub.docker.com/r/leplusorg/tor) +## Usage + The simplest way to launch a TOR proxy using this container is to use the following command: ```bash @@ -26,6 +28,46 @@ Once the docker container has finished starting, you can test it with the follow curl --socks5 localhost:9050 --socks5-hostname localhost:9050 https://check.torproject.org/api/ip ``` +## Configuration + +The configuration file used by Tor in this container is +`/et/tor/torrc` but it is generated on startup by the script +`tor-wrapper.sh` using the `torrc.template` file. The file is based on +the `torrc.sample` configuration that comes with Tor. But some +configuration options have been made configurable using OS environment +variables. You can set a custom value for these variables for example +using the `-e` option of Docker. Below are the variables currently +available: + +| Variable name | Usage | Default | +| -------------- | ----------------------- | ------------ | +| DATA_DIRECTORY | The data directory. | /var/lib/tor | +| LOG_LEVEL | The logging level. | notice | +| LOG_FILE | The log file or device. | stdout | +| SOCKS_HOSTNAME | The SOCKS hostname. | 127.0.0.0.1 | +| SOCKS_PORT | The SOCKS port. | 9150 | + +Note that the defaults are the same as Tor's default if the +configuration option is not set. + +You can use the `-m` option of Docker to mount a custom template the +inmage at `/etc/tor/torrc.template`. The templating engine +(`envsubst`) will only replace specific environment variables in the +template. These are controlled by the environment variable +`SHELL_FORMAT` (the default list is +`${DATA_DIRECTORY},${LOG_LEVEL},${LOG_FILE},${SOCKS_HOSTNAME},${SOCKS_PORT}`). If +you create a custom template with extra variables in it, you can set +your own list using the environment variable `SHELL_FORMAT` or you can +just append the extra variables to the existing list using the +environment variable `SHELL_FORMAT_EXTRA`. Be careful to escape the +`$` characters since you don't want them to be interpolated when +defining `SHELL_FORMAT` or `SHELL_FORMAT_EXTRA`. + +Of course you can also build an image on top of this one with a custom `torrc.template`. + +For troubleshooting, you can enable verbose logging by setting the +value of environment variable `DEBUG` to `true`. + ## Request configuration change -Please use [this link](https://github.com/leplusorg/docker-tor/issues/new?assignees=thomasleplus&labels=enhancement&template=feature_request.md&title=%5BFEAT%5D) (GitHub account required) to suggest a change in this image configuration. +Please use [this link](https://github.com/leplusorg/docker-tor/issues/new?assignees=thomasleplus&labels=enhancement&template=feature_request.md&title=%5BFEAT%5D) (GitHub account required) to suggest a change in this image configuration or to expose a new Tor configuration option. diff --git a/tor/Dockerfile b/tor/Dockerfile index 32323e9..d0bfffd 100644 --- a/tor/Dockerfile +++ b/tor/Dockerfile @@ -3,15 +3,17 @@ FROM alpine:3.20.3@sha256:beefdbd8a1da6d2915566fde36db9db0b524eb737fc57cd1367eff HEALTHCHECK CMD ["/usr/bin/curl", "--socks5", "localhost:9050", "--socks5-hostname", "localhost:9050", "https://check.torproject.org/api/ip"] # hadolint ignore=DL3018 -RUN apk --update --no-cache add tor \ - && rm -rf /var/cache/apk/* - -COPY torrc /etc/torrc +RUN apk --update --no-cache add bash gettext tor \ + && rm -rf /var/cache/apk/* \ + && chmod o+rwx /etc/tor USER "tor" +COPY torrc.template tor-wrapper.sh /etc/tor/ + WORKDIR "/var/lib/tor" EXPOSE 9050 -ENTRYPOINT ["/usr/bin/tor", "-f", "/etc/torrc"] +CMD ["/etc/tor/tor-wrapper.sh"] +ENTRYPOINT [] diff --git a/tor/docker-compose.test.yml b/tor/docker-compose.test.yml index 70ca861..8da4e2a 100644 --- a/tor/docker-compose.test.yml +++ b/tor/docker-compose.test.yml @@ -12,3 +12,5 @@ services: build: context: . dockerfile: Dockerfile + environment: + - SOCKS_HOSTNAME=0.0.0.0 diff --git a/tor/tor-wrapper.sh b/tor/tor-wrapper.sh new file mode 100755 index 0000000..fee1ea3 --- /dev/null +++ b/tor/tor-wrapper.sh @@ -0,0 +1,61 @@ +#!/bin/bash +set -euo pipefail +IFS=$'\n\t' + +# debug mode is off by default +if [ -z "${DEBUG+x}" ]; then + DEBUG=false +fi + +# Honoring GitHub runner debug mode +if [ -n "${RUNNER_DEBUG+x}" ] && [ "${RUNNER_DEBUG}" = 1 ]; then + DEBUG=false +fi + +if [ "${DEBUG}" = true ]; then + set -o xtrace + LOG_LEVEL=debug +fi + +if [ -f '/etc/torrc' ]; then + if [ "${DEBUG}" = true ]; then + \echo 'DEBUG: Found existing /etc/torrc, overwritting /etc/tor/torrc.' + fi + \cp -f '/etc/torrc' '/etc/tor/torrc' +else + if [ -n "${SHELL_FORMAT+x}" ]; then + if [ "${DEBUG}" = true ]; then + \echo "DEBUG: Found custom Shell Format for envsubst: ${SHELL_FORMAT}" + fi + elif [ -n "${SHELL_FORMAT_EXTRA+x}" ]; then + if [ "${DEBUG}" = true ]; then + \echo "DEBUG: Found extra Shell Format for envsubst: ${SHELL_FORMAT_EXTRA}" + fi + # Escaping predefined variables names except + # SHELL_FORMAT_EXTRA, as we don't want them to be + # expanded. + SHELL_FORMAT="\${DATA_DIRECTORY},\${LOG_LEVEL},\${LOG_FILE},\${SOCKS_HOSTNAME},\${SOCKS_PORT},${SHELL_FORMAT_EXTRA}" + else + # Using single quotes here on purpose, we don't want the + # variables names to be expanded. + # shellcheck disable=SC2016 + SHELL_FORMAT='${DATA_DIRECTORY},${LOG_LEVEL},${LOG_FILE},${SOCKS_HOSTNAME},${SOCKS_PORT}' + fi + DATA_DIRECTORY="${DATA_DIRECTORY:-/var/lib/tor}" \ + LOG_LEVEL="${LOG_LEVEL:-notice}" \ + LOG_FILE="${LOG_FILE:-stdout}" \ + SOCKS_HOSTNAME="${SOCKS_HOSTNAME:-127.0.0.1}" \ + SOCKS_PORT="${SOCKS_PORT:-9050}" \ + envsubst "${SHELL_FORMAT}" /etc/tor/torrc +fi + +if [ "${DEBUG}" = true ]; then + \echo 'DEBUG: Content of /etc/tor/torrc:' + \echo 'DEBUG: ==========================' + \sed -e 's/^/DEBUG: /' /etc/tor/torrc + \echo 'DEBUG: ==========================' +fi + +cmd=$(\which tor) + +"${cmd}" -f /etc/tor/torrc "$@" diff --git a/tor/torrc b/tor/torrc deleted file mode 100644 index e29931f..0000000 --- a/tor/torrc +++ /dev/null @@ -1,2 +0,0 @@ -Log notice stdout -SocksPort 0.0.0.0:9050 diff --git a/tor/torrc.template b/tor/torrc.template new file mode 100644 index 0000000..95f8564 --- /dev/null +++ b/tor/torrc.template @@ -0,0 +1,252 @@ +## Configuration file for a typical Tor user +## Last updated 28 February 2019 for Tor 0.3.5.1-alpha. +## (may or may not work for much older or much newer versions of Tor.) +## +## Lines that begin with "## " try to explain what's going on. Lines +## that begin with just "#" are disabled commands: you can enable them +## by removing the "#" symbol. +## +## See 'man tor', or https://www.torproject.org/docs/tor-manual.html, +## for more options you can use in this file. +## +## Tor will look for this file in various places based on your platform: +## https://support.torproject.org/tbb/tbb-editing-torrc/ + +## Tor opens a SOCKS proxy on port 9050 by default -- even if you don't +## configure one below. Set "SOCKSPort 0" if you plan to run Tor only +## as a relay, and not make any local application connections yourself. +#SOCKSPort 9050 # Default: Bind to localhost:9050 for local connections. +#SOCKSPort 192.168.0.1:9100 # Bind to this address:port too. + +SOCKSPort ${SOCKS_HOSTNAME}:${SOCKS_PORT} + +## Entry policies to allow/deny SOCKS requests based on IP address. +## First entry that matches wins. If no SOCKSPolicy is set, we accept +## all (and only) requests that reach a SOCKSPort. Untrusted users who +## can access your SOCKSPort may be able to learn about the connections +## you make. +#SOCKSPolicy accept 192.168.0.0/16 +#SOCKSPolicy accept6 FC00::/7 +#SOCKSPolicy reject * + +## Logs go to stdout at level "notice" unless redirected by something +## else, like one of the below lines. You can have as many Log lines as +## you want. +## +## We advise using "notice" in most cases, since anything more verbose +## may provide sensitive information to an attacker who obtains the logs. +## +## Send all messages of level 'notice' or higher to /var/log/tor/notices.log +#Log notice file /var/log/tor/notices.log +## Send every possible message to /var/log/tor/debug.log +#Log debug file /var/log/tor/debug.log +## Use the system log instead of Tor's logfiles +#Log notice syslog +## To send all messages to stderr: +#Log debug stderr + +Log ${LOG_LEVEL} ${LOG_FILE} + +## The directory for keeping all the keys/etc. By default, we store +## things in /var/lib/tor/.tor on Unix, and in Application Data\tor on Windows. +DataDirectory ${DATA_DIRECTORY} + +## The port on which Tor will listen for local connections from Tor +## controller applications, as documented in control-spec.txt. +#ControlPort 9051 +## If you enable the controlport, be sure to enable one of these +## authentication methods, to prevent attackers from accessing it. +#HashedControlPassword 16:872860B76453A77D60CA2BB8C1A7042072093276A3D701AD684053EC4C +#CookieAuthentication 1 + +############### This section is just for location-hidden services ### + +## Once you have configured a hidden service, you can look at the +## contents of the file ".../hidden_service/hostname" for the address +## to tell people. +## +## HiddenServicePort x y:z says to redirect requests on port x to the +## address y:z. + +#HiddenServiceDir /var/lib/tor/hidden_service/ +#HiddenServicePort 80 127.0.0.1:80 + +#HiddenServiceDir /var/lib/tor/other_hidden_service/ +#HiddenServicePort 80 127.0.0.1:80 +#HiddenServicePort 22 127.0.0.1:22 + +################ This section is just for relays ##################### +# +## See https://community.torproject.org/relay for details. + +## Required: what port to advertise for incoming Tor connections. +#ORPort 9001 +## If you want to listen on a port other than the one advertised in +## ORPort (e.g. to advertise 443 but bind to 9090), you can do it as +## follows. You'll need to do ipchains or other port forwarding +## yourself to make this work. +#ORPort 443 NoListen +#ORPort 127.0.0.1:9090 NoAdvertise +## If you want to listen on IPv6 your numeric address must be explicitly +## between square brackets as follows. You must also listen on IPv4. +#ORPort [2001:DB8::1]:9050 + +## The IP address or full DNS name for incoming connections to your +## relay. Leave commented out and Tor will guess. +#Address noname.example.com + +## If you have multiple network interfaces, you can specify one for +## outgoing traffic to use. +## OutboundBindAddressExit will be used for all exit traffic, while +## OutboundBindAddressOR will be used for all OR and Dir connections +## (DNS connections ignore OutboundBindAddress). +## If you do not wish to differentiate, use OutboundBindAddress to +## specify the same address for both in a single line. +#OutboundBindAddressExit 10.0.0.4 +#OutboundBindAddressOR 10.0.0.5 + +## A handle for your relay, so people don't have to refer to it by key. +## Nicknames must be between 1 and 19 characters inclusive, and must +## contain only the characters [a-zA-Z0-9]. +## If not set, "Unnamed" will be used. +#Nickname ididnteditheconfig + +## Define these to limit how much relayed traffic you will allow. Your +## own traffic is still unthrottled. Note that RelayBandwidthRate must +## be at least 75 kilobytes per second. +## Note that units for these config options are bytes (per second), not +## bits (per second), and that prefixes are binary prefixes, i.e. 2^10, +## 2^20, etc. +#RelayBandwidthRate 100 KBytes # Throttle traffic to 100KB/s (800Kbps) +#RelayBandwidthBurst 200 KBytes # But allow bursts up to 200KB (1600Kb) + +## Use these to restrict the maximum traffic per day, week, or month. +## Note that this threshold applies separately to sent and received bytes, +## not to their sum: setting "40 GB" may allow up to 80 GB total before +## hibernating. +## +## Set a maximum of 40 gigabytes each way per period. +#AccountingMax 40 GBytes +## Each period starts daily at midnight (AccountingMax is per day) +#AccountingStart day 00:00 +## Each period starts on the 3rd of the month at 15:00 (AccountingMax +## is per month) +#AccountingStart month 3 15:00 + +## Administrative contact information for this relay or bridge. This line +## can be used to contact you if your relay or bridge is misconfigured or +## something else goes wrong. Note that we archive and publish all +## descriptors containing these lines and that Google indexes them, so +## spammers might also collect them. You may want to obscure the fact that +## it's an email address and/or generate a new address for this purpose. +## +## If you are running multiple relays, you MUST set this option. +## +#ContactInfo Random Person +## You might also include your PGP or GPG fingerprint if you have one: +#ContactInfo 0xFFFFFFFF Random Person + +## Uncomment this to mirror directory information for others. Please do +## if you have enough bandwidth. +#DirPort 9030 # what port to advertise for directory connections +## If you want to listen on a port other than the one advertised in +## DirPort (e.g. to advertise 80 but bind to 9091), you can do it as +## follows. below too. You'll need to do ipchains or other port +## forwarding yourself to make this work. +#DirPort 80 NoListen +#DirPort 127.0.0.1:9091 NoAdvertise +## Uncomment to return an arbitrary blob of html on your DirPort. Now you +## can explain what Tor is if anybody wonders why your IP address is +## contacting them. See contrib/tor-exit-notice.html in Tor's source +## distribution for a sample. +#DirPortFrontPage /etc/tor/tor-exit-notice.html + +## Uncomment this if you run more than one Tor relay, and add the identity +## key fingerprint of each Tor relay you control, even if they're on +## different networks. You declare it here so Tor clients can avoid +## using more than one of your relays in a single circuit. See +## https://support.torproject.org/relay-operators/multiple-relays/ +## However, you should never include a bridge's fingerprint here, as it would +## break its concealability and potentially reveal its IP/TCP address. +## +## If you are running multiple relays, you MUST set this option. +## +## Note: do not use MyFamily on bridge relays. +#MyFamily ,,... + +## Uncomment this if you want your relay to be an exit, with the default +## exit policy (or whatever exit policy you set below). +## (If ReducedExitPolicy, ExitPolicy, or IPv6Exit are set, relays are exits. +## If none of these options are set, relays are non-exits.) +#ExitRelay 1 + +## Uncomment this if you want your relay to allow IPv6 exit traffic. +## (Relays do not allow any exit traffic by default.) +#IPv6Exit 1 + +## Uncomment this if you want your relay to be an exit, with a reduced set +## of exit ports. +#ReducedExitPolicy 1 + +## Uncomment these lines if you want your relay to be an exit, with the +## specified set of exit IPs and ports. +## +## A comma-separated list of exit policies. They're considered first +## to last, and the first match wins. +## +## If you want to allow the same ports on IPv4 and IPv6, write your rules +## using accept/reject *. If you want to allow different ports on IPv4 and +## IPv6, write your IPv6 rules using accept6/reject6 *6, and your IPv4 rules +## using accept/reject *4. +## +## If you want to _replace_ the default exit policy, end this with either a +## reject *:* or an accept *:*. Otherwise, you're _augmenting_ (prepending to) +## the default exit policy. Leave commented to just use the default, which is +## described in the man page or at +## https://support.torproject.org/relay-operators +## +## Look at https://support.torproject.org/abuse/exit-relay-expectations/ +## for issues you might encounter if you use the default exit policy. +## +## If certain IPs and ports are blocked externally, e.g. by your firewall, +## you should update your exit policy to reflect this -- otherwise Tor +## users will be told that those destinations are down. +## +## For security, by default Tor rejects connections to private (local) +## networks, including to the configured primary public IPv4 and IPv6 addresses, +## and any public IPv4 and IPv6 addresses on any interface on the relay. +## See the man page entry for ExitPolicyRejectPrivate if you want to allow +## "exit enclaving". +## +#ExitPolicy accept *:6660-6667,reject *:* # allow irc ports on IPv4 and IPv6 but no more +#ExitPolicy accept *:119 # accept nntp ports on IPv4 and IPv6 as well as default exit policy +#ExitPolicy accept *4:119 # accept nntp ports on IPv4 only as well as default exit policy +#ExitPolicy accept6 *6:119 # accept nntp ports on IPv6 only as well as default exit policy +#ExitPolicy reject *:* # no exits allowed + +## Bridge relays (or "bridges") are Tor relays that aren't listed in the +## main directory. Since there is no complete public list of them, even an +## ISP that filters connections to all the known Tor relays probably +## won't be able to block all the bridges. Also, websites won't treat you +## differently because they won't know you're running Tor. If you can +## be a real relay, please do; but if not, be a bridge! +## +## Warning: when running your Tor as a bridge, make sure than MyFamily is +## NOT configured. +#BridgeRelay 1 +## By default, Tor will advertise your bridge to users through various +## mechanisms like https://bridges.torproject.org/. If you want to run +## a private bridge, for example because you'll give out your bridge +## address manually to your friends, uncomment this line: +#BridgeDistribution none + +## Configuration options can be imported from files or folders using the %include +## option with the value being a path. This path can have wildcards. Wildcards are +## expanded first, using lexical order. Then, for each matching file or folder, the following +## rules are followed: if the path is a file, the options from the file will be parsed as if +## they were written where the %include option is. If the path is a folder, all files on that +## folder will be parsed following lexical order. Files starting with a dot are ignored. Files +## on subfolders are ignored. +## The %include option can be used recursively. +#%include /etc/torrc.d/*.conf +