Table of Contents

About

This is a docker build for tor, the anonymizing network, plus the snowflake transport plugin.

Running

After building, execute:

docker run \
    -it \
    --restart=always \
    -p 9050:9050 \
    -p 9053:9053 \
    -v /local/storage/tor/etc:/etc/tor \
    wizardrysteamworks/tor-snowflake

where:

and then point a program supporting SOCKS proxies at the local address and 9050 tor port.

Note that the image will create a default configuration if it is not found in the local host directory at /local/storage/tor/etc.

Compose

Here is the corresponding Docker compose file:

version: "3.7"

services:
  tor-snowflake:
    image: docker.internal:5000/tor-snowflake:latest
    ports:
      - "9050:9050"     # SOCKS
      - "9053:9053"     # DNS
    volumes:
      - /mnt/docker/data/tor-snowflake/:/etc/tor
    healthcheck:
      test: [ "CMD", "/usr/local/bin/tor-check-circuit", "localhost", "8050", "tor" ]
      interval: 5m
      timeout: 3s

Filesystem Disposition

.
├── Dockerfile
└── rootfs
    ├── etc
    │   └── tor
    └── usr
        └── local
            ├── bin
            │   ├── run
            │   └── tor-check-circuit
            └── share
                └── tor-snowflake
                    └── torrc

8 directories, 4 files

Dockerfile

File: https://svn.grimore.org/docker/build/tor-snowflake/Dockerfile -

FROM debian:stable-slim
 
# update package manager
RUN  apt-get update -y && \
     apt-get upgrade -y && \
     apt-get dist-upgrade -y && \
     apt-get -y autoremove && \
     apt-get clean
 
# install required packages
RUN apt-get install -y \
    expect \
    telnet \
    coreutils \
    bash \
    curl \
    git \
    build-essential \
    distcc \
    autoconf \
    automake \
    libtool \
    pkgconf \
    libevent-dev \
    libssl-dev \
    libzstd-dev \
    liblzma-dev \
    zlib1g \
    zlib1g-dev 
 
# install the latest golang
WORKDIR /tmp
RUN curl -fsSL "https://go.dev/dl/$(curl -s 'https://go.dev/VERSION?m=text' | head -1).linux-amd64.tar.gz" -o go.tar.gz && \
    tar -xzf go.tar.gz && \
    rm go.tar.gz && \
    git clone https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake.git && \
    cd /tmp/snowflake/client && \
    /tmp/go/bin/go build && \
    mkdir -p /usr/local/bin && \
    cp client /usr/local/bin/snowflake-client && \
    cd /tmp && \
    rm -rf /tmp/{go,snowflake}
 
# compile the latest tor
WORKDIR /tmp
RUN git clone https://gitlab.torproject.org/tpo/core/tor.git && \
    cd /tmp/tor && \
    export DISTCC_HOSTS="docker.internal:35001 docker.internal:35002" CC=distcc CXX='distcc g++' && \
    ./autogen.sh && \
    ./configure \
        --enable-lzma \
        --enable-zstd \
        --disable-gcc-hardening \
        --disable-linker-hardening \
        --disable-manpage \
        --disable-html-manual \
        --disable-asciidoc \
        --disable-unittests && \
    make -j4 && \
    mkdir -p /usr/local/bin && \
    cp /tmp/tor/src/app/tor /usr/local/bin/ && \
    cd /tmp && \
    rm -rf /tmp/tor
 
# remove packages that will not be used
WORKDIR /
RUN apt-get purge -y \
        curl \
        git \
        build-essential \
        autoconf \
        automake \
        libtool \
        pkgconf && \
    apt-get autoremove -y 
 
# tor required port
EXPOSE 9050 9053
 
# add filesystem requirements
ADD rootfs /
 
# set up healthcheck
HEALTHCHECK --interval=15m --timeout=3s \
    CMD /usr/local/bin/tor-check-circuit
 
# execute the bootstrapper that will start tor
ENTRYPOINT [ "/usr/local/bin/run" ]

Files

Here are the files that are placed under the rootfs filesystem path.

run

File: https://svn.grimore.org/docker/build/tor-snowflake/rootfs/usr/local/bin/run -

#!/usr/bin/env bash
 
mkdir -p /etc/tor
if [ ! -f /etc/tor/torrc ]; then
    cp /usr/local/share/tor-snowflake/torrc /etc/tor/torrc
fi
 
/usr/local/bin/tor -f /etc/tor/torrc

tor-check-circuit

File: https://svn.grimore.org/docker/build/tor-snowflake/rootfs/usr/local/bin/tor-check-circuit -

#!/usr/bin/expect -f
###########################################################################
##  Copyright (C) Wizardry and Steamworks 2024 - License: MIT            ##
###########################################################################
# This is an "expect" script that checks whether tor has established a    #
# circuit and sets the return status depending on whether it has or not.  #
#                                                                         #
# In other words, iff. the script returns 0, then tor has an established  #
# circuit; otherwise no circuit has been established.                     #
#                                                                         #
# Requirements:                                                           #
#   * expect (TCL program)                                                #
#   * tor must expose a control port and must have a control password     #
#                                                                         #
# In order to generate a control password, issue: tor --hash-password PWD #
# where PWD is the desired control port password. After that, amend the   #
# tor configuration file to set the control port address, port and pass:  #
#                                                                         #
# ControlPort 0.0.0.0:8051                                                #
# HashedControlPassword 16:A482ADEAAWF43EE...                             #
#                                                                         #
# Running: ./this-script ADDRESS PORT PASSWORD                            #
# where:                                                                  #
#   * ADDRESS is the tor listening control address,                       #
#   * PORT is the tor listening control port,                             #
#   * PASSWORD is the plaintext control password                          #
#                                                                         #
# after which the return status can be checked on the shell with:         #
# echo $?                                                                 #
###########################################################################
 
set address [lindex $argv 0];
set port [lindex $argv 1];
set password [lindex $argv 2];
 
set timeout 5
spawn telnet $address $port
 
send "AUTHENTICATE \"$password\"\n"
expect "250 OK\r\n"
send "GETINFO status/circuit-established\n"
expect {
    timeout {
        exit 1
    }
    -ex "250-status/circuit-established=1\r\n250 OK\r\n"
}

tor-check-circuit

File: https://svn.grimore.org/docker/build/tor-snowflake/rootfs/usr/local/share/tor-snowflake/torrc -

RunAsDaemon 0
Log notice file /dev/stdout
 
ControlPort 0.0.0.0:8050
HashedControlPassword 16:9F840FFC85EF83CE60469C431DC9FF43DB889305B7653C2CB653302594
 
SocksPort 0.0.0.0:9050
SocksPolicy accept 0.0.0.0
VirtualAddrNetwork 10.192.0.0/10
AutomapHostsOnResolve 1
AutomapHostsSuffixes .exit,.onion
DNSPort 0.0.0.0:9053
 
UseBridges 1
ClientTransportPlugin snowflake exec /usr/local/bin/snowflake-client -log /dev/stdout
Bridge snowflake 192.0.2.3:80 2B280B23E1107BB62ABFC40DDCC8824814F80A72 fingerprint=2B280B23E1107BB62ABFC40DDCC8824814F80A72 url=https://1098762253.rsc.cdn77.org/ fronts=www.cdn77.com,www.phpmyadmin.net ice=stun:stun.l.google.com:19302,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 utls-imitate=hellorandomizedalpn
Bridge snowflake 192.0.2.4:80 8838024498816A039FCBBAB14E6F40A0843051FA fingerprint=8838024498816A039FCBBAB14E6F40A0843051FA url=https://1098762253.rsc.cdn77.org/ fronts=www.cdn77.com,www.phpmyadmin.net ice=stun:stun.l.google.com:19302,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.net:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 utls-imitate=hellorandomizedalpn