Next.js ウェブサイトを Docker を使って Tor に公開してみる
この記事は Docker/コンテナ仮想環境 Advent Calendar 2023 7日目の記事です。ですが、そんなに Docker に詳しいわけじゃないので何か間違っていることなどあれば教えてください。
突然 Web サイトを Tor で公開したくなることってあると思います。
でも、日常的に作っている Web サイトは Vercel だったりにデプロイするのが一般的で、多くの Tor 導入記事にあるような Apache とか Nginx とかの Web サーバーを使うことはあんまり少なくなっていると思います。
そこで今回は Next.js で作った Web サイトを Docker を使って少しモダンな感じで Tor に公開してみます。ここでは Next.js を使っていますが、Remix や Vite など別のフレームワークでも大体同じようにできるはずです。
成果物
Web サイト: (家の PC でホストしているので気分で落ちているかも)
http://nextjs3vm7rw667eqzgfi66c6k7rfc5q5l4yi2qakvcm76wtf3u7nzad.onion
レポ:
今回の環境
- Node.js v18.18.0
- pnpm 8.11.0 (npm や yarn でも可能ですが読み替える必要があります)
- Docker 24.0.6
- Tor Browser 13.0.5
- Windows 11
Next.js セットアップ
ここは公式解説ほぼそのままですが、pnpm
を使います。
pnpm dlx create-next-app@latest
聞かれる質問は好きなものを答えていいですが、 src
ディレクトリは使うようにするとこの後の説明と合うので作業が楽です。
今回はこのプロジェクトのルートディレクトリで作業していく形になります。
Tor セットアップ
Tor に関するものをセットアップしていきます。
Tor Browser インストール
もしまだ Tor ブラウザをインストールしていない場合、.onion
ドメインの Web サイトを見れないのでインストールする必要があります。インストールしていたら飛ばしてください。
公式サイト からダウンロードしてもいいですが、winget
や brew
からも入れることができます。
brew install --cask tor-browser
winget install TorProject.TorBrowser
これでインストールができました。
ウィンドウサイズに関する余談
少し使っていると、ウィンドウサイズによっては画面の左右や下に謎の余白が発生することに気づくかもしれません。
これはレターボックスと呼ばれ、多くのユーザーのウィンドウサイズを統一することで、ウィンドウサイズによる個人の特定を難しくするために導入されています。[1]
.onion ドメインを取る
Tor では、通常の Web サイトとは異なる .onion
というドメインを使います。これは、Tor のネットワーク内でのみ有効なドメインで、Tor 以外のブラウザではアクセスすることができません。
例えば DuckDuckGo の .onion
ドメインは duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion
です。
このようなクソ長いドメインは V3 Onion Service のドメインで、この 56 文字のドメイン自体に Ed25519 公開鍵 とバージョン番号 (3)、チェックサムが含まれています。[2]V2 は RSA 公開鍵のハッシュが利用されていましたが、現在は非推奨になっており、最新の Tor Browser では V2 のドメインにはアクセスできません。[3]
めちゃくちゃ長いドメインなので、判別しやすくするために先頭をわかりやすくしたドメインがよく使われます。
DuckDuckGo では duckduckgogg42x...
となっているのがわかります。他にも、
- ProtonMail:
protonmailrmez3lotccipshtkleegetolb73fuirgj7r4o4vfu7ozyd.onion
- Twitter:
twitter3e4tixl4xyajtrzo62zg5vztmjuricljdp2c5kshju4avyoid.onion
などがあります。[4]このように、判別しやすくしたアドレスを Vanity Address と呼びます。[5]
このようなドメインですが、先程も言ったように公開鍵なので、すべての文字を任意に決めることはできません。先頭の数文字が欲しい文字列に一致するまで総当たりで探していくことになります。
今回は V3 対応の Vanity Address を作るためのツールである mkp224o を使って独自の .onion
ドメインを取っていきます。
Releases から使っている OS に合わせてダウンロードします。今回は Windows なので mkp224o-1.6.1-w64.zip
をダウンロードしました。
解凍すると次のようになっていると思います。
mkp224o.exe
を使って取りたいドメインを総当りで探していきます。
例えば先頭が zenn
から始まって欲しい場合は次のようになります。
mkp224o.exe -d domains -n 5 zenn
引数の詳しい説明
-
-d
: 見つけたドメインの鍵を出力するディレクトリ。指定しないと実行フォルダ直下になります。 -
-n
: ドメインをいくつまで探すかどうか。指定しないと無限に探すことになります。
今回のように 4 文字だけであれば結構速く見つかります。6文字以上になると少し時間がかかるようになってきます。
処理中は CPU をフルで使うので他の作業がほとんどできなくなることに注意が必要です。(RunCat の猫がチカチカするレベル)
ここで見つかったドメインの鍵などは domains
ディレクトリに保存され、後の作業で使うことになります。
.onion ドメインの余談
Tor の設定
先程作成したプロジェクトフォルダのルートに tor
ディレクトリを作成して、 Tor に関する設定ファイルなどを置くことにします。
まず、Tor の設定を記述する torrc
を tor
フォルダに作成します。
HiddenServiceDir /app/tor/hidden_service/
HiddenServicePort 80 127.0.0.1:3000
それぞれ解説すると、HiddenServiceDir
は Tor が生成した秘密鍵や .onion
ドメインを保存するディレクトリを指定します。(このディレクトリはパーミッションの設定が厳しいです)。 HiddenServicePort
は公開するポートと、そのポートに対してどのようなアクセスをするかを指定します。ここでは 80
ポートへのアクセスを Next.js のデフォルトのポートである 3000
ポートに転送するようにしています。ここの 80
は、Docker の設定に合わせて変更することができます。
次に、先程作成したドメインを使うための設定をします。作成したドメインのフォルダの中には次のファイルが入っていると思います。
これらのファイルを /tor/hidden_service
にコピーします。
これで Tor の設定は完了です。
Docker セットアップ
Dockerfile
Dockerfile
を書いていきます。
FROM node:20.4.0-bookworm-slim AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
WORKDIR /app
# Setting up Tor
RUN apt update
RUN apt install -y tor
COPY ./tor/torrc /etc/tor/torrc
COPY ./tor/hidden_service /app/tor/hidden_service
# Installing pnpm
FROM base AS deps
COPY package.json pnpm-lock.yaml* ./
RUN pnpm install --frozen-lockfile
# Building Next.js
FROM deps AS build
COPY . /app
RUN pnpm build
FROM deps AS prod
COPY /app/node_modules /app/node_modules
COPY /app/public /app/public
COPY /app/.next /app/.next
COPY /app/next.config.js /app
RUN chmod 700 /app/tor/hidden_service
CMD tor & pnpm start
上から順に解説していきます。
FROM node:20.4.0-bookworm-slim AS base
軽量な Node.js の slim
イメージを使います。[6]
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
pnpm
を使えるようにします。もし yarn
を使う場合は変更が必要になりそうですが、調べてないので具体的な変更はわかりません。
WORKDIR /app
作業ディレクトリを /app
にします。
# Setting up Tor
RUN apt update
RUN apt install -y tor
COPY ./tor/torrc /etc/tor/torrc
COPY ./tor/hidden_service /app/tor/hidden_service
ここで先程作成した Tor の設定ファイルやドメインのファイルをコピーして Tor をセットアップしています。
# Installing pnpm
FROM base AS deps
COPY package.json pnpm-lock.yaml* ./
RUN pnpm install --frozen-lockfile
pnpm
で依存関係をインストールしています。
# Building Next.js
FROM deps AS build
COPY . /app
RUN pnpm build
Next.js のビルドをしています。
FROM deps AS prod
COPY /app/node_modules /app/node_modules
COPY /app/public /app/public
COPY /app/.next /app/.next
COPY /app/next.config.js /app
RUN chmod 700 /app/tor/hidden_service
CMD tor & pnpm start
ビルドしたものや依存関係をコピーします。また、/app/tor/hidden_service
フォルダのパーミッションを変更しています。権限ゆるゆるだと怒られて起動できません。
最後に tor
と pnpm start
で Tor と Next.js を起動します。
docker-compose.yaml
docker-compose.yaml
も書いていきます。
services:
app:
build:
context: .
target: prod
ports:
- "3000:3000" # 一般ネットワークでの確認用
- "8888:80"
environment:
- NODE_ENV=production
Dockerfile
で定義した prod
ターゲットを使ってビルドします。
また、3000:3000
で Next.js のポートをローカルホストに公開している他、8888:80
で Tor のポートもローカルホストに公開しています。この 80
は torrc
で指定したポートと同じにする必要がありますが、 8888
は適当でいいと思います。(だめだったら教えてください)
Tor に公開!
さて、最低限のファイルは作成できたので Docker を起動して Tor に最初の Web サイトを公開してみましょう。
docker compose up --build
[notice] Bootstrapped 100%
というログが出たら Tor の接続完了です。Tor Browser で先程作成したドメインにアクセスしてみましょう!
ちゃんと Next.js のテンプレページが表示されました!
ホットリロード対応する
これだけでも十分なのですが、開発中も Tor 経由で表示を確認したくなるかもしれません。そこで、ホットリロードにも対応してみましょう。
Dockerfile の変更
Dockerfile
に追記していきます。
# For development
FROM deps AS dev
COPY next.config.js /app
COPY tsconfig.json /app
RUN chmod 700 /app/tor/hidden_service
CMD tor & pnpm dev
docker-compose.dev.yaml の作成
また、docker-compose.dev.yaml
を作成して次のようにします。
services:
app:
build:
context: .
target: dev
environment:
- NODE_ENV=development
ports:
- "3000:3000"
- "8888:80"
volumes:
- ./public:/app/public
- ./src:/app/src
docker-compose.yaml
と比べて変更された点は以下です。
-
build
のtarget
がdev
になっている -
environment
のNODE_ENV
がdevelopment
になっている -
volumes
が追加されている
target
は先程と同じように、Dockerfile
で定義した dev
ターゲットを使ってビルドします。
volumes
では、開発中に編集する可能性があるディレクトリを、直接コンテナ内の特定のディレクトリと同期するように設定しています。これによって、エディタでファイルを編集すると、コンテナを再起動しなくてもコンテナ内のコードも更新されるようになります。(package.json
などを変更した場合は再起動が必要になります)
next.config.js の変更
next.config.js
にも追記が必要です。
/** @type {import('next').NextConfig} */
const nextConfig = {
webpackDevMiddleware: (config) => {
config.watchOptions = {
poll: 1000,
aggregateTimeout: 300,
};
return config;
},
};
module.exports = nextConfig;
現在(2923/12/07時点) の Next.js では Docker 環境でのホットリロードに少し問題があるようで、webpackDevMiddleware
を指定する必要があります。[7]
ここまで設定すると、
docker compose -f ./docker-compose.dev.yaml up --build
を実行することで Tor Browser でのホットリロードを確認することができます。
package.json の変更
コマンド名が長いので、"scripts"
を編集して簡単にコンテナを起動できるようにします。
...
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"dev:tor": "docker compose -f ./docker-compose.dev.yaml up --build",
"start:tor": "docker compose up --build"
},
...
このようにすることで、
pnpm dev:tor
ホットリロードが有効な状態で Tor 経由で Web サイトを公開できるようになります。
ホットリロードしてる動画 (等倍速です):
.onion
ドメインでホットリロードが効いているのを見ると、少し奇妙な感覚があって面白いですね。
おまけ
Tor Browser の規定の保護では、クライアントサイドの JavaScript の実行は特に制限されません。
「最大限の保護」に設定すると JavaScript やフォントの読み込みが制限されるようになる
なので、React の Client Component とかも問題なく動作します。
最初の方に載せた成果物の Web サイトでは React のカウンターを置いてあるので動作を確認することができます。
他にも PandaCSS を使って見た目をいじったりしているので、興味があれば見て行ってね。
WELCOME TO UNDERGROUND!
まとめ
今回は Docker を使って Next.js の Web サイトを Tor に公開してみました。
利点
- Tor なので セキュア
-
無料で独自ドメインの Web サイトが公開できる
- Cloudflare Tunnel や ngrok の設定が不要
- Docker なので環境構築が楽
欠点
- 超遅い!!!!!
感想
Tor で Web サイトを公開する記事を探すと Next.js とか Vite を使った記事が全然なかったのでやってみました。これで突然 Tor に Web サイトを公開したくなったときも安心ですね。
個人的には Tor の Web サイトは機能が制限されていたり、モダンな技術が使えないと思いこんでいたのですが、特にそんなことはなかったですね。重いことだけが難点です。
実際にダークウェブと聞いて我々 (私だけかも?) がイメージするような Web サイトが、フォントがダサかったりデザインが終わっていたりなんか怪しい色づかいをしている理由としては
- 通信量減らすため
- 余計な情報を排除して特定の可能性を減らしている
- 「最大限の保護」を利用することを想定して無駄なものを排除している
- デザイナーがいない
- わざと雰囲気出すためにダサくしている
のどれかなんじゃないかなと思います。詳しい人がいたら教えてください。
参考
Discussion