🪶

Apache・NginxおよびSquidによるWebサーバーの構築(LinuC202学習) #1

2024/07/30に公開

各種ソフトウェアに関しての概要

Apache

正式にはApache HTTP Server。歴史が長く、今も多く使われるOSSのWebサーバーソフトウェア。

Nginx

現在最も多く利用されているとされるWebサーバーソフトウェア。リバースプロキシとしても機能。

Squid

フォワードプロキシとして動作させることができるソフトウェア。リバースプロキシとしても機能できる。

構築

本稿の作業はすべてrootユーザーで行われる想定。(そのため、sudoは行っていません)

サーバー構成

構成図

サーバーが様々存在するので超簡易的に構成を記載。

  • マシンはVagrantテンプレートをもとにVirtualBoxにプロビジョニングされる。
  • サーバー・クライアントともにUbuntu。
  • クライアントフォワードプロキシとしてSquidが機能。
  • 各Webサーバーのうち、Nginxがホストされている192.168.3.2/redirect-sourceにアクセスするときリバースプロキシとして機能する。
  • Apacheの192.168.3.1/redirect-targetからコンテンツを取得し、レスポンスする。

Vagrantfile

Vagrantfile
Vagrant.configure("2") do |config|
    config.vm.define "apache-server" do |apache_server|
      apache_server.vm.box = "bento/ubuntu-24.04"
      apache_server.vm.hostname = "apache-server"
      apache_server.vm.network "private_network", ip: "192.168.3.10"
      apache_server.vm.provider "virtualbox" do |vb|
        vb.name = "apache-server"
        vb.memory = 1024
        vb.cpus = 1
      end
    end
    
    config.vm.define "nginx-server" do |nginx_server|
      nginx_server.vm.box = "bento/ubuntu-24.04"
      nginx_server.vm.hostname = "nginx-server"
      nginx_server.vm.network "private_network", ip: "192.168.3.11"
      nginx_server.vm.provider "virtualbox" do |vb|
        vb.name = "nginx-server"
        vb.memory = 1024
        vb.cpus = 1
      end
    end
    
    config.vm.define "squid-server" do |squid_server|
      squid_server.vm.box = "bento/ubuntu-24.04"
      squid_server.vm.hostname = "squid-server"
      squid_server.vm.network "private_network", ip: "192.168.3.12"
      squid_server.vm.provider "virtualbox" do |vb|
        vb.name = "squid-server"
        vb.memory = 1024
        vb.cpus = 1
      end
    end

    config.vm.define "web-client" do |web_client|
      web_client.vm.box = "bento/ubuntu-24.04"
      web_client.vm.hostname = "web-client"
      web_client.vm.network "private_network", ip: "192.168.3.13"
      web_client.vm.provider "virtualbox" do |vb|
        vb.name = "web-client"
        vb.memory = 1024
        vb.cpus = 1
      end
    end

  
    config.vm.provision "shell", inline: <<-SHELL
      sudo sh -c "echo 'DNS=8.8.8.8' >> /etc/systemd/resolved.conf"
      unlink /etc/resolv.conf
      ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf
      sudo systemctl restart systemd-resolved
    SHELL
  end
  

Apache設定

インストールとアクセス確認

以下コマンドでインストール~アクセス確認までを一気に実施。

apt update
apt install apache2 -y
systemctl status apache2 # この時点で起動と有効化がされているはず。
systemctl start apache2
systemctl enable apache2
ufw status
ufw allow http
ufw allow https
ufw enable
ufw status
curl http://localhost

Basic認証

Apacheにおける簡易的な認証はBasic認証によって行える。Basic認証用のモジュールはmod_auth_basic
a2enmod auth_basicで有効化する。

a2enmod auth_basic
# 下記ではすでにenableになっている
> Considering dependency authn_core for auth_basic:
> Module authn_core already enabled
> Module auth_basic already enabled

以下コマンドでtestuserユーザーを作成。パスワードはtestuser。

htpasswd -c /etc/apache2/.htpasswd testuser # testuserというユーザー名で/etc/apache2/.htpasswd にユーザー情報を作成
> New password: # パスワード。ここではtestuserと入力
> Re-type new password: # testuserと入力
> Adding password for user testuser

デフォルトで作成されている/etc/apache2/sites-available/000-default.confを編集する。

000-default.conf
 <VirtualHost *:80>
+        <Directory "/var/www/html/basic">
+                AuthType Basic
+                AuthName "Basic Auth Area"
+                AuthUserFile /etc/apache2/.htpasswd # 先ほど作成したユーザー情報が格納されたファイルを指定
+                Require valid-user
+        </Directory>
 </VirtualHost>

apache2ctl configtestコマンドでconfの構文を確認する。問題がなければSyntax OKと出力される。

apache2ctl configtest
# サーバー名がないと言われているものの、Syntax OKの出力。
> AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.2.1. Set the 'ServerName' directive globally to suppress this message
> Syntax OK

以下コマンドで/var/www/html/basic/index.htmlを作成する

mkdir /var/www/html/basic/
echo "Hello, Authorized User!"> /var/www/html/basic/index.html

apache2を再起動。

systemctl restart apache2

/var/www/html/basic/index.htmlにcurl。正しいユーザー・パスワードを-uオプションで指定すれば認証に成功し、htmlコンテンツがレスポンスされる。

curl -u testuser:testuser http://localhost/basic/index.html
> Hello, Authorized User!

# もし認証情報に誤りがあった場合は401番が返される
curl -u testuser:badpassword http://localhost/basic/index.html
> <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
> <html><head>
> <title>401 Unauthorized</title>
> </head><body>
> <h1>Unauthorized</h1>
> <p>This server could not verify that you
> are authorized to access the document
> requested.  Either you supplied the wrong
> credentials (e.g., bad password), or your
> browser doesn't understand how to supply
> the credentials required.</p>
> <hr>
> <address>Apache/2.4.58 (Ubuntu) Server at localhost Port 80</address>
> </body></html>

アクセス制限

mod_authz_hostによってIPアドレスによるアクセス制限が可能。

a2enmod authz_host
# 通常時はすでにenabled
> Considering dependency authz_core for authz_host:
> Module authz_core already enabled
> Module authz_host already enabled
mkdir /var/www/html/client-only
echo "hello, Client!"> /var/www/html/client-only/index.html

/etc/apache2/sites-available/000-default.confを以下のように編集。

000-default.conf
 <VirtualHost *:80>
+         <Directory "/var/www/html/client-only"> # 先ほどmkdirしたclient-onlyにIP制限をかける
+                 Order Deny,Allow # DenyとAllowが評価される順番。Denyが先に評価される。
+                 Deny from all # 全IPアドレスからのアクセスをDeny
+                 Allow from 192.168.3.13 # 192.168.3.13からのアクセスをAllow
+         </Directory>
 </VirtualHost>

評価順がDeny→Allowなので、allで塞がれた経路があとに評価されるallow {特定のIPアドレス}によって、そのアドレスのみに穴が開く形となる。

ここまででconfigtestと再起動を行う。

apache2ctl configtest # Syntax OKの出力を確認
systemctl restart apache2

これで準備完了。Webサーバーからのローカルアクセスとクライアントアクセスを比較する。

まずはWebサーバーからのCurl。

root@apache-server:~# curl http://192.168.3.10/client-only/index.html # apache-serverからCurlを実行。
> <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
> <html><head>
> <title>403 Forbidden</title> # 403でアクセス拒否。
> </head><body>
> <h1>Forbidden</h1>
> <p>You don't have permission to access this resource.</p>
> <hr>
> <address>Apache/2.4.58 (Ubuntu) Server at 192.168.3.10 Port 80</address>
> </body></html>

アクセス権限がないということで403番がレスポンスされる。

続いてクライアントからのCurl。

vagrant@web-client:~$ curl http://192.168.3.10/client-only/index.html # クライアントからCurl
hello, Client! # index.htmlのコンテンツをレスポンス

アクセス成功。

リライト(.htaccessの編集によって)

特定のURLにアクセスされた際、URLを書き換えるリライトの実装。
今回はドキュメントディレクトリにリライトの設定が記載された.htaccessを設置し、実装する。.htaccessは個々のディレクトリに配置することで、その範囲内で記載の設定を機能させることができる設定ファイルである。
しかし、.conf上で対象のディレクトリで明示的にAllowOverrideをしている必要がある。設定への記載は後述。

ディレクトリとファイルの準備。

mkdir /var/www/html/redirect
mkdir /var/www/html/redirect-target
echo "hello, Redirected User!"> /var/www/html/redirect-target/index.html

モジュールmod_rewriteを有効化する。

a2enmod rewrite

/var/www/html/redirect ディレクトリに.htaccessファイルを作成、以下のように編集する。

.htaccess
RewriteEngine On # mod_rewriteを有効にする
RewriteRule ^$ /redirect-target [R=301,L] # リライトのルールを記載。

/redirect に対して「/redirect-target」を置き換える(追記する)。
また、301番でリダイレクトをし、以降のリライト処理を中止する。
つまり、もしドメインがexample.comの場合、 http://example.com/redirectにアクセスしたら、http://example.com/redirect-targetにURLが置き換えられたうえで301リダイレクトが行われる。
http://example.com/redirect-targetでは更にリダイレクトが発生しhttp://example.com/redirect-target/index.htmlが返される格好である。

最後に/etc/apache2/sites-available/000-default.confを以下のように編集し、AllowOverrideする。

000-default.conf
 <VirtualHost *:80>
+         <Directory /var/www/html>
+             AllowOverride All
+         </Directory>
 </VirtualHost>

再起動とCurlの実行。Curlに-Lオプションを付けてリダイレクトを許容。リダイレクト先であるhttp://localhost/redirect-target/index.htmlのレスポンスが得られる。

systemctl restart apache2
curl -L http://localhost/redirect/
> hello, Redirected User!

TLS設定(OpenSSLによる自己署名証明書の発行)

HTTPS通信を行うためにはサーバー証明書が必要になる。一般的にはLet's Encrypt、Degicertなどの認証局にCSRを渡し、それらCAの署名付きの証明書を受け取り、利用する。
しかし今回はテストということで自己署名証明を使うことでそれらを賄う。もちろん一般に公開するWebサイトでは利用に耐えないので、テスト用、プライベート環境での利用に留めるべきである。

証明書のSANsにはIPアドレスを設定することができるので、今回はそれを用いてSSLを実現する。

秘密鍵を作成。

openssl genpkey -algorithm RSA -out private.key

とほほのOpenSSL入門 - とほほのWWW入門

以下のopenssl.cnfを作成する。

openssl.cnf
[ req ]
default_bits        = 2048 # 鍵の長さを2048ビット
distinguished_name  = req_distinguished_name # ディスティングイッシュネームを設定する。ここでは下記の[ req_distinguished_name ] で規定するとしている。
req_extensions     = req_ext # 追加の拡張設定を[ req_ext ] にて規定するとしている
prompt              = no # コマンド実行時に対話的なプロンプトは表示せず、このcnfファイル内の設定を使用する

[ req_distinguished_name ] # 証明書に含まれる識別情報
C  = JP # CountryのC。日本のJP
ST = Tokyo # StateのST。Tokyo
L  = Chiyoda-ku # LocalityのL。千代田区としてChiyoda-ku
O  = Example Company # OrganizationのO。一般には企業名などが入るが、Example Companyと仮置き。
CN = example.com # Common NameのCN。ドメイン名を入れる。ここではexample.comと仮置き。

[ req_ext ]
subjectAltName = @alt_names # SANの設定。下の[ alt_names ]の部分で定義。

[ alt_names ]
IP.1 = 192.168.3.10 # サブジェクトの別名としてこのApacheのIPアドレスを設定  
IP.2 = 192.168.3.11 # NginxのIPアドレスも設定。(もちろん実運用ではドメイン名を設定することがごく一般的)

先ほど作成した秘密鍵をもとにCSR(証明書署名要求)を作成。

openssl req -new -key private.key -out certificate.csr -config openssl.cnf

CSRをもとに証明書を作成。

openssl x509 -req -days 365 -in certificate.csr -signkey private.key -out selfsigned.crt

これらを/etc/ssl/配下に移動

cp selfsigned.crt /etc/ssl/certs/
cp private.key /etc/ssl/private/
rm private.key # 証明書は削除しておく

TLS設定(apacheへの証明書インストール)

mod_sslを有効化する。

sudo a2enmod ssl
> a2enmod ssl
> Considering dependency mime for ssl:                                                                                                                                                                      Module mime already enabled
> Considering dependency socache_shmcb for ssl:
> Module socache_shmcb already enabled
> Enabling module ssl.
> See /usr/share/doc/apache2/README.Debian.gz on how to configure SSL and create self-signed certificates.
> To activate the new configuration, you need to run:
> systemctl restart apache2

すでに存在する/etc/apache2/sites-enabled/default-ssl.confを以下のように編集する。

default-ssl.conf
 <VirtualHost *:443>
         ServerAdmin webmaster@localhost
 
         DocumentRoot /var/www/html
 
         ErrorLog ${APACHE_LOG_DIR}/error.log
         CustomLog ${APACHE_LOG_DIR}/access.log combined
 
+         SSLEngine on
+ 
+         SSLCertificateFile      /etc/ssl/certs/selfsigned.csr # 先ほど設置した証明書
+         SSLCertificateKeyFile   /etc/ssl/private/private.key # 先ほど設置した秘密鍵
+         SSLProtocol             +TLSv1.2 # TLS1.2以上を使用する(逆に安全ではないSSLやTLS1.0、TLS1.1は使わない)
+         SSLCipherSuite          HIGH:!aNULL:!MD5:!3DES # 暗号スイート設定。HIGH:キー長が128bit以上、!aNULL:暗号化なしを除外(!は除外の意)、!MD5/!3DES:安全ではないスイートを除外
 </VirtualHost>

先程までとは別の設定ファイルを編集しているが、default-ssl.confはデフォルトで設定ファイルとして読み込まれない。
a2ensiteコマンドで有効化してあげる。

a2ensite default-ssl
> Enabling site default-ssl.
> To activate the new configuration, you need to run:
> systemctl reload apache2

ここまでの設定を適用するためにapache2を再起動

systemctl restart apache2

Curlしてみる。curl https://192.168.3.10では失敗する。自己署名証明書であり、その正当性を確認できないためである。

curl -v https://192.168.3.10
> *   Trying 192.168.3.10:443...
> * Connected to 192.168.3.10 (192.168.3.10) port 443
> * ALPN: curl offers h2,http/1.1
> * TLSv1.3 (OUT), TLS handshake, Client hello (1):
> *  CAfile: /etc/ssl/certs/ca-certificates.crt
> *  CApath: /etc/ssl/certs
> * TLSv1.3 (IN), TLS handshake, Server hello (2):                                                                                                                                                          * TLSv1.2 (IN), TLS handshake, Certificate (11):
> * TLSv1.2 (OUT), TLS alert, unknown CA (560):
> * SSL certificate problem: self-signed certificate
> * Closing connection
> curl: (60) SSL certificate problem: self-signed certificate # 自己署名証明書だということのお叱り
> More details here: https://curl.se/docs/sslcerts.html
> 
> curl failed to verify the legitimacy of the server and therefore could not
> establish a secure connection to it. To learn more about this situation and
> how to fix it, please visit the web page mentioned above.

verboseで提示されたdocsを見ると「-kオプションで無視するか、--cacert--with-ca-bundle=などオプションで証明書を提示してくれ」とのこと。
無視するのは甲斐がないので、--cacertしてみよう。

 curl --cacert /etc/ssl/certs/selfsigned.crt https://192.168.3.10
> hello, world!

成功。

次回に続く

続きはTBUです。

参考情報

Discussion