4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Misskeyのおひとりさまサーバを建ててみた

Last updated at Posted at 2023-10-20

はじめに

六葩くるです。
いままでおひとりさまサーバつくるつくる詐欺して全然作ってなかったので、そろそろ #MisskeyInstallBattle に参戦しようかと思い作成してみました。
これはその見様見真似な構築手順の備忘録になります。
Linuxなにもわからない™のにちょっとだけめんどくさいことしているので、どこかの箇所が誰かの参考になればと。

今回の構成

手順やソフトウェアについては公式のインストール方法(Dockerなし)をある程度参考にした上で、Fedoraで実施している。

VPS環境

KAGOYA CLOUDを使用。

項目
OS Fedora 39 Beta
CPU 2コア
メモリ 2 GB
ストレージ 200GB

DNS

Gandi.netで取得。

作業時点の各ソフトウェアのバージョン

Nginxは一応QUIC対応にするためビルドする。

ソフトウェア バージョン
Misskey v2023.10.1
Node.js v20.8.1
PostgreSQL 15.4
Redis 7.2.1
FFmpeg 6.0
Nginx 1.25.2 w/ quictls

オブジェクトストレージ

Wasabiを使用。

証明書

ZeroSSLを使用。

メールボックス

さくらのメールボックスを使用。

使用しないもの

  • Docker
  • Cloudflare

OSインストール

VPSインスタンスの作成

KAGOYA CLOUD VPS コントロールパネルにログインする。
テンプレートにないパッケージを使用するため、FedoraのISOイメージをアップロードする。
FedoraのダウンロードページのISOの実URLをコピペする。
ISOイメージは1日で消えるので名前はなんでもいい。

rezjr97uum9u96g.png

他のOSイメージを使用する場合
KAGOYAのイメージはBIOSで実行されるため、UEFIでしか動作しないカスタムISO(CentOS Stream 9など)は起動できません。

インスタンス作成画面に移動し、ISOイメージにアップロードしたイメージを選択。
スペックにCPU 2コア / メモリ 2GB / ストレージ 200GB のものを選択。(今回はオブジェクトストレージを使用するので、大容量のものでなくてもよかったかもしれない)
virtioはONのままでOK。
コンソールログインパスワードとインスタンス名(VPS管理用のためなんでもいい)を入力し、作成する。
インスタンスが作成されたらインスタンス情報に移動し、付与されたIPv4・IPv6を確認しておく。

DNSレコード登録

正引き

Gandi.netにログインし、対象のドメインにタイプAレコード(IPv4)とタイプAAAAレコード(IPv6)を追加する。
TTLはデフォルトのままでOK。

逆引き

KAGOYAのインスタンス情報の逆引き設定のところにドメイン名を入力。

インストレーション

コンソールを起動すると、ポップアップでコンソールが表示される。
ブラウザによってはポップアップを開く際に一度ブロックされるので、許可してあげた上でもう一度起動すること。
あとわりとすぐ画面がタイムアウトするので、都度F5やCtrl+Rでリロードするか、ウィンドウを開き直すこと。

Fedoraのインストール画面が表示されていることを確認する。

言語設定

日本語にすると多分ちょっと面倒なのでEnglishのまま続行。

hfyk9gapjgb3t23.png

INSTALLATION SUMMARY

以下、設定する順に記載する。

uxxknkz79537ytd.png

Keyboard

自身で使用しているキーボードに応じて English (US) または Japanese を選択。
(実際にはほぼSSHで操作するため最初のパスワード入力以外はあまり関係ない)

Language Support

一応、 日本語(日本) を追加する。

Software Selection

Fedora Server Edition が選択されていることを確認。

Installation Destination

Customを選択しDone。
パーティション作成画面に移動するのでまずはLVMで自動作成(Click here to create them automatically)する。
BIOS Boot, /boot, / の3つのパーティションが自動で作成される。
メモリ2GBだとMisskeyをビルドするのに足りないので、「+」でswapを作成する。今回はせっかく大容量の方のストレージにしてるのでちょっと多めに8GiBで作った。
あとは / パーティションのDesired Capacityをめっちゃ多い量で記入してUpdateすると、残りの全容量が自動で割り当てられる。

aj86nb8ie8atm7z.png

Network & Hostname

(Ethernet (ens3) が選択されている状態で)Configureを押下。

IPv4 Settings
項目
Method Manual
Address インスタンス画面のIPv4のIP
Netmask インスタンス画面のサブネット
Gateway インスタンス画面のゲートウェイ
DNS servers インスタンス画面のDNS、
またはお好きなDNSサーバのIPをカンマ(,)形式で入力
IPv6 Settings
項目
Method Manual
Address インスタンス画面のIPv6のIP
Prefix 64
Gateway IPv6の先頭4つ部分に::1を付加 (xxxx:yyyy:zzzz:wwww::1)
DNS servers お好きなDNSサーバのIPをカンマ(,)形式で入力

入力が完了したら保存して有効化。DNSレコード登録が反映されていれば、Current host nameのところにドメイン名が表示される。
kagoyaなんちゃらが表示された場合は、使用したいドメイン名をHost Nameのところに入力してApply。

Time & Date

日本のあたりをクリック。Asia/Tokyoになることと、Network Timeが有効になっていることを確認。

User Creation

User name と Password を入力。チェックボックスに2つともチェックが入っていることを確認。

Begin Installation

以上の設定でBegin Installationが押せるようになるので、インストールを開始。
完了したらReboot Systemで再起動を実施。
インストールされたOSが無事上がってくることを確認する。

SSH接続設定

手持ちのターミナルソフトでSSH接続し、セキュリティ強化のためSSHのポート番号変更と公開鍵認証への変更を行う。

公開鍵認証化

鍵の追加

  • クライアント側で鍵を作成する(手順省略)
  • サーバにSCPなどを用いて作成した公開鍵をリモートに転送する(手順省略)
  • 以下コマンドでauthorized_keysに鍵を登録
mkdir ~/.ssh
cat (持ってきたファイル) >> ~/.ssh/authorized_keys
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
rm (持ってきたファイル)
  • 公開鍵認証で対応する秘密鍵を指定し、SSH接続できることを確認する

パスワードログイン、rootログインの禁止

  • /etc/ssh/sshd_config.d/内にファイルを作成し、追記
/etc/ssh/sshd_config.d/99-sshd_config.conf
PermitRootLogin no
PasswordAuthentication no
  • sudo systemctl restart sshd.serviceでSSHサーバを再起動し、今のセッションを維持したまま別セッションでログインできることを確認する

ポート番号変更

ポート番号追加

sshd_config変更
  • /etc/ssh/sshd_config.d/内のファイルに追記
/etc/ssh/sshd_config.d/99-sshd_config.conf
+ Port 22
+ Port (変更後のポート番号)
PermitRootLogin no
PasswordAuthentication no
ファイアウォール追加
  • sshのサービスファイルを複製する
sudo cp /usr/lib/firewalld/services/ssh.xml /etc/firewalld/services/ssh-alt.xml
  • ssh-alt.xmlを変更
/etc/firewalld/services/ssh-alt.xml
<?xml version="1.0" encoding="utf-8"?>
<service>
- <short>SSH</short>
+ <short>SSH-ALT</short>
  <description>Secure Shell (SSH) is a protocol for logging into and executing commands on remote machines. It provides secure encrypted communications. If you plan on accessin    g your machine remotely via SSH over a firewalled interface, enable this option. You need the openssh-server package installed for this option to be useful.</description>
- <port protocol="tcp" port="22"/>
+ <port protocol="tcp" port="(変更後のポート番号)"/>
</service>
  • 以下でファイアウォールルール追加
# ssh-altを反映
sudo firewall-cmd --reload
sudo firewall-cmd --get-services | grep ssh-alt

# ssh-altをファイアウォールルールに追加
sudo firewall-cmd --permanent --add-service=ssh-alt
sudo firewall-cmd --reload

# 確認
sudo firewall-cmd --list-all
SELinuxルール追加
  • 以下実行
sudo semanage port --add --type ssh_port_t --proto tcp (変更後のポート番号)
  • sudo systemctl restart sshd.serviceでSSHサーバを再起動し、変更後のポート番号でログインできることを確認する

ポート番号削除

sshd_config変更
  • /etc/ssh/sshd_config.d/内のファイルを変更
/etc/ssh/sshd_config.d/99-sshd_config.conf
- Port 22
Port (変更後のポート番号)
PermitRootLogin no
PasswordAuthentication no
ファイアウォール削除
  • 以下でファイアウォールルール削除
# sshをファイアウォールルールから削除
sudo firewall-cmd --permanent --remove-service=ssh
sudo firewall-cmd --reload

# 確認
sudo firewall-cmd --list-all
  • sudo systemctl restart sshd.serviceでSSHサーバを再起動し、デフォルトのポート番号でログインできないことを確認する

その他ファイアウォール変更

Cockpit無効化

sudo firewall-cmd --permanent --remove-service=cockpit
sudo firewall-cmd --reload
sudo systemctl disable --now cockpit.socket

HTTP通信を通せるようにする

ファイアウォールルール

http3が定義されていない場合は443/udpadd-portで通すようにする。

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --permanent --add-service=http3
sudo firewall-cmd --reload

SELinuxルール

sudo setsebool -P httpd_can_network_connect 1
sudo setsebool -P httpd_can_network_relay 1
sudo semanage port --add --type http_port_t --proto udp 443

fail2ban

一応いれておく。

sudo dnf install fail2ban
sudo systemctl enable --now fail2ban.service

Nginx構築

NginxをQUIC対応にする場合、パッケージで入るnginxだと組み込みのOpenSSL(QUIC非対応)で動作するためQUICが動かない。
そのためQUIC対応のSSLパッケージを組み込んだnginxをビルドする必要がある。
また、Wasabiのストレージはパブリックアクセスができない(以下参照)ため、AWSの認証アクセス機能を何らかの方法で組み込む必要がある。

今回はOpenSSLの代わりにquictlsを使い、AWSの認証用にngx_aws_authモジュールを使用する。

基本パッケージのインストール

ビルドに必要な汎用的なパッケージを入れる。個々のビルドで必要なパッケージは都度記載する。

sudo dnf groupinstall 'Development Tools'
sudo dnf install cmake

quictlsの導入

kTLSの有効化

TLSの処理をカーネルに任せるとパフォーマンスが向上するらしいのでちょっと入れてみる。

sudo modprobe tls

必要なパッケージのインストール

sudo dnf install perl wget gcc gcc-arm-linux-gnu tcp_wrappers

ソースダウンロード

cd /usr/local/src
sudo git clone https://github.com/quictls/openssl.git
cd openssl
sudo git submodule update --init

インストール

sudo ./Configure enable-ktls
sudo make
sudo make install

ライブラリのパスを通す

/usr/local/lib64とかのパスがなんか通っていないので、通す必要がある。

  • /etc/ld.so.conf.d/内にファイルを作成し、追記
/etc/ld.so.conf.d/99-ld.so.conf
/usr/local/lib
/usr/local/lib64
  • sudo ldconfigで反映
  • openssl versionを実行し、バージョンに+quicがついていることを確認する

quictlsのソースはNginxのビルド時に用いるため、残しておくこと。

AWS proxyモジュールの用意

Wasabiはパブリックアクセスができないのに、SigV4での認証もうまくいかないことがあるらしい。というかうまくいかなかった。

仕方がないのでSigV2で認証できるようにするため、モジュールをV2のブランチの方に変更する。

cd /usr/local/src
sudo git clone https://github.com/anomalizer/ngx_aws_auth.git
cd ngx_aws_auth
sudo git checkout AuthV2

ただしこれ古いプログラムなので、このままだと今のNginxのビルド時に動かない。
以下を実行して事前にコードを一部書き換えておく。

sudo sed -i -e 's/\(ngx_http_variable_unknown_header(\)\(val\)/\1r, \2/' ngx_http_aws_auth_module.c

Brotli圧縮モジュールの用意

通信量が減るらしい?のでせっかくなので入れてみる。

cd /usr/local/src
sudo git clone https://github.com/google/ngx_brotli.git
cd ngx_brotli
sudo git submodule update --init

Nginxのインストール

ユーザー追加

sudo useradd --system --comment 'Nginx web server' --home-dir /var/local/lib/nginx --shell /sbin/nologin nginx

必要なパッケージのインストール

無駄にconfigureオプションを盛ったため必要なパッケージが増えている。不要ならオプションを削ってもいいかも。

sudo dnf install pcre-devel libxslt-devel gd-devel GeoIP-devel gperftools

ソースダウンロード

公式のダウンロードサイトからバージョンを確認し、該当するものをダウンロードする。

cd /usr/local/src
sudo curl -O https://nginx.org/download/nginx-1.25.2.tar.gz
sudo tar xvf nginx-1.25.2.tar.gz
cd nginx-1.25.2

configure実行

試行錯誤の結果、以下のオプションで実行した。詳細の説明はあまりよくわかってないので割愛。

sudo ./configure \
--prefix=/var/local/nginx \
--sbin-path=/usr/local/sbin/nginx \
--modules-path=/usr/local/lib64/nginx/modules \
--conf-path=/usr/local/etc/nginx/nginx.conf \
--error-log-path=/var/local/log/nginx/error.log \
--http-log-path=/var/local/log/nginx/access.log \
--http-client-body-temp-path=/var/local/lib/nginx/tmp/client_body \
--http-proxy-temp-path=/var/local/lib/nginx/tmp/proxy \
--http-fastcgi-temp-path=/var/local/lib/nginx/tmp/fastcgi \
--http-uwsgi-temp-path=/var/local/lib/nginx/tmp/uwsgi \
--http-scgi-temp-path=/var/local/lib/nginx/tmp/scgi \
--pid-path=/run/nginx.pid \
--lock-path=/run/lock/nginx.lock \
--user=nginx \
--group=nginx \
--with-threads \
--with-file-aio \
--with-debug \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_v3_module \
--with-http_realip_module \
--with-http_addition_module \
--with-http_xslt_module=dynamic \
--with-http_image_filter_module=dynamic \
--with-http_geoip_module=dynamic \
--with-http_sub_module \
--with-http_dav_module \
--with-http_flv_module \
--with-http_mp4_module \
--with-http_gunzip_module \
--with-http_gzip_static_module \
--with-http_auth_request_module \
--with-http_random_index_module \
--with-http_secure_link_module \
--with-http_degradation_module \
--with-http_slice_module \
--with-http_stub_status_module \
--with-http_perl_module=dynamic \
--with-mail=dynamic \
--with-mail_ssl_module \
--with-stream=dynamic \
--with-stream_realip_module \
--with-stream_geoip_module=dynamic \
--with-stream_ssl_module \
--with-stream_ssl_preread_module \
--with-google_perftools_module \
--with-compat \
--with-pcre \
--with-pcre-jit \
--with-openssl=/usr/local/src/openssl \
--with-openssl-opt=enable-ktls \
--add-module=/usr/local/src/ngx_brotli \
--add-module=/usr/local/src/ngx_aws_auth \
--with-cc-opt='-g -O2 -m64 -mtune=generic -fPIC -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=3 -Wp,-D_GLIBCXX_ASSERTIONS -fstack-protector-all -fstack-clash-protection -fcf-protection -grecord-gcc-switches -I/usr/local/src/openssl/include' \
--with-ld-opt='-no-pie -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -Wl,-Bsymbolic-functions -L/usr/local/src/openssl/ssl'

make

ビルドしてインストール。

sudo make
sudo make install

SELinuxコンテキスト修正

unconfined_service_tとかになっているとSELinuxを動かしている意味があんまりない気がするので、dnfで入れたときと同じような形になるようにコンテキストを修正する。

関連フォルダの作成

sudo mkdir -p /usr/local/etc/nginx/conf.d
sudo mkdir -p /usr/local/etc/nginx/default.d
sudo mkdir -p /usr/local/lib64/nginx/modules
sudo mkdir -p /var/local/cache/nginx
sudo mkdir -p /var/local/lib/nginx/tmp/client_body
sudo mkdir -p /var/local/lib/nginx/tmp/proxy
sudo mkdir -p /var/local/lib/nginx/tmp/fastcgi
sudo mkdir -p /var/local/lib/nginx/tmp/uwsgi
sudo mkdir -p /var/local/lib/nginx/tmp/scgi

コンテキスト変更

sudo semanage fcontext -a -f f -s system_u -t httpd_exec_t '/usr/local/sbin/nginx'
sudo semanage fcontext -a -f a -s system_u -t httpd_config_t '/usr/local/etc/nginx(/.*)?'
sudo semanage fcontext -a -f a -s system_u -t httpd_sys_content_t '/var/local/nginx/html(/.*)?'
sudo semanage fcontext -a -f a -s system_u -t httpd_var_lib_t '/var/local/lib/nginx(/.*)?'
sudo semanage fcontext -a -f a -s system_u -t httpd_log_t '/var/local/log/nginx(/.*)?'
sudo semanage fcontext -a -f a -s system_u -t httpd_cache_t '/var/local/cache/nginx(/.*)?'

# 確認
sudo semanage fcontext -l | grep nginx

コンテキスト反映

sudo restorecon -vF /usr/local/sbin/nginx
sudo restorecon -vFR /usr/local/etc/nginx
sudo restorecon -vFR /var/local/nginx/html
sudo restorecon -vFR /var/local/lib/nginx
sudo restorecon -vFR /var/local/log/nginx
sudo restorecon -vFR /var/local/cache/nginx

nginx.confの編集

ビルド時に生成されるnginx.confはパスとかがデフォルトのままなので、ちゃんと書き換えてあげないと動かない。
なんかもうめっちゃ変えた™(dnfで入るnginxの形式にある程度合わせただけ)のでファイルを丸々以下のものに置き換えること。

/usr/local/etc/nginx/nginx.conf
user  nginx;
worker_processes  auto;
error_log  /var/local/log/nginx/error.log notice;
pid  /run/nginx.pid;

# Load dynamic modules.
include  /usr/local/lib64/nginx/modules/*.conf;

events {
    worker_connections  1024;
}

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/local/log/nginx/access.log  main;

    sendfile             on;
    tcp_nopush           on;
    keepalive_timeout    65;
    types_hash_max_size  4096;

    charset utf-8;

    include              /usr/local/etc/nginx/mime.types;
    default_type         application/octet-stream;

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    include  /usr/local/etc/nginx/conf.d/*.conf;

    server {
        listen       80;
        listen       [::]:80;
        server_name  _;
        root         /var/local/nginx/html;

        # Load configuration files for the default server block.
        include  /usr/local/etc/nginx/default.d/*.conf;

        #error_page  404              /404.html;
        #location = /404.html {
        #}

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
        }
    }
}

sudo nginx -tの結果がsuccessfulになることを確認する。

systemdのユニットファイル作成

以下のファイルを作成する。

(🗒2023-10-22 追記: Restart=alwaysを入れないとOS再起動時にこけるので下部のmisskey.serviceの記載と同じものを追記しました。)

/etc/systemd/system/nginx.service
[Unit]
Description=The NGINX HTTP and reverse proxy server
After=network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target

[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/bin/rm -f /run/nginx.pid
ExecStartPre=/usr/local/sbin/nginx -t
ExecStart=/usr/local/sbin/nginx
ExecReload=/usr/local/sbin/nginx -s reload
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true
TimeoutSec=60
StandardOutput=journal
StandardError=journal
SyslogIdentifier=nginx
Restart=always

[Install]
WantedBy=multi-user.target

sudo systemctl enable --now nginx.serviceでで起動、常駐化し、ブラウザからhttp://(ドメイン名)でアクセス可能なことを確認する。

証明書発行

以下を参考に、ZeroSSLの証明書をlegoを用いて発行する。

ZeroSSL API Keyの入手

  • ZeroSSLのアカウントを登録する
  • 登録完了後、DeveloperタブよりAPI Keyを確認する

legoのインストール

スクリプト用にJSONプロセッサも入れる。

sudo dnf install golang-github-acme-lego jq

証明書発行スクリプトの作成

以下のスクリプトを作成する。(場所はわりとどこでもOK)

/usr/local/bin/lego-invoke.sh
#!/bin/bash

API_KEY=(ZeroSSLで取得したAPIキー)
EMAIL=(ZeroSSLに登録したメールアドレス?)
DOMAIN=(認証したいドメイン名)
WEBROOT_PATH=/var/local/nginx/html
CERTKEY_PATH=/etc/zerossl

JSON=$(curl -s -X POST "https://api.zerossl.com/acme/eab-credentials?access_key=$API_KEY")
EAB_KID=$(echo "$JSON" | jq -r .eab_kid)
EAB_HMAC_KEY=$(echo "$JSON" | jq -r .eab_hmac_key)

lego --http --http.webroot "$WEBROOT_PATH" --path "$CERTKEY_PATH" --eab --kid "$EAB_KID" --hmac "$EAB_HMAC_KEY" --server "https://acme.zerossl.com/v2/DV90" --domains "$DOMAIN" --email "$EMAIL" --accept-tos "$1"

証明書発行

sudo chmod a+x /usr/local/bin/lego-invoke.sh
sudo lego-invoke.sh run

Misskey用のnginx設定の用意

公式のNginxの設定をベースに、QUIC対応、kTLS対応、Brotli圧縮対応を行ったもの。
オブジェクトストレージへのプロキシの記述は後ほど追記する。

/usr/local/etc/nginx/conf.d/misskey.conf
# For WebSocket
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

proxy_cache_path /var/local/cache/nginx 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/local/nginx/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 quic reuseport;
    listen [::]:443 quic reuseport;
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name (ドメイン名);

    # QUIC settings
    http3 on;
    http3_hq on;
    http2 on;
    quic_retry on;
    ssl_early_data on;

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

    # To use ZeroSSL certificate
    ssl_certificate     /etc/zerossl/certificates/(ドメイン名).crt;
    ssl_certificate_key /etc/zerossl/certificates/(ドメイン名).key;

    # 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;

    # 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 TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_8_SHA256:TLS_AES_128_CCM_SHA256: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;
    ssl_conf_command Options KTLS;

    # Change to your upload limit
    client_max_body_size 80m;

    # brotli setting
    brotli on;
    brotli_comp_level 7;
    brotli_static on;
    brotli_types application/atom+xml application/javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-opentype application/x-font-truetype application/x-font-ttf application/x-javascript application/xhtml+xml application/xml audio/mpeg font/eot font/opentype font/otf font/truetype image/svg+xml image/vnd.microsoft.icon image/x-icon image/x-win-bitmap text/css text/javascript text/plain text/xml;

    # Proxy to S3 storage

    # 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;

        # To negotiate with QUIC directly
        add_header Alt-Svc 'h3=":443"; h3-29=":443"; ma=86400';
    }
}

  • sudo nginx -tの結果がsuccessfulになることを確認する。
  • sudo systemctl restart nginx.serviceでサービスを再起動する。
  • ブラウザからhttps://(ドメイン名)でアクセスし、ZeroSSLで認証されていることを確認する。(現時点ではリクエスト結果は502エラーでOK)

Misskey構築準備

ユーザー作成

sudo useradd --system --comment 'Misskey app server' --home-dir /opt/misskey --shell /bin/bash misskey

Node.jsのインストール

# インストール
sudo dnf install https://rpm.nodesource.com/pub_20.x/nodistro/repo/nodesource-release-nodistro-1.noarch.rpm
sudo dnf install nodejs --setopt=nodesource-nodejs.module_hotfixes=1

# バージョン確認
node -v

# corepack enable
sudo corepack enable

PostgreSQLの導入

インストール

sudo dnf install postgresql postgresql-server postgresql-upgrade
sudo postgresql-setup --initdb

# バージョン確認
postgres --version

# 実行
sudo systemctl enable --now postgresql.service

ユーザーとデータベースの作成

sudo -u postgres psql
CREATE ROLE misskey LOGIN PASSWORD '(pgsqlのmisskeyユーザ用のパスワード)';
CREATE DATABASE (データベース名) OWNER misskey;
\q

接続設定

このままだとパーミッション的にデータベースアクセスがうまくいかないので、all allの行のMETHODtrustに変更する。

/var/lib/pgsql/data/pg_hba.conf
# TYPE  DATABASE        USER            ADDRESS                 METHOD

# "local" is for Unix domain socket connections only
- local   all             all                                     peer
+ local   all             all                                     trust
# IPv4 local connections:
- host    all             all             127.0.0.1/32            ident
+ host    all             all             127.0.0.1/32            trust
# IPv6 local connections:
- host    all             all             ::1/128                 ident
+ host    all             all             ::1/128                 trust
# Allow replication connections from localhost, by a user with the
# replication privilege.
local   replication     all                                     peer
host    replication     all             127.0.0.1/32            ident
host    replication     all             ::1/128                 ident

DBのチューニング

PGTuneを用いることで、使用しているマシンのスペックに合わせてPostgreSQL用のチューニング設定を得ることができる。
今回は以下の通り入力し、出力結果を確認した。

項目
DB version 15
Total Memory (RAM) 2GB
Number of CPUs 2
Number of Connections 100

得られた出力結果をもとに、/var/lib/pgsql/data/postgresql.confの対象行を編集していく。
順序がバラバラなので注意。

設定値 行番号
max_connections 65行目
shared_buffers 127行目
effective_cache_size 403行目
maintenance_work_mem 140行目
checkpoint_completion_target 238行目
wal_buffers 226行目
default_statistics_target 426行目
random_page_cost 395行目
effective_io_concurrency 187行目
work_mem 138行目
huge_pages 129行目
min_wal_size 241行目
max_wal_size 242行目

編集が完了したら、systemctl restart postgresql.serviceで再起動する。

Redisのインストール

sudo dnf install redis
sudo systemctl enable --now redis

# バージョン確認
redis-server --version

FFmpegのインストール

sudo dnf install https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm
sudo dnf install ffmpeg

# バージョン確認
ffmpeg -version

jemallocの確認

メモリアロケータをjemallocに変えるとMisskey v13のメモリ問題が緩和されるらしいのでjemallocを使う想定にする。
以下のコマンドで入っていることを確認する。

sudo find / -name "*jemalloc*"

入っていない場合はsudo dnf install jemallocで入れる。

Misskeyのインストール

Misskeyのダウンロード

sudo mkdir /opt/misskey
sudo chown misskey:misskey /opt/misskey
sudo -iu misskey
cd /opt
git clone --recursive https://github.com/misskey-dev/misskey.git
cd misskey
git checkout master
git submodule update --init
NODE_ENV=production pnpm install --frozen-lockfile

default.ymlの設定

以下のファイルを作成する。

/opt/misskey/.config/default.yml
# Misskey URL
url: https://(ドメイン名)/
port: 3000

# PostgreSQL setting
db:
  host: localhost
  port: 5432
  db  : (PostgreSQLのデータベース名)
  user: (PostgreSQLのユーザー名)
  pass: (PostgreSQLのパスワード)

# Redis setting
redis:
  host: localhost
  port: 6379

# ID type
id: 'aid'

# syslog setting
syslog:
  host: localhost
  port: 514

# proxy setting
proxyRemoteFiles: true

# ActivityPub setting
signToActivityPubGet: true

Misskeyのビルド

cd /opt/misskey
NODE_ENV=production pnpm run build
pnpm run init

起動テスト

NODE_ENV=production pnpm run start

ブラウザでアクセスできることを確認する。

systemdのユニットファイル作成

以下のファイルを作成する。

/etc/systemd/system/misskey.service
[Unit]
Description=Misskey daemon

[Service]
Type=simple
User=misskey
ExecStart=/usr/bin/npm start
WorkingDirectory=/opt/misskey
Environment="NODE_ENV=production"
Environment="LD_PRELOAD=/usr/lib64/libjemalloc.so.2"
TimeoutSec=60
StandardOutput=journal
StandardError=journal
SyslogIdentifier=misskey
Restart=always

[Install]
WantedBy=multi-user.target

sudo systemctl enable --now misskey.serviceで起動、常駐化する。

Misskeyの設定

初期設定

  • 最初の管理ユーザーを作成
  • 「コントロールパネル」→「設定」→「モデレーション」
    • 「誰でも新規登録できるようにする」のチェックを外す
    • 「アカウント登録にメールアドレスを必須にする」のチェックを入れる
    • 「利用規約URL」「プライバシーポリシーURL」を入力する
      • 使わない場合でもなんらかの値をいったん入れないと保存できないので、いったん入れて保存後に空欄にして保存し直す
    • 「予約ユーザー名」に「media」を追加
  • 「コントロールパネル」→「設定」→「全般」
    • 「サーバー名」「管理者の名前」「管理者のメールアドレス」「運営者情報URL」を入力する
      • 使わない場合でも(以下略)
  • 「コントロールパネル」→「設定」→「その他」
    • 「サーバーのマシン情報を公開する」のチェックを入れる
      • 反映にはMisskeyの再起動が必要

メール配信機能

独自ドメインのがそれっぽいのでGandi.netのメールボックス機能を使おうと思っていたが、じきに無料じゃなくなるらしい。

有料になるくらいなら、それよりは安いさくらのメールボックスでメールサーバのみホストし、ドメインは移管せずに利用することにした。

メールアドレスの追加

  • さくらインターネットにログインし、「契約中のサービス一覧」→「さくらのメールボックス」よりコントロールパネルを開く
  • 「メール」→「メール一覧」→「新規追加」より、メール配信に使用したいユーザー名とパスワードでメールアドレスを追加する
  • 「(ユーザー名)@ドメイン」が追加されることを確認

ドメインの追加

  • 「ドメイン/SSL」→「ドメイン/SSL」→「ドメイン新規追加」を選択
  • 「他社で取得したドメインを移管せずに使う」を選択
  • 「他社で取得した独自ドメインの追加」のところに契約しているドメインを入力し、追加

DNSレコード設定

  • 追加したドメインの「設定」に、「SPFレコードの使用」にチェックが入っていることを確認し、「ドメインコントロールパネル」に遷移する
  • 「登録済みゾーン」→「ゾーン >」にあるTXTレコード(SPFレコードのやつ)のテキストをコピー
  • Gandi.netに移動し、「DNSレコード」の「詳細表示」よりレコードを直接編集
+ @ 10800 IN MX 0 xxx.sakura.ne.jp.(xxxはSPFレコードについているwwwなんちゃらを入れる)
+ @ 10800 IN TXT (コピーしてきたSPFレコード)
- @ 10800 IN MX 10 spool.mail.gandi.net.
- @ 10800 IN MX 50 fb.mail.gandi.net.
- @ 10800 IN TXT "v=spf1 include:_mailcust.gandi.net ?all"
- _imap._tcp 10800 IN SRV 0 0 0   .
- _imaps._tcp 10800 IN SRV 0 1 993 mail.gandi.net.
- _pop3._tcp 10800 IN SRV 0 0 0   .
- _pop3s._tcp 10800 IN SRV 10 1 995 mail.gandi.net.
- _submission._tcp 10800 IN SRV 0 1 465 mail.gandi.net.
- gm1._domainkey 10800 IN CNAME gm1.gandimail.net.
- gm2._domainkey 10800 IN CNAME gm2.gandimail.net.
- gm3._domainkey 10800 IN CNAME gm3.gandimail.net.
- webmail 10800 IN CNAME webmail.gandi.net.

Misskey側の設定

  • 「コントロールパネル」→「設定」→「メールサーバー」
    • 「メール配信機能を有効化する」にチェックを入れる
  • 以下の通り設定
項目
メールアドレス (ユーザー名)@(独自ドメイン名)
ホスト (さっきのMXレコードに入れたやつ、末尾の.は不要)
ポート 587
ユーザー名 (メールアドレスと同じ)
パスワード (メールアカウントのパスワード)
  • 保存し、配信テストを実施し、テストメールが届くことを確認する
    • 数分くらい時間かかるかも

オブジェクトストレージの設定

Wasabiの設定

(🗒2023-11-02 追記: ログ用プレフィックスでフォルダを切れるっぽいので1つのフォルダに入るよう修正しました。)

  • Wasabi コンソールにログインし、Bucketを作成
    • 一応プロパティはバケットのログだけ有効にし、ログ用プレフィックスに「log/」を指定しておいた

リージョンにTokyoを指定した場合はap-northeast-1ではなく「ap-northeast-1-ntt」が、
Osakaを指定した場合はap-northeast-2ではなく「ap-northeast-2-ntt」が正しいリージョン名になる。

  • 「アクセスキー」→「Create Access Key」より、ルートユーザーでキーを作成
    • アクセスキーと秘密鍵の両方を控えておく

Nginxの設定

オブジェクトストレージへのプロキシを以下のように追記する。

/usr/local/etc/nginx/conf.d/misskey.conf
    # Proxy to S3 storage
    location /media/ {
        rewrite /(.*) /(バケット名)/$1 break;

        # 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;

        # AWS compatible authentication with Signature Version 2
        proxy_pass http://s3.(リージョン名).wasabisys.com;
        proxy_set_header Host s3.(リージョン名).wasabisys.com;

        aws_access_key (アクセスキー);
        aws_secret_key (秘密鍵);
        s3_bucket (バケット名);
        chop_prefix /(バケット名);

        proxy_set_header Authorization $s3_auth_token;
        proxy_set_header x-amz-date $aws_date;
    }
  • sudo nginx -tの結果がsuccessfulになることを確認する。
  • sudo systemctl restart nginx.serviceでサービスを再起動する。

Misskeyの設定

  • 「コントロールパネル」→「設定」→「オブジェクトストレージ」より「オブジェクトストレージを使用」にチェックを入れ、以下の通り設定する。
項目
Base URL (Misskeyと同じURL)
Bucket (バケット名)
Prefix media
Endpoint (proxy_passのところに書いたURL)
Region (リージョン名)
Access Key (アクセスキー)
Secret Key (秘密鍵)
SSLを使用する 有効
Proxyを利用する 有効
アップロード時に'public-read'を設定する 無効
s3ForcePathStyle 有効

保存後、ドライブにファイルを上げ、読み書きができることを確認する。

その他の機能

ServiceWorker

以下の手順でPublic KeyとPrivate Keyを生成し、設定する。

sudo npm install web-push -g
web-push generate-vapid-keys

Botプロテクション

一般公開しないため不要な気もするが、一応hCaptchaを利用する。

  • アカウントを登録し、サイトキーとシークレットキーを控えておき、Misskeyで入力

DeepL API

不要な気もするが、一応使ってみる。

  • DeepLでアカウントを登録
  • 「アカウント」→「アカウント」→「DeepL APIで使用する認証キー」のキーを控えておき、Misskeyで入力

プロキシアカウントとかロール割り当てとかについて

事前に何でもいいのでノートを投稿してからじゃないと検索に出てこないらしいね。

まとめ

まだたったいまサーバを建てたばかりなので、これからおひとりさまに住む感じです。
あと運用面についてはまだ全然調べられてないので、気が向いたら追々。

🗒2024/10/13 追記: アップデート自動化について

おひとりさまサーバーはもう閉鎖してしまったのですが、当初書いてたアップデートのコードが1年放置してても一応動作していたっぽいので、ついでの備忘録として以下に記しておきます。ただしずっと動く保証はしません。

自動アプデスクリプト

以下のファイルを配置し、実行権限をつける。

/usr/bin/local/auto-update-misskey.sh
#!/bin/bash

# enable beta version
GET_BETA=false

# rollback function
pnpm_rollback () {
  sudo -u misskey git switch --detach "refs/tags/$1"
  sudo -u misskey git submodule update --init
  env NODE_ENV=production sudo -Eu misskey pnpm install --frozen-lockfile
  sudo -u misskey pnpm run clean
  env NODE_ENV=production sudo -Eu misskey pnpm run build
  sudo -u misskey pnpm run migrate
}

# check certification renewal
/usr/local/bin/lego-invoke.sh renew

# package upgrade
if ! dnf upgrade --assumeyes; then exit 1; fi

# misskey update check
cd /opt/misskey
if ! sudo -u misskey git fetch; then exit 1; fi

LOCAL_VERSION=$(sudo -u misskey git ls-remote --tags 2>/dev/null | grep ^$(sudo -u misskey git show --no-patch --format=%H) | cut -d '/' -f 3)
if "$GET_BETA"; then
  REMOTE_VERSION=$(sudo -u misskey git for-each-ref refs/tags/ --sort='-committerdate' --count=1 --format='%(refname:short)')
else
  REMOTE_VERSION=$(sudo -u misskey git ls-remote --tags 2>/dev/null | grep ^$(sudo -u misskey git ls-remote --heads 2>/dev/null | grep refs/heads/$(sudo -u misskey git branch --contains | cut -d ' ' -f 2)\$ | awk '{print $1}') | cut -d '/' -f 3)
fi

# misskey upgrade
if [ "$LOCAL_VERSION" != "$REMOTE_VERSION" ]; then
  systemctl stop misskey
  if "$GET_BETA"; then
    sudo -u misskey git switch --detach "refs/tags/$REMOTE_VERSION"
  else
    sudo -u misskey git switch master
    sudo -u misskey git pull
  fi
  sudo -u misskey git submodule update --init

  env NODE_ENV=production sudo -Eu misskey pnpm install --frozen-lockfile
  if [ $? -eq 0 ] ; then
    sudo -u misskey pnpm run clean
    env NODE_ENV=production sudo -Eu misskey pnpm run build
    if [ $? -eq 0 ] ; then
      sudo -u misskey pnpm run migrate
    else
      pnpm_rollback "$LOCAL_VERSION"
    fi
  else
    pnpm_rollback "$LOCAL_VERSION"
  fi
  
  # restart misskey or restart os
  if ! dnf needs-restarting --reboothint; then
    systemctl reboot
  else
    systemctl restart misskey
  fi

# restart os
elif ! dnf needs-restarting --reboothint; then
  systemctl reboot
fi

タイマー化

以下の2つのファイルを配置する。

/etc/systemd/system/check-update-misskey.service
[Unit]
Description=Check Update Misskey

[Service]
Type=oneshot
Restart=no
ExecStart=/usr/local/bin/auto-update-misskey.sh

[Install]
WantedBy=multi-user.target
/etc/systemd/system/check-update-misskey.timer
[Unit]
Description=Check Update Misskey Timer

[Timer]
OnCalendar=3:20
Unit=check-update-misskey.service
 
[Install]
WantedBy=timers.target

配置したら、以下の手順で動作確認する。

  • sudo systemctl daemon-reload で読み込み
  • sudo systemctl enable --now check-update-misskey.timer で有効化する
  • systemctl list-timers で起動確認
  • journalctl -fu check-update-misskey.service で動作確認
4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?