この記事は力強くブログを108記事アウトプットする日の20251227、Youreinの1記事目です。

SeafileというセルフホストのクラウドストレージサービスをTailscaleのVPN (tailnet) 内で運用しているという話をします。

発端

元々4TBくらいあるHDDを積んだサーバ機を運用しており、基本的にサイズが大きなファイルや過去の動画像、スキャンした書類データなどはそのサーバー機に保存していました。この運用の始まりは古く、およそ6年程度になります。その間にサーバー機のOSがWindows 10 ProからTrueNASになり、紆余曲折を経てUbuntuになって現在に至るなどの話はあるのですが、いつの時代もsmb (samba)を利用したファイル共有でこれを実現していました。

この運用にはいくつかの問題点がありました。

  1. sambaなので結局OSのどこかのフォルダを公開しているにすぎない
  2. 仮に複数人がストレージを利用する状況ではアクセスコントロールが煩雑
  3. PCから利用する分には便利だが、スマホからアクセスする際は面倒 (特にiPhoneだった頃は)
  4. ファイル数が多いフォルダをMacから開いたときにファイルエクスプローラーソフトが停止する (これは主にMacが悪い)

これらの問題は今まで自分が主にWindowsからsambaで共有しているフォルダにアクセスしていたため回避できていましたが、様々な環境からアクセスするようなことが多くなり、段々と不十分になってきました。

そんなある日、身内とサイズの大きなファイルの共有を行いたいという要望があり、いい機会だったのでセルフホストのクラウドストレージサービスを立ち上げることにしました。

手法

Seafileをセルフホストしています。
自宅でこういうクラウドストレージ系をセルフホストする時はNextCloudが最近は流行りだと思いますが、普通にオフィススイートはいらないので、純粋にクラウドストレージ部分をセルフホストできるソフトウェアを探した形です。

このSeafileに許可を受けたtailnet内の人間がアクセスできるようにしています。まあ今自分のtailnetに入ってる人間全員許可されてますが。

Tailscale内にサービスを公開する方法は色々あります。自分がよく使うのは yourein.net のDNSレコードに新しくAレコードを設定し、Aレコードが指すIPアドレスをtailnet内のプライベートIPに設定する方法です。そこから先は普通にサービスを公開する時と同じで、なんとかかんとか.yourein.net でアクセスが来るのでnginxなりcaddyなりのリバプロで捌いてあげるという形です。ということを長々と書いた記事があるので、詳しくはTailscale + Cloudflare + VaultWardenでプライベートなパスワードマネージャーを所有するを読んでください。

当然、今回もこの方法を使えばいつも通りサービスをVPN内部に公開できます。が、今回はいつもと異なる形を取り、TailscaleのDockerコンテナを用いて、Seafileにtailnet内でユニークなIPが振られるようにしました。簡単に理由を説明すると、アクセスコントロールと現在のセルフホスト方法が関係しています。現在Youreinは yourein.net のサブドメインを切ってVPN内に公開しているサービスをいくつか持っていますが、それは単一のサーバー上でdocker compose up -dして動いているもので、どのVPN内サービスのドメインが指すIPアドレスも単一のtailnet内のIPアドレス、つまりサービス運用に利用しているサーバー機のtailnet内IPとなっています。したがって、今までと同じようにSeafileをサブドメイン切って公開すると自分しか使っていないサービスに他人からのアクセスが行われる可能性があります。

そこで、TailscaleをDockerコンテナやVM上で実行することでホストコンピューターとネットワークを分離するという手法がしばしば用いられています。自分もホストコンピューターとSeafileのネットワークを分離する目的でDockerコンテナ内部のTailscaleを利用しています。

services:
  ts-seafile:
    image: tailscale/tailscale:latest
    container_name: ts-seafile
    hostname: drive
    environment:
      - TS_AUTHKEY=***
      - TS_EXTRA_ARGS=--advertise-tags=tag:drive
      - TS_STATE_DIR=/var/lib/tailscale
      - TS_USERSPACE=false
    volumes:
      - ts-authkey:/home/username/seafile_root/ts
      - /dev/net/tun:/dev/net/tun
    cap_add:
      - net_admin
      - sys_module
    restart: unless-stopped

  seafile-db:
    ———省略———
  
  memcached:
    ———省略———
  
  seafile:
    image: seafileltd/seafile-mc:11.0-latest
    ———省略———
    network_mode: service:ts-seafile

volumes:
  ts-authkey:
    driver: local

こんな感じに書くとtailnet内部からSeafileが見えるようになります。
一応何をしているのかということを説明しておくと、Tailscaleが動いている ts-seafile というコンテナとSeafileが動いている seafile というコンテナのネットワーク空間を繋げることで、まるで ts-seafile コンテナ内部でSeafileが動いているかのように見せかけているというわけです。 ts-seafile には hostname: drive と書いてありますので、tailnetに参加し、Seafileへのアクセスが許可されたユーザーは http://drive とURLを打つことでSeafileにアクセスできます。実質的には seafile コンテナのlocalhostに接続していることになります。

これで完了…というわけではなく、もう少し設定が必要です。Seafileのadminコンソールに入って、Seafileが内部で使っているURLの設定を書き換えてあげます。これを正しくやらないとファイルのアップロードが一生できません。

アクセスコントロールについて

ts-seafileenvironmentTS_EXTRA_ARGS=--advertise-tags=tag:driveを設定しているので、Seafileへのアクセスを許可したいユーザーについてtag:driveがついたデバイスへのアクセスを許可し、それ以外を不許可とすれば良いです。

TS_AUTHKEYについて

通常はKeysから期限付きの認証キーを発行して利用しますが、実はTrust Credentialsメニューから作成できるOAuthトークンでもTailscaleへのログインとデバイスの作成が行えるとされています。 その際にさっき出てきた--advertise-tags=tag:driveを設定してあげると、勝手にdriveのtagがつくので、docker compose up -dと同時に作成されるデバイスにtagを付与し忘れることがなくなって良いです。

不満1

TS_AUTHKEYの末尾にクエリパラメーターのように ephemeral=true とつけると、docker compose up -dして作成されたデバイスはdocker compose downしてしばらく経つと自動でtailnetから消えてくれます。が、消えるまでの時間がやけに長いため結局役に立っていません。これで何が起こるのかというと、tailnetの内部には同じデバイス名を持つデバイスは存在できないため、docker compose pullとかのために短時間コンテナを停止して再起動するなどすると、drive-1drive-2drive-3、…というように末尾の数字がインクリメントされたデバイスがtailnet内部に増えていくことになります。

TailscaleにはMagicDNSという機能があって、先ほどのように http://drive と単にtailnet内部のデバイス名をアクセス先に指定してもtailnet内部で利用されているドメインに自動で変換してくれる機能があります。さっき画像でチラッと見せた drive.なんとか.ts.net の形に変換されるわけですね。当然ですが、drivedrive-1は別の内部ドメインに変換されるためSeafileへのアクセスができなくなります。今まで drive でアクセスできていたものの実体が drive-1 に移ってしまうためです。

これを防ぐには docker compose down の際に内部でシグナルをキャッチして tailscale logout を実行するようにすれば良いのですが、どうにも面倒で手を出せていません。

不満2

Tailscaleのアクセスコントロールにはテスト機能があります。まあ当たり前っちゃ当たり前なんですが、このアクセスコントロールのテストは形式的なテスト、例えばこのユーザーはtag:hogeに紐づけられたデバイスにアクセスできるなどのテストしか行うことができず、たとえばこのユーザーはこのIP (or デバイス)にアクセスすることができるかみたいなのはテストができません。
まあそもそもそういうテストが必要になるようなtag管理をするなってことなんでしょうが、テストできたら設定ミスに気づきやすくていいのになぁと思いました。

…書いてて思いましたが、アクセスコントロールのテストとしてはTailscaleが提供するテストがスコープとして正しくて、自分が求めているテストはtailnet内のデバイス側で行うべきテストですか?

実際使えるんですか?

実際クラウドストレージとして利用可能なスループットが出るのかという話ですが、出ます。
Seafileでは動画や写真はSeafile内のビューワーで閲覧することができますが、ネットワーク由来の不調は大抵の場合クライアントサイドのネットワークの不調であることが多いです。
実際スループットを計測したわけではないですが、30GBくらいのファイルをWAN (ベストエフォート1GbpsのFTTH) からアップロードするのに30分とちょっととかだったはずなので10MB/sec以上の転送速度が出ていそうな感じがあります。 実際に計測したわけじゃないですが

おわり

みんなもRAIDすら組んでいない信頼性皆無なクラウドストレージサービスを提供するオタクになろう