MastodonインスタンスをFreeBSDで立てたメモ (2023/5時点、Ver. 4.1.2)

Twitterの雲行きが怪しいと言われてしばらく経っていますが、今のところ何とか生きながらえてはいますね。
が、先が読めない状況は変わりませんので、GWの自由研究としてMastodonインスタンスを立ててみました。

# TL;DR
* Mastodon、Ruby、FreeBSDのバージョンに注意
* SSLアクセラレータを外に用意する場合はX-Forwardedの細工をお忘れなく
* ファイアウォールはちゃんと設定しましょう (精神衛生上非常に悪い)

# MastodonをFreeBSDで動かす意味
普通に考えれば、Mastodonインスタンスを立てるだけならばLinuxの母艦を用意してDocker使うのが手っ取り早いです。
ではなぜFreeBSDでやるか、というと…

* Linuxの哲学は「パフォーマンスを出すこと」に重きを置いており、個人的にはパフォーマンスよりも安定性を重視する哲学であるFreeBSDを信頼している
* ABIがあるとはいえ、compat-linux系のpkgを入れていなければ基本的にはLinuxバイナリが動作しないため任意コード実行系の脆弱性が発見された場合の耐性を期待できる
* (今回は搭載メモリ量の観点で見送ったが) ZFSが使える!!!
* とかゴチャゴチャ言ってるけど、要はBSDの方が好きなんだよ!!!<br>(AsiaBSDConに自費参加するような人なので)

といった感じです。

# 今回の構成
サーバ環境はVPSを使いますが、合理的に楽できるところは楽をします。

| 構成要素 | 使うもの | 備考 |
|---------|--------|--------|
| サーバ | さくらのVPS | 2Gプラン (3Core / メモリ2GB) |
| ドメイン名 | Gandi | IDN使いたかったので |
| DNS、SSLアクセラレータ | Cloudflare | VPSのリソースに楽をさせるため |

Cloudflareを使う構成は[軽量MastodonことPleromaインスタンスを立てたメモ - Lambdaカクテル](https://blog.3qe.us/entry/2023/04/09/220252)を参考にして採用しました。ただでさえVPSの計算能力がビミョーなので、SSL処理にCPUを持っていかれるのが癪なためCloudflareが肩代わりしてくれるのは有り難いです (トンネル処理があるので丸儲けとはなりませんが)。
またMastodonインスタンスとしてサーバを公開する関係上、Cloudflareがリバースプロクシとして動いてくれるお陰で若干秘匿性が高まるのも魅力です。
何より、SSL証明書の更新のような些事に振り回されないのは心の安定に繋がります。

https://blog.3qe.us/entry/2023/04/09/220252

# 構築作業に入る前に決めておくべきこと
* MastodonインスタンスのURL
  * Mastodonのサイトとして公開するURLは当たり前ですが「サーバ名 + ドメイン名」になります
  * 一方で、Cloudflareのトンネルを使う場合はサービス公開するURLはCNAMEで定義することになります。なので実体サーバ名も合わせて考えておきましょう<br>(あとで気が付きましたが、トンネルを張る先は別に同じドメイン名である必然もないので別ドメインにしたほうがセキュリティ上良いかもしれません)
* メール送信サーバ
  * 2000年代前半ならまだしも、2020年代になるとメールサーバの自前管理なんて苦痛でしかないので「どこのサービスを使ってメール送信するか」を考えておきましょう<br>(ただでさえ面倒なのに、今どきはSPFやらDMARCやらの面倒も…)
  * 今回はGandiでドメイン名を買ったので、Gandiのメール送信サーバを使い…たいのですが、現状うまく使えていません。なんでだろう…

# 最初のステップ: ドメイン名の取得
お好きなドメインレジストラで新規取得するなり、持っているドメインから権限委譲して切り出すなり、お好きなようにどうぞ。
ただしCloudflareのトンネルを使う場合は当該ドメインの管理をCloudflareに委ねる必要があるため、既に持っているドメインの中に新規ホスト作成の形で建てようとしている場合は要注意です。
…別のドメインでトンネルの定義をやった上で多段CNAMEしても動くのだろうか。よくわからない。

# 第2のステップ: DNS
Cloudflareのトンネルを使う場合はCloudflare一択になります。
まぁお好きな感じでDNS登録してあげてください。

# 第3のステップ: VPSの構築
OSのインストールは公式の「OS再インストール」に従い、FreeBSDをインストールします。

https://manual.sakura.ad.jp/vps/os-reinstall/iso-install/freebsd.html

## インストール後のこまごました設定
### IPv6の有効化
払い出されている情報をもとに、IPv6のアドレスを設定してあげましょう。
なお`defaultrouter`の`%vtnet0`を忘れがちなのでご注意 (1敗)。

```diff_conf:/etc/rc.conf
@@ -1,8 +1,10 @@
+# $Id: rc.conf,v 1.2 2023/05/02 15:53:53 yaizawa Exp $
 hostname="xxxxxxxx"
 keymap="us.kbd"
 ifconfig_vtnet0="inet xxx.xxx.xxx.xxx netmask 255.255.254.0"
 defaultrouter="xxx.xxx.xxx.xxx"
-ifconfig_vtnet0_ipv6="inet6 accept_rtadv"
+ifconfig_vtnet0_ipv6="inet6 xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx"
+ipv6_defaultrouter="fe80::1%vtnet0"
 sshd_enable="YES"
 ntpd_enable="YES"
 # Set dumpdev to "AUTO" to enable crash dumps, "NO" to disable
```

### シリアルコンソールの有効化
VNC経由だとキーボード配列とかで泣かされることがまれによくあるので、シリアルコンソールを有効にしてあげましょう。

https://gihyo.jp/admin/serial/01/bsd-yomoyama/0022

```diff_conf:/boot/loader.conf
@@ -0,0 +1,4 @@
+boot_multicons="YES"
+console="comconsole,vidconsole"
+comconsole_port="1016"
+comconsole_speed="9600"
```

```diff_conf:/etc/ttys
@@ -41,7 +41,7 @@
 ttyv8  "/usr/local/bin/xdm -nodaemon"  xterm   off secure
 # Serial terminals
 # The 'dialup' keyword identifies dialin lines to login, fingerd etc.
-ttyu0  "/usr/libexec/getty 3wire"      vt100   onifconsole secure
+ttyu0  "/usr/libexec/getty std.9600"   xterm   on secure
 ttyu1  "/usr/libexec/getty 3wire"      vt100   onifconsole secure
 ttyu2  "/usr/libexec/getty 3wire"      vt100   onifconsole secure
 ttyu3  "/usr/libexec/getty 3wire"      vt100   onifconsole secure
```

## 各種パッケージ導入の準備
TL;DRにも書きましたが、プロダクトバージョン間の互換性に注意しなければいけません。
ここでキモになるのがRubyのバージョンです。2023/5時点では
* FreeBSD 13.2-ReleaseにおけるRubyのデフォルトバージョン … 3.1
* pkgにあるMastodon 4.1.1におけるRubyのインストール要件 … >= 2.7.0, < 3.1.0

となっており、絶妙に噛み合いません。
このためpkgによるパッケージ管理を諦め、昔ながらのportsを活用する必要があります。

ちなみに今この文章を書いていて気が付きましたが、portsのMastodonが4.1.2に更新されており、こちらはインストール要件におけるRubyの最高バージョンが< 3.2.0に更新されていました。Rubyを3.0に落とすにしても、Mastodonを4.1.2に上げるにしても、どちらにせよportsを使わなければいけないことには変わりありませんが…。

### portsツリーの取得
ここはセオリー通りに[`portsnap(8)`](http://www.koganemaru.co.jp/cgi-bin/mroff.cgi?subdir=man&lc=1&cmd=&man=portsnap&dir=jpman-13.2.2%2Fman&sect=0)を使います。

```console
root@xxxxxxxx:~ # portsnap fetch extract
Looking up portsnap.FreeBSD.org mirrors... 5 mirrors found.
Fetching public key from dualstack.aws.portsnap.freebsd.org... done.
Fetching snapshot tag from dualstack.aws.portsnap.freebsd.org... done.
Fetching snapshot metadata... done.
Fetching snapshot generated at Wed May  3 09:41:58 JST 2023:
(中略)
/usr/ports/x11/yakuake/
/usr/ports/x11/yalias/
/usr/ports/x11/yeahconsole/
/usr/ports/x11/yelp/
/usr/ports/x11/zenity/
Building new INDEX files... done.
```

### 使用Rubyバージョンの指定 (結果的にやらなくても良かった)
portsのMastodonが4.1.2に更新されていることに気がついていなかったのでやってしまいましたが、Mastodon 4.1.1のインストール要件に合わせてportsで使用するRubyのバージョンを3.0に固定します。

```conf:/etc/make.conf
DEFAULT_VERSIONS+=ruby=3.0
```

### portsのインストールその1: portupgrade
`portinstall(1)`が便利なので、[`ports-mgmt/portupgrade`](https://wiki.freebsd.org/portupgrade)を入れます。
portsを素のままで使うと、入れたいportsごとにcdしてmake && make installしなきゃいけないし、個別にmake cleanしなきゃいけないし…なので。

```console
root@xxxxxxxx:~ # cd /usr/ports/ports-mgmt/portupgrade
root@xxxxxxxx:/usr/ports/ports-mgmt/portupgrade # make && make install && make clean
===> Building/installing dialog4ports as it is required for the config dialog
===>  Cleaning for dialog4ports-0.1.6_1
===>  License BSD2CLAUSE accepted by the user
(略)
```

入れ終わったら、念のために`portupgrade -a`を回しておきましょう。

### portsのインストールその2: Mastodonと愉快な仲間たち
portsを入れる下準備が揃いましたので、必要なportsを一気に入れます。
必要なportsは先駆者に倣うこととします。

* [Ports/net-im/mastodon - FreeBSD Wiki](https://wiki.freebsd.org/Ports/net-im/mastodon#CA-c5e2f62e6a6f9c476711b8b63732eaede0c71113_1)
* [Mastodon on FreeBSD · GitHub](https://gist.github.com/jsm222/df6bb7c045859cf1972015e78ad4cc4b)

※ いろいろ試行錯誤した影響でそのもののログが残っていないので、こちらのcodeブロックはイメージです

```console
root@xxxxxxxx:~ # portinstall ImageMagick7-nox11 ffmpeg libxml2 \
libxslt git protobuf node16 pkgconf autoconf bison libyaml readline \
lzlib libffi gdbm nginx redis postgresql15-server \
postgresql15-contrib py39-certbot libidn2 icu ruby sudo ruby-gems \
rubygem-bundler gmake mastodon
```

ちなみにnode、rustのビルドにかなり時間がかかります。こういう時はpkgが羨ましい。
(その代わりpkgよりも更新頻度が高いし、マニアックなarchの場合はpkgが揃ってなかったり半年更新だったりする)

### nginxの下準備
基本的にWiki通りです。
ポイントは次の2点。
* デフォルトで定義されているサーバ定義の削除
* Mastodonのnginx.confをinclude

```diff_conf:/usr/local/etc/nginx/nginx.conf
@@ -1,3 +1,4 @@
+# $Id$

 #user  nobody;
 worker_processes  1;
@@ -37,53 +38,53 @@

     #gzip  on;

-    server {
-        listen       80;
-        server_name  localhost;
+#    server {
+#        listen       80;
+#        server_name  localhost;
+#
+#        #charset koi8-r;
+#
+#        #access_log  logs/host.access.log  main;
+#
+#        location / {
+#            root   /usr/local/www/nginx;
+#            index  index.html index.htm;
+#        }
+#
+#        #error_page  404              /404.html;
+#
+#        # redirect server error pages to the static page /50x.html
+#        #
+#        error_page   500 502 503 504  /50x.html;
+#        location = /50x.html {
+#            root   /usr/local/www/nginx-dist;
+#        }
+#
+#        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
+#        #
+#        #location ~ \.php$ {
+#        #    proxy_pass   http://127.0.0.1;
+#        #}
+#
+#        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
+#        #
+#        #location ~ \.php$ {
+#        #    root           html;
+#        #    fastcgi_pass   127.0.0.1:9000;
+#        #    fastcgi_index  index.php;
+#        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
+#        #    include        fastcgi_params;
+#        #}
+#
+#        # deny access to .htaccess files, if Apache's document root
+#        # concurs with nginx's one
+#        #
+#        #location ~ /\.ht {
+#        #    deny  all;
+#        #}
+#    }

-        #charset koi8-r;

-        #access_log  logs/host.access.log  main;
-
-        location / {
-            root   /usr/local/www/nginx;
-            index  index.html index.htm;
-        }
-
-        #error_page  404              /404.html;
-
-        # redirect server error pages to the static page /50x.html
-        #
-        error_page   500 502 503 504  /50x.html;
-        location = /50x.html {
-            root   /usr/local/www/nginx-dist;
-        }
-
-        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
-        #
-        #location ~ \.php$ {
-        #    proxy_pass   http://127.0.0.1;
-        #}
-
-        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
-        #
-        #location ~ \.php$ {
-        #    root           html;
-        #    fastcgi_pass   127.0.0.1:9000;
-        #    fastcgi_index  index.php;
-        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
-        #    include        fastcgi_params;
-        #}
-
-        # deny access to .htaccess files, if Apache's document root
-        # concurs with nginx's one
-        #
-        #location ~ /\.ht {
-        #    deny  all;
-        #}
-    }
-
-
     # another virtual host using mix of IP-, name-, and port-based configuration
     #
     #server {
@@ -119,4 +120,5 @@
     #    }
     #}

+    include /usr/local/www/mastodon/dist/nginx.conf;
 }
```

またincludeされる側であるMastodonのnginx.confも編集します。
ポイントは次の通り。
※ Cloudflareのトンネルを活用する設定のため注意!!!

* Mastodonのサービス待受URLの入れ込み
* HTTPでの待受設定の削除<br>(HTTPSへのリダイレクト設定のため、無限リダイレクトになってしまう)
* HTTP over SSLでの待受設定のHTTPへの変更
* `X-Forwarded-Proto`の`https`決め打ち設定 **(超重要)**<br>(Cloudflare側でHTTP over SSLのエンドポイントを受け持つため、nginxに届く通信はすべてHTTP over SSLと見做す。これを入れておかないと、Rails側で無限リダイレクトになる)

https://qiita.com/shikumiya_hata/items/c72a0242e987b63d2f5c

https://mikamisan.hatenablog.com/entry/2017/09/28/230215

```diff_conf:/usr/local/www/mastodon/dist/nginx.conf
@@ -13,20 +13,22 @@

 proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=CACHE:10m inactive=7d max_size=1g;

-server {
-  listen 80;
-  listen [::]:80;
-  server_name example.com;
-  root /usr/local/www/mastodon/public;
-  location /.well-known/acme-challenge/ { allow all; }
-  location / { return 301 https://$host$request_uri; }
-}
+#server {
+#  listen 80;
+#  listen [::]:80;
+#  server_name example.com;
+#  root /usr/local/www/mastodon/public;
+#  location /.well-known/acme-challenge/ { allow all; }
+#  location / { return 301 https://$host$request_uri; }
+#}

 server {
-  server_name example.com;
+  server_name mstdn.xxxxxxxx.jp;
   # Uncomment these lines once you acquire a certificate:
   # listen 443 ssl http2;
   # listen [::]:443 ssl http2;
+  listen 80;
+  listen [::]:80;

   # ssl_protocols TLSv1.2 TLSv1.3;
   # ssl_ciphers HIGH:!MEDIUM:!LOW:!aNULL:!NULL:!SHA;
@@ -117,7 +119,7 @@
     proxy_set_header Host $host;
     proxy_set_header X-Real-IP $remote_addr;
     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
-    proxy_set_header X-Forwarded-Proto $scheme;
+    proxy_set_header X-Forwarded-Proto https;
     proxy_set_header Proxy "";

     proxy_pass http://streaming;
@@ -136,7 +138,7 @@
     proxy_set_header Host $host;
     proxy_set_header X-Real-IP $remote_addr;
     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
-    proxy_set_header X-Forwarded-Proto $scheme;
+    proxy_set_header X-Forwarded-Proto https;
     proxy_set_header Proxy "";
     proxy_pass_header Server;

```

急いで起動する必要もないですが、とりあえず上げときます。

```console
root@xxxxxxxx:~ # sysrc nginx_enable="YES"
nginx_enable:  -> YES
root@xxxxxxxx:~ # service nginx start
Performing sanity check on nginx configuration:
nginx: the configuration file /usr/local/etc/nginx/nginx.conf syntax is ok
nginx: configuration file /usr/local/etc/nginx/nginx.conf test is successful
Starting nginx.
```

### PostgreSQLの初期設定
ほぼWikiのままです。

```console
root@xxxxxxxx:~ # sysrc postgresql_enable="YES"
postgresql_enable:  -> YES
root@xxxxxxxx:~ # service postgresql initdb
The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.

The database cluster will be initialized with locales
  COLLATE:  C
  CTYPE:    C.UTF-8
  MESSAGES: C.UTF-8
  MONETARY: C.UTF-8
  NUMERIC:  C.UTF-8
  TIME:     C.UTF-8
The default text search configuration will be set to "english".

Data page checksums are disabled.

creating directory /var/db/postgres/data13 ... ok
creating subdirectories ... ok
selecting dynamic shared memory implementation ... posix
selecting default max_connections ... 100
selecting default shared_buffers ... 128MB
selecting default time zone ... Japan
creating configuration files ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... ok
syncing data to disk ... ok

initdb: warning: enabling "trust" authentication for local connections
You can change this by editing pg_hba.conf or using the option -A, or
--auth-local and --auth-host, the next time you run initdb.

Success. You can now start the database server using:

    /usr/local/bin/pg_ctl -D /var/db/postgres/data13 -l logfile start

root@xxxxxxxx:~ # service postgresql start
2023-05-03 02:04:51.053 JST [3225] LOG:  ending log output to stderr
2023-05-03 02:04:51.053 JST [3225] HINT:  Future log output will go to log destination "syslog".
root@xxxxxxxx:~ # service postgresql status
pg_ctl: server is running (PID: 3225)
/usr/local/bin/postgres "-D" "/var/db/postgres/data13"
```

また小規模運用であればMastodon ←→ PostgreSQL間の通信はTCP/IPよりもUNIX domain socketの方が安全かつ低負荷なので、UNIX domain socketを有効化します。
将来的にAWS RDSとかを使う場合にはちょっと面倒になりますが、5432/tcpを閉じられるので気分的にはかなり楽です。

https://www.servernote.net/article.cgi?id=unix-domain-socket-postgresql-redis

```diff_conf:/var/db/postgres/data13/postgresql.conf
@@ -64,10 +64,10 @@
 #port = 5432                           # (change requires restart)
 max_connections = 100                  # (change requires restart)
 #superuser_reserved_connections = 3    # (change requires restart)
-#unix_socket_directories = '/tmp'      # comma-separated list of directories
+unix_socket_directories = '/tmp'       # comma-separated list of directories
                                        # (change requires restart)
-#unix_socket_group = ''                        # (change requires restart)
-#unix_socket_permissions = 0777                # begin with 0 to use octal notation
+unix_socket_group = ''                 # (change requires restart)
+unix_socket_permissions = 0777         # begin with 0 to use octal notation
                                        # (change requires restart)
 #bonjour = off                         # advertise server via Bonjour
                                        # (change requires restart)
```

### Redisの設定
こちらは上がるようにするだけ。

```conole
root@xxxxxxxx:~ # sysrc redis_enable="YES"
redis_enable:  -> YES
root@xxxxxxxx:~ # service redis start
Starting redis.
```

### Mastodonの下準備
これもWikiのまんまです。
ちなみにRuby 3.1 + Mastodon 4.1.1の組み合わせだと`bundle install -j$(getconf _NPROCESSORS_ONLN)`のところでハマります。

```console
root@xxxxxxxx:~ # su -l mastodon
$ pwd
/usr/local/www/mastodon
$ yarn set version classic
Resolving classic to a url...
Downloading https://github.com/yarnpkg/yarn/releases/download/v1.22.19/yarn-1.22.19.js...
Saving it into /usr/local/www/mastodon/.yarn/releases/yarn-1.22.19.cjs...
Updating /usr/local/www/mastodon/.yarnrc...
Done!
$ bundle config deployment 'true'
$ bundle config without 'development test'
$ bundle install -j$(getconf _NPROCESSORS_ONLN)
Fetching gem metadata from https://rubygems.org/.........
Fetching https://github.com/ClearlyClaire/webpush.git
Fetching rake 13.0.6
Installing rake 13.0.6
Fetching zeitwerk 2.6.6
Fetching concurrent-ruby 1.2.0
Fetching minitest 5.17.0
(略)
The new methods to use are:
  - lock_ttl
  - lock_ttl=
  - lock_timeout
  - lock_timeout=
$ yarn install --pure-lockfile
yarn install v1.22.19
[1/6] Validating package.json...
[2/6] Resolving packages...
[3/6] Fetching packages...
[4/6] Linking dependencies...
(略)
  syscall: 'spawn node-gyp',
  path: 'node-gyp',
  spawnargs: [ 'rebuild' ]
[6/6] Cleaning modules...
Done in 42.62s.
```

### Mastodonの初期設定

setupを実行します。
ちょっと分かりにくいのが、`Your instance is identified by its domain name.`を訊かれていますが、**ドメイン名ではなくMastodonのサービス提供名**を入れる必要があります。なので最終的にブラウザに打ち込ませる名前を入れます。例えば`mstdn.example.org`みたいな感じ。

またPostgreSQLはUNIX domain socket (`/tmp/.s.PGSQL.5432`) を使うので、hostは`/tmp`を指定します。
そしてDomain Socketなのでパスワード認証も不要。
メールサーバは後ほど設定入れ込むので、一旦はsetupの既定値のまま置いておきます。なおこのままだとまだマトモには使えません…。

```console
$ RAILS_ENV=production bundle exec rake mastodon:setup
Your instance is identified by its domain name. Changing it afterward will break things.
Domain name: mstdn.xxxxxxxx.jp

Single user mode disables registrations and redirects the landing page to your public profile.
Do you want to enable single user mode? No

Are you using Docker to run Mastodon? No

PostgreSQL host: /tmp
PostgreSQL port: 5432
Name of PostgreSQL database: mastodon_production
Name of PostgreSQL user: mastodon
Password of PostgreSQL user:
Database configuration works! 🎆

Redis host: localhost
Redis port: 6379
Redis password:
Redis configuration works! 🎆

Do you want to store uploaded files on the cloud? No

Do you want to send e-mails from localhost? No
SMTP server: smtp.mailgun.org
SMTP port: 587
SMTP username:
SMTP password:
SMTP authentication: plain
SMTP OpenSSL verify mode: none
Enable STARTTLS: auto
E-mail address to send e-mails "from": Mastodon <notifications@mstdn.xxxxxxxx.jp>
Send a test e-mail with this configuration right now? no

This configuration will be written to .env.production
Save configuration? Yes

Now that configuration is saved, the database schema must be loaded.
If the database already exists, this will erase its contents.
Prepare the database now? Yes
Running `RAILS_ENV=production rails db:setup` ...


Created database 'mastodon_production'
Done!

The final step is compiling CSS/JS assets.
This may take a while and consume a lot of RAM.
Compile the assets now? Yes
Running `RAILS_ENV=production rails assets:precompile` ...


yarn install v1.22.19
[1/6] Validating package.json...
[2/6] Resolving packages...
success Already up-to-date.
Done in 0.68s.
Everything's up-to-date. Nothing to do
Done!

All done! You can now power on the Mastodon server 🐘

Do you want to create an admin user straight away? Yes
Username: admin
E-mail: admin@xxxxxxxx.jp
You can login with the password: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
You can change your password once you login.
```

ここまでできたら、rootに戻ってサービスを起動します。

```console
root@xxxxxxxx:~ # sysrc mastodon_sidekiq_enable=YES
mastodon_sidekiq_enable:  -> YES
root@xxxxxxxx:~ # sysrc mastodon_streaming_enable=YES
mastodon_streaming_enable:  -> YES
root@xxxxxxxx:~ # sysrc mastodon_web_enable=YES
mastodon_web_enable:  -> YES
root@xxxxxxxx:~ # service mastodon_sidekiq start
Starting mastodon_sidekiq.
root@xxxxxxxx:~ # service mastodon_streaming start
Starting mastodon_streaming.
root@xxxxxxxx:~ # service mastodon_web start
Starting mastodon_web.
```

### Cloudflaredの設定
ごめんなさい、ログがどっか行っちゃいました…。
流れとしては
* `portinstall`でインストールする
* rootユーザで`cloudflared`コマンドを使って初期設定
* `/root/.cloudflared/config.yaml`を用意する

なおCloudflaredの設定は公式も参照してみてください。

https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/tunnel-guide/local/

```yaml:/root/.cloudflared/config.yaml
tunnel: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX

credentials-file: /root/.cloudflared/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.json

ingress:
  - hostname: mstdn.xxxxxxxx.jp
    service: http://127.0.0.1:80
  - service: http_status:404
```

* サービス登録 & 起動

ここまで設定することで、[クライアント] ─ (HTTP over SSL)→ [CloudflareのHTTP over SSLのエンドポイント] ─ (Cloudflareトンネル)→ [Nginx] ─ (HTTP)→ [Mastodon (RoR)] という経路が完成します。

# pfの設定
クライアントからの接続がCloudflareのトンネル経由で来るので、Nginxで直接待ち受けする必要性が全くなくなりました。なのでpfで締めておきましょう。
ついでにSSHなども絞っておいたほうがセキュリティ上も精神衛生上も良いです。
何しろインターネット公開サーバだとやたらとアタックが来るので、authlogがあっという間に埋まっていきます…。

https://note.com/sqm/n/n69989a158182

```conf:/etc/rc.conf (追記分のみ)
pf_enable="YES"
```

↓`trusted`のところのIPアドレスはお使いの環境に合わせてください。ここに書かれたところからだけSSHアクセスを許容するように設定します。
(最初の方でシリアルコンソールを有効にしたのは、万が一ここの設定でトチった時に入る手段が無くならないようにする保険)
```conf:/etc/pf.conf
# $Id: pf.conf,v 1.3 2023/05/04 07:18:24 yaizawa Exp yaizawa $
#       $FreeBSD$
#       $OpenBSD: pf.conf,v 1.34 2007/02/24 19:30:59 millert Exp $
#
# See pf.conf(5) and /usr/share/examples/pf for syntax and examples.
# Remember to set gateway_enable="YES" and/or ipv6_gateway_enable="YES"
# in /etc/rc.conf if packets are to be forwarded between interfaces.

ext_if="vtnet0"
#int_if="int0"

trusted = "{127.0.0.0/16, XXX.XXX.XXX.XXX/32, XXXX:XXXX:XXXX:XXXX::/64}"

#table <spamd-white> persist

set skip on lo0

#scrub in

#nat-anchor "ftp-proxy/*"
#rdr-anchor "ftp-proxy/*"
#nat on $ext_if inet from !($ext_if) -> ($ext_if:0)
#rdr pass on $int_if proto tcp to port ftp -> 127.0.0.1 port 8021
#no rdr on $ext_if proto tcp from <spamd-white> to any port smtp
#rdr pass on $ext_if proto tcp from any to any port smtp \
#       -> 127.0.0.1 port spamd

#anchor "ftp-proxy/*"
#block in
#pass out

#pass quick on $int_if no state
#antispoof quick for { lo $int_if }

#pass in on $ext_if proto tcp to ($ext_if) port ssh
#pass in log on $ext_if proto tcp to ($ext_if) port smtp
#pass out log on $ext_if proto tcp from ($ext_if) to port smtp
#pass in on $ext_if inet proto icmp from any to ($ext_if) icmp-type { unreach, redir, timex }

# allow ssh from trusted network
block in proto tcp from any to any port = 22
pass in proto tcp from $trusted to any port = 22

# allow icmp
pass inet proto icmp all
pass inet6 proto ipv6-icmp all

# block http from internet
block in on $ext_if proto tcp to ($ext_if) port http

# block smtp from internet
block in on $ext_if proto tcp to ($ext_if) port smtp
block in on $ext_if proto tcp to ($ext_if) port submission

# Sakura Internet watchdog
pass in inet proto udp from any to any port 1985 keep state
pass in inet6 proto udp from any to any port 2029 keep state
pass in inet6 proto udp from any to any port 5353 keep state
```

# ToDo
* SMTP設定

# その他参考資料

https://qiita.com/neustrashimy/items/870769d7db4d95cde238

https://docs.gandi.net/ja/gandimail/index.html