深夜2時、データベースが接続を拒否した
よくあるデプロイの話から始まる。Fedora ServerにMariaDBをインストールし、systemctl start mariadbを実行してアプリサーバーから接続を試みたら——Connection refused。ファイアウォール、ポート、設定ファイルを確認したが、どれも問題なさそうだった。結局、犯人を見つけるまでに40分近くかかった。SELinuxがひっそりと通信をブロックしていたのだ。ログにも明確なエラーメッセージも出ないまま。
Fedoraはパッケージの更新が非常に速い。メインの開発マシンとして2年間使い続けて、その点はかなり気に入っている。ただその代わり、SELinuxとfirewalldはUbuntuやCentOSと比べてデフォルトでかなり厳しく設定されている。セキュリティが高いのは当然いいことだ。眠れない夜が増えるのも、また当然のことだが。
この記事では、Fedora Serverの初期設定を終えた後、MariaDBをproduction環境に導入するために実際に使った手順をまとめた。他のガイドの多くが流し読みしてしまうSELinuxコンテキストとfirewalldの設定も含めて。
Fedoraが他のディストリビューションと異なる理由
UbuntuやCentOS 7であれば、MariaDBをインストールしてmysql_secure_installationを実行すればほぼ完了だ。しかしFedoraはUbuntuと根本的に異なる設計思想を持っており、SELinuxがenforcingモード(デフォルト)で動作しており、事前に理解しておくべき3つの層がある:
- SELinuxコンテキスト:すべてのファイル、プロセス、ポートにはそれぞれのコンテキストがある。MariaDBがdatadirの読み込み、ソケットのオープン、ポートのリッスンを行うには、適切なコンテキストが必要だ。
- firewalldゾーン:デフォルトの
publicゾーンはポート3306を完全にブロックする——自分で開放するまで例外は一切ない。 - systemdハードニング:Fedora上のMariaDBのユニットファイルには
PrivateTmpやProtectSystemといったディレクティブがあり、ソケットパスやカスタムdatadirに影響する。
この3点を先に把握しておくだけで、無駄なデバッグ時間を少なくとも1〜2時間は節約できる。
実践:ステップバイステップのインストールと設定
ステップ1:MariaDBのインストール
FedoraのリポジトリにはMariaDBが含まれており、バージョンもかなり新しい。複数バージョンを管理したい場合はDNF Modularityを活用する方法もあるが、まずは現在のバージョンを確認しておこう:
dnf info mariadb-server
インストール:
sudo dnf install -y mariadb-server mariadb
自動起動を有効にして即座に起動:
sudo systemctl enable --now mariadb
systemctl status mariadb
ステータスにactive (running)と表示されれば問題ない。failedと表示された場合は、journalctl -xe -u mariadbでログを確認しよう——たいていエラーは最初の数行に出ている。
ステップ2:初期セキュリティ設定
sudo mysql_secure_installation
スクリプトが順番に質問してくるので、以下のように回答する:
- rootパスワードの設定 → Y、大文字と特殊文字を含む16文字以上のパスワードを設定
- 匿名ユーザーの削除 → Y
- rootのリモートログインを禁止 → Y(本番環境では必須)
- テストデータベースの削除 → Y
- 権限テーブルのリロード → Y
完了後、ログインを確認する:
sudo mysql -u root -p
# sudoで実行する場合はUnixソケット経由になるため、パスワード不要:
sudo mysql
ステップ3:アプリケーション用データベースとユーザーの作成
アプリケーションをrootで接続させてはいけない——これは例外のない原則だ。必要最小限の権限を持つ専用ユーザーを作成する:
-- 先にMariaDBにログインしておく
CREATE DATABASE myapp_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'myapp_user'@'localhost' IDENTIFIED BY 'StrongPassword123!';
GRANT ALL PRIVILEGES ON myapp_db.* TO 'myapp_user'@'localhost';
-- アプリが別サーバーにある場合(アプリサーバーの実際のIPに置き換える):
CREATE USER 'myapp_user'@'192.168.1.50' IDENTIFIED BY 'StrongPassword123!';
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp_db.* TO 'myapp_user'@'192.168.1.50';
FLUSH PRIVILEGES;
最初のGRANT ALLはアプリとDBが同じサーバーにある低リスクな場合のみ使用する。リモート接続の場合は下の例のように必要な権限のみに絞ること。
ステップ4:ファイアウォールの適切な設定
ポート3306はデフォルトで完全にブロックされている——アプリとDBが別サーバーの場合、このステップは省略できない。firewalldのZone管理とRich Rulesの詳細も併せて参照してほしい。
# アクティブなゾーンを確認
firewall-cmd --get-active-zones
# 推奨:アプリサーバーの特定IPからの接続のみ許可
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.50" port port="3306" protocol="tcp" accept'
# または内部ネットワーク全体に開放(セキュリティは低下する)
firewall-cmd --permanent --add-service=mysql
# 変更を適用
firewall-cmd --reload
# 確認
firewall-cmd --list-all
本番環境では--add-service=mysqlの代わりにrich ruleを使っている。送信元IPでの制限が可能になるため、アプリサーバーのみがポート3306に接続できる——同じネットワーク内の他のマシンからもアクセスできない。
ステップ5:SELinux——沈黙の犯人
このセクションがこの記事を書いた主な理由だ。FedoraのSELinuxはenforcingモードで動作しており、何も説明せずにブロックする。SELinuxのカスタムポリシー作成まで踏み込んだ解説も参考になるだろう。
状態を確認:
getenforce
# 期待される出力:Enforcing
デフォルトのdatadir(/var/lib/mysql)を使用する場合は追加作業は不要だ——このパスにはすでにmysqld_db_tコンテキストが設定されており、MariaDBは正常に動作する。
カスタムdatadirこそが問題が起きやすい箇所だ。例えばdatadirを/data/mysqlに変更した場合:
# 現在のコンテキストを確認——通常はunlabeled_tまたはdefault_tになっている
ls -laZ /data/mysql
# MariaDB用の適切なSELinuxコンテキストを設定
sudo semanage fcontext -a -t mysqld_db_t "/data/mysql(/.*)?"
sudo restorecon -Rv /data/mysql
# 再確認——mysqld_db_tが表示されるはず
ls -laZ /data/mysql
# system_u:object_r:mysqld_db_t:s0
semanageがない場合:
sudo dnf install -y policycoreutils-python-utils
SELinuxが接続をブロックしている場合のデバッグ:
# 直近のAVC denyを確認
sudo ausearch -m avc -ts recent
# audit2whyで読みやすく表示
sudo ausearch -m avc -ts recent | audit2why
# 最強ツール:sealert——自動分析と修正提案
sudo dnf install -y setroubleshoot-server
sudo sealert -a /var/log/audit/audit.log
あの夜、audit2whyによってMariaDBがSELinuxで許可されていないカスタムソケットパスにbindしようとしていることが判明した。/etc/my.cnfでソケットパスをデフォルトに戻すことで5分で解決した——それ以前の40分は、どこを見ればいいかわからないまま手探りで過ごした時間だった。
ステップ6:本番環境向け設定
/etc/my.cnfを直接編集するのではなく、別ファイルを作成する——管理しやすく、パッケージ更新時に上書きされない:
sudo vim /etc/my.cnf.d/production.cnf
[mysqld]
# デフォルト文字セット——utf8mb4は絵文字と完全なUnicodeをサポート
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
# アプリが同じサーバーにある場合はコメントを外してlocalhostのみリッスン
# bind-address = 127.0.0.1
# パフォーマンス——innodb_buffer_pool_sizeは利用可能なRAMの約50〜70%に設定
# RAM 1GBのサーバーなら512M、RAM 4GBなら2〜3G
innodb_buffer_pool_size = 256M
max_connections = 100
query_cache_type = 0 # クエリキャッシュはMariaDB 10.1から非推奨
# エラーログ
log_error = /var/log/mariadb/mariadb.log
# スロークエリ——デプロイ後のパフォーマンス問題を検出するために有効化
slow_query_log = 1
slow_query_log_file = /var/log/mariadb/slow.log
long_query_time = 2
[client]
default-character-set = utf8mb4
設定変更後に再起動:
sudo systemctl restart mariadb
systemctl status mariadb
ステップ7:リモート接続のテスト
アプリサーバーや同じネットワーク内の別マシンから接続テストを行う:
# 192.168.1.100をFedora ServerのIPアドレスに置き換える
mysql -h 192.168.1.100 -u myapp_user -p myapp_db
# mysqlクライアントがない場合はnetcatでポートを確認
nc -zv 192.168.1.100 3306
ファイアウォールを開放したのに接続が拒否される場合は、MariaDBが正しいインターフェースでbindしているか確認しよう:
sudo ss -tlnp | grep 3306
# 0.0.0.0:3306と表示される必要がある——127.0.0.1:3306ではいけない
127.0.0.1:3306と表示されている場合、MariaDBはlocalhostのみをリッスンしている——設定ファイルのbind-addressを再確認しよう。
本番環境チェックリスト
- MariaDBが起動中かつ自動起動設定済み:
systemctl is-enabled mariadbがenabledを返す - rootのリモートログインがブロックされている(
mysql_secure_installationで設定済み) - アプリは専用ユーザーを使用し、必要最小限の権限のみ付与——rootは使用しない
- firewalldはrich ruleを使用して特定のアプリサーバーIPからのポート3306のみ開放
- SELinuxがenforcingモードで動作し、datadirに正しいコンテキスト
mysqld_db_tが設定されている - スロークエリログが有効で、デプロイ後のパフォーマンス問題を検出できる
- バックアップ計画がある:小規模DBには
mysqldump、大規模な本番環境にはmariabackup
まとめ
Fedora ServerへのMariaDBインストールは、他のディストリビューションより複雑というわけではない——ただ、Ubuntu向けに書かれたガイドが省いてしまうステップがいくつかあるだけだ。SELinuxとfirewalldは敵ではない。実際の環境でFedoraサーバーが侵害されにくい理由が、まさにこの2つにある。
デバッグが面倒だからといってSELinuxを無効にしてはいけない。かつてステージングサーバーでそれをやったことがある。代償は、なぜステージングでパスしたのに本番で失敗したのかをチームに説明する羽目になることだった。全く嬉しくない経験だ。
手順通りに進めてもエラーが出た場合:MariaDBにはjournalctl -xe -u mariadb、SELinuxにはausearch -m avc -ts recent——この2つのコマンドで一般的な問題の90%は解決できる。スムーズなデプロイを。
