※ この記事はMastodonにTor連合対応が組み入れられていなかった当時のバージョンの解説をしています。2.4.0から公式に対応が入りましたのでそれに関する記事は別途ご参照下さい。→2.4.0版
前置き
Mastodonはリクエストを受け付けるだけではなく、相手の情報を得たりこちらの投稿を配信したりするために自分から外へのアクセスが通るようにしなければなりません。しかし制限されたネットワークへの(あるいは、からの)アクセスを捌くためのプロクシを通すようにするための処理がありません。このままではインターネットからTorネットワークへのアクセスはおろか、Tor内同士での連携すらままなりません。
この文章はMastodonをプロクシによるアクセス対応を追加するための簡単なメモに留め、Mastodonインスタンスを1から作る手順は書きません。
また、この文章ではTorへの接続を主眼としていますが、やりたいことはMastodonのHTTPプロクシ対応なので、制限されたネットワークから出るためといった目的には汎用的に使えるものと思います。その場合にはTorとPrivoxyのインストールする節を飛ばしても構いません。
必要なもの・インターネット側
- プロクシ対応コード追加
- Tor
- Privoxy
- Mastodon環境変数設定
ここでは既に動いているMastodonと同一のサーバーにTorとPrivoxyを立てるという前提で進めていきます。OSはDebianを想定しますので、インストールなどのコマンド・設定ファイルの置き場所は適宜読み替えて下さい。(※deb系ではインストール後にすぐにサービスが開始するようになっています)
プロクシ対応コード追加
Mastodonにプロクシ対応を追加するためのコミットを含むブランチを
https://github.com/hcmiya/v6don/tree/mod-proxy に作りましたので、それを含むリポジトリをfetchした上でmergeします。改変内容については後述。
$ git remote add v6don https://github.com/hcmiya/v6don.git
$ git fetch v6don
$ git merge v6don/mod-proxy
gem改変を含むため、masterからマージするとGemfile.lockで多分競合します。本体の動作に影響を与えない大したことないものなので適当にチェックアウトだけして解決してください。mod-proxy自体はタグv2.0.0から分岐しています。2017-12-05現在のバニラなコードではGemfile.lock以外で競合を起こさないはずですが、起こってしまったら後の改変内容の説明を見てなんとかしてください。
$ git checkout --theirs Gemfile.lock
$ git add Gemfile.lock
$ git commit
そしてbundle install。
$ bin/bundle install
Tor対応HTTPプロクシが既にしてある、またはプロクシ対応コードだけ必要であるという場合は環境変数設定へジャンプ。
Tor
Debianでは公式リポジトリにパッケージがありますのでそのままAPTでインストールします。
# apt install tor
Torネットワークに単にアクセスするだけならばポート開放などネットワークの変更とTorの追加の設定は必要無い事が多いです。標準ではそこにアクセスするためのSOCKS5プロクシが127.0.0.1:9050
に作られますのでcurl(1)
などのHTTPクライアントでアクセスを確認しておきましょう。
$ curl --proxy socks5h://127.0.0.1:9050 https://www.facebookcorewwwi.onion/
Privoxy
Torで作られるプロクシはSOCKS5プロクシです。これをそのまま使うと全てのアクセスがTorネットワークを通過してしまいます。接続先のドメインを見て.onionだけTorを通すようにしたいということをするにはHTTPプロクシを使うのが簡単です。そのプロクシをPrivoxyを使って作ります。
まずはインストール。
$ apt install privoxy
普通のアクセスは素通し、.onionはTorへ通すという機能を提供するため設定ファイルに以下の1行を追記します。
forward-socks5t .onion 127.0.0.1:9050 .
Privoxyを再起動。その後標準では127.0.0.1:8118
と[::1]:8118
でアクセスを待ち受けています。
# systemctl restart privoxy
インターネットとTorでアクセス先の振り分けが出来ていることを確認します。まずはインターネット側へ。
$ curl -Ss --proxy http://127.0.0.1:8118 https://check.torproject.org/ |sed '/<h1/,/<\/h1>/!d'
Tor経由でなければ以下の様に出力されるはずです(2017-12-05現在)。
<h1 class="off">
Sorry. You are not using Tor.
</h1>
同じプロクシでTor内のサイトへもアクセスできていることを確認します。
$ curl --proxy https://127.0.0.1:8118 https://www.facebookcorewwwi.onion/
なお、インターネット側のサーバーが外向きのアクセスをすべてTorを通すようにするのは無意味で迷惑なのでやめましょう。
Mastodon環境変数設定
.env.productionに先程構築したプロクシを使うよう指定します
http_proxy=http://127.0.0.1:8118
## ↓Tor対応だけならHTTPSは基本的に無いので不要
# https_proxy=http://127.0.0.1:8118
この設定を反映させるためpuma・sidekiq・nodeを再起動したら完了です。
確認
投稿欄の上の検索フォームに私のアカウント@root@nq5jmc5rsyo4fiph.onionを入れて検索してみましょう。ちょっと時間が掛かりますが木之本桜とDebianの渦巻きアイコンが表示されるはずです。
必要なもの・Tor側
※ 2017-12-09にsocksifyを使う方法に書き換えました。
- プロクシ対応コード追加
- Tor
- Dante (socksify)
- Mastodon環境変数設定・起動方法の変更
プロクシ対応コード追加
インターネット側と同じようにプロクシ対応コードを適用させます。
$ git remote add v6don https://github.com/hcmiya/v6don.git
$ git fetch v6don
$ git merge v6don/mod-proxy
$ bin/bundle install
Tor
インストールしていなければ入れます。必要に応じてHidden Serviceに関する設定をtorrcに追加します(説明略)。
# apt install tor
Dante (socksify)
- 2017-12-11以前の
/etc/dante.conf
にはIPv6通信に関する問題があったので書き直しています。
Tor側から外へ出るアクセスは例外なくすべてTorネットワークを経由させるようにします。上記のHTTPプロクシを使う方法を使うと、プロクシ対応部分以外のライブラリで通信が発生した場合に生のアクセスが相手のサーバーに行き匿名性が失われる事になります。それを実現するプログラムとしてDanteのsocksify(1)を使います。
# apt install dante-client
プログラムをsocksify経由で起動させることにより、そのプログラムで発せられた通信を全てSOCKSプロクシ経由で行わせるようにすることが出来ます。ただし、データベースやオブジェクトストレージなどへのアクセスはプロクシを通さず直接接続させる必要があります。以下はそれを実現させるための設定の一例です。
## 名前解決をSOCKSの先でさせるようにする
resolveprotocol: fake
## bindは素通しさせる
route {
from: 0.0.0.0/0 to: 0.0.0.0/0 via: direct
command: bind
}
## データベースなど、サービスに必要なローカルネットへのアクセスを
## SOCKS接続から除外させるためのルールを列挙していく。
## ポスグレが192.168.16.2:5432にある時
route {
from: 0.0.0.0/0 to: 192.168.16.2/32 port = 5432 via: direct
}
## redisが127.0.0.1:6379にある時
route {
from: 0.0.0.0/0 to: 127.0.0.1/32 port = 6379 via: direct
}
## その他メールサーバーやminioなどがあればそれにも同じように指定していく
## 列挙が面倒臭ければネットワークの範囲を指定して除外させるという設定も可
#route {
# from: 0.0.0.0/0 to: 127.0.0.0/8 via: direct
#}
#route {
# from: 0.0.0.0/0 to: 192.168.16.0/24 via: direct
#}
## 残りの外向きの通信はTorに投げる
route {
from: 0.0.0.0/0 to: 0.0.0.0/0 via: 127.0.0.1 port = 9050
proxyprotocol: socks_v5
method: none
}
以上の設定でプログラムをsocksifyで起動した時に外部への接続にTorが使われていること、ローカルのデータベース等への接続が出来ていることを確認します。
$ socksify curl -sS https://check.torproject.org/ |sed '/<h1/,/<\/h1>/!d'
$ socksify psql -U mastodon -h 192.168.16.2 mastodon_production
$ socksify redis-cli keys \*
- なお、socksifyは今の処IPv6のプロクシに対応していません。
resolveprotocol
がfake
だと接続が出来なくなり、それ以外だとプロクシは使われずスルーされます(!)。他のローカルのサービスとの通信にIPv6を使っていた場合は諦めてIPv4を使うようにネットワークを変更するか、Mastodonサービスをしているホストと同居させるようにしてUNIXドメインソケットを使うようにする必要が出てきます。IPv6使いたいのにとてもつらい……
Mastodon環境変数設定・起動方法の変更
Tor上ではHTTPS通信をさせないようにします。
LOCAL_HTTPS=false
サービスを起動させているコマンドの前にsocksifyを追加します。例えばpumaをsystemdのユニットで起動させている場合、ExecStartは以下のようになるでしょう。
ExecStart=/usr/bin/socksify /home/mastodon/live/bin/bundle exec puma -C config/puma.rb -b tcp://127.0.0.1:3000
同じ改変をSidekiqとストリーミングサービスに対して行います。
- socksify(1)は
LD_PRELOAD
をexportしたあとでexecをする簡単なシェルスクリプトなので、起動スクリプトの中でLD_PRELOAD
を設定すればsocksifyは不要になります。
各種サービスを再起動させたあと、他のTor上インスタンス、またはTor接続に対応したインターネット上のユーザーを検索できること、そのユーザーをフォローしたときにフォローボタンが砂時計にならないことを確認しましょう。IPv6ドンはTor接続に対応しているインスタンスです。
注意
SOCKSに通信を全部投げているためHTTPプロクシの使用はありませんが、ソースの改変は必要です。ユーザー情報を取得する処理にHTTPS通信を必須としている箇所があり、Tor上のインスタンスに対してはTLSの無いHTTP通信を投げるという処理が必要になるからです。
改変内容について
http(s)_proxy 読み取り
デーモンの起動時にconfig/initializers/http_proxy.rb
で環境変数からプロクシ設定を読み取り、HTTPリクエストライブラリにオプションとして渡せるようにしています。
Request
Mastodon自身が処理を書いている部分に於いては、インスタンスから出ていくリクエストはapp/lib/request.rb
のRequestクラスが司っており、さらにその内部ではgemのhttp.rbを使っています。Requestをnewしたときにオプションをデーモン初期化時に読み取ったプロクシ設定と常にマージするようにしています。
Goldfinger
GoldfingerはWebfingerを処理するためのgemです。内部ではMastodonと同じくhttp.rbを使っています。
v2.0.1の時点では、Goldfinger.fingerの引数がWebfinger URIしかなく、プロクシ対応などに必要なhttp.rbに渡されるオプションを指定することが出来ません。
またRFC 7033の規定でWebfingerのリクエストはHTTPSでしなければならない(MUST)とあるためv2.0.0からHTTPリクエストは出来ないようになったのですが、Tor内のような普通はHTTPS通信が使われないところではそれでは困ってしまいます。
その問題を解決するためGoldfingerをforkし、上記2つの使用有無を指定するためのI/Fを追加しました。そしてGemfileでGoldfingerをforkしたリポジトリとそのブランチを指すようにしています。
Goldfingerを使うときはいつでもプロクシを使うためのオプションを渡すことと、.onionだったときにTLSを使うかの判別をしないといけないので、それを直接使っていたところではMastodon::goldfingerというラッパー用モジュールを作ってそれを使うように置き換えています。
twitter-text
ドメイン名に使えるTLDに.onionはあっても.i2pが無かったのでそれを追加しています。ただ、追加する方法はそれでよかったのか感はある。
これらプロクシ対応を本体に組み込んでほしいというのはあってプルリクを出すつもりはあるのですが、その前段階としてGoldfingerに出したプルリクが執筆時点で受理されていないので、本体の方にまだ出せないでいます。
Tor内サービスで更に考慮すべきもの
メールのTor対応
2017-12-05現在のところ、Mastodonのアカウント作成にはメールアドレスが必須ですが、Tor上のアドレスにメール送信できるようにするにはメールデーモンのTor対応が必要です。これはMastodon本体の設定の範囲外になるので解説しません。
また、私が構築したTor上のインスタンス「Mastodon (Darknet)」はメールをTor外から送信するので、匿名性はその程度になります。このインスタンスを立てた目的が表題の通り「Tor内にあるMastodonインスタンスと投稿が相互に流れるようにする為」の実験なので匿名性を重視していないからです。
User Agentの秘匿
Mastodonはインスタンス間の通信だけではなくoEmbedの取得のために任意のサイトに対してリクエストを送ります。その際のUser AgentにはインスタンスのURLが含まれているため、インスタンス内で話題にされていることが容易に知られてしまいます。
リファラの秘匿
リファラを制御するためのヘッダやmeta要素を入れたほうが良いのですがまだ入れていません。
宣伝
というわけで、Tor上にあるインスタンスと繋がれるのがIPv6ドン、実際にTor上にあるのがMastodon (Tor)です。Torインスタンスを作ったらこちらのインスタンスのユーザーをフォローできるか試してみてください。私のそれぞれのアカウントは@hcm (v6)、@hcm (Tor)です。