Misskeyサーバーにnginxを導入して「プデチゲ」を入れる

この記事は約34分で読めます。
当サイトには、広告及びアフィリエイトリンクが含まれ、それによって収益を得ています。記事の内容やリンクには、プロモーションが含まれる場合があります。詳細については、プライバシーポリシーをご覧ください。

先日から再び某ポークランチョンミートによる行為が確認されるようになってきたため、いい加減にnginxとプデチゲ(スパム対策フィルター)を導入して対策を行うことにしました。ちなみにcloudflaredを導入している自宅鯖でやっています。

ちなみに悩みながら書いていたので、最終的には採用していない部分を含みます。導入自体は最後のまとめまで飛ばしても導入できるはずです。

budae-jjigae/README.md at master · Interstellar-Relay-Community/budae-jjigae
ActivityPub Spam Filter. Contribute to Interstellar-Relay-Community/budae-jjigae development by creating an account on G...

手順(試行錯誤の過程)

Docker版nginxをリバースプロキシの第一候補として検討したものの、Dockerだとネットワーク周りで色々と面倒そうなので、とりあえず今回はローカルにnginxを導入することにしました。どっちにしろプデチゲはDockerで導入することになりますが、Misskey自体はDockerで動かしているしまぁいいでしょう。

nginxのインストール

GPGキーを追加したりするいつものやつをします。公式のをコピペして実行するだけです。

Bash
sudo apt install curl gnupg2 ca-certificates lsb-release ubuntu-keyring # 既にあるなら不要

curl "https://nginx.org/keys/nginx_signing.key" | gpg --dearmor \
    | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
    
gpg --dry-run --quiet --no-keyring --import --import-options import-show /usr/share/keyrings/nginx-archive-keyring.gpg # 確認のコマンド

echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
"http://nginx.org/packages/ubuntu" `lsb_release -cs` nginx" \
    | sudo tee /etc/apt/sources.list.d/nginx.list
    
sudo apt update
sudo apt install nginx

これでインストールできるはずです。

nginxの設定

Bash
vim /etc/nginx/conf.d/misskey.conf

どうやらnginxの設定ファイルは/etc/nginx/conf.d/以下にあるらしいので、そこにmisskey.confという名前のファイルを新規作成します。ファイルの内容は以下の通り(このあと書き換えるのでまだ見るだけでOK)。

misskey.conf(Misskey Hub版)
# For WebSocket
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

proxy_cache_path /tmp/nginx_cache levels=1:2 keys_zone=cache1:16m max_size=1g inactive=720m use_temp_path=off;

server {
    listen 80;
    listen [::]:80;
    server_name #あなたのドメイン;

    # For SSL domain validation
    root /var/www/html;
    location /.well-known/acme-challenge/ { allow all; }
    location /.well-known/pki-validation/ { allow all; }
    location / { return 301 https://$server_name$request_uri; }
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name #あなたのドメイン;

    ssl_session_timeout 1d;
    ssl_session_cache shared:ssl_session_cache:10m;
    ssl_session_tickets off;

    # To use Let's Encrypt certificate
    #ssl_certificate     /etc/letsencrypt/live/example.tld/fullchain.pem;
    #ssl_certificate_key /etc/letsencrypt/live/example.tld/privkey.pem;
    ssl_certificate #公開鍵のpublic.pemの保存先ディレクトリをここに書く
    ssl_certificate_key #シークレットキーのprivkey.pemの保存先ディレクトリをここに書く
    ssl_trusted_certificate #Origin CA証明書(origin_ca_ecc_root.pemとか)の保存先ディレクトリをここに書く

    # To use Debian/Ubuntu's self-signed certificate (For testing or before issuing a certificate)
    #ssl_certificate     /etc/ssl/certs/ssl-cert-snakeoil.pem;
    #ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;

    # SSL protocol settings
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    ssl_stapling on;
    ssl_stapling_verify on;

    # Change to your upload limit
    client_max_body_size 4000m;

    # Proxy to Node
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_http_version 1.1;
        proxy_redirect off;

        # If it's behind another reverse proxy or CDN, remove the following.
        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;

        # For WebSocket
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        # Cache settings
        proxy_cache cache1;
        proxy_cache_lock on;
        proxy_cache_use_stale updating;
        proxy_force_ranges on;
        add_header X-Cache $upstream_cache_status;
    }
}

今回はCloudflareのSSL証明書を使うので、dash.cloudflare.comの今回使うドメインからSSL/TLSからオリジンサーバーの項目を選び、証明書を発行しておきます(この時、RSAで作ったかECCで作ったか覚えておく)。privkeyに相当するものはこの作成時しか見られないので、忘れずに保存しておきましょう(まぁ使い始める前なら発行し直せばいいのですが…)。

そして、オリジン証明書の内容を上記ファイル(misskey.conf)で設定したディレクトリのpublic.pemに、プライベートキーをprivkey.pemにコピペして保存します。

次に、[warn] "ssl_stapling" ignored, issuer certificate not found for certificateという警告に対処します1。まず、ここにアクセスし、さっき作った証明書でRSAを使ったかECCを使ったかを思い出し、同じ方のリンクを右クリックでコピーします。コピーしたURLをターミナルに貼り付け、対象のマシンで

Bash
wget https://developers.cloudflare.com/ssl/static/origin_ca_ecc_root.pem

とすれば、現在のディレクトリに新たに.pem形式のファイルがダウンロードされます。これをmisskey.confssl_trusted_certificateとして指定すればよいです。

ここまでできたら次に進みます。

Bash
sudo nginx -t # 設定の読み込み
sudo systemctl enable nginx # nginxの自動起動を設定
sudo systemctl restart nginx # nginxの再起動

いけるか…?

プデチゲ

Misskeyのdocker-compose.yml(最近はcompose.ymlなんだっけ?)に、以下を追記します。なお、追記してからdocker-compose downはできないので、コンテナを止めてからやりましょう。

docker-compose.yml
  budae:
    image: perillamint/budae-jjigae:latest
    restart: always
    command:
      - "/usr/bin/budae-jjigae"
      - "--backend"
      - "web:3000"
    networks:
      - external_network
      - internal_network
    environment:
      - RUST_LOG=info
    ports:
      - '127.0.0.1:7000:3000'
    depends_on:
      - web

特に設定をしなければ、次に行うnginxの設定をしなくてもそのまま通信はできるはずです。

nginxのmisskey.confに以下を追記します。ただこれはカスタムしなきゃいけないらしい。どうすればいいんだろう…?

misskey.conf(プデチゲ用追記部分)
    location /inbox {
        try_files $uri @budae;
    }

    location ~ /users/(.*)/inbox {
        try_files $uri @budae;
    }

    location @budae {
        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;
        # Avoid 504 HTTP Timeout Errors
        proxy_connect_timeout       605;
        proxy_send_timeout          605;
        proxy_read_timeout          605;
        send_timeout                605;
        keepalive_timeout           605;

        proxy_pass http://127.0.0.1:7000;
        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;
    }
misskey.conf
# For WebSocket
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

proxy_cache_path /tmp/nginx_cache levels=1:2 keys_zone=cache1:16m max_size=1g inactive=720m use_temp_path=off;

server {
    listen 80;
    listen [::]:80;
    server_name #あなたのドメイン;

    # For SSL domain validation
    root /var/www/html;
    location /.well-known/acme-challenge/ { allow all; }
    location /.well-known/pki-validation/ { allow all; }
    location / { return 301 https://$server_name$request_uri; }
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name #あなたのドメイン;

    ssl_session_timeout 1d;
    ssl_session_cache shared:ssl_session_cache:10m;
    ssl_session_tickets off;

    # To use Let's Encrypt certificate
    #ssl_certificate     /etc/letsencrypt/live/example.tld/fullchain.pem;
    #ssl_certificate_key /etc/letsencrypt/live/example.tld/privkey.pem;
    ssl_certificate #公開鍵のpublic.pemの保存先ディレクトリをここに書く
    ssl_certificate_key #シークレットキーのprivkey.pemの保存先ディレクトリをここに書く
    ssl_trusted_certificate #Origin CA証明書(origin_ca_ecc_root.pemとか)の保存先ディレクトリをここに書く

    # To use Debian/Ubuntu's self-signed certificate (For testing or before issuing a certificate)
    #ssl_certificate     /etc/ssl/certs/ssl-cert-snakeoil.pem;
    #ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;

    # SSL protocol settings
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    ssl_stapling on;
    ssl_stapling_verify on;

    # Change to your upload limit
    client_max_body_size 4000m;

    # Proxy to Node
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_http_version 1.1;
        proxy_redirect off;

        # If it's behind another reverse proxy or CDN, remove the following.
        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;

        # For WebSocket
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        # Cache settings
        proxy_cache cache1;
        proxy_cache_lock on;
        proxy_cache_use_stale updating;
        proxy_force_ranges on;
        add_header X-Cache $upstream_cache_status;
    }
}

location /inbox {
        try_files $uri @budae;
    }

    location ~ /users/(.*)/inbox {
        try_files $uri @budae;
    }

    location @budae {
        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;
        # Avoid 504 HTTP Timeout Errors
        proxy_connect_timeout       605;
        proxy_send_timeout          605;
        proxy_read_timeout          605;
        send_timeout                605;
        keepalive_timeout           605;

        proxy_pass http://127.0.0.1:7000;
        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;
    }

終わったら

Bash
sudo nginx -t
sudo systemctl restart nginx

を実行します。とりあえず、Misskeyにアクセスできないことは無かったけど、効いてもなさそうで…→これはCloudflare Zero TrustのPublic Hostnameがポート3000を指してて、nginxを通ってなかったからっぽい

ただ設定を変えると今度はMisskeyにエラーが起こる…

解決

cloudflaredについてふんわりとしか分かってなかったことに気づき、ネットワークの図を書いてみたら解決できました。

cloudflaredはホストコンピューターからCloudflare CDNまでをトンネリングする技術です。ということはつまり、ホストの中は全部HTTPでよくて、cloudflaredのPublic HostnameをHTTPSにすると暗号化されてエラーが出ることになります。

これまでは、cloudflaredの設定で、Public HostnameをHTTPSにしたりしていましたが、これは普通にHTTPで良い(でもHTTPSはじゃあなんであるんだろう?これは要調査)。

次にnginxですが、Misskey Hubで公開されているmisskey.confファイルはcloudflaredを使わない想定で書かれていて、しかもNAT超えとかしなくても普通にインターネットに出られるレンタルサーバーとかそういう環境で使うもののようでした。ということは、SSL化の設定もおかしいことになります。cloudflaredを使えばホストまでは暗号化でき、ホスト内部は安全なはずだから、もうこの時点で暗号化については考えなくて良い(やらなくていい)。
これに従って、misskey.confを次のように書き換えました。要するに、ポート80でnginxがリッスンするようにし、SSL関係の設定は全てコメントアウトしました。いっそのこと削除してしまってもいいはずですが、一応残しておくことにしました。

Bash
# For WebSocket
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

proxy_cache_path /tmp/nginx_cache levels=1:2 keys_zone=cache1:16m max_size=1g inactive=720m use_temp_path=off;

#server {
#    listen 80;
#    listen [::]:80;
#    server_name #あなたのドメイン;

    # For SSL domain validation
    # root /var/www/html;
    # location /.well-known/acme-challenge/ { allow all; }
    # location /.well-known/pki-validation/ { allow all; }
    # location / { return 301 https://$server_name$request_uri; }
#}

server {
    # listen 443 ssl;
    # listen [::]:443 ssl;
    listen 80;
    listen [::]:80;
    http2 on;
    server_name #あなたのドメイン

    # ssl_session_timeout 1d;
    # ssl_session_cache shared:ssl_session_cache:10m;
    # ssl_session_tickets off;

    # To use Let's Encrypt certificate
    #ssl_certificate     /etc/letsencrypt/live/example.tld/fullchain.pem;
    #ssl_certificate_key /etc/letsencrypt/live/example.tld/privkey.pem;
    #ssl_certificate #公開鍵のpublic.pemの保存先ディレクトリをここに書く
    #ssl_certificate_key #シークレットキーのprivkey.pemの保存先ディレクトリをここに書く
    #ssl_trusted_certificate #Origin CA証明書(origin_ca_ecc_root.pemとか)の保存先ディレクトリをここに書く

    # To use Debian/Ubuntu's self-signed certificate (For testing or before issuing a certificate)
    #ssl_certificate     /etc/ssl/certs/ssl-cert-snakeoil.pem;
    #ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;

    # SSL protocol settings
    # ssl_protocols TLSv1.2 TLSv1.3;
    # ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
    # ssl_prefer_server_ciphers off;
    # ssl_stapling on;
    # ssl_stapling_verify on;

    # Change to your upload limit
    # client_max_body_size 4000m;

    # Proxy to Node
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_http_version 1.1;
        proxy_redirect off;

        # If it's behind another reverse proxy or CDN, remove the following.
        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;

        # For WebSocket
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        # Cache settings
        proxy_cache cache1;
        proxy_cache_lock on;
        proxy_cache_use_stale updating;
        proxy_force_ranges on;
        add_header X-Cache $upstream_cache_status;
    }
}

location /inbox {
        try_files $uri @budae;
    }

    location ~ /users/(.*)/inbox {
        try_files $uri @budae;
    }

    location @budae {
        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;
        # Avoid 504 HTTP Timeout Errors
        proxy_connect_timeout       605;
        proxy_send_timeout          605;
        proxy_read_timeout          605;
        send_timeout                605;
        keepalive_timeout           605;

        proxy_pass http://127.0.0.1:7000;
        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;
    }

次に、cloudflaredの設定をします。といっても、Public Hostnameのポート番号を、上記misskey.confに合致するように変更するだけでOKです。プロトコルはHTTPのままにします。

これで正常にプデチゲにinboxの通信が割り振られるようになり、ポークランチョンミートの投稿が弾かれるようになりました。プデチゲに感謝…

設定方法まとめ

1. サーバーにnginxをインストール

Bash
sudo apt install curl gnupg2 ca-certificates lsb-release ubuntu-keyring # 既にあるなら不要

curl "https://nginx.org/keys/nginx_signing.key" | gpg --dearmor \
    | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
    
gpg --dry-run --quiet --no-keyring --import --import-options import-show /usr/share/keyrings/nginx-archive-keyring.gpg # 確認のコマンド

echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
"http://nginx.org/packages/ubuntu" `lsb_release -cs` nginx" \
    | sudo tee /etc/apt/sources.list.d/nginx.list
    
sudo apt update
sudo apt install nginx

nginxの設定ファイルを作り(misskey.conf)、それにnginxの設定を追記

なお、レンタルサーバーなどで既にnginxを使用している場合は異なるので注意する。

misskey.confの内容
# For WebSocket
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

proxy_cache_path /tmp/nginx_cache levels=1:2 keys_zone=cache1:16m max_size=1g inactive=720m use_temp_path=off;

server {
    # listen 443 ssl;
    # listen [::]:443 ssl;
    listen 80;
    listen [::]:80;
    http2 on;
    server_name #あなたのドメイン;

    # Proxy to Node
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_http_version 1.1;
        proxy_redirect off;

        # If it's behind another reverse proxy or CDN, remove the following.
        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;

        # For WebSocket
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        # Cache settings
        proxy_cache cache1;
        proxy_cache_lock on;
        proxy_cache_use_stale updating;
        proxy_force_ranges on;
        add_header X-Cache $upstream_cache_status;
    }
}

location /inbox {
        try_files $uri @budae;
    }

    location ~ /users/(.*)/inbox {
        try_files $uri @budae;
    }

    location @budae {
        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;
        # Avoid 504 HTTP Timeout Errors
        proxy_connect_timeout       605;
        proxy_send_timeout          605;
        proxy_read_timeout          605;
        send_timeout                605;
        keepalive_timeout           605;

        proxy_pass http://127.0.0.1:7000;
        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;
    }

以下のコマンドを実行する。

Bash
sudo nginx -t # 設定の読み込み
sudo systemctl enable nginx # nginxの自動起動を設定
sudo systemctl restart nginx # nginxの再起動

docker-compose.ymlにbudae-jjigaeの項目を追記

適用したいMisskeyサーバーのdocker-compose.ymlがあるディレクトリに移動し、Dockerコンテナをdocker-compose downなどで止めた後、docker-compose.yml(今はcompose.yml)を編集する。編集後のファイルは以下の通りです(versionとか色々古い書き方です)。

docker-compose.yml
version: "3"

services:
  web:
    build: .
    restart: always
    links:
      - db
      - redis
#     - es
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    ports:
      - "3000:3000"
    networks:
      - internal_network
      - external_network
    volumes:
      - ./files:/misskey/files
      - ./.config:/misskey/.config:ro

  redis:
    restart: always
    image: redis:7-alpine
    networks:
      - internal_network
    volumes:
      - ./redis:/data
    healthcheck:
      test: "redis-cli ping"
      interval: 5s
      retries: 20
      
  db:
    restart: always
    image: postgres:15-alpine
    shm_size: 1G
    networks:
      - internal_network
    env_file:
      - .config/docker.env
    volumes:
      - ./db:/var/lib/postgresql/data
    healthcheck:
      test: "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"
      interval: 5s
      retries: 20

  budae:
    image: perillamint/budae-jjigae:latest
    restart: always
    command:
      - "/usr/bin/budae-jjigae"
      - "--backend"
      - "web:3000"
    networks:
      - external_network
      - internal_network
    environment:
      - RUST_LOG=info
    ports:
      - '127.0.0.1:7000:3000'
    depends_on:
      - web
      
  networks:
    internal_network:
     internal: true
    external_network:

できたら止めていたサーバーを起動する。

Bash
docker-compose up -d

cloudflaredのPublic Hostnameを編集

cloudflaredの設定を開き、TunnelsのPublic Hostnameを編集する。具体的には、nginxが待ち受け(リッスン)しているポート番号にする。プロトコルはHTTPのまま。ポークランチョンミートが弾かれていればOK!

参考情報

  1. nginxでCloudflareのオリジン証明書のエラーが出るので対処する https://zenn.dev/waya0125/articles/d00a4fdc891aa1 ↩︎

病気療養中のガジェットオタクです。PC、スマホからオーディオ、家電まで、デジモノ・IT系中心に自分の興味のあるものならなんでも記事にします。誤字脱字など、ミスの報告歓迎です。
お問い合わせは、当ブログのお問い合わせフォームにお願いします。レビュー依頼など、各種ご依頼承っています。

でじぃをフォローする↓
サーバー
スポンサーリンク
でじぃをフォローする↓
タイトルとURLをコピーしました