Directory Setup

  1. Set up the files and directories:
mkdir -p tor-site/keys tor-site/html tor-site/logs
touch tor-site/torrc
  1. Set permissions:
    chmod 700 tor-site/keys 
    chmod 600 tor-site/logs
    sudo chown root:root tor-site/keys tor-site/logs
    

Content Setup

Add the files for your website into the tor-site/html folder: example:

<!DOCTYPE html>
<html>
<body>
    <h1>Hello from the Onion Router!</h1>
    <p>This site is hosted inside Docker.</p>
</body>
</html>

Docker Setup

[[Install Docker]] Docker Compose File compose.yaml

services:
  nginx:
    container_name: nginx
    image: nginx
    cap_drop:
      - ALL
    cap_add:
      - CHOWN
      - SETGID
      - SETUID
    volumes:
      - ./html:/usr/share/nginx/html:ro
      - ./logs:/var/log/nginx
    networks:
      - tor_network
  tor:
    container_name: tor
    volumes:
      - ./torrc:/etc/tor/torrc:ro
      - ./keys:/var/lib/tor/hidden_service/
    image: alpine:latest
    entrypoint: sh -c "apk add --no-cache tor && tor -f /etc/tor/torrc"
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    networks:
      - tor_network
    depends_on:
      - nginx

networks:
  tor_network:
  • nginx is the name of your web server container - this is important for the torrc file.
  • :ro sets the volume to read only
  • networks: tor_network means all the traffic stays inside the tor network
  • security_opt: - no-new-privileges:true prevents the user from running as root through setuid or setgid
  • cap_drop: -All removes all default Linux capabilities granted to a container
  • cap_add: - NET_BIND_SERVICE will allow tor to work with only the necessary capabilities
  • networks ensures that all traffic stays inside the docker network with a custom bridge tor_network to access the tor relays See Docker Permissions

Create torrc:

# Standard Tor config
DataDirectory /var/lib/tor

# Define the Hidden Service
HiddenServiceDir /var/lib/tor/hidden_service/
HiddenServicePort 80 nginx:80
  • note: the name nginx should be the same as you name your web server container in the compose.yaml (see [[#Docker Setup]]).

Notes:


services:
  nginx:
    container_name: nginx
    image: nginx
    volumes:
      - /home/mechanicus/code/tor-site/html:/usr/share/nginx/html:ro
      - /home/mechanicus/code/tor-site/logs:/var/log/nginx
    networks:
      - tor_network
    deploy: 
      mode: replicated
      replicas: 1
    labels:
      - "com.centurylinklabs.watchtower.enable=true"
      - "label=shepherd.autodeploy=true"
  tor:
    container_name: tor
    volumes:
      - /home/mechanicus/code/tor-site/torrc:/etc/tor/torrc:ro
      - /home/mechanicus/code/tor-site/keys:/var/lib/tor/hidden_service/
    image: alpine:latest
    entrypoint: sh -c "apk add --no-cache tor && tor -f /etc/tor/torrc"
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    networks:
      - tor_network
    depends_on:
      - nginx
    deploy: 
      mode: replicated
      replicas: 1
    labels:
      - "com.centurylinklabs.watchtower.enable=true"
      - "label=shepherd.autodeploy=true"

networks:
  tor_network: