はじめに
六葩くるです。
いままでおひとりさまサーバつくるつくる詐欺して全然作ってなかったので、そろそろ #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日で消えるので名前はなんでもいい。
他の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のまま続行。
INSTALLATION SUMMARY
以下、設定する順に記載する。
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すると、残りの全容量が自動で割り当てられる。
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/
内にファイルを作成し、追記
PermitRootLogin no
PasswordAuthentication no
-
sudo systemctl restart sshd.service
でSSHサーバを再起動し、今のセッションを維持したまま別セッションでログインできることを確認する
ポート番号変更
ポート番号追加
sshd_config変更
-
/etc/ssh/sshd_config.d/
内のファイルに追記
+ 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
を変更
<?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/
内のファイルを変更
- 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/udp
をadd-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/
内にファイルを作成し、追記
/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の形式にある程度合わせただけ)のでファイルを丸々以下のものに置き換えること。
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
の記載と同じものを追記しました。)
[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)
#!/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圧縮対応を行ったもの。
オブジェクトストレージへのプロキシの記述は後ほど追記する。
# 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
の行のMETHOD
をtrust
に変更する。
# 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の設定
以下のファイルを作成する。
# 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のユニットファイル作成
以下のファイルを作成する。
[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」を入力する
- 使わない場合でも(以下略)
- 「サーバー名」「管理者の名前」「管理者のメールアドレス」「運営者情報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の設定
オブジェクトストレージへのプロキシを以下のように追記する。
# 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年放置してても一応動作していたっぽいので、ついでの備忘録として以下に記しておきます。ただしずっと動く保証はしません。
自動アプデスクリプト
以下のファイルを配置し、実行権限をつける。
#!/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つのファイルを配置する。
[Unit]
Description=Check Update Misskey
[Service]
Type=oneshot
Restart=no
ExecStart=/usr/local/bin/auto-update-misskey.sh
[Install]
WantedBy=multi-user.target
[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
で動作確認