Mastodon インスタンスのリモートフォローの仕組みと必要な購読更新の設定方法

![スライド1.PNG](https://qiita-image-store.s3.amazonaws.com/0/176345/26322754-2da4-5d3b-9561-0a60f9789b72.png)

[追記]Mastodon バージョン1.3.3がリリースされて、このページに書いている不具合は解決されて、今後は発生しなさそうです。このページも御役御免ということですね。問題にハマってる人は1.3.3にアップデートしましょう。

---

Mastodon のインスタンス運営をする上で、v1.3.2以下の Mastodon の設定が悪いとリモートフォローのユーザーの投稿がある日突然見えなくなることがあります。逆に相手サーバーの設定が悪いとせっかくリモートフォローしてくれたユーザーの投稿が届かなくなります。原因はいくつかあって、主に

- 受信側の購読の更新設定が出来ていなくて７日で期限が切れた場合
- 受信側サーバーのエラー時に送信側が投稿し購読が解除された場合
- 受信側か送信側の SSL の Mastodon への設定ミス 

の３つが挙げられます。

-----

まず、なぜそもそも購読とか期限とか、ややこしいことが必要なのでしょうか？
それは、Mastodon 等の OStatus を使ったSNSでは、異なるインスタンス間の通信をする上で成りすましを防ぐために署名の鍵を更新しながらポストを送る必要があるからです。その部分を Mastodon は PubSubHubbub という push 通知の方式を使って実装されていて、鍵は７日間で期限切れとするようになっています。大まかには下記のような仕組みになっています。

![スライド2.PNG](https://qiita-image-store.s3.amazonaws.com/0/176345/335912d5-de01-3a2e-f8a7-f371a79bcdfb.png)

前提として、server1.com 上のユーザー user1 が、server2.com 上のユーザー user2 の投稿を読みたくなってリモートフォローしたとします。この場合、リモートフォローボタンを押したときに、server1.com から server2.com へ購読申請(Subscribe)が届きます。流れる情報には、

 - `hub.topic` に読みたいユーザーの投稿の URL
 - `hub.callback` に投稿があった時に呼んでほしい callback URL
 - `hub.secret` に署名をするときの鍵
 
という情報が含まれます。callback URL には id という数字(例では`{id2}`)が含まれていて、これは server1.com 上のリストの中の user2 を示す番号です。購読申請を受けた server2.com は、callback URL と署名の鍵 secret1 を保存します。（その直後にcallback URL の検証などの通信もなされます）リモートフォローのときに行われる一連の手続き(Subscribe)はこれで完了です。

次に、user2 から実際の投稿(post)があったときの動きです。user2 はSubscribe 時に設定された callback URL に向けて body に投稿本文を入れて送ります。このとき、この本文を先ほどの鍵 secret1 をキーとして作ったハッシュが署名のための付加情報として送られます。この鍵は、成りすましを防ぐために、７日間で更新されます。７日以上経った鍵はクラックされている可能性があるので捨てられるというわけです。

更新は受信側の方がもう一度購読申請(Subscribe)を行うことでなされます。この更新は受信側で、 mastodon v1.3.3 以降では sidekiq で、v1.2.2 以前では cron などのスケジューラーによって、期限が１日未満になっているユーザーを自動で検出して再申請するようになっています。再申請のコマンドは `SubscribeService.new.call(Account.find({id2}))` です。

----

![スライド3.PNG](https://qiita-image-store.s3.amazonaws.com/0/176345/63338578-eb1a-ddd3-ff11-c9f572d7af52.png)

見えなくなるケースの１つ目は、v1.2.2以前を使っているのにcron 設定を忘れていて受信側でこの購読再申請がスケジューリングされていなかった場合です。この場合、７日経つと知らない間に user2 の投稿が user1 のホームタイムラインや server1.com の連合タイムラインに現れなくなります。これがまた、 server1 のローカルユーザーの投稿や、期限が切れていない server2.com 上のリモートユーザーの投稿はちゃんと流れるので、user2 だけの投稿が見えなくなっていることになかなか気づきにくいのです。

これを復旧するには、server1.com の方から `SubscribeService.new.call(Account.find({id2}))` を実行するか、コマンド `mastodon:push:refresh` や `mastodon:daily` を実行する必要があります。

[追記]Mastodon バージョン1.3.2以前は、この再購読はインスタンス管理者の自前のcron(スケジューラ)設定で自動的にやってね、という仕様でした。1.3.3以降は、sidekiqがこれをやるようになったので再購読に関するcron設定は必要なくなります。というわけでこの問題にハマった人は1.3.3に上げましょう。

1.3.2以下をそのまま使いたい人は `/etc/cron.d/` や crontab にこれらの実行を１日に２回以上入れてやるとよいです。(１日２回以上なのは、ラスト１日からやっと期限切れの検出がはじまるから。)

```
% ubuntu の例:
server1$ sudo vim /etc/cron.d/mastodon
RAILS_ENV=production
12 3,6  * * * root cd /var/www/mastodon/ && docker-compose run --rm web rake mastodon:daily
```

１行目（mastodon:daily のある行) がその設定です。


ついでに、忘れやすい設定として SSL 証明書の更新があります。これも忘れると90日で期限が切れます。下記はletsencrypt を使った更新例です。他の証明書を使っているときはこの行も足しましょう。また、1.3.3以降の人もこれは入ってないので入れましょう。

```
12 4   23 * * root systemctl stop nginx && /opt/letsencrypt/letsencrypt-auto renew --force-renew && systemctl start nginx
```

また、これは古い画像キャッシュを捨てるというcronです。容量節約になるのでいいと思う。

```
42 3    * * 1 root cd /var/www/mastodon/ && docker-compose run --rm web rake mastodon:media:remove_remote
```

-----

![スライド4.PNG](https://qiita-image-store.s3.amazonaws.com/0/176345/6d086606-7bd1-9693-fa74-2eabd16ab642.png)

見えなくなる次のケースとして、受信側のサーバーでメンテナンスなどをしているときに、運悪くそのときに server2 からの user2 の投稿が行われてしまったケースです。server error 3xx や 4xx などが出ると即時に送信側で更新解除がなされ、購読していない状態に戻ってしまいます。受信側でもそれが起こったかどうかが直ぐには分からず、```
cat /var/log/nginx/access.log | grep /api/subscriptions/ | egrep '" [34][0-9][0-9] ' | grep -v '" 429 '
```などで調べると分かることが [Mastodonのサーバ間通信が切れた場合のリカバリ](http://qiita.com/clworld/items/bc3d8f97d38f2ba58492) で紹介されています。

この場合が起こったときは、受信側で `SubscribeService.new.call(Account.find({id2}))` を実行するか、期限切れ1日前以降に `mastodon:push:refresh` か `mastodon:daily` を実行することで復旧できます。こういうことが起こるので、メンテナンス中はちゃんと HTTP 503 Service Unavailable を出すようにした方が良いです。簡単に切り替える方法がこちらに書かれているので、メンテナンスのときにはこれをやればいいでしょう。→[Nginxでメンテナンスページに編集なしで即時切り替える方法](http://qiita.com/kotaroito/items/8b7d4b8ad6935edcdba8)

---
![スライド5.PNG](https://qiita-image-store.s3.amazonaws.com/0/176345/43f641df-c911-5b53-37ff-b370e7df9fbc.png)


最後に、MastodonのSSLの設定が悪いケースです。送信側、または、受信側のサーバーの .env.production の `LOCAL_HTTPS` が `false` になっているのに https を使っていると、リモートフォロー後に相手に投稿が届かない現象が起きます。この場合もローカルユーザーの投稿だけは見えるので、気づきにくいかもしれません。リモートフォローを使って初めて気付きます。`LOCAL_HTTPS` が `true` になっていることを確認して下さい。

```
server1$ sudo vim /var/www/mastodon/.env.production
# Federation
...
LOCAL_HTTPS=true
...
```

---------
参考
 - [Mastodon OStatus API の叩き方](https://gist.github.com/okapies/60d62d0df0163bbfb4ab09c1766558e8)
 - [Mastodon でリモートインスタンスから自分のトゥートがみえなくなることがある](http://cryks.hateblo.jp/entry/2017/04/18/125351)
 - [Mastodon インスタンス管理者が忘れてはならないたったひとつのこと](http://cryks.hateblo.jp/entry/2017/04/22/005755)
 - [Mastodonのサーバ間通信が切れた場合のリカバリ](http://qiita.com/clworld/items/bc3d8f97d38f2ba58492)