📡

Misskey を Tor でも連合できるようにしてみた

2023/12/31に公開

前回の記事 (Tor 上に Misskey インスタンスを立ててみた)の続きみたいな感じです。

前回の状態では、 .onion ドメインで公開できても他のインスタンスと連合することはできませんでした。
今回はその問題を解決していきます。

連合できない理由

まず、なぜ連合できないかというと、Misskey のバックエンドで行われている通信が Tor を経由していないため、他の Tor 上のインスタンスを見つけられないからです。

そのため、Tor から生えてくる SOCKS5 プロキシを使うように変更することで解決できます。

SOCKS5 プロキシを使えるようにする

Misskey ではもともと HTTP プロキシは使えているので、そこの部分に機能追加して SOCKS5 プロキシに対応させれば OK です。

具体的には、packages/backend/src/core/HttpRequestService.ts にプロキシの設定があるため、socks-proxy-agent を使って SOCKS5 が指定されたときにそっちを使うように設定します。

packages/backend/src/core/HttpRequestService.ts
import { SocksProxyAgent } from 'socks-proxy-agent';

function getHttpProxyAgent({proxy, ...config}: any): http.Agent {
    const url = new URL(proxy);

    switch (url.protocol) {
        case 'socks5:':
        case 'socks5h:':
            return new SocksProxyAgent(proxy, config);
        default:
            return new HttpProxyAgent({
                proxy: proxy,
                ...config,
            });
    }
}

function getHttpsProxyAgent({proxy, ...config}: any): https.Agent {
    const url = new URL(proxy);

    switch (url.protocol) {
        case 'socks5:':
        case 'socks5h:':
            return new SocksProxyAgent(proxy, config);
        default:
            return new HttpsProxyAgent({
                proxy: proxy,
                ...config,
            });
    }
}

を追加して、

packages/backend/src/core/HttpRequestService.ts
this.httpAgent = config.proxy
-    ? new HttpProxyAgent({
+    ? getHttpProxyAgent({
        keepAlive: true,
        keepAliveMsecs: 30 * 1000,
        maxSockets,
...

this.httpsAgent = config.proxy
-    ? new HttpsProxyAgent({
+    ? getHttpsProxyAgent({
        keepAlive: true,
        keepAliveMsecs: 30 * 1000,
        maxSockets,

みたいな感じにしました。

詳細: https://github.com/p1atdev/onionskey/commit/355c5e9d91f8765e085895d06986bb664b6cb3eb

ついでに、torrc も編集して SOCKS5 プロキシを有効にし、Misskey の設定ファイルのプロキシ URL も変更します。

torrc
HiddenServiceDir /etc/tor/hidden_service/
HiddenServicePort 80 web:3000
- HTTPTunnelPort 0.0.0.0:8118
+ SOCKSPort 0.0.0.0:9050
ExitPolicy reject *:*

にすると、 9050 ポートで Tor の SOCKS5 プロキシが使えるようになります。

Misskey の設定も以下のようにします。

.config/docker_example.yml
proxy: socks5h://tor:9050

socks5hsocks5 と異なり、名前解決もプロキシサーバーに任せます。.onion ドメインは Tor じゃないと解決できないので socks5h にする必要があります。

HTTP を許可する

普通ならありえないのですが、Tor では普通に HTTP で通信するため HTTP での通信を許す必要があります。

コードにところどころ強制 HTTPS を使うところや HTTP を許さない処理があるのでそこを編集します。

packages/backend/src/core/FetchInstanceMetadataService.tspackages/backend/src/misc/check-https.tspackages/frontend/src/scripts/lookup.ts を編集しました。

packages/backend/src/core/FetchInstanceMetadataService.ts
const url = 'https://' + instance.host;

というようなコードがあるので、

function prependProtocolScheme(host: string): string {
    const tld = host.split('.').pop();
    switch (tld) {
        case 'onion':
            return 'http://' + host;
        default:
            return 'https://' + host;
    }
}

みたいな関数を作って

const url = prependProtocolScheme(instance.host);

に変更しました。.onion ドメインが全て HTTP というわけではないのですが、別に大きな問題もない気がするので .onion なら問答無用で htpp:// を付与しています。

packages/backend/src/misc/check-https.ts には https:// かどうかを判定する関数

packages/backend/src/misc/check-https.ts
export function checkHttps(url: string): boolean {
	return url.startsWith('https://') ||
		(url.startsWith('http://') && process.env.NODE_ENV !== 'production');
}

があるので、こちらも同様に .onion だったら http:// でも OK になるようにします。

export function checkHttps(url: string): boolean {
    if (url.startsWith('https://')) {
        return true;
    }

    if (url.startsWith('http://') && process.env.NODE_ENV !== 'production') {
        return true;
    }

    // Allow http for .onion
    const tld = new URL(url).hostname.split('.').pop();
    switch (tld) {
        case 'onion': {
            return url.startsWith('http://')
        }
        default: {
            return false;
        }
    }
}

packages/frontend/src/scripts/lookup.ts は照会操作の部分で https:// のみを許可していたので、ここはもうめんどいので http:// も普通に通すようにしました。(本当は上みたいに個別に許可したほうがよさそう)

詳細: https://github.com/p1atdev/onionskey/commit/39a4dab750634b7479b95131df9d56d6fdab4183

連合できるようになった

これで連合できるようになりました。現状連合できるインスタンスは以下くらいです。

デフォルトで Tor に対応している[1] mitra.social というインスタンスがあるのですが、そことの連合にはバグがあるので個別で修正する必要があります:

詳細:

まとめ

SOCKS5 プロキシを使うにようして、HTTP も許可したら連合できるようになりました。

こまかいコードとかは GitHub に上がっているのでコミット履歴を見るといいと思います。

全体のコードはこちらです:

https://github.com/p1atdev/onionskey

参考

https://degreesofzero.com/article/fetch-web-page-from-tor-hidden-service-with-nodejs.html

https://qiita.com/obsolete-standard/items/97b1274fe0abf147f659

https://qiita.com/obsolete-standard/items/8ade39e834091b1195fa

脚注
  1. https://codeberg.org/silverpill/mitra/src/branch/main/docs/onion.md ↩︎

GitHubで編集を提案

Discussion