MastodonをAWS+オレオレ証明書で動かしてみる

## 概要
自分の勉強がてらAWSインスタンスにMastodonを設定してみる。SSL必須のようなのだが、恒常的に動かすつもりがないので、オレオレ証明書でとりあえず動かしてみる。当然ながら、このまま利用するのはセキュリティ的に問題なのでご承知頂きたい。

## 参考サイト
以下のサイトの情報を参考にした。
[Dockerで雑にMastodonを起動する方法](https://qiita.com/zembutsu/items/fd52a504321dd5d6f0b8)
[AWSでMastodonインスタンスを作るまで。自分まとめ](https://qiita.com/shibafu/items/6f3646115acc16c748ad)
[今何かと話題のマストドン(mastodon)鯖を自分用に無料で立てる方法](https://jtwp470.hatenablog.jp/entry/2017/04/15/174036)
[Docker+MacでMastodonインスタンスをローカルで構築する](https://murabitoleg.com/mastodon/)
[自己署名証明書でnginxをSSL化](https://qiita.com/murank/items/41bb0cecfe8c8b65220e)

## 構築手順
### 1. AWSでEC2インスタンスを作成
手順は省略。
とりあえずOSはUbuntu18.04,インスタンスタイプはt2.medium,ボリュームサイズは20GBで設定。セキュリティグループはssh（22）とHTTPS（443）をインバウンドに設定しておく。

### 2. スワップファイルの作成
インスタンスタイプはt2.micro程度だとメモリが足りないようなので、スワップファイルを作成する。ただし、dockerコンテナのbuildに無茶苦茶かかるので、buildまではインスタンスタイプを高くしておく手もある。

```
$ sudo fallocate -l 4G /swapfile
$ sudo chmod 600 /swapfile
$ sudo mkswap /swapfile
$ sudo swapon /swapfile
$ echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
```
### 3.ミドルウェアのインストール
必要なパッケージを入れていく。

```sh
$ sudo apt-get update
$ sudo apt-get install -y python3-pip unzip docker.io nginx
$ sudo pip3 install docker-compose
```

### 4.Mastodonを設定する
MastodonをGithubから取得する。とりあえず、ホームディレクトリ直下へ。
バージョンによって出来ることの差が激しいので構築するバージョンを固定する。
作成時点で最新のtagであるv2.7.3に切り替える。

```sh
$ git clone https://github.com/tootsuite/mastodon.git
$ cd mastodon
$ git checkout -b v2.7.3 refs/tags/v2.7.3
```

ここで、永続化するためにdocker-compose.ymlの編集する例が多いが、v2.7.3では既に編集済みだったので、初期状態のままで大丈夫。

永続化用のディレクトリを作っておく。作成場所はdocker-compose.ymlと同階層。

```sh
$ mkdir postgres
$ mkdir redis
```
Dockerイメージを作成する。インスタンスタイプによってはすごく時間がかかる。

```sh
$ docker-compose build
```

その間にSSLの設定用に秘密鍵とオレオレ証明書を作成する。

### 5.SSLの設定
#### オレオレ証明書の作成
opensslを使って証明書を作成する。Ubuntu18.04にはプレインストールされていたが、なければインストールする。

オレオレ証明書はオレオレ認証局（ca）から発行する。そのため、まずは認証局を作る必要がある。認証局の秘密鍵（ca.key）を作成してから、認証局の証明書（ca.crt）を作成する。
証明書の有効期限は10年 (3650日）とする。証明書作成は対話式なので好きな値を入れよう。未設定でも構わない。

```sh
$ mkdir certs
$ cd certs
$ openssl genrsa -out ca.key 2048
$ openssl req -new -x509 -days 3650 -key ca.key -out ca.crt
```
次にngnixに設定するオレオレサーバ証明書（mastodon.crt）を作成する。
秘密鍵（mastodon.key）を作って、その後認証局に依頼するための署名要求ファイル（mastodon.csr）を作成する。ここも対話式になるので、少なくともOUとCNは入力する。（ここではOU=example, CN=example.com）
最後に署名要求ファイルに認証局が署名してサーバ証明書（mastodon.crt）を作る。

```sh
$ openssl genrsa -out mastodon.key 2048
$ openssl req -new -key mastodon.key -out mastodon.csr
$ openssl x509 -req -days 3650 -CA ca.crt -CAkey ca.key -CAcreateserial -in mastodon.csr -out mastodon.crt
```

#### nginxの設定
nginxの設定ファイルを編集する。先ほど作成したサーバ証明書と秘密鍵は
ssl_certificate,ssl_certificate_keyにパス付で指定する。

```sh
$ sudo vim /etc/nginx/sites-enabled/default
```

以下の内容に入れ替える。

```conf:/etc/nginx/sites-enabled/default
map $http_upgrade $connection_upgrade {
  default upgrade;
  ''      close;
}

server {
  listen 80;
  listen [::]:80;
  server_name localhost:3000;
  root /home/ubuntu/mastodon/live/public;
  # Useful for Let's Encrypt
  #location /.well-known/acme-challenge/ { allow all; }
  #location / { return 301 https://$host$request_uri; }
}

server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  server_name localhost:3000;

  ssl_protocols TLSv1.2;
  ssl_ciphers HIGH:!MEDIUM:!LOW:!aNULL:!NULL:!SHA;
  ssl_prefer_server_ciphers on;
  ssl_session_cache shared:SSL:10m;

  ssl_certificate     /home/ubuntu/mastodon/certs/mastodon.crt;
  ssl_certificate_key /home/ubuntu/mastodon/certs/mastodon.key;

  keepalive_timeout    70;
  sendfile             on;
  client_max_body_size 80m;
  root /home/ubuntu/mastodon/live/public;

  gzip on;
  gzip_disable "msie6";
  gzip_vary on;
  gzip_proxied any;
  gzip_comp_level 6;
  gzip_buffers 16 8k;
  gzip_http_version 1.1;
  gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

  add_header Strict-Transport-Security "max-age=31536000";

  location / {
    try_files $uri @proxy;
  }

  location ~ ^/(emoji|packs|system/accounts/avatars|system/media_attachments/files) {
    add_header Cache-Control "public, max-age=31536000, immutable";
    try_files $uri @proxy;
  }
  
  location /sw.js {
    add_header Cache-Control "public, max-age=0";
    try_files $uri @proxy;
  }

  location @proxy {
    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 https;
    proxy_set_header Proxy "";
    proxy_pass_header Server;

    proxy_pass http://127.0.0.1:3000;
    proxy_buffering off;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    tcp_nodelay on;
  }

  location /api/v1/streaming {
    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 https;
    proxy_set_header Proxy "";

    proxy_pass http://127.0.0.1:4000;
    proxy_buffering off;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    tcp_nodelay on;
  }

  error_page 500 501 502 503 504 /500.html;
}
```

nginxを再起動する。

```sh
$ sudo systemctl start nginx
```

### 6.再びMastodonの設定
以下のコマンドを2回打って、出力された文字列をどこかに記録しておく。

```sh
$ cd ~/mastodon
$ docker-compose run --rm web rake secret
```

次に環境設定ファイルを作る。テンプレートがあるのでそれをコピーして作成する。

```sh
$ cp .env.production.sample .env.production
$ sudo vi .env.production
```
以下のSECRET_KEY_BASE, OTP_SECRETに先ほどの文字列を設定する。

```conf:.env.production
20 LOCAL_DOMAIN=localhost:3000
34 # Application secrets
35 # Generate each with the `RAILS_ENV=production bundle exec rake secret` task     (`docker-compose run --rm web rake secret` if you use docker compose)
36 SECRET_KEY_BASE=
37 OTP_SECRET=
```
Databaseを作成する。

```sh
$ docker-compose run --rm web rails db:migrate
```

アセットを作成する。

```sh
$ docker-compose run --rm web rails assets:precompile
```

Mastodonのコンテナを起動する。docker-compose psできちんと立ち上がっているか確認しよう。

```sh
$ docker-compose up
$ docker-compose ps
        Name                      Command                   State                 Ports
-------------------------------------------------------------------------------------------------
mastodon_db_1          docker-entrypoint.sh postgres    Up (healthy)
mastodon_redis_1       docker-entrypoint.sh redis ...   Up (healthy)
mastodon_sidekiq_1     /tini -- bundle exec sidekiq     Up
mastodon_streaming_1   /tini -- yarn start              Up (unhealthy)   127.0.0.1:4000->4000/tcp
mastodon_web_1         /tini -- bash -c rm -f /ma ...   Up (unhealthy)   127.0.0.1:3000->3000/tcp
```

### Mastodonに接続する
以上で完了。ChromeかEdgeあたりのWebブラウザで接続する。
FireFoxはオレオレ証明書のサイトは見れないので他のブラウザを使う。

```
https://EC2インスタンスのパブリックDNS or パブリックIP/about
```