diff --git a/Dockerfile b/Dockerfile index d93a8d9..de9286c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,3 +2,16 @@ ARG ALPINE_VERSION=3.13.6 ARG BASE_IMAGE=sutty/monit FROM ${BASE_IMAGE}:${ALPINE_VERSION} MAINTAINER "f " + +ENV SUTTY="sutty.nl" +ENV HTTP_BASIC_USER="" +ENV HTTP_BASIC_PASSWORD="" + +RUN apk add --no-cache tor curl jq +COPY ./monit.conf /etc/monit.d/tor.conf +COPY ./torrc /etc/tor/torrc +COPY ./hidden_services.sh /usr/local/bin/hidden_services +RUN chmod +x /usr/local/bin/hidden_services +RUN chmod 644 /etc/tor/torrc + +VOLUME /var/lib/tor diff --git a/hidden_services.sh b/hidden_services.sh new file mode 100644 index 0000000..3af01ed --- /dev/null +++ b/hidden_services.sh @@ -0,0 +1,53 @@ +#!/bin/sh +# +# Get every website with hidden service (HS) enabled from Sutty's API +# and generate a Tor configuration file for it. We do it this way +# because the original plan was to use Tor Control Protocol, but it only +# allows to create ephemeral hidden services if you don't store the +# private key separately. HS are lost when Tor is restarted. We don't +# want Sutty to have access to HS' private keys nor we want to device +# some algorithm to keep HS' alive when Tor restarts. +# +# So we did this that generates the persistent config file, reload Tor +# to make it generate the HS and inform Sutty of the public key / onion +# address. This way private keys are only stored in Tor. + +set -e + +if test "$1" = "bootstrap" ; then + install -dm 2755 -o tor -g root /var/lib/tor/hidden_services + install -dm 2755 -o root -g root /var/lib/tor/hidden_services/conf.d + exit 0 +fi + +# API client +api_client () { + local _path="$1"; shift + + curl --basic --user "${HTTP_BASIC_USER}:${HTTP_BASIC_PASSWORD}" \ + $@ "https://api.${SUTTY}${_path}" +} + +# Get all sites with HS enabled +api_client "/v1/sites/hidden_services.json" | jq --raw-output .[] | while read name; do + conf_file="/var/lib/tor/hidden_services/conf.d/${name}.conf" + hs_dir="/var/lib/tor/hidden_services/${name}" + + # The config file + echo "HiddenServiceDir ${hs_dir}" > "${conf_file}" + echo "HiddenServicePort 80 nginx:80" >> "${conf_file}" + echo "HiddenServiceEnableIntroDoSDefense 1" >> "${conf_file}" + + chmod 644 "${conf_file}" + + # Reload Tor + cat /var/lib/tor/tor.pid | xargs -r kill -SIGHUP + + # Wait for the hidden service to be created + while ! test -f "${hs_dir}/hostname"; do sleep 1 ; done + + # Inform the hidden service to Sutty + api_client "/v1/sites/add_onion.json" \ + --data "name=${name}" \ + --data-urlencode "onion@${hs_dir}/hostname" +done diff --git a/monit.conf b/monit.conf new file mode 100644 index 0000000..a311589 --- /dev/null +++ b/monit.conf @@ -0,0 +1,13 @@ +check program hidden_services_bootstrap + with path "/usr/local/bin/hidden_services bootstrap" + every 1 cycle + if status = 0 then unmonitor + +check process tor with pidfile /var/lib/tor/tor.pid + start program = "/usr/bin/tor" + stop program = "/bin/sh -c 'cat /var/lib/tor/tor.pid | xargs -r kill'" + +check program hidden_services + with path "/usr/local/bin/hidden_services" + every 1 cycle + if status != 0 then alert diff --git a/torrc b/torrc new file mode 100644 index 0000000..d4d8e81 --- /dev/null +++ b/torrc @@ -0,0 +1,8 @@ +User tor +RunAsDaemon 1 +SafeLogging 1 +PidFile /var/lib/tor/tor.pid +NoExec 1 +Log notice syslog +DataDirectory /var/lib/tor +%include /var/lib/tor/hidden_services/conf.d/