RedHatでPHPからDBサーバに接続できないときはSELinuxの設定が原因かも?

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

数年ぶりにRedHat系OS(Amazon Linuxは除く)を使い、記憶の彼方に消えていた SELinux と再会する機会があったので、メモを残しておきます。

事象

RedHat 7.5 + Apache/PHP + AWS RDS(MySQL)というよくある構成で、PHP から RDS MySQL に接続できないという現象に出くわしました。

RedHat サーバーからは

  • MySQL CLI
  • コマンドラインの PHP

ともにリモートの MySQL に接続できますが、Apache 上で動作する PHP プログラムからは MySQL へ接続できませんでした。

Warning: mysqli_real_connect(): (HY000/2003): Can't connect to MySQL server on 'foo.bar.eu-central-1.rds.amazonaws.com' (13) in /var/www/html/wordpress/wp-includes/wp-db.php on line 1531

原因

RedHat 7.5ではデフォルトで SELinux が有効になっています。

特に、Apacheプロセスで動作するHTTPスクリプト・モジュールから外部への通信(httpd_can_network_connect)が無効化(off)されています。

# SELinux のステータス
$ getenforce
Enforcing
$ getsebool httpd_can_network_connect
httpd_can_network_connect --> off

このような設定になっているために PHP からリモートで動作する MySQL サーバへ接続できませんでした。

対応

対応としては

  1. SELinux を無効化
  2. 外部通信全般を許可($ sudo setsebool -P httpd_can_network_connect=1)
  3. 主要データベース向けポートへの通信だけを許可(#2 のサブセット)($ sudo setsebool -P httpd_can_network_connect_db=1)

などがあります。

SELinux の機構を残したままの対応としては、後ろ二つが有力です。

なお setsebool-P オプションは設定変更を永続化(permanent)させるものです。

似ている 2つの boolean の違いはなに?

今回登場した名前が似通った2つの boolean

  • httpd_can_network_connect
  • httpd_can_network_connect_db

の違いを確認します。

sesearch を利用するため、このプログラムが存在しない場合、$ sudo yum install -y setools-console でインストールします。

まず httpd_can_network_connect_db の Access Vector(AV) ルールを確認します。

$ sesearch -A -s httpd_t -b httpd_can_network_connect_db -p name_connect
Found 6 semantic av rules:
   allow httpd_t mongod_port_t : tcp_socket name_connect ;
   allow httpd_t gds_db_port_t : tcp_socket name_connect ;
   allow httpd_t mssql_port_t : tcp_socket name_connect ;
   allow httpd_t oracle_port_t : tcp_socket name_connect ;
   allow httpd_t postgresql_port_t : tcp_socket name_connect ;
   allow httpd_t mysqld_port_t : tcp_socket name_connect ;

主要データベースの名前が並んでいます。

これらのデータベースに接続するために許可されているポート一覧を確認してみましょう。

$ seinfo --protocol=tcp --port | grep -E 'mongod_port_t|gds_db_port_t|mssql_port_t|oracle_port_t|postgresql_port_t|mysqld_port_t'
	portcon tcp 1186 system_u:object_r:mysqld_port_t:s0
	portcon tcp 1521 system_u:object_r:oracle_port_t:s0
	portcon tcp 2483 system_u:object_r:oracle_port_t:s0
	portcon tcp 2484 system_u:object_r:oracle_port_t:s0
	portcon tcp 3050 system_u:object_r:gds_db_port_t:s0
	portcon tcp 3306 system_u:object_r:mysqld_port_t:s0
	portcon tcp 5432 system_u:object_r:postgresql_port_t:s0
	portcon tcp 9898 system_u:object_r:postgresql_port_t:s0
	portcon tcp 1433-1434 system_u:object_r:mssql_port_t:s0
	portcon tcp 27017-27019 system_u:object_r:mongod_port_t:s0
	portcon tcp 28017-28019 system_u:object_r:mongod_port_t:s0
	portcon tcp 63132-63164 system_u:object_r:mysqld_port_t:s0

たとえば MySQL はデフォルトポートの 3306PostgreSQL はデフォルトポートの 5432 だけでなく、pgpool-II 用の 9898 ポートも許可されていることがわかります。

一方で、システムが様々なデータストアを利用している場合、httpd_can_network_connect_db だけでは許可しきれない可能性が高まります。 そのようなケースでは、より汎用的な httpd_can_network_connect を許可しましょう。

参考