A turnkey solution for deploying web applications as Tor hidden services.
1.8K
.d88b. w 888b. 8
8P Y8 8d8b. w .d8b. 8d8b. 8 8 .d8b. .d8b 8.dP
8b d8 8P Y8 8 8' .8 8P Y8 8 8 8' .8 8 88b
`Y88P' 8 8 8 `Y8P' 8 8 888P' `Y8P' `Y8P 8 Yb
Hardened Tor v3 hidden-service container. Bundles tor + vanguards (guard/band/rend) + pluggable transports (obfs4, snowflake). Non-root, single Python entrypoint, GPG-verified bundle, SHA-pinned vanguards.
services:
tor:
image: tn3w/oniondock:latest
environment:
SECURITY_LEVEL: high
TOR_SERVICE_PORTS: '80:webapp:80'
TOR_TRANSPORT_TYPE: snowflake
volumes: [./data/hidden_service:/var/lib/tor/hidden_service]
networks: [onion]
depends_on: [webapp]
restart: unless-stopped
webapp:
build: ./app
networks: [onion]
networks: { onion }
docker compose up -d
docker compose exec tor cat /var/lib/tor/hidden_service/hostname
Mounted volume = onion key. Back it up. Lose it → lose the address.
| Env | Default | Values |
|---|---|---|
SECURITY_LEVEL | high | high / medium / low |
TOR_TRANSPORT_TYPE | snowflake | snowflake, obfs4, none |
TOR_SERVICE_PORTS | 80:webapp:80 | outer:host:inner[,…] |
Custom vanguards: bind-mount /etc/tor/vanguards.conf (else upstream defaults).
Encrypted onion key: mount as Docker secret hs_ed25519_secret_key.
docker build -t oniondock tor/
docker compose -f example/docker-compose-dev.yml up --build
[your app] ──(docker net)──► [tor container]
├── tor (alpine)
├── pluggable transports (Tor Expert Bundle, GPG-verified)
└── vanguards × {guards, band, rend}
EF6E286D…3298290.c3961ac4… (v0.3.1). Override: --build-arg VANGUARDS_SHA=….tor/
Dockerfile 2-stage
oniondock.py entrypoint
config/torrc hardened template
tor drop verified post-setuid; env wiped + rebuilt; chown refuses symlinks (follow_symlinks=False).secrets.SystemRandom (CSPRNG). iat-mode forced to 2.SocksPort/ORPort/DirPort/ExitRelay/BridgeRelay/PublishServerDescriptor → cannot become a relay.ControlPort 127.0.0.1:9051, cookie auth, cookie in /run/tor/ (not in mountable data dir).SafeLogging 1, HiddenServiceEnableIntroDoSDefense 1, HiddenServicePoWDefensesEnabled 1, ConnectionPadding 1, CircuitPadding 1.torrc written O_NOFOLLOW mode 0600.pip/setuptools; bytecode precompiled.tor shell = /bin/false.0555 / 0444.STOPSIGNAL SIGTERM, PYTHONDONTWRITEBYTECODE=1, PYTHONUNBUFFERED=1./run/secrets/hs_ed25519_secret_key if present.services:
tor:
image: tn3w/oniondock:latest
read_only: true
tmpfs: [/tmp, /run]
security_opt: ['no-new-privileges:true']
cap_drop: [ALL]
cap_add: [CHOWN, FOWNER, SETUID, SETGID]
pids_limit: 128
volumes:
- tor-data:/var/lib/tor # guard-state persistence
- ./data/hs:/var/lib/tor/hidden_service
Four caps = minimum for chown + setuid. Mounting full /var/lib/tor persists guards (otherwise restart → fresh guards → broader exposure).
pt_config.json snapshot at build time → rebuild periodically (snowflake unaffected, broker-discovered).linux/amd64 only (bundle URL hardcoded)./var/lib/tor fingerprints client → treat as sensitive.lyrebird ships with Go stdlib + x/net + x/crypto from whichever Go version Tor's release-build env used (CVE scanners flag it). Upstream-fix-only: rebuild image when Tor Project ships a new Expert Bundle. conjure-client (which carried more of these CVEs and additional utls/pion ones) is removed from the image — experimental, rarely used, biggest Go-CVE surface. Rebuild with it kept if you need conjure.busybox in Alpine has open CVEs without a fixed version yet; rebuild when Alpine ships one.webapp.cosign verify before pull + host HIDS after.Never expose webapp's port to the host in production.
GitHub Actions builds + publishes tn3w/oniondock on push, signs digests with cosign keyless OIDC.
Apache 2.0 LICENSE.
Content type
Image
Digest
sha256:fcd455819…
Size
43.5 MB
Last updated
5 months ago
docker pull tn3w/oniondock