でApache2.4(httpd 2.4.43)+PHP7.4でWebサーバー構築 - 4.セキュリティ(chownとfirewalld)編


前提と準備

Linuxサーバー構築の記事

前回まではApache2.4にPHP7.4+MySQL8.0でWebアプリ環境を構築しましたが、セキュリティについても触れないといけませんね;;

Linux標準のfirewalldであれば、iptablesのような難しいコマンドルールだったり、SELinuxといったファイルごとの複雑な制御を行うことなく、誰がどのポートを使うかのみを指定するだけで、ファイアウォールで基本的なセキュリティを設定することができます(もちろん制御が複雑なセキュリティを使ったほうが内部侵入されたときも比較的安心ですが…)

他にもファイルの暗号化も併用したいところだが、話が難しくなるので、今回はfirewalldによるファイアウォール、chownでアクセス権を設定するにとどめておきます

また今回の検証では、別のネットワークセグメントからもアクセスの可否を調べたいので、Webサーバーはまたネットワークアダプタを増設して、もう一つのネットワークセグメントを増設します(192.168.1.0/24を使っていますが、Webサーバーにもう一つネットワークアダプタ(仮想ブリッジですが)増設して、192.168.5.0/24を構築しています)

環境

  • Webサーバープログラム:Apache 2.4.43 + PHP 7.4.6 + MySQL 8.0
  • クライアント(メイン):Windows10 Pro
  • クライアント(増築側):GUIのOSであれば、フリー(ここではInsider Preview使用中のWindows10イタリア語をHyper-V(第1世代)で使用しました)
  • サーバーのアーキテクチャ:x64(動作はHyper-Vの第2世代で確認)
  • Linuxディストリビューション:CentOS 8.1 / openSUSE 15.1 Leap / Ubuntu 20.04(すべて64bit)

前提

  • ユーザーはrootでインストール(私の検証ではadminという管理者アカウントにて、そこからsudoで処理しています)
  • どのディストリビューションでも、ファイアウォールはfirewalldを使う(ディストリビューション独自のファイアウォールコマンドは使用しない)
  • 前回の記事のApache+PHP+MySQLの一式のWebアプリサーバー構築そのものを完了していること

サーバー条件

IPアドレス

  • クライアント(メイン):192.168.1.11
  • クライアント(増築側):192.168.5.2
  • Webサーバー(2ポート):(メイン側IP)192.168.1.18、(増築側IP)192.168.5.1 (どのディストリビューションでも同じIPアドレスで検証)
  • データベースサーバー:(Webサーバーと一体)
  • 所属ネットワークセグメント:(メイン)192.168.1.0/24、(増築)192.168.5.0/24

パッケージを個別ダウンロードしてインストールする機能とバージョン(2020年6月時点)

  • zlib-1.2.11.tar.gz
  • apr-1.7.0.tar.gz
  • apr-util-1.6.1.tar.gz
  • mysql80-community-release-el8-1.noarch.rpm (CentOS 8.1)
  • mysql80-community-release-sl15-3.noarch.rpm (openSUSE 15.1)
  • mysql-apt-config_0.8.15-1_all.deb (Ubuntu 20.04)
  • oniguruma-devel-6.8.2-1.el8.x86_64.rpm (CentOS 8.1)
  • httpd-2.4.43.tar.gz
  • php-7.4.6.tar.gz

それ以外の必要なパッケージは、ディストリビューションの標準パッケージコマンド(dnfやaptなど)でインストールし、個別ダウンロードは不要です。

ダウンロードについては、公式サイトにアクセスして、そこからダウンロードしてFTPで転送するか、ダウンロードファイルのURLさえわかれば、wgetで入手することもできますが、入手方法は省略しています。

firewalldによるネットワークごとのアクセス制御

現時点でのfirewalldの状態

前回の構築そのものを行った時点では、firewalldには以下のルールが存在しているかと思います。

  • 192.168.1.0/24からのポート80(HTTP):許可
  • 192.168.1.0/24からのポート443(HTTPS):許可
  • それ以外に、SSHやSambaなど別途ポートを許可しているものもあるが、ここではWebサーバーと関係ないので省略

この時点では「サーバー条件」の「IPアドレス」の図のように192.168.5.0/24のネットワークを新しく増設しても、ファイアウォールを開けていないので、アクセスできないはずです。

ここではクライアント(192.168.5.2)からWebサーバーへアクセスするには、Webサーバーの増設側のポートのIPアドレス、https(ポートは443)://192.168.5.1/ を入力すればわかります。もちろん192.168.5.0/24側のクライアントからはメイン側(192.168.1.18)へはアクセスできないことは、ネットワークの構造から明白です。

ほらね(* ॑꒳ ॑* )⋆*

firewalldでネットワークセグメントごとのアクセスを許可・拒否

別のネットワークセグメントのアクセスを許可

では192.168.5.0/24でも許可できるよう、Webサーバーのfirewalldに「192.168.5.0/24からのポート443(HTTPS):許可」というルールを追加してみます

ネットワークセグメントを特定して、特定のポートを受信ルールを入力するには、firewalldのrich ruleを使います。--add-rich-ruleを用いて、'~'で囲ったルールを入れます。

# firewall-cmd --add-rich-rule='rule family="ipv4" source address="192.168.5.0/24" port port="443" protocol="tcp" accept'

これは、IPファミリーがIPv4、送信元が192.168.5.0/24で、ポート443、プロトコルがTCPで、受信を許可する、という意味です。

  • 192.168.1.0/24からのポート80(HTTP):許可
  • 192.168.1.0/24からのポート443(HTTPS):許可
  • 192.168.5.0/24からのポート443(HTTPS):許可

上の画面は、このfirewalldの状態でアクセスしてみた画面です。確かにアクセスできるようになりました!!ちなみに永続的にアクセスできるようにするには「firewall-cmd」の後ろに「--permanent」というオプションを付ければ、再起動してもルールが適用されたままになります

ちなみにネットワーク192.168.5.0/24からのポート443(HTTPS)の許可をやめたい場合は、--remove-rich-ruleを使って、'~'で囲った中に、削除したいルールを入れます。例えば、IPv4で、192.168.5.0/24からの、ポート443(TCP)の許可しているルールを削除するには、

# firewall-cmd --remove-rich-rule='rule family="ipv4" source address="192.168.5.0/24" port port="443" protocol="tcp" accept'

を入力すると、192.168.5.0/24からポート443(HTTPS)にアクセスできなくなります。

ネットワークアクセスを拒否するには

次にこんな実験をしてみた。メインのネットワークセグメント192.168.1.0/24で、ポート80(HTTP)を拒否し、ポート443(HTTPS)のみを許可した状態にしたままにする場合には、IPv4、192.168.1.0/24からのポート80(TCP)の許可というルールを、削除します

# firewall-cmd --remove-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port port="80" protocol="tcp" accept'

基本的にfirewalldはルールに存在しないものはデフォルトで遮断しているので、ルールにリストアップされていない場合は受信拒否する(ホワイトリスト方式)ので、上記みたいに、192.168.1.0/24のポート80がルールにない場合は、アクセス不可能になります。

Apacheの所有者とパーミッションの管理

Apacheの起動ユーザーを変更する

Apacheではデフォルトでは(ソースをコンパイルしてインストールした場合)、ユーザーは「daemon」で起動しています。ps -auxで、全部のプロセスとユーザー一覧を表示し、うち「httpd」を起動しているものに絞るので、grep httpdで絞り込みます

# ps -aux | grep httpd
root     14249  0.0  0.4 121504 17124 ?        Ss   17:15   0:00 /usr/local/apache2/bin/httpd -k start
daemon   14250  0.0  0.5 1332460 22448 ?       Sl   17:15   0:00 /usr/local/apache2/bin/httpd -k start
daemon   14251  0.0  0.4 1330220 19460 ?       Sl   17:15   0:00 /usr/local/apache2/bin/httpd -k start
daemon   14252  0.0  0.6 1332460 25428 ?       Sl   17:15   0:00 /usr/local/apache2/bin/httpd -k start
admin    17782  0.0  0.0   8176   924 pts/0    S+   18:45   0:00 grep --color=auto httpd

上記のように「/usr/local/apache2/bin/httpd」というhttpd本体を動かしているユーザーは「daemon」というデフォルトのユーザーになります。

では、明示的にApacheを動かすユーザーが決まっている場合、どのようにして編集すればいいのかどうかは、httpd.confを編集して対応しました。例えば、Apacheをユーザー名「apache」、グループ名「users」で実行したい場合(openSUSEで実験したので、グループ名はusersになっています)は、

# vi /usr/local/apache2/conf/httpd.conf
httpd.conf
User daemon   ← apacheに置き換える
Group daemon   ← usersに置き換える

それで、Apacheを再起動して、再度ps -auxで、httpdを動かしているユーザーを確認すると、

# ps -aux | grep httpd
root     17985  0.0  0.4 121504 16944 ?        Ss   18:48   0:00 /usr/local/apache2/bin/httpd -k start
apache   17986  0.0  0.3 1327980 16060 ?       Sl   18:48   0:00 /usr/local/apache2/bin/httpd -k start
apache   17987  0.0  0.2 1327980 11980 ?       Sl   18:48   0:00 /usr/local/apache2/bin/httpd -k start
apache   17988  0.0  0.2 1327980 11980 ?       Sl   18:48   0:00 /usr/local/apache2/bin/httpd -k start
admin    18071  0.0  0.0   8176   836 pts/0    S+   18:48   0:00 grep --color=auto httpd

ちゃんとapacheがhttpdを実行していることがわかります。

Apache上のファイルを他人に見せたい場合と見せたくない場合

Apacheの実行ユーザーを変更できたので、今度は他人に見せるものと見せたくないものを管理したいと思います。

他人のユーザーで、読み込みを許可しない場合

例えば、ApacheのhtdocsフォルダがWebで公開されているものとして、そこには以下のファイルが存在しているものとしましょう。

# cd /usr/local/apache2/htdocs
# ls -l
合計 24
-rw-r--r-- 1 admin users  304  6月 25 17:43 connect.php
-rw-r--r-- 1 root  root    45  6月 12  2007 index.html
-rw-r--r-- 1 root  root    20  6月 25 17:14 phpi.php
-rw-r--r-- 1 root  root   172  6月 25 18:52 some.html
-rw-r--r-- 1 admin users  346  6月 25 17:45 test_form.html
-rw-r--r-- 1 admin users 1271  6月 25 17:45 test_form.php

そこで「some.html」にアクセスするとしましょう。所有者はroot、パーミッションは644なので、この設定は、誰でも「some.html」はアクセスできるという意味で、ちなみにApacheでのアクセスは読み込みだけを行い、書き込みも実行も行わないので、読み込むだけにします。

Apacheの実行ユーザーはapacheにしました。その際はちゃんと読み込めました

そこで所有者はrootのままで「some.html」のパーミッションを600(他のユーザーは読み込み不可能にし、ユーザー自身だけが使用できる状態にする)に変更してみたんです

# chmod 600 some.html
# ls -l
合計 24
-rw-r--r-- 1 admin users  304  6月 25 17:43 connect.php
-rw-r--r-- 1 root  root    45  6月 12  2007 index.html
-rw-r--r-- 1 root  root    20  6月 25 17:14 phpi.php
-rw------- 1 root  root   172  6月 25 18:52 some.html
-rw-r--r-- 1 admin users  346  6月 25 17:45 test_form.html
-rw-r--r-- 1 admin users 1271  6月 25 17:45 test_form.php

今度はForbiddenになって読み込めなくなった。Apacheの実行ユーザーapacheがrootのものを読み込むことを禁止されているから明らかだね。

Apache実行ユーザーと同じ所有者で、他人のユーザーを許可しないファイル

今度は、さきほどの「some.html」を、Apache実行ユーザーの所有にし、他のユーザーが読み込めない状態のままに変更して、再度アクセスできるか調べます。

# chown apache:users some.html
# ls -l
合計 24
-rw-r--r-- 1 admin  users  304  6月 25 17:43 connect.php
-rw-r--r-- 1 root   root    45  6月 12  2007 index.html
-rw-r--r-- 1 root   root    20  6月 25 17:14 phpi.php
-rw------- 1 apache users  172  6月 25 18:52 some.html
-rw-r--r-- 1 admin  users  346  6月 25 17:45 test_form.html
-rw-r--r-- 1 admin  users 1271  6月 25 17:45 test_form.php

今度はアクセスできた( ´ •̥ ̫ •̥ ` )
自分自身のファイルだから読み込めるんだね

Apache実行ユーザーと同じ所有者で、どのユーザーを許可しないファイル

最後に、誰も読み込みを許可しないhtmlで検証してみます
「some.html」パーミッションは000か200に設定し、誰も読み込みを許可しない設定にします

# chmod 200 some.html
# ls -l
合計 24
-rw-r--r-- 1 admin  users  304  6月 25 17:43 connect.php
-rw-r--r-- 1 root   root    45  6月 12  2007 index.html
-rw-r--r-- 1 root   root    20  6月 25 17:14 phpi.php
--w------- 1 apache users  172  6月 25 18:52 some.html
-rw-r--r-- 1 admin  users  346  6月 25 17:45 test_form.html
-rw-r--r-- 1 admin  users 1271  6月 25 17:45 test_form.php

そうだよね( ˙꒳​˙ᐢ )
自分自身も読み込みを許可していないと、確かにこうなるしね…

まとめ

ミドルウェアを構築しました、その次に来るのは、誰が何を許可・拒否するかの取り決めを行うことが重要視されるので、Qiitaにはぜひとも基本としてfirewalldと所有者でアクセス権を設定する方法もここに載せておきたかったです

高度なセキュリティ分野だと暗号化も入るらしいが、ファイルシステムの暗号化に踏み切ると、パスワードの入力や鍵のタイミングについても複雑な課題が入ってしまうので、初心者にはあまりお勧めできないかな…と思ったので、必要最低限の暗号化についてはHTTPSの構築方法にとどめておいた( ´ •̥ ̫ •̥ ` )

まぁ大事なのは、漏れては問題となるようなものを置かない、不要なポートを開放しない、それが絶対的な前提だしね…

参照サイト