カテゴリー別アーカイブ: Linux

Linux一般に関すること

X.509 証明書による IPsec 認証 (libreswan)

libreswan を使って、IPsec の X.509 証明書認証を検証する。

検証環境は前回記事と同様で、以下の図の通りである。

vpn1、vpn2、host1、host2、router1 OS は全て Ubuntu 18.04 である。

1. 証明書の用意

libreswan で証明書を扱う場合、NSS Tools の流儀に従って管理しなければならない。strongswan のようにファイルベースでの証明書管理はできないようなので、少し不便である。

vpn1 側の NSS Tools でオレオレ CA を作って、vpn1・vpn2 両方の証明書をここで作成することにする。あとでエクスポートして vpn2 へ持っていく。

1.1 初期化

最初に、NSS ライブラリの証明書ストアを両サーバで初期化する。Ubuntu の場合は標準のパスが /var/lib/ipsec/nss になっている。

user@vpn1:~$ sudo rm -f /var/lib/ipsec/nss/*.db
user@vpn1:~$ sudo ipsec initnss
user@vpn2:~$ sudo rm -f /var/lib/ipsec/nss/*.db
user@vpn2:~$ sudo ipsec initnss

1.2 CA鍵・証明書の作成

次に、vpn1 上で CA 鍵と証明書のセットを作成する。以下のように certutil コマンドを使用する。
user@vpn1:~$ sudo certutil -S -k rsa -n ca -s "CN=ca.example.com" -v 120 -t "CT,C,C" -x -d sql:/var/lib/ipsec/nss

A random seed must be generated that will be used in the
creation of your key. One of the easiest ways to create a
random seed is to use the timing of keystrokes on a keyboard.

To begin, type keys on the keyboard until this progress meter
is full. DO NOT USE THE AUTOREPEAT FUNCTION ON YOUR KEYBOARD!

Continue typing until the progress meter is full:

|************************************************************|

Finished. Press enter to continue:

Generating key. This may take a few moments...
途中で、乱数の種としてキーボードからの入力を求められるので、適当にランダム入力する。

コマンドラインオプションについては、それぞれ以下のような意味がある。
-S は鍵・証明書の生成とデータベース登録を指示する。
-k は鍵のタイプを指定する。ここでは RSA を使う。
-n は、この鍵/証明書の NSS ストア内でのニックネームを指定する。
-s は、CA の CN (コモンネーム) を指定する。適当に決めてよい。
-v は、証明書の有効期限を指定する。(単位: 月)
-t "CT,C,C" は、証明書の信頼属性を指定する。
3つのフィールドの最初は SSL、2番目はメール、3番目はオブジェクト署名の用途を表す。
C は CA、T はクライアント認証証明書発行のための CA を表す。
IPsec の認証以外にこの CA を使うことがなければ、C,, だけでも良い。
-x は、自己署名証明書にすることを指定する。
-d sql:/var/lib/ipsec/nss は、証明書ストアのパスを表す。sql: を付けておかないと libreswan から利用できないので注意。

1.3 ホスト鍵・証明書の作成

次に、vpn1 のホスト鍵/証明書セットを作る。途中でキーボードからの入力を求められるのは先ほどと同様である。
user@vpn1:~$ sudo certutil -S -k rsa -c ca -n vpn1 -s "CN=vpn1.example.com" -v 60 -t "u,u,u" -d sql:/var/lib/ipsec/nss

コマンドラインオプションについては以下の通り。
-c で、先ほど作成した CA 鍵 (ニックネームが “ca”) での署名を指定する。
-n は、この鍵/証明書の NSS ストア内でのニックネームを指定する。あとでこれを利用する。
-s は、証明書の CN (コモンネーム) を指定する。ここでは vpn1 の FQDN にしておく。
-t "u,u,u" は、証明書の信頼属性を指定する。u はユーザ証明書を表す。

さらに、同じ作り方で vpn2 用の鍵・証明書を作る。同様に、ランダムキー入力が求められる。
user@vpn1:~$ sudo certutil -S -k rsa -c ca -n vpn2 -s "CN=vpn2.example.com" -v 60 -t "u,u,u" -d sql:/var/lib/ipsec/nss

ここまでで CA と vpn1 と vpn2 の 3セットの鍵・証明書が作成できた。

1.4 vpn2 用の鍵と証明書をエクスポート・インポート

vpn2 の鍵・証明書と、CA 証明書をファイルへエクスポートして、vpn2 へ持っていく。鍵を含むファイルは PEM 形式でエクスポートする方法が無いので、PKCS#12 形式でエクスポートする。
user@vpn1:~$ sudo certutil -L -n ca -a -d sql:/var/lib/ipsec/nss > ca.pem
user@vpn1:~$ sudo pk12util -n vpn2 -o vpn2.p12 -d sql:/var/lib/ipsec/nss
Enter password for PKCS12 file: password (適当に決めた PKCS#12 ファイル用パスワード)
Re-enter password: password (再度入力)
pk12util: PKCS12 EXPORT SUCCESSFUL
user@vpn1:~$ sudo chown user vpn2.p12

エクスポートしたファイルを vpn2 へネットワークコピーする。
user@vpn1:~$ scp ca.pem vpn2.p12 vpn2:
user@vpn1:~$ rm ca.pem vpn2.p12

vpn2 側でインポートする。
user@vpn2:~$ sudo certutil -A -a -i ca.pem -n ca -t 'CT,,' -d sql:/var/lib/ipsec/nss
user@vpn2:~$ sudo pk12util -i vpn2.p12 -d sql:/var/lib/ipsec/nss
Enter password for PKCS12 file: password (先ほど決めたパスワード)
pk12util: PKCS12 IMPORT SUCCESSFUL
user@vpn2:~$ rm ca.pem vpn2.p12

2. libreswan の設定

2.1 vpn1 側

vpn1 の libreswan 設定を編集する。

user@vpn1:~$ sudo vi /etc/ipsec.d/linux-to-linux.conf

conn linux-to-linux
	authby=rsasig
	auto=add
	dpdaction=clear
	leftcert=vpn1
	leftid="CN=vpn1.example.com"
	left=198.51.100.100
	leftsubnet=10.0.1.0/24
	rightid="CN=vpn2.example.com"
	right=203.0.113.100
	rightsubnet=10.0.2.0/24

leftcert に vpn1 を指定する。これは vpn1 証明書を作成した時のニックネームを指定している。

leftid、rightid は、それぞれの端点の Peer ID である。証明書の CN フィールドをダブルクォーテーションで囲って指定する。

2.2 vpn2 側

vpn2 の方も同様に設定する。

user@vpn2:~$ sudo vi /etc/ipsec.d/linux-to-linux.conf

conn linux-to-linux
	authby=rsasig
	auto=start
	dpdaction=restart
	leftcert=vpn2
	leftid="CN=vpn2.example.com"
	left=203.0.113.100
	leftsubnet=10.0.2.0/24
	rightid="CN=vpn1.example.com"
	right=198.51.100.100
	rightsubnet=10.0.1.0/24

2.3 再起動

デーモンを再起動する。
user@vpn1:~$ sudo systemctl restart ipsec
user@vpn2:~$ sudo systemctl restart ipsec

2.4 確認

接続状態を確認する。

user@vpn1:~$ sudo ip xfrm state
src 203.0.113.100 dst 198.51.100.100
        proto esp spi 0xc8f7a785 reqid 16389 mode tunnel
        replay-window 32 flag af-unspec
        auth-trunc hmac(sha1) 0xed85061c48c4fbc2dcce034191fb5a5a7c12d9e3 96
        enc cbc(aes) 0x7e39a613f56f3cb06b022561c0a8e65b
        anti-replay context: seq 0x0, oseq 0x0, bitmap 0x00000000
src 198.51.100.100 dst 203.0.113.100
        proto esp spi 0x90027aeb reqid 16389 mode tunnel
        replay-window 32 flag af-unspec
        auth-trunc hmac(sha1) 0xf3fa1c2609590b83e4c9c95b1ad38d80492f267a 96
        enc cbc(aes) 0x9e039bf741f7a4c7a9e1920d241fae65
        anti-replay context: seq 0x0, oseq 0x0, bitmap 0x00000000

参考:
HOWTO: Using NSS with libreswan
certutilによる証明書管理 [Fedora14]

Linux サーバ間 IPsec 接続 (libreswan)

Linux サーバ同士の間で libreswan を使って IPsec を接続してみる。strongswan を使ったやり方は別記事にて。

I. 前提

環境は以下の通り。

vpn1、vpn2、host1、host2、router1 OSは全てUbuntu 18.04である。

vpn1←→vpn2の間で、libreswanでトンネルモードIPsec接続をする。10.0.1.0/24 から 10.0.2.0/24 へのパケット、またその逆方向のパケットはトンネルへ入るようにする。つまり、例えばhost1からhost2へpingを打つとトンネルを通ることになる。10.0.1.0/24や10.0.2.0/24へのスタティックルートはrouter1に追加しないようにしておくので、VPNトンネルが出来なければhost1からhost2へのpingは到達できない。

vpn2側に自動接続開始の設定を入れることで、VPNトンネルを自動的に張ることにする。

II. 設定

以下、設定を記述する(IPアドレス設定など基本的なところは省略)

1. router1 の設定:

通過パケットを転送できるように、カーネルパラメータを変更する。
user@router1:~$ sudo vi /etc/sysctl.conf

net.ipv4.ip_forward=1  #28行目のコメントを外す

上記カーネルパラメータを有効化する。
user@router1:~$ sudo sysctl -p /etc/sysctl.conf

2. vpn1の設定:

libreswan をインストールする。このネットワーク構成ではインターネットからの apt install 不可なので、インターネット接続可能なネットワークに一時的に接続しておく。
user@vpn1:~$ sudo apt install libreswan

インストールが終わったら、ネットワーク構成を検証用の構成に戻す。以下のように netplan 設定ファイルを編集する。
user@vpn1:~$ vi /etc/netplan/50-cloud-init.yaml

network:
    version: 2
    ethernets:
        ens160:
            addresses: [198.51.100.100/24]
            gateway4: 198.51.100.1
        ens192:
            addresses: [10.0.1.1/24]

編集したら適用する。
user@vpn1:~$ sudo netplan apply

カーネルパラメータを設定する。
user@vpn1:~$ sudo vi /etc/sysctl.conf

net.ipv4.ip_forward=1  #28行目のコメントを外す

上記カーネルパラメータを有効化する。
user@vpn1:~$ sudo sysctl -p /etc/sysctl.conf

IPsecの事前共有鍵を設定する。
user@vpn1:~$ sudo vi /etc/ipsec.d/linux-to-linux.secrets

: PSK "mypresharedkey"

一般ユーザで読めないようパーミッションを変えておく。
user@vpn1:~$ sudo chmod 600 /etc/ipsec.d/linux-to-linux.secrets

IPsecの接続設定を記述する。
user@vpn1:~$ sudo vi /etc/ipsec.d/linux-to-linux.conf

conn linux-to-linux
        authby=secret		# 共有鍵認証とする
        auto=add		# こちら側からはVPN接続を自動開始しない
        dpdaction=clear
        left=198.51.100.100	# 自ホストのIPアドレス
        leftsubnet=10.0.1.0/24	# 自分側のプライベートネットワーク
        right=203.0.113.100	# 対向側ホストのIPアドレス
        rightsubnet=10.0.2.0/24	# 対向側のプライベートネットワーク

デーモンを起動する。
user@vpn1:~$ sudo systemctl enable ipsec
user@vpn1:~$ sudo systemctl start ipsec

3. vpn2 の設定:

vpn1 と同様に、libreswan をインストールする。検証構成ではインターネットからの apt install 不可なのも vpn1 と同様である。一時的にインターネット接続可能なネットワークに接続しておく。
user@vpn2:~$ sudo apt install libreswan

インストールが終わったら、ネットワーク構成を検証用の構成に戻す。
user@vpn2:~$ vi /etc/netplan/50-cloud-init.yaml

network:
    version: 2
    ethernets:
        ens160:
            addresses: [203.0.113.100/24]
            gateway4: 203.0.113.1
        ens192:
            addresses: [10.0.2.1/24]

編集したら適用する。
user@vpn2:~$ sudo netplan apply

カーネルパラメータを設定する。
user@vpn2:~$ sudo vi /etc/sysctl.conf

net.ipv4.ip_forward=1  #28行目のコメントを外す

上記カーネルパラメータを有効化する。
user@vpn2:~$ sudo sysctl -p /etc/sysctl.conf

IPsec 事前共有鍵を設定する。
user@vpn2:~$ sudo vi /etc/ipsec.d/linux-to-linux.secrets

: PSK "mypresharedkey"

一般ユーザで読めないようパーミッションを変えておく。
user@vpn2:~$ sudo chmod 600 /etc/ipsec.d/linux-to-linux.secrets

IPsec の設定を記述する。right/left を vpn1 側とは入れ換える。
user@vpn2:~$ sudo vi /etc/ipsec.d/linux-to-linux.conf

conn linux-to-linux
        authby=secret
        auto=start		# こちら側から VPN 接続を自動開始する
        dpdaction=restart
        left=203.0.113.100
        leftsubnet=10.0.2.0/24
        right=198.51.100.100
        rightsubnet=10.0.1.0/24

デーモンを起動する。
user@vpn2:~$ sudo systemctl enable ipsec
user@vpn2:~$ sudo systemctl start ipsec

これで完成。

III. 確認

ipsec status コマンドで、接続状況を確認できる。

user@vpn1:~$ sudo ipsec status
(snip)
000 #3: "linux-to-linux":500 STATE_MAIN_R3 (sent MR3, ISAKMP SA established); EVENT_SA_REPLACE in 3326s; newest ISAKMP; lastdpd=-1s(seq in:0 out:0); idle; import:not set
000 #4: "linux-to-linux":500 STATE_QUICK_R2 (IPsec SA established); EVENT_SA_REPLACE in 28526s; newest IPSEC; eroute owner; isakmp#3; idle; import:not set
000 #4: "linux-to-linux" esp.a61da06f@203.0.113.100 esp.53ec235d@198.51.100.100 ref=0 refhim=0 Traffic: ESPin=0B ESPout=0B! ESPmax=4194303B

router1 で tcpdump を仕掛けておき、host1 から host2 あてに ping を打ってみる。
user@host1:~$ ping 10.0.2.100
user@router1:~$ sudo tcpdump -n -i ens192 not tcp port 22
15:27:34.103230 IP 198.51.100.100 > 203.0.113.100: ESP(spi=0xa61da06f,seq=0x1), length 132
15:27:34.103475 IP 203.0.113.100 > 198.51.100.100: ESP(spi=0x53ec235d,seq=0x1), length 132
15:27:35.131026 IP 198.51.100.100 > 203.0.113.100: ESP(spi=0xa61da06f,seq=0x2), length 132
15:27:35.131271 IP 203.0.113.100 > 198.51.100.100: ESP(spi=0x53ec235d,seq=0x2), length 132

ESPにカプセル化されてパケットが通過していることが確認できた。

※パケットがトンネルに入るか入らないかは、IP ルーティングではなく xfrm ポリシーによって決まっている。
ip xfrm policy コマンドで確認できる。

user@vpn1:~$ sudo ip xfrm policy
src 10.0.1.0/24 dst 10.0.2.0/24
        dir out priority 2344
        tmpl src 198.51.100.100 dst 203.0.113.100
                proto esp reqid 16389 mode tunnel
src 10.0.2.0/24 dst 10.0.1.0/24
        dir fwd priority 2344
        tmpl src 203.0.113.100 dst 198.51.100.100
                proto esp reqid 16389 mode tunnel
src 10.0.2.0/24 dst 10.0.1.0/24
        dir in priority 2344
        tmpl src 203.0.113.100 dst 198.51.100.100
                proto esp reqid 16389 mode tunnel
(snip)

CentOS7 + nginx + phpMyAdmin

CentOS7にphpMyAdminをEPEL経由でインストールしてみた。

環境:
OS: CentOS 7.5
Webサーバ: nginx (EPELパッケージ)
PHP: php-fpm 5.4.16 (CentOS標準パッケージ)
DB: MariaDB 5.5.56 (CentOS標準パッケージ)

1. nginxインストール

EPELリポジトリを追加する(まだ追加していない場合)
$ sudo yum install epel-release

nginxをインストールする。
$ sudo yum install nginx
追加機能をコンパイルしたい場合は、SRPM を自前でリビルドしてもよい

あとは自分の用意したいWebサーバに合わせてバーチャルホスト設定ファイルを作成することになる。設定ファイルの中に、phpMyAdmin 用の記述を入れる。Apache 用の設定が /etc/httpd/conf.d/phpMyAdmin.conf にあるので、参考にする。

  root /usr/share/phpMyAdmin;
  ...
  location ~ ^/libraries/ { deny all; }
  location ~ ^/setup/lib/ { deny all; }
  location ~ ^/setup/frames/ { deny all; }
  location ~ ^/.*\.php$ {
    allow 203.0.113.100;		←自宅からのアクセス許可
    deny all;				←それ以外は全部拒否
    fastcgi_pass unix:/run/php-fpm/php-fpm.sock;
    include fastcgi_params;
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    fastcgi_param PATH_INFO $fastcgi_path_info;
    fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
    fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
  }
  location / {
    allow 203.0.113.100;
    deny all;
  }

$ sudo systemctl enable nginx
$ sudo systemctl start nginx

2. mariadb インストール・設定

$ sudo yum install mariadb mariadb-server
$ sudo systemctl enable mariadb
$ sudo systemctl start mariadb

rootパスワードの初期設定を行っておく。
$ mysql_secure_installation

3. phpインストール・設定

必要そうなパッケージをまとめてインストールしておく。
$ sudo yum install php-fpm php-mysql php-gd php-mbstring

phpの基本設定を行う。
$ sudo vi /etc/php.ini

expose_php = Off			←PHPの情報を隠す

ほかにもパフォーマンスチューニングのパラメータがphp.iniの中にあるので、必要に応じて変更しておく。

php-fpmとして動作するときの設定を行う。
$ sudo vi /etc/php-fpm.d/www.conf

listen = /run/php-fpm/php-fpm.sock	←UNIXソケットを利用
listen.owner = nginx			←UNIXソケットの所有者をnginx実行ユーザにする
listen.group = nginx
listen.mode = 0600			←nginx以外からソケットを利用できないようにする
user = nginx				←php-fpm実行ユーザをnginxにそろえておく
group = nginx

php-fpmデーモンを起動する。
$ sudo systemctl enable php-fpm
$ sudo systemctl start php-fpm

3. phpMyAdminインストール・設定

phpMyAdminをインストールする。
$ sudo yum install phpMyAdmin

必要なディレクトリの作成、nginxから読めるよう権限の修正を行う。
$ sudo mkdir -p /var/lib/php/session
$ sudo chown nginx:nginx /var/lib/php/session
$ sudo chgrp -R nginx /etc/phpMyAdmin
$ sudo chown -R nginx:nginx /var/lib/phpMyAdmin

phpMyAdminに必要なDB・テーブルの作成を行う。
$ mysql -u root -p
Enter password:
MariaDB [(none)]> source /usr/share/phpMyAdmin/sql/create_tables.sql;
MariaDB [(none)]> \q

あとはWebブラウザで http://自分のサーバ/phpMyAdmin/ にアクセスし、動作確認を行う。

Ubuntu 18.04 + MariaDB

Ubuntu 標準パッケージの MariaDB を使うにあたってハマりそうな箇所を記述してみる。

DB root ユーザでの接続に失敗する

MariaDB/MySQL データベースを使い始めるときは、まず mysql_secure_installation コマンドで状態を初期化して root パスワードを設定するのが一般的だろう。そして、設定したパスワードを使って DB root 接続し、アプリケーション等に必要なデータベースや DB 一般ユーザを作成する。この手順を実行しようとすると、DB に root ログインできない (パスワード認証が通らない) という事象にぶち当たる。

user@bionic$ sudo mysql_secure_installation (初期化してrootパスワードを設定)
Set root password? [Y/n]y
New password: testpass01
Re-enter new password: testpass01
Remove anonymous users? [Y/n]y
Disallow root login remotely? [Y/n]y
Remove test database and access to it? [Y/n]y
Reload privilege tables now? [Y/n]y

user@bionic$ mysql -u root -p
Enter password: testpass01
ERROR 1698 (28000): Access denied for user 'root'@'localhost' ←認証エラー!

上記動作の原因は、初期状態の DB root ユーザ認証に unix_socket 認証プラグインが使われていることである。この状態では、DB root として接続するために OS の root 権限にならなければならない (sudo または su コマンドを使う)。OS root になっていれば、以下のようにパスワードなしで DB root として接続可能である。

user@bionic:~$ sudo mysql
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 35
Server version: 10.1.29-MariaDB-6 Ubuntu 18.04

Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]>

DB root でログインすると、認証プラグインの状態も確認できる。

MariaDB [(none)]> SELECT user,host,plugin from mysql.user;
+------+-----------+-------------+
| user | host      | plugin      |
+------+-----------+-------------+
| root | localhost | unix_socket | ← unix_socket 認証プラグインが使われている
+------+-----------+-------------+
1 row in set (0.00 sec)

さて、この認証方式をそのまま使うか?それとも昔ながらのパスワード認証に戻すか?

初期状態の認証設定でそのまま利用する

初期設定に乗っかっていくなら、DB の root パスワードを設定する必要がない。

また一般ユーザでも同一の仕組みを使う場合は、UNIX ユーザと DB ユーザを同名で作成すればよい。

(ただし、この認証方式では外部ホストから TCP/3306 経由でログインできないことになる。必ずローカルの UNIX ドメインソケット経由でなければならない)

以下はブログエンジン WordPress を「wordpress」というユーザ名で利用する場合の例。OS の wordpress ユーザを作り、DB の wordpress ユーザも作成する。

user@bionic:~$ sudo useradd -m -s /bin/bash wordpress
user@bionic:~$ sudo mysql
MariaDB [(none)]> CREATE USER wordpress@localhost IDENTIFIED VIA unix_socket;

wordpress ユーザで接続できるかどうか確認する。

user@bionic:~$ sudo -i -u wordpress
wordpress@bionic:~$ mysql -u wordpress
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 57
Server version: 10.1.29-MariaDB-6 Ubuntu 18.04

Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> \q
Bye

このやり方では、WordPress のファイル群を実行する PHP の実行ユーザが wordpress ユーザである必要がある。例えば PHP 実行環境に php-fpm を使っている場合は、/etc/php/7.2/fpm/pool.d/www.confを編集して php-fpm 実行ユーザを wordpress に変更する。

/etc/php/7.2/fpm/pool.d/www.conf 抜粋

user = wordpress
group = wordpress

wordpressデータベースの作成

user@bionic:~$ sudo mysql
MariaDB [(none)]> CREATE DATABASE wordpress;
MariaDB [(none)]> GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,INDEX,ALTER ON wordpress.* TO wordpress@localhost;

wordpress 側の設定 (wp-config.php) では、パスワードを空にしておく。

define('DB_NAME', 'wordpress');
define('DB_USER', 'wordpress');
define('DB_PASSWORD', '');
define('DB_HOST', 'localhost');

従来のパスワード認証を使っていく場合

DB の root ユーザを従来のパスワード認証に変更する (以前の方式に戻す) 。私自身は、こちらの手法で行くことにする。

user@bionic:~$ sudo mysql
MariaDB [(none)]> set password for 'root'@'localhost'=password('testpass02');
MariaDB [(none)]> use mysql;
MariaDB [mysql]> update user set plugin='' where user='root';
MariaDB [mysql]> flush privileges;
MariaDB [mysql]> \q

root ユーザの認証プラグインを外し、通常のパスワードを設定している。

一般ユーザについても、通常のパスワードを設定して作成する。

user@bionic:~$ mysql -u root -p
Enter password: testpass02
MariaDB [(none)]> CREATE USER wordpress@localhost IDENTIFIED BY 'testpass03';

この場合、ログローテートの処理 (/etc/logrotate.d/mysql-server の16行目) にrootパスワードが必要になる。/etc/mysql/debian.cnf の password 行に平文パスワードを記述しておくこと。
user@bionic:~$ sudo vi /etc/mysql/debian.cnf

5行目、10行目
password = testpass02

ffmpeg vs libav

動画・音声コーデック界のスイスアーミーナイフと呼ばれる ffmpeg から、数年前に libav がforkして分裂した。

参考: ffmpegとlibavの背景事情

Debian/Ubuntu の標準パッケージは libav の方に追随したのだが、結局メンテナンス頻度の問題で ffmpeg に再度鞍替えしたらしい。

参考: Ubuntu 15.10 その12 – Libavが無くなり、FFmpegに戻りました

forkがあったときにどっちに付いていくかは微妙な問題だけど、単なるいちユーザーとしては多数派になった方が使えればどうでもいいのよ、と思わないでもない。ffmpeg コマンドを前提にして色々スクリプトを書いていたが、コマンドが avconv に変わって、また ffmpeg に変わって、しかも微妙に互換性がないという困ったことになっている。

OpenOffice.org とか MySQL とか、ほかにも色々あるはず。

nginx のモジュール追加リビルド (CentOS 7版)

nginx 公式や epel パッケージの nginx バイナリでは使いたいモジュールがコンパイルされていない。特に nginx-dav-ext-module、headers-more-nginx-module を使いたいので、ソースからビルドすることにする。

前提として、使用 OS は CentOS 7 である。

nginx 公式の RPM パッケージもあるが、この記事では epel リポジトリの nginx RPM をベースに、必要なモジュールの記述を追加して SRPM からリビルドしてみる。

1. コンパイルに必要なパッケージをインストール

$ sudo yum install gperftools-devel openssl-devel pcre-devel zlib-devel GeoIP-devel gd-devel perl-devel perl-ExtUtils-Embed libxslt-devel expat-devel git

2. epel リポジトリを追加

epel リポジトリを追加する。追加済みであれば作業の必要なし。
$ sudo yum install epel-release

3. SRPM の入手

SRPM をダウンロード・インストールする。
$ sudo yum install -y yum-utils
$ yumdownloader --source nginx
$ rpm -ivh nginx-1.12.2-2.el7.src.rpm

今後 yum update で勝手にアップデートされないように、epel レポジトリを無効化しておく。
$ sudo yum-config-manager --disable epel

4. カスタマイズ

SRPM の spec ファイルを編集する。
$ vi rpmbuild/SPECS/nginx.spec

以下、nginx.spec に青字の部分を追加する。

(46行目から)
%if 0%{?with_gperftools}
BuildRequires:     gperftools-devel
%endif
BuildRequires:     openssl-devel
BuildRequires:     pcre-devel
BuildRequires:     zlib-devel
BuildRequires:     expat-devel
BuildRequires:     git

Requires:          nginx-filesystem = %{epoch}:%{version}-%{release}

%if 0%{?rhel} || 0%{?fedora} < 24
# Introduced at 1:1.10.0-1 to ease upgrade path. To be removed later.
Requires:          nginx-all-modules = %{epoch}:%{version}-%{release}
%endif

Requires:          openssl
Requires:          pcre
Requires:          expat
Requires(pre):     nginx-filesystem

(172行目から)
%prep
%setup -q
git clone https://github.com/arut/nginx-dav-ext-module.git
git clone https://github.com/openresty/headers-more-nginx-module.git
%patch0 -p0

(234行目から)
    --with-pcre-jit \
    --with-stream=dynamic \
    --with-stream_ssl_module \
    --add-module=./nginx-dav-ext-module \
    --add-module=./headers-more-nginx-module \
%if 0%{?with_gperftools}
    --with-google_perftools_module \
%endif
(以下略)

5. ビルド、インストール

ビルドを実行する。
$ rpmbuild -ba rpmbuild/SPECS/nginx.spec

RPM が出来上がったらインストールする。すでに EPEL や nginx 公式 RPM をインストールしてしまっている場合は、先に削除しておくこと。

既存パッケージの削除:
$ sudo yum erase `rpm -qa | grep nginx`

カスタマイズした RPM のインストール:
$ sudo yum localinstall rpmbuild/RPMS/x86_64/nginx-1.12.2-2.el7.centos.x86_64.rpm rpmbuild/RPMS/x86_64/nginx-mod-*.rpm rpmbuild/RPMS/noarch/nginx-*.rpm

6. 起動

デーモンを起動する。
$ sudo systemctl enable nginx
$ sudo systemctl start nginx

openswan vs strongswan vs libreswan

IPsec の実装として、openswan / strongswan / libreswan どれを使えばいいの?というお話。

どの実装もかつてのFreeS/WAN IPsecの末裔であって、似た設定で動作するのだけど微妙に書式が違う、というやっかいなことになっている。

openswan:
もうメンテされていない古い実装。Ubuntu 18.04 LTS も CentOS 7 もパッケージを用意していないので、あえて使う理由がない。Debian 9.x (stretch) には残っているが…
libreswan:
openswanのフォークで、現状メンテされている。RedHat/CentOS 系や Fedora、Ubuntu にも標準パッケージで用意されている。Debian 9.x (stretch) にはパッケージなし。
strongswan:
ご先祖から大幅に書き直されたもの。Debian/Ubuntu 系 OS、また Fedora では標準パッケージで用意されている。RedHat/CentOS 系の場合は epel リポジトリに入っている。

ということで openswan は使わないものとして、ざっくり RedHat 系は libreswan、Debian 系は strongswan を使えば良いのではと考える。

※RedHat Enterprise Linux の場合は libreswan を推奨しているようなので、これを使うべきと思われる。
RedHat セキュリティーガイド 4.7. 仮想プライベートネットワーク (VPN) のセキュア化

重要: Libreswan が実施する IPsec は、Red Hat Enterprise Linux 7 での使用が推奨される唯一の VPN 技術です。他の VPN は、そのリスクを理解することなく使用しないでください。

参考:
IPsec for Linux – strongSwan vs Openswan vs Libreswan vs other(?)

DNSSEC ルートゾーン KSK ロールオーバーについて

DNSSECのルートゾーンKSKロールオーバーについて、ロールオーバー前後でDNSの検索に支障が出ないよう、各所から通達が出ている。

ルートゾーンKSKロールオーバーによる影響とその確認方法について (JPRS)
KSKロールオーバーについて (JPNIC)

(2017/10月予定だった切り替えが延期され、現在は 2018/10/11 に予定されています)

1. 管理下の DNS キャッシュは DNSSEC 検証をしているか

DNS キャッシュサーバーを管理している場合は、一応気を付けたほうが良い。管理下の DNS キャッシュサーバーに対して、dig コマンドでDNSSEC 対応済みドメイン (例: jprs.jp) の情報を検索したときに、回答に ad フラグが付いていたら「キャッシュサーバーでDNSSEC署名検証が有効になっている」状態なので、更新後の鍵を設定してやる必要があるかもしれない。

adフラグが付いている例:

$ dig jprs.jp. @mydns.example.com

; <<>> DiG 9.10.3-P4-Ubuntu <<>> jprs.jp.
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 26772
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 4, ADDITIONAL: 9
...

(参考: DNSSECチュートリアル~実践編~)

2. RHEL 7 / CentOS 7 + BIND の場合

named.confにデフォルトで以下の設定が入っていて、DNSSEC署名検証が有効になっている。

options {
...
        dnssec-enable yes;
        dnssec-validation yes;

        /* Path to ISC DLV key */
        bindkeys-file "/etc/named.iscdlv.key";

        managed-keys-directory "/var/named/dynamic";
...
};
...
include "/etc/named.root.key";

対処法1. パッケージアップデート

/etc/named.iscdlv.key と /etc/named.root.key に、ルートゾーンの鍵が入っている。パッケージバージョンがbind-9.9.4-38.4以降であれば、上記2ファイルに新しい鍵が追加されるので、bindのパッケージをアップデートしてしまうのが一番手っ取り早く安心できる方法ではある。

対処法2. 自動更新に任せる

9.9.4-38.3以前のパッケージを使っている場合でも、RFC5011 の自動更新に対応している。named を起動すると更新後の鍵は /var/named/dynamic/managed-keys.bind{,.jnl} として自動保存される。このため、特に何かをする必要はない。

# とはいえ、CVE-2017-3142、CVE-2017-3143への対処が9.9.4-38.5以降で行われているので、パッケージを更新した方が良い。

3. RHEL 7 / CentOS 7 + Unbound の場合

デフォルトの /var/lib/unbound/root.key には古い鍵しか入っていないが、/etc/unbound/unbound.conf に以下の記述があり、自動更新が有効になっている。

server:
...
        auto-trust-anchor-file: "/var/lib/unbound/root.key"
...

unbound のデーモンを起動すると、/var/lib/unbound/root.key に新しい鍵が追加される。

このため、これらの設定を変更していなければ、特に対処の必要はない。

4. 確認、その他

EDNS0を無効化していないこと、経路上でTCP53が通ること、フラグメントパケットが通ることも確認しておく。

大きなサイズの DNS 応答に対応できているかどうかは、DNS-OARCが確認用のレコードを用意してくれているので、以下の dig コマンドで確認できる。

$ dig +bufsize=4096 +short rs.dns-oarc.net txt

非対応のキャッシュサーバだと、以下のような回答が返ってくる。

rst.x476.rs.dns-oarc.net.
rst.x485.x476.rs.dns-oarc.net.
rst.x490.x485.x476.rs.dns-oarc.net.
"203.0.113.1 DNS reply size limit is at least 490"
"203.0.113.1 lacks EDNS, defaults to 512"
"Tested at 2017-08-31 01:19:47 UTC"

HTTP/2 (ALPN) 対応の nginx を SRPM からビルド

※以下はCentOS 7.3までの古い内容です。CentOS 7.4 では OpenSSL 1.0.2 が標準となったため、以下の作業は不要です。

CentOS7.3 に含まれる標準パッケージの OpenSSL が 1.0.1 系なので、それをリンクしてビルドした nginx 公式のバイナリでは HTTP/2 (ALPN) が利用できない。そこで、openssl-1.0.2 系のソースツリーを使って nginx 公式の SRPM をリビルドすることとする。

ついでに、geoipモジュールとdav_extモジュールも使いたいので、追加した上でビルドする。

1. コンパイルに必要なパッケージをインストール

$ sudo yum install expat-devel pcre-devel zlib-devel GeoIP-devel

2. nginxの公式レポジトリを追加

レポジトリファイルを作成する。
$ sudo vi /etc/yum.repos.d/nginx.repo

[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=0
enabled=1

[nginx-source]
name=nginx repo - Source
baseurl=http://nginx.org/packages/centos/$releasever/SRPMS/
gpgcheck=0
enabled=1

3. ダウンロード

SRPMをダウンロード・インストールする。
$ sudo yum install -y yum-utils
$ yumdownloader --source nginx
$ rpm -ivh --nosignature nginx-1.12.1-1.el7.ngx.src.rpm

今後yum updateで勝手にアップデートされないように、nginxレポジトリを無効化しておく。
$ sudo yum-config-manager --disable nginx
$ sudo yum-config-manager --disable nginx-source

OpenSSL 1.0.2 のソースをダウンロードして展開する。
$ cd /tmp
$ wget https://www.openssl.org/source/openssl-1.0.2l.tar.gz
$ tar xzf openssl-1.0.2l.tar.gz
$ cd ~

OpenSSL のビルドはしなくてよい。

4. カスタマイズ

SRPM の spec ファイルを編集する。
$ vi rpmbuild/SPECS/nginx.spec

青字の部分は追加、赤字で打消し線のところは削除する。

#
%define nginx_home %{_localstatedir}/cache/nginx
%define nginx_user nginx
%define nginx_group nginx
%define nginx_loggroup adm

# distribution specific definitions
%define use_systemd (0%{?fedora} && 0%{?fedora} >= 18) || (0%{?rhel} && 0%{?rhel
} >= 7) || (0%{?suse_version} == 1315)

%if 0%{?rhel} == 5
%define _group System Environment/Daemons
Requires(pre): shadow-utils
Requires: initscripts >= 8.36
Requires(post): chkconfig
Requires: openssl
BuildRequires: openssl-devel
%endif

%if 0%{?rhel} == 6
%define _group System Environment/Daemons
Requires(pre): shadow-utils
Requires: initscripts >= 8.36
Requires(post): chkconfig
Requires: openssl >= 1.0.1
BuildRequires: openssl-devel >= 1.0.1
%endif

%if 0%{?rhel} == 7
%define _group System Environment/Daemons
%define epoch 1
Epoch: %{epoch}
Requires(pre): shadow-utils
Requires: systemd
Requires: openssl >= 1.0.1
BuildRequires: systemd
BuildRequires: openssl-devel >= 1.0.1
BuildRequires: GeoIP-devel
BuildRequires: expat-devel
%endif

%if 0%{?suse_version} == 1315
%define _group Productivity/Networking/Web/Servers
%define nginx_loggroup trusted
Requires(pre): shadow
Requires: systemd
BuildRequires: libopenssl-devel
BuildRequires: systemd
%endif

# end of distribution specific definitions

%define main_version 1.12.1
%define main_release 1%{?dist}.ngx

%define bdir %{_builddir}/%{name}-%{main_version}

%define WITH_CC_OPT $(echo %{optflags} $(pcre-config --cflags)) -fPIC
%define WITH_LD_OPT -Wl,-z,relro -Wl,-z,now -pie
%define BASE_CONFIGURE_ARGS $(echo "--prefix=%{_sysconfdir}/nginx --sbin-path=%{
_sbindir}/nginx --modules-path=%{_libdir}/nginx/modules --conf-path=%{_sysconfdi
r}/nginx/nginx.conf --error-log-path=%{_localstatedir}/log/nginx/error.log --htt
p-log-path=%{_localstatedir}/log/nginx/access.log --pid-path=%{_localstatedir}/r
un/nginx.pid --lock-path=%{_localstatedir}/run/nginx.lock --http-client-body-tem
p-path=%{_localstatedir}/cache/nginx/client_temp --http-proxy-temp-path=%{_local
statedir}/cache/nginx/proxy_temp --http-fastcgi-temp-path=%{_localstatedir}/cach
e/nginx/fastcgi_temp --http-uwsgi-temp-path=%{_localstatedir}/cache/nginx/uwsgi_
temp --http-scgi-temp-path=%{_localstatedir}/cache/nginx/scgi_temp --user=%{ngin
x_user} --group=%{nginx_group} --with-compat --with-file-aio --with-threads --wi
th-http_addition_module --with-http_auth_request_module --with-http_dav_module -
-with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module -
-with-http_mp4_module --with-http_random_index_module --with-http_realip_module 
--with-http_secure_link_module --with-http_slice_module --with-http_ssl_module -
-with-http_stub_status_module --with-http_sub_module --with-http_v2_module --wit
h-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-s
tream_ssl_module --with-stream_ssl_preread_module --with-http_geoip_module --add
-module=./nginx-dav-ext-module --with-openssl=/tmp/openssl-1.0.2l")

Summary: High performance web server
Name: nginx
Version: %{main_version}
Release: %{main_release}
Vendor: Nginx, Inc.
URL: http://nginx.org/
Group: %{_group}

Source0: http://nginx.org/download/%{name}-%{version}.tar.gz
Source1: logrotate
Source2: nginx.init.in
Source3: nginx.sysconf
Source4: nginx.conf
Source5: nginx.vh.default.conf
Source7: nginx-debug.sysconf
Source8: nginx.service
Source9: nginx.upgrade.sh
Source10: nginx.suse.logrotate
Source11: nginx-debug.service
Source12: COPYRIGHT
Source13: nginx.check-reload.sh

License: 2-clause BSD-like license

BuildRoot: %{_tmppath}/%{name}-%{main_version}-%{main_release}-root
BuildRequires: zlib-devel
BuildRequires: pcre-devel

Provides: webserver

%description
nginx [engine x] is an HTTP and reverse proxy server, as well as
a mail proxy server.

%if 0%{?suse_version} == 1315
%debug_package
%endif

%prep
%setup -q
cp %{SOURCE2} .
sed -e 's|%%DEFAULTSTART%%|2 3 4 5|g' -e 's|%%DEFAULTSTOP%%|0 1 6|g' \
    -e 's|%%PROVIDES%%|nginx|g' < %{SOURCE2} > nginx.init
sed -e 's|%%DEFAULTSTART%%||g' -e 's|%%DEFAULTSTOP%%|0 1 2 3 4 5 6|g' \
    -e 's|%%PROVIDES%%|nginx-debug|g' < %{SOURCE2} > nginx-debug.init
git clone https://github.com/arut/nginx-dav-ext-module.git

%build
./configure %{BASE_CONFIGURE_ARGS} \
    --with-cc-opt="%{WITH_CC_OPT}" \
    --with-ld-opt="%{WITH_LD_OPT}" \
    --with-debug --with-openssl-opt="-fPIC"
make %{?_smp_mflags}
%{__mv} %{bdir}/objs/nginx \
    %{bdir}/objs/nginx-debug
./configure %{BASE_CONFIGURE_ARGS} \
    --with-cc-opt="%{WITH_CC_OPT}" \
    --with-ld-opt="%{WITH_LD_OPT}" --with-openssl-opt="-fPIC"
make %{?_smp_mflags}

(以下略)

5. ビルド、インストール

ビルドを実行する。
$ rpmbuild -ba rpmbuild/SPECS/nginx.spec

出来上がったら、RPMをインストールする。
$ sudo yum localinstall rpmbuild/RPMS/x86_64/nginx-1.12.1-1.el7.centos.ngx.x86_64.rpm

展開した openssl ソースを掃除しておく。
$ rm -r /tmp/openssl-1.0.2l

6. 起動

デーモンを起動する。
$ sudo systemctl enable nginx
$ sudo systemctl start nginx

参考URL:
HTTP/2対応nginxのrpmパッケージ作成とインストール

CentOS7でmastodon自分インスタンスを立てる

VPS上に自分用のインスタンスを立ててみた。

1. dockerインストール

dockerをパッケージでインストールする。レポジトリとしては、dockerの公式を使う。
$ sudo yum install yum-utils
$ sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
$ sudo yum-config-manager --disable docker-ce-edge
$ sudo yum-config-manager --disable docker-ce-stable

docker-ce-edgeレポジトリで最新のバージョン名を取得してインストールする。
$ yum --enablerepo=docker-ce-edge list docker-ce.x86_64 --showduplicates | sort -r
docker-ce.x86_64 17.05.0.ce-1.el7.centos docker-ce-edge
docker-ce.x86_64 17.04.0.ce-1.el7.centos docker-ce-edge
$ sudo yum --enablerepo=docker-ce-edge install docker-ce-17.05.0.ce-1.el7.centos

dockerデーモンを起動する。
$ sudo systemctl enable docker
$ sudo systemctl start docker

docker-composeをgithubからダウンロードして/usr/bin以下に配置する。
$ sudo -i
# curl -L https://github.com/docker/compose/releases/download/1.12.0/docker-compose-`uname -s`-`uname -m` > /usr/bin/docker-compose
# chmod +x /usr/bin/docker-compose
$ exit

2. mastodon

mastodonのソースツリーを用意する。

最初に、mastodon用の作業用一般ユーザを追加する。
$ sudo useradd mastodon
$ sudo passwd mastodon
$ sudo usermod -aG docker mastodon

mastodonのソースツリーを/home/mastodon/liveに展開する。これ以降はmastodonユーザで作業する。
$ sudo -i -u mastodon
$ git clone https://github.com/tootsuite/mastodon.git live
$ cd live
$ git tag
バージョンタグの一覧が表示されるので、最新のリリースタグを選ぶ。
$ git checkout v1.3.3

ソースツリーに付属している、dockerコンテナの設定ファイル docker-compose.yml を編集する。
$ vi docker-compose.yml

version: '2'
services:

  db:
    restart: always
    image: postgres:alpine
### Uncomment to enable DB persistance
    volumes: ←ここの行頭コメント記号を外す
      - ./postgres:/var/lib/postgresql/data ←ここの行頭コメント記号を外す

  redis:
    restart: always
    image: redis:alpine
### Uncomment to enable REDIS persistance
    volumes: ←ここの行頭コメント記号を外す
      - ./redis:/data ←ここの行頭コメント記号を外す

  web:
    restart: always
    build: .
    image: gargron/mastodon
    env_file: .env.production
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    ports:
      - "3000:3000"
    links: ←depends_onをlinksに変更する
      - db
      - redis
    volumes:
      - ./public/assets:/mastodon/public/assets
      - ./public/system:/mastodon/public/system

  streaming:
    restart: always
    build: .
    image: gargron/mastodon
    env_file: .env.production
    command: npm run start
    ports:
      - "4000:4000"
    links: ←depends_onをlinksに変更する
      - db
      - redis

  sidekiq:
    restart: always
    build: .
    image: gargron/mastodon
    env_file: .env.production
    command: bundle exec sidekiq -q default -q mailers -q pull -q push
    links: ←depends_onをlinksに変更する
      - db
      - redis
    volumes:
      - ./public/system:/mastodon/public/system

次に、.env.productionを編集する。
$ cp -p .env.production.sample .env.production
$ vi .env.production

以下のような行を編集する。

DB_USER=mastodon ←この後PostgreSQLで設定する
DB_NAME=mastodon ←同上
DB_PASS=password ←同上
LOCAL_DOMAIN=mastodon.example.com ←mastodonサーバのFQDN
SMTP_SERVER=mail.example.com ←メールサーバのホスト名
SMTP_PORT=587 ←メールサーバのポート SMTP AUTHが使えるポートを指定
SMTP_LOGIN=mastodon ←SMTP AUTHのユーザ名
SMTP_PASSWORD=password ←SMTP AUTHのパスワード
SMTP_FROM_ADDRESS=postmaster@example.com

dockerコンテナをダウンロードし、ビルドする。ここは時間がかかる。
$ docker-compose build

次に、同じコマンドを3回実行してキーを作成する。
$ docker-compose run --rm web rake secret
3d3748d778c215f269c18c4c46dc2fb94da50ec569b2bc0c593353b5f54f2f4b6e4307978bc0304042f8d086716f1896046751d4b0fc8b9420a79c33454caa81
$ docker-compose run --rm web rake secret
5377ae140e0c1eaf857b5423067c4295029c9cdb23feefa9f382c7aa94753f224ed097834f15447c419d31065ce0dc816ef6efeec594de8996b776910b2e3326
$ docker-compose run --rm web rake secret
772fab57e3e71fa6c57f14e8ab42e986daf0aaf67a1ebe469d04fc3187f989ec77bd8bebdc08b62c4a456e8851755140000286c8babc1b8e4c9203782aadd6f2

出力された鍵文字列を、.env.production設定ファイルに記述する。
$ vi .env.production

PAPERCLIP_SECRET=3d3748d778c215f269c18c4c46dc2fb94da50ec569b2bc0c593353b5f54f2f4b6e4307978bc0304042f8d086716f1896046751d4b0fc8b9420a79c33454caa81
SECRET_KEY_BASE=5377ae140e0c1eaf857b5423067c4295029c9cdb23feefa9f382c7aa94753f224ed097834f15447c419d31065ce0dc816ef6efeec594de8996b776910b2e3326
OTP_SECRET=772fab57e3e71fa6c57f14e8ab42e986daf0aaf67a1ebe469d04fc3187f989ec77bd8bebdc08b62c4a456e8851755140000286c8babc1b8e4c9203782aadd6f2

コンテナを起動する。
$ docker-compose up -d

dbコンテナ内のPostgreSQLに、ユーザとデータベースを作成する。
$ docker exec -it live_db_1 bash
# su - postgres
43e296a2871c:~$ createuser -P mastodon
Enter password for new role: password
Enter it again: password
43e296a2871c:~$ createdb mastodon -O mastodon
43e296a2871c:~$ exit
# exit

dbコンテナの変更部分をアップデートする。また、assetsファイル(静的コンテンツ)を生成する。
$ docker-compose run --rm web rails db:migrate
$ docker-compose run --rm web rails assets:precompile

いったんコンテナを再起動する。
$ docker-compose stop
$ docker-compose up -d

3. certbot (letsencrypt)

Let’s EncryptプロジェクトCAに、証明書を発行してもらう。

certbotソースツリーを、/opt以下に展開してcertbot-autoを実行する。
$ sudo -i
# cd /opt
# chgrp wheel .
# chmod g+w .
# exit
$ cd /opt
$ git clone https://github.com/certbot/certbot
$ cd certbot
$ ./certbot-auto certonly -n --standalone --agree-tos -m 管理者メールアドレス -d www.example.com,mail.example.com,mastodon.example.com

今回は直接関係ないが、-dオプションで SubjectAltNameを指定することで、他のバーチャルホストと証明書を共用する。

4. nginx のインストールと設定

nginxはpuma+railsのリバースプロキシ兼、静的コンテンツを自前で返すWebサーバとなる。

nginx のインストール用 repo を追加する。
$ sudo vi /etc/yum.repos.d/nginx.repo

[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=0
enabled=0

nginxをインストールする。
$ sudo yum --enablerepo=nginx install nginx

nginxの設定
$ sudo vi /etc/nginx/conf.d/mastodon.conf

map $http_upgrade $connection_upgrade {
  default upgrade;
  ''      close;
}

server {
  listen 80;
  listen [::]:80;
  server_name mastodon.example.com;
  location / { return 301 https://$host$request_uri; }
}

server {
  listen 443 ssl;
  listen [::]:443 ssl;
  server_name mastodon.example.com;

  ssl_protocols TLSv1.2;
  ssl_ciphers EECDH+AESGCM:EECDH+AES;
  ssl_ecdh_curve prime256v1;
  ssl_prefer_server_ciphers on;
  ssl_session_cache shared:SSL:10m;

  ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
  ssl_dhparam         /etc/pki/tls/dhparam.pem;

  keepalive_timeout    70;
  sendfile             on;
  client_max_body_size 0;

  root /home/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 /assets {
    add_header Cache-Control "public, max-age=31536000, immutable";
  }

  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://localhost: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を起動する。
$ sudo openssl dhparam 2048 -out /etc/pki/tls/dhparam.pem
$ sudo systemctl enable nginx
$ sudo systemctl restart nginx

5. 起動後の設定

mastodonの画面にWebブラウザでアクセスし、最初のユーザを作成する。

mastodon上の最初のユーザを作成してから、以下のコマンドで管理者ユーザ化する
$ docker-compose run --rm web rails mastodon:make_admin USERNAME=firstusername

さらにシングルユーザーモード化して、ほかのユーザではアクセスできなくする
$ vi .env.production
SINGLE_USER_MODE=true

設定を反映するため、再起動する。
$ docker-compose stop
$ docker-compose up -d

systemd起動スクリプトを作成して、VPSを再起動したときも自動でmastodon用コンテナが起動するようにする。
$ sudo vi /etc/systemd/system/mastodon.service

[Unit]
Description=Mastodon
Requires=docker.service
After=network.target
After=docker.service

[Service]
Type=simple
User=mastodon
Group=mastodon
WorkingDirectory=/home/mastodon/live
ExecStart=/usr/bin/docker-compose up
ExecStop=/usr/bin/docker-compose stop

[Install]
WantedBy=multi-user.target

systemctlを使ってmastodonコンテナを再起動してみる。
$ sudo systemctl enable mastodon
$ sudo systemctl stop mastodon
$ sudo systemctl start mastodon

以上で完了である。