はじめに

このサイトを開設してから2年以上、3年以下?が経過した。

更新頻度からほとんど放置と思われるかもしれないが,ALVINE[.]orgでは日々新しい技術の導入を熱心に行っている。その一環としてTor Browser向けのOnion Serviceへの対応を開始した。この記事では,Caddyでホストしている(既存の)WebサイトをOnion Serviceに対応させつつ経路を完全に分離する方法について説明する。

Tor導入の目的は純粋にTorユーザーの利便性向上のためでサーバーの所在地秘匿ではないため,同一のサーバーにセットアップする。Vanguard Add-onの導入もしない。

そもそもこのサイトの技術的な話などは今までほとんどしてこなかったので,その反省も含めて周辺技術や考え方などにも言及する内容の濃い記事にしたい。冗長かもしれないが自分が「なぜ?」と思ったことは徹底的に解説するつもりだ。

Torとは

Torについての説明は必要ないかもしれないが,単純に言えば「通信経路を暗号化したネットワーク」である。技術としてのTorは既存のTCP/IPネットワークの上に乗っかる形で存在しており,OSI参照モデルでいうところのレイヤ5からレイヤ7にまたがる幅広い存在だ。

前提

検索から到達した人にとって知りたいのはここから先の部分だろう。この記事では次のシステム条件をもとに手順を説明する:

  • Debian 12 bookworm 以降

  • Caddy v2 (Unix Socketのパーミッション指定を利用するため少なくとも2.7.4以降)

  • Caddyfile (JSON Configではなく)

  • Tor version 0.4.9.11

  • ACL 2.3.1-3

  • 50MB程度のディスクの空き容量

私の個人的な環境を再現するのなら,次の条件も必要だ:

  • 既に手元にあるV3用Vanity Addressのhostnameや秘密鍵などの関連ファイル

  • パスワードなしでSSH越しにsudoが実行できるユーザー(ファイル転送のため)

  • nftablesなどのファイアウォール設定(Onion Serviceに関係なく必ずやっておくべき)

構成と大まかな手順

システムの構成

最終的にこんな構成にしたい:

作業内容

作業の内容は次のとおり:

  1. CaddyのRuntimeDirectoryを追加する

  2. Caddyifleを編集し,Unix Socket ListenerをCaddyに追加

  3. Torをインストール

  4. ACLを使ってユーザーのアクセス権限を設定

  5. hidden_serviceディレクトリを作成しファイルを転送後,パーミッションを正しく設定

  6. torrcを編集する

  7. Torを再起動

1. CaddyのRuntimeDirectoryを追加する

ランタイムファイルとは

ランタイムファイルとはシステムの実行時に/run以下に生成されるファイルのことで,PIDファイル(.pid),ソケットファイル(.sock),ロックファイル(.lock)などがある。

ソケットファイルは異なるプロセス間での通信に使われるもので,通常のファイルシステム上のファイルではない。カーネルの機能に関わる領域というのもありパーミッションについては直感に反する決まりごとがある。通常のファイルシステム上のファイルなら+rは読み込みread()を,+wは書き込みwrite()をそれぞれ許可する。しかしソケットファイルはクライアントがconnect()によって通信チャネルを確立し,開かれたファイルディスクリプタに対してread()write()を実行するという点が異なる。ソケットへのconnect()の操作の許可,すなわちソケットに接続するための権限としてwつまり書き込みの権限が使われる1

ソケットを使った通信はTCP/IPと比較して高速で,個別のポートを占有することもないというメリットがある。

今回はCaddyをソケットの受け手側として設定するのでCaddyのランタイムディレクトリにソケットを配置し,Torをソケットに接続させて両者の間の通信を行うことにする。つまりCaddyはサーバーでTorはクライアントという関係になる。まずは/run以下にCaddy専用のディレクトリを作る。

systemd drop-inファイルを使ってユニットを追加する

/runはtmpfs上にマウントされており再起動するとディレクトリも消えてしまう。systemdにはランタイムディレクトリを永続化するための設定があるのでそれを利用する。systemdにはdrop-inファイルで一部だけを上書きするという便利な機能がある。

既存のサービスの設定を変更する,あるいは追加する場合にもとのユニット設定ファイルを直接編集するのはおすすめしない。systemdには/etc/systemd/systemを使うものと/lib/systemd/systemを使うものがあって紛らわしいだけでなく,アップデートをすれば上書きされてしまうためだ。drop-inファイルなら上書きされることはないが,サービスを停止するとランタイムディレクトリも削除されるので注意。

$ sudo systemctl edit caddy

すぐさまエディターが立ち上がる。英語の部分を読めばわかるが,2行目の### Anything between here and the comment below will become the new contents of the fileから### Lines below this comment will be discardedまでの行の間に追加する項目を書いていく。ここに追加した内容はもとのユニット設定ファイルの項目を上書きする。

/etc/systemd/system/caddy.service.d/override.conf
### Editing /etc/systemd/system/caddy.service.d/override.conf
### Anything between here and the comment below will become the new contents of the file

[Service]
RuntimeDirectory=caddy
RuntimeDirectoryMode=0750

### Lines below this comment will be discarded

### /lib/systemd/system/caddy.service
### (省略)

これで,次回の起動時にモード750の/run/caddy/が作成されるはずだ。

早速確認してみよう

$ sudo systemctl daemon-reload

$ sudo systemctl restart caddy

$ ls -ld /run/caddy
drwxr-x--- caddy caddy  /run/caddy

成功していれば,drwxr-x---/run/caddyが見えるはずだ。

もし見えていなければ,systemctl status caddyがFailedになっていないか,drop-inが正しく適用されているかもう一度確認しよう。それからsystemdの再起動が必要な操作なのでcaddy reloadとも混同しないように注意。

2. Caddyifleを編集

CaddyfileとはJSONに似たCaddy独自の設定ファイルで,JSONほど面倒でなく書いていて楽しい。さらに再読み込みはダウンタイムを発生させずに行うことができ,万が一エラーが出た場合でも元の設定にフォールバックするようになっているので安心だ。

編集作業は基本的にCaddyfileと同じディレクトリで行う。作業を始める前にバックアップを取っておくことをおすすめする。

$ cd /etc/caddy

編集,設定のチェック,設定の再読み込みは次のコマンドで:

# 編集
sudo nano Caddyfile

# 設定のチェック
sudo caddy validate

# 設定の再読み込み
sudo caddy reload

Unix Domain Socketを追加

Caddyfileを編集して新たなSite blockにてUnix Domain Socket Listenerを追加しソケットからの通信を待ち受ける。極限まで単純化すると以下のような感じだ。普段使っているクリアネット向けのSite blockに続く形でhttp://で始まるブロックを追記する。

/etc/caddy/Caddyfile
{
    # Global options block
    default_bind 203.0.113.1 [2001:db8::1]
}
example.org {
    # Site block for Clearnet (HTTP/HTTPS)
}
http:// {
    # Site block for Onion Service
    bind unix//run/caddy/onion.sock|0220
}

bind unix//run/caddy/onion.sock|0220によって,Caddyの再読込後にソケットが追加される。0220は所有者とグループに対して書き込み権限が与えられるモードである。ソケットのパーミッションについては前述の通りだが,0660にする必要はない。ちなみにCaddyのデフォルトの値も0200である2

bindディレクティブを使うので,グローバルオプションでdefault_bindでクリアネット向けのIPアドレスを設定しておくことをおすすめする。このサイトではIPv4とIPv6どちらも利用できるので2つとも指定している。

なおアドレス部分のhttp//というのはHTTPによるcatch-allなブロックであることを示す。すなわちフォールバック的な動作を期待するものなので,クリアネット向けのサイトブロックの上に書いてはいけない。.onionで終わるアドレスを書いても良いのだが,プロトコルを省略するとHTTP 400 Bad Requestが返ってきてしまうので注意。この部分を:80とポート番号で指定しても一応動作はするのだが,それはTorネットワーク側から見えている論理的な:80であって,物理的なインターフェースに紐づいたlocalhost:80とは異なる概念であるため,紛らわしさを避けるためにあえてhttp//としている。それからこの方法だとHTTPのプロトコル指定になってしまうが,Onion Serviceの場合はHTTPが一般的なので問題ない。一応付け足しておくと,Torネットワークの内部では.onionのドメインそのものが公開鍵として接続の認証に使われているため,必ずしも外部の認証機関を必要としないためだ3

HTTPをサポートするには

クリアネットの方でHTTPをサポートしたい場合はちょっと工夫がいる。CaddyではTLS証明書の取得,更新,リダイレクトまで自動でやってくれるので,上の例ではhttp://example.orgにアクセスすると1つ目のサイトブロックにマッチしたうえで308 Permanent Redirectが返ってくる。しかしCaddyfileのグローバルオプションにauto_https disable_redirectsを追記してリダイレクトを無効にするだけではうまくいかない。試してみると空の200 OKが返ってくるので,example.orgのブロックではなくTor用のフォールバックにマッチしてしまうのかもしれない。

この問題はサイトアドレスにプロトコルを明記することで解消する。

/etc/caddy/Caddyfile
{
    # Global options block
    auto_https disable_redirects
    default_bind 203.0.113.1 [2001:db8::1]
}
https://example.org {
    # Site block for Clearnet (HTTPS)
}
http://example.org {
    # Site block for Clearnet (HTTP)
}
http:// {
    # Site block for Onion Service
    bind unix//run/caddy/onion.sock|0220
}

複雑になった設定を整理する

経路を完全に分離する流れでここまで進めてきたが,Onion Service,クリアネットのHTTP/HTTPS,プロトコルによらずアクセスされたときの挙動は共通にするのが当然だろう。

これまでかなり単純化された形のCaddyfileを扱ってきたが,実際の運用ではもっと複雑になるはずだ。こんなときは一部分だけを一般化できるCaddyfileのスニペット機能が便利だ。

共通部分をcommon_rulesetとしてスニペット化してみると一括で管理できるし,見た目にもわかりやすい。

/etc/caddy/Caddyfile
{
    # Global options block
    auto_https disable_redirects
    default_bind 203.0.113.1 [2001:db8::1]
}
(common_ruleset) {
    root * /usr/share/caddy
    file_server
    encode gzip
    handle_errors {
        header Content-Type text/plain
        respond "{err.status_code} {err.status_text}" {
            close
        }
    }
}
https://example.org {
    import common_ruleset
    log {
        output file /var/log/caddy/access.log
    }
}
http://example.org {
    import common_ruleset
    log {
        output file /var/log/caddy/access.log
    }
}
http:// {
    # Site block for Onion Service
    bind unix//run/caddy/onion.sock|0220
    import common_ruleset
    log {
        output file /var/log/caddy/tor-access.log
    }
}

スニペットのブロックはグローバルオプションの直後,サイトブロックより前に置く必要がある4

それから個人的な事情でログファイルを分割しているが,TorからのリクエストをUnix Domain Socket Listenerで受けるとクライアントのIPどころかポートすらログに残らなくなるので,Tor専用のログファイルに分けることを強くおすすめする。

抽象化はほどほどにしておき,caddy validateしてみてValid configurationと表示されることを確認したら次の工程に進む。

3. Torをインストール

$ sudo apt install tor

Tor関連パッケージが何も入っていなかった場合,次の依存パッケージのインストールが必要かもしれない:

  • libevent-2.1-7t64
  • libtorsocks
  • tor-geoipdb
  • torsocks

私の場合,追加で33MBの空き容量が必要になった。想像してたよりもデカい!貧弱なリソースで供されているシステムにとっては一大事だ。

追加されるユーザー

インストールするとdebian-torいうユーザーが登録される。Torはこのユーザーで実行される。

$ id debian-tor
uid=110(debian-tor) gid=118(debian-tor) groups=118(debian-tor)

Torのsystemdユニットを確認

Debianの場合systemdからコントロールできる。systemdのユニット設定ファイルはインスタンスごとに分けて使うことを想定しているためか複数ある。

$ dpkg -L tor | grep '\.service'
/lib/systemd/system/tor.service
/lib/systemd/system/tor@.service
/lib/systemd/system/tor@default.service

tor.serviceは複数インスタンスをラッパーとして存在し,常にリターンコード0とステータスactiveを返しExecReloadを提供するだけの空のサービスである。ちなみにtor@.serviceというのは共通のテンプレートで,各インスタンスは<サービス名>@<インスタンス名>.serviceとして管理される。

tor.serviceの依存関係を覗いてみるとtor@default.serviceに依存しているのがわかる。

$ systemctl list-dependencies tor.service
tor.service
● ├─system.slice
● ├─tor@default.service
● └─sysinit.target
# (省略)

この場合,実際にTorをコントロールするのはtor@default.serviceである。したがって実際のステータスを確認するときは以下のコマンドを使う:

$ systemctl status tor@default.service

4. 実行ユーザーのアクセス許可を設定

権限周りの設定は最も重要な部分だ。詳細は後述するがTorは堅牢な設計をしており,よくある補助グループを使ったアクセス許可は通用しない。

そこで今回はACL (Access Control List) を使ってdebian-torにCaddyのソケットまでのアクセス権を与えることにする。美しい方法ではないが,こういう例外的なアクセス許可を与える目的にはピッタリの解決法だ。

事情があってACLを使えない場合でも,すべてのユーザーに対してソケットへの接続を許可するのはやめたほうが良いと思う。

ACLはディストリによってはsystemdの依存パッケージになっていたりするが,ミニマムなDebianの場合おそらくインストールされていないのでインストールする。

$ sudo apt install acl

揮発性のファイルに対して権限を変更する都合上,毎起動ごとにいちいちACL操作する必要があるので自動化しておきたい。できるだけ依存関係を増やさないためにCaddyのdrop-inにACLのコマンドを追記することにした。

Caddyのdrop-inファイルを編集する。

$ sudo systemctl edit caddy

先ほど追加した[Service]ユニットに3行追加する。

setfaclコマンドでdebina-torユーザーに対してランタイムディレクトリに対する実行権限とソケットに対する書き込み権限を与えている。ExecStartPostでCaddyの起動後に実行される。Caddyはrootで実行する前提だが,一応Prefix+をつけてroot権限が必要であることを明確にしてみた。

一応testコマンドでソケットファイルの存在を確認してから実行するようにしているが,この場合ソケットファイルが存在しないとACLどころかCaddyのサービスごと失敗してしまうので注意。

/etc/systemd/system/caddy.service.d/override.conf
# /etc/systemd/system/caddy.service.d/override.conf
[Service]
RuntimeDirectory=caddy
RuntimeDirectoryMode=0750

ExecStartPost=+/usr/bin/test -e /run/caddy/onion.sock
ExecStartPost=+/usr/bin/setfacl -m u:debian-tor:rx /run/caddy
ExecStartPost=+/usr/bin/setfacl -m u:debian-tor:w /run/caddy/onion.sock

変更を反映させる。

$ sudo systemctl daemon-reload

$ sudo systemctl restart caddy

確認するときはgetfaclコマンドを使う。user:debian-torに適切に権限が許可されていれば大丈夫だ。

$ getfacl /run/caddy
getfacl: Removing leading '/' from absolute path names
# file: run/caddy
# owner: caddy
# group: caddy
user::rwx
user:debian-tor:--x
group::r-x
mask::r-x
other::---

$ sudo getfacl /run/caddy/onion.sock
getfacl: Removing leading '/' from absolute path names
# file: run/caddy/onion.sock
# owner: caddy
# group: caddy
user::-w-
user:debian-tor:-w-
group::-w-
mask::-w-
other::---

ACLで権限をいじられるとls -lコマンドの出力に+という記号がつくので判別できる。

$ sudo ls -ld /run/caddy
drwxr-x---+ 2 caddy caddy 60  /run/caddy

$ sudo ls -l /run/caddy
total 0
s-w--w----+ 1 caddy caddy 0  onion.sock

これでCaddyとTorのプロセス間通信ができるようになったはずだ。いよいよTorのセットアップに進む。

5. 必要なファイルを転送する

完全にランダムなアドレスで構わないという場合,この章を読む必要はない。

torrcを編集してHiddenServiceDir /var/lib/tor/hidden_service/を追加し,torを再起動すると自動でアドレスが生成される。

Vanity Addressについて

Onion Service用のv3アドレスはオフラインからリセマラすることができる。見た目以外に利益はないことからVanity Addressと呼ばれる。

私の場合はmkp224oというソフトウェアを使わせてもらった。詳細なやり方についてはリポジトリを見てほしい。

転送

先にディレクトリを作成して適切なパーミッションを設定しておく。

リモート
$ sudo mkdir -P /var/lib/tor/hidden_service

$ sudo chmod 700 /var/lib/tor/hidden_service

$ sudo chown -R debian-tor:debian-tor /var/lib/tor/hidden_service

$ sudo ls -ld /var/lib/tor/hidden_service
drwx------ debian-tor debian-tor

手元にファイルがあるなら,このような構成になっているはずだ。

~/keys/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.onion/
├── hostname
├── hs_ed25519_public_key
└── hs_ed25519_secret_key

hostnameと公開鍵は644,秘密鍵は600が設定されている。scpコマンドを使うとパーミッション関係が面倒なので,tarでアーカイブ化してssh経由でリモートに送る。

ここだけローカルのマシンで実行する。リモートのマシンのsudoにパスワードが求められる場合はうまくいかない。

ローカル
$ sudo tar -C "~/keys/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.onion/" -cf - hostname hs_ed25519_public_key hs_ed25519_secret_key | ssh debian@host 'sudo tar -C /var/lib/tor/hidden_service -xf -'

所有者とパーミッションを設定する。

リモート
$ sudo chown debian-tor:debian-tor /var/lib/tor/hidden_service/*

$ sudo chmod 600 /var/lib/tor/hidden_service/hs_ed25519_secret_key

$ sudo chmod 644 /var/lib/tor/hidden_service/hs_ed25519_public_key

$ sudo chmod 644 /var/lib/tor/hidden_service/hostname

6. torrcを編集

基本的にはデフォルトの設定で大丈夫なはずだが,Onion Service向けの項目がいくつかあるので設定する。

その前にTorのPoWが利用できるかどうかを確認しておく。

$ tor --list-modules
relay: yes
dirauth: yes
dircache: yes
pow: yes

pow: yesとなっていれば大丈夫だ。そうでなければ他のパッケージを探すか,自分でソースコードからコンパイルする必要がある。

続いてtorrcを編集する。

$ sudo nano /etc/tor/torrc

個々の項目について詳しい説明は省略するので,公式のドキュメントなどから確認してほしい。それから具体的な数値はTor公式フォーラムの投稿から確認できたデフォルトの値をもとに設定してみたが,動かすサービスの規模によって変動するものなので参考程度に留めてほしい。

/etc/tor/torrc
# 今回は解説しないが,nyxなどのコントローラーを利用する場合は:9051を開けておく
#  - localhostにのみbindされている
#  - デフォルトでは閉じているのでそのままでOK
# ControlPort 9051
# CookieAuthentication 1

# 鍵とhostnameのあるディレクトリ
HiddenServiceDir /var/lib/tor/hidden_service/

# TorのPort 80に来た通信をソケットに転送する設定
HiddenServicePort 80 unix:/run/caddy/onion.sock

# DoS対策用の設定

# 1. Introduction Pointのレート制限
#  - Introduction PointはOnion Service側からクライアントに提示されるノード(リレー)のこと
#  - Onion Serviceに対するリクエストを受け取り,暗号化された通信を確立するための仲介をする

# Introduction Point 側でDoS rate limitingを有効化
HiddenServiceEnableIntroDoSDefense 1

# Introduction request のバースト許容量
# デフォルトは200
HiddenServiceEnableIntroDoSBurstPerSec 25

# Introduction request の持続レート
# デフォルトは25
HiddenServiceEnableIntroDoSRatePerSec 5

# 2. Proof of Workによる攻性防御
#  - 攻撃を受けている場合,クライアント側にも負荷をかけて攻撃を諦めさせる
#  - サービスが過負荷でない場合はPoWを完全に無効化する仕組みになっている
#  - 必要に応じてレートを絞る運用で十分かもしれない

# Proof-of-Work によるDoS防御を有効化
HiddenServicePoWDefensesEnabled 1

# PoW priority queueから処理するRendezvous requestの持続レート
# デフォルトは250
HiddenServicePoWQueueRate 50

# 一度に処理できるRendezvous requestの最大バースト
# デフォルトは2500
HiddenServicePoWQueueBurst 500

# PoWが利用可能なら使用し,利用できなければインタプリタへフォールバック
# 通常は自動選択
CompiledProofOfWorkHash auto

# 3. Rendezvous circuitごとのStream数の制限
# Rendezvous circuitとはIntroduction Pointの仲介によってホストとクライアント間に構築された経路

# 1つのRendezvous circuitから同時に作れるstream数
#  - HTTP/2を使っている場合1つのcircuitから複数streamを利用することに注意
#  - 小さければOKというものではない
#  - 最大値は65535
HiddenServiceMaxStreams 100

# 制限を超えたときにそのRendezvous circuit自体を切断
HiddenServiceMaxStreamsCloseCircuit 1

# リレーの設定は触らない

7. Torを再起動

再起動の前に設定内容に誤りがないか確認する。torはdebian-torユーザーとして実行する必要がある。

$ sudo -u debian-tor tor --verify-config

“Configuration was valid”と表示されたらtorを再起動する。hidden_serviceが存在しなければこのタイミングで作成され,鍵とhostnameが自動的に生成される。

$ sudo systemctl enable --now tor

TorのSOCKSプロキシ越しにcurlを実行して簡単な動作確認をしてみよう。curlはonionドメインを名前解決できないので--socks5-hostnameオプションを使う。

$ curl -v -I --socks5-hostname 127.0.0.1:9050 'http://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.onion'
*   Trying 127.0.0.1:9050...
* Connected to 127.0.0.1 (127.0.0.1) port 9050 (#0)
* SOCKS5 connect to xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.onion:80 (remotely resolved)
* SOCKS5 request granted.
* Connected to 127.0.0.1 (127.0.0.1) port 9050 (#0)
> HEAD / HTTP/1.1
> Host: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.onion
> User-Agent: curl/7.88.1
> Accept: */*
> 
< HTTP/1.1 200 OK
# (省略)

curlから200 OKが返ってきていたら多分成功している。

今度は適当なマシンのTor Browserから接続できるか試してみよう。

うまくできたらあとはメタタグかレスポンスヘッダーにOnion-Locationを設定したり,ログを監視してDoS対策用の設定を詰めたりするだけ!お疲れ様でした!

接続のトラブルシューティング

Can’t complete SOCKS5 connection

次のようなエラーが出る場合,TorとCaddyのプロセス間接続がうまくいっていないかもしれない。psコマンドでTorのプロセスを確認して:

  • TorはどのUID/GIDで実行されているか

  • /var/lib/tor/hidden_service以下のパーミッションはTorを実行中のUID/GIDと一致しているか

  • /run/caddy/onion.sockに設定されたACLはそのユーザーに書き込み許可を与えているか

  • TorのログにRendezvous circuitを切断した記録がないか

  • AppArmorのステータスに”DENIED”が出ていないか

などを確認しよう。

curlのエラーコード4はプロトコルが見つからないという意味らしい5が,どうしてそういう処理になるのかまではわからない。

* Can't complete SOCKS5 connection to xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.onion. (4)
* Closing connection 0
curl: (97) Can't complete SOCKS5 connection to xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.onion. (4)

エラーコードが(4)ではなく(1)のときはクライアント認証が有効になっている可能性が高い。/var/lib/tor/hidden_service/authorized_clients以下にファイルがあるはずなので確認する。

* Can't complete SOCKS5 connection to xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.onion. (1)
* Closing connection 0
curl: (97) Can't complete SOCKS5 connection to xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.onion. (1)

Connection refused

そもそもSOCKSプロキシに接続できない場合,torrcでSocksPortの設定が変更されているはずなのでtorrcを確認する。何も指定されていなければデフォルトはlocalhost:9050のはずだ。

* connect to 127.0.0.1 port 9050 failed: Connection refused
* Failed to connect to 127.0.0.1 port 9050 after 0 ms: Couldn't connect to server
* Closing connection 0
curl: (7) Failed to connect to 127.0.0.1 port 9050 after 0 ms: Couldn't connect to server

HTTPのエラー

HTTP 400 Bad Requestや空のHTTP 200 OKが返ってきたらCaddyの設定に問題がある。Caddyfileを見直したり,クリアネット版にもリクエストを送ってみて結果を比較したりしてみよう。

その他

エラーは出ないがなかなかレスポンスが返ってこないときは一旦中断して,5分くらい待ってからもう一度実行しよう。

最後に

ここまで読んでくださった皆様にお礼申し上げたい。

最後までスクロールするのも一苦労な長大な記事になってしまった。誤りに気がついたり重要なアップデートがあれば更新するかもしれないが,具体的な仕様変更や運用していて気づいたことなどがあれば別の記事で共有したい。

余談

Torの特殊さ

Torのプロセスには今回かなり手を焼いた。

そもそもCaddyのソケットにTorを接続させるにあたってまず最初に試したのは「debian-torをcaddyグループに追加する」方法だった。通常なら実行ユーザーの補助グループもファイルアクセス判定に使われるのでこれだけで十分のはずだ。

$ sudo usermod -aG caddy debian-tor

$ id debian-tor
uid=110(debian-tor) gid=118(debian-tor) groups=118(debian-tor),997(caddy)

しかしtorのPIDを特定し補助グループの情報を表示してみると……

$ systemctl show --property MainPID tor@default.service
MainPID=1008

$ ps -o supgid,supgrp -p 1008
SUPGID               SUPGRP
118                  debian-tor

全く反映されていない。プロセスに反映されないのでは意味がない。

次にtorのsystemdにて補助グループを指定してみるのも試したがこれもうまくいかず。

[Service]
SupplementaryGroups=caddy

続いてtorrcに設定項目がないか調べてみた。torrcで指定可能なオプションはtor --list-torrc-options一覧することができる。しかしこの中に任意のユーザーで実行するための設定はあったが,結局のところプライマリグループでしか実行できないらしいので今回は見送った。

User Username

On startup, setuid to this user and setgid to their primary group. Can not be changed while tor is running.

torrc(5) — tor — Debian bookworm — Debian Manpages

結論:

  • Torは権限降格によって補助グループへの追加を意図的に外す設計をしている

  • systemdからもtorrcからも手を加えることはできない

参考にさせていただいたサイト

注釈も参照

公式ドキュメント

Socket

systemd

Tor

Onion Service

Vanity Address

ACL

コマンド類


  1. Key Takeaway: To connect to a UNIX socket, a client needs write permission on the socket file. Without it, connect() will fail with EACCES (Permission denied).

    UNIX Socket Permissions in Linux: Essential Guide for C Server Developers (Directory Rules & BSD Differences) — linuxvox.com↩︎

  2. The default is 0200 (octal), i.e. u=w,g=,o= (symbolic).

    Conventions — Caddy Documentation↩︎

  3. Onion Service は HTTP で提供されています。 Onion Service のプロトコルはすでにエンドツーエンドで暗号化されているため、セキュリティ上の問題は一切ありません。

    Onion Service - 機能 - Tor Browser — Tor↩︎

  4. An optional global options block can be the very first thing in the file. Snippets or named routes may optionally appear next.

    Caddyfile Concepts — Caddy Documentation↩︎

  5. CURLE_NOT_BUILT_IN (4)

    A requested feature, protocol or option was not found built into this libcurl due to a build-time decision. This means that a feature or option was not enabled or explicitly disabled when libcurl was built and in order to get it to function you have to get a rebuilt libcurl.

    libcurl - Error Codes↩︎

  6. こちらの記事が参照している『コネクション型通信:サーバプログラムの作成』という記事も大変に参考になったのだが,その記事のインデックスページを見ると「出版用の原稿のため、コピー及びリンクは御遠慮ください。」と明記されていたのでリンクは掲載しない。↩︎

ライセンス情報

badge

Caddy v2でOnion Serviceを運用するクリエイティブ・コモンズ [表示 4.0 国際]ライセンスの下に提供されています。