0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ApacheのVirtualHost設定で嵌った話し

Last updated at Posted at 2025-02-27

先日、Apacheを使ってTLS・SSLに対応したバーチャルホストを構築していてど嵌まりしたことについて書いてみました。
そもそもの発端はApacheでバーチャルホストを構築する際の設定は何処に書くべきなのか?なのだけど、古い(例えばApache 2.2系列時代の)記事では/etc/httpd/conf/httpd.confやTLS・SSL通信に対応するなら/etc/httpd/conf.d/ssl.confを修正するなり、設定を追加するなりして自分のホスト環境に合わせての設定を行っている記事が多かったように思います。
しかし最近のApache 2.4系列のApacheでは、直接/etc/httpd/conf/httpd.conf/etc/httpd/conf.d/ssl.confを修正するするのではなく、/etc/httpd/conf.d/の下に新しく自分のホスト(WEBサイト)向けの設定ファイルを新たに作りここに設定を書き込むことで設定を行うことが主流になっているようです。
で、今回も新しいWEBサイトを設定するにあたり/etc/httpd/conf/httpd.confの下に[ドメイン名].confファイルを作り、そこで以下のような設定を行いApacheを起動してWEBブラウザからサイトにアクセスしてみましたが、TLS・SSL接続を使わずにhttp://[ドメイン名]でアクセスするとちゃんと想定通りの内容が表示されるのだけど、何故か設定がTLS・SSL接続(https://[ドメイン名])でアクセスするとデフォルトのApacheテストページが表示されてしまうし、TLS・SSL証明書も/etc/httpd/conf.d/ssl.confで指定されている自動生成されるダミーの自己証明書が使われてしまうと言う謎の現象に遭遇しました。

/etc/httpd/conf.d/の下に追加して作った設定ファイルの内容

webserver.example.com.conf
<VirtualHost *:80>
    ServerName webserver.example.com
    DocumentRoot /var/www/webserver.example.com/public_html
    ErrorLog /var/log/httpd/webserver.example.com-error.log
    CustomLog /var/log/httpd/webserver.example.com.log combined
</VirtualHost>

<VirtualHost *:443>
    ServerName webserver.example.com
    DocumentRoot /var/www/webserver.example.com/public_html
    ErrorLog /var/log/httpd/webserver.example.com-ssl_error.log
    CustomLog /var/log/httpd/webserver.example.com.ssl_log combined

    SSLCertificateFile /etc/letsencrypt/live/webserver.example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/webserver.example.com/privkey.pem
    Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>

「あれ?変だな〜」と思い、まず試してみたのは自分が運用している他のバーチャルホストをサポートする環境に同じ設定を作り、そちらにDNSの向き先を変えて動かしてみた。
でも特に同じような問題は発生せずにhttp://webserver.example.com/でもリアルなホスト名でもアクセスができる。
次にチェックしたのがmod_sslをインストールしたときに同時にインストールされた/etc/httpd/conf.d/ssl.confの設定。
/etc/httpd/conf.d/ssl.confの中には以下のようにすでにデフォルトのバーチャルホストの設定が書かれている。

/etc/httpd/conf.d/ssl.confの40行目あたり

/etc/httpd/conf.d/ssl.conf
##
## SSL Virtual Host Context
##

<VirtualHost _default_:443>

# General setup for the virtual host, inherited from global configuration
#DocumentRoot "/var/www/html"
#ServerName www.example.com:443

あ〜、そうか!ここでデフォルト(_defallt_)のバーチャルホストの設定がされているから、別の設定ファイルでバーチャルホストの設定をしても、そのホスト名(ServerNameServerAlias)が実ホスト名にマッチしちゃうとこちらが優先されちゃうのか!
と気がついて、Apacheを稼働させているホストの名前をwebserver.example.comから、vhost.example.comとか別の名前に変えてみた。
しかし、それでも結果は変わらない。
そこで思いついたのは、もしかしてTLS・SSL証明書のコモンネーム(CN)がマッチする証明書を使う設定があるとそれが優先されるのかな?と…

で、opensslコマンドで証明書の内容をチェックしてみた。

openssl x509 -text -in /etc/pki/tls/certs/localhost.crt
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 475065158036033417 (0x697c53e85e70f89)
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=US, O=Unspecified, OU=ca-2753923304029444439, CN=webserver.example.com, emailAddress=root@webserver.example.com
        Validity
            Not Before: Feb 21 06:28:31 2025 GMT
            Not After : Feb 21 06:28:31 2026 GMT
        Subject: C=US, O=Unspecified, CN=webserver.example.com, emailAddress=root@webserver.example.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:bf:28:8b:f3:c1:eb:02:38:4f:1c:05:7f:a0:4f:

あれ?証明書のコモンネーム(CN)hが変わっていない?
ちょっと調べてみるとこのダミーの証明書はApache起動時に/usr/libexec/httpd-ssl-gencertsスクリプトで自動生成され、ダミーの証明書がすでに生成済みな場合には再生成はされずに、過去に生成されたものをそのまま使うらしいので、まずは過去に生成されたダミーの証明書を削除してApacheを再起動してみた。

ダミーのTLS・SSL証明書の削除と再生成

sudo rm /etc/pki/tls/certs/localhost.crt
sudo rm /etc/pki/tls/private/localhost.key
sudo systemctl restart httpd

もう一度opensslコマンドで証明書の内容をチェックしてみた。
ダミーの証明書のコモンネーム(CN)は設定しなおしたホスト名('vhost.example.com')に変わっていたので、WEBブラウザでhttps://webserver.example.com/へとアクセスしてみる。

やっぱり表示されるのは変わらずにApacheのテストページ

opensslでリモートからhttps://webserver.example.com/へとアクセスして証明書をチェックしてみるとやはりダミーの証明書(コモンネームは'vhost.example.com'に変わっている)が使われているので、Apacheの設定ファイルとしては/etc/httpd/conf.d/ssl.confが使われているらしい。

openssl s_client -connect webserver.example.com:443 -servername webserver.example.com
Connecting to nnn.nnn.nnn.nnn
CONNECTED(00000005)
depth=1 C=US, O=Unspecified, OU=ca-5015121004957820975, CN=vhost.example.com, emailAddress=root@vhost.example.com
verify error:num=19:self-signed certificate in certificate chain
verify return:1
depth=1 C=US, O=Unspecified, OU=ca-5015121004957820975, CN=vhost.example.com, emailAddress=root@vhost.example.com
verify return:1
depth=0 C=US, O=Unspecified, CN=vhost.example.com, emailAddress=root@vhost.example.com
verify return:1
---
Certificate chain
 0 s:C=US, O=Unspecified, CN=vhost.example.com, emailAddress=root@vhost.example.com
   i:C=US, O=Unspecified, OU=ca-5015121004957820975, CN=vhost.example.com, emailAddress=root@vhost.example.com
   a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256
   v:NotBefore: Feb 25 05:47:07 2025 GMT; NotAfter: Feb 25 05:47:07 2026 GMT
 1 s:C=US, O=Unspecified, OU=ca-5015121004957820975, CN=vhost.example.com, emailAddress=root@vhost.example.com
   i:C=US, O=Unspecified, OU=ca-5015121004957820975, CN=vhost.example.com, emailAddress=root@vhost.example.com
   a:PKEY: rsaEncryption, 4096 (bit); sigalg: RSA-SHA256
   v:NotBefore: Feb 25 05:47:07 2025 GMT; NotAfter: Feb 25 05:47:07 2026 GMT
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIE5jCCAs6gAwIBAgIIPZ3D9IPUdvQwDQYJKoZIhvcNAQELBQAwgYkxCzAJBgNV
BAYTAlVTMRQwEgYDVQQKDAtVbnNwZWNpZmllZDEfMB0GA1UECwwWY2EtNTAxNTEy

クライアントからアクセスするのに使ったホスト名(-servername)はwebserver.example.comで、証明書のコモンネーム(CN)はvhost.example.comで、ましてや自己証明書なので、当然検証の結果はエラー(Verify return code: 19 (self-signed certificate in certificate chain))

    00d0 - 88 55 85 af 9a 0e 37 4f-43 9e f4 3b 6f 33 82 39   .U....7OC..;o3.9
    00e0 - ef db 42 4e 9b 55 00 b1-90 8f 1b bc 9a 2b 95 5e   ..BN.U.......+.^

    Start Time: 1740462620
    Timeout   : 7200 (sec)
    Verify return code: 19 (self-signed certificate in certificate chain)
    Extended master secret: no
    Max Early Data: 0
---
read R BLOCK
closed

ちなみに、hostnameコマンドで問い合わせても、念の為に/etc/hostnameファイルを確認してみてもApacheを動作させているホストのホスト名はvhost.example.comとなっているし、ダミーのTLS・SSL証明書のコモンネーム(CN)はvhost.example.comだし、ではApacheは何処で_default_にマッチするホスト名としてwebserver.example.comを設定してしまったのだろう?ということで、次の手段としてhttpd-Sオプションを付けて起動して、設定ファイルの読み込みと設定の経過を観察してみた。

/usr/sbin/httpd -S
VirtualHost configuration:
*:80                   webserver.example.com (/etc/httpd/conf.d/webserver.example.com.conf:1)
*:443                  is a NameVirtualHost
         default server webserver.example.com (/etc/httpd/conf.d/ssl.conf:40)
         port 443 namevhost webserver.example.com (/etc/httpd/conf.d/ssl.conf:40)
         port 443 namevhost webserver.example.com (/etc/httpd/conf.d/webserver.example.com.conf:8)
ServerRoot: "/etc/httpd"
Main DocumentRoot: "/var/www/html"
Main ErrorLog: "/etc/httpd/logs/error_log"

すると何故か?/etc/httpd/conf.d/ssl.confがデフォルトのホスト名(_default_)として認識しているホスホネームがwebserver.example.comとなっている。
grepを使いgrep -lr "webserver.example.com" /etcとかやって文字列webserver.example.comを探しても何処にも無いし、このホスト名は一体どこから持ってきた?と思ったら、思わぬところに伏兵が!
今回のWEBサーバー構築にはさくらインターネットの「さくらのVPS」を使ったので、「さくらのVPS」ではちゃんとVPSホストに割り当てられたIPアドレスに対してDNSの逆引きのホスト名を設定できるので、これをwebserver.example.comに設定していたのだが、どうやらApacheは設定ファイルを読み込んでのデフォルトのバーチャルホストのホスト名(_default_)には、ホストのIPアドレスから逆引きして得られたホスト名を使うらしい。

と言うことは!
もしかして、DNSの逆引きホスト名をwebserver.example.comじゃないホスト名に変えてしまうか、さくらインターネットが決めた元のホスト名に戻してしまえば上手く動くんじゃないの?
と思ってやってみました。

dig -x nnn.nnn.nnn.nnn
; <<>> DiG 9.16.23-RH <<>> -x nnn.nnn.nnn.nnn
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 46072
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;nnn.nnn.nnn.nnn.in-addr.arpa.	IN	PTR

;; ANSWER SECTION:
nnn.nnn.nnn.nnn.in-addr.arpa. 3600 IN	PTR	wwwXXXXue.sakura.ne.jp.

;; Query time: 511 msec
;; SERVER: 210.224.163.3#53(210.224.163.3)
;; WHEN: Wed Feb 26 16:53:40 JST 2025
;; MSG SIZE  rcvd: 91

案の定!、DNSの逆引きホスト名を元に戻したらhttpd -Sで確認したデフォルトサーバー(default server)のホスト名がさくらインターネットで定義されたホスト名に変わっています。
※この時、WEBサーバーのホストのhostnameコマンドや/etc/hostnameの設定はwebserver.example.comのままです。

/usr/sbin/httpd -S
VirtualHost configuration:
*:80                   webserver.example.com (/etc/httpd/conf.d/webserver.example.com.conf:1)
*:443                  is a NameVirtualHost
         default server wwwXXXXue.sakura.ne.jp (/etc/httpd/conf.d/ssl.conf:40)
         port 443 namevhost wwwXXXXue.sakura.ne.jp (/etc/httpd/conf.d/ssl.conf:40)
         port 443 namevhost webserver.example.com (/etc/httpd/conf.d/webserver.example.com.conf:8)
ServerRoot: "/etc/httpd"
Main DocumentRoot: "/var/www/html"
Main ErrorLog: "/etc/httpd/logs/error_log"

この状態でWEBブラウザなどのクライアントアプリからhttps://webserver.example.com/へとアクセスすれば期待通りのバーチャルホスト設定/etc/httpd/conf.d/webserver.example.com.confが使われるはずです。

試しにopensslを使ってWEBサーバーへと接続して想定どおりの証明書が使われているかを確認してみます。

openssl s_client -connect webserver.example.com443 -servername webserver.example.com
Connecting to nnn.nnn.nnn.nnn
CONNECTED(00000005)
depth=2 C=US, O=Internet Security Research Group, CN=ISRG Root X1
verify return:1
depth=1 C=US, O=Let's Encrypt, CN=R11
verify return:1
depth=0 CN=example.com
verify return:1
---
Certificate chain
 0 s:CN=example.com
   i:C=US, O=Let's Encrypt, CN=R11
   a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256
   v:NotBefore: Feb 15 11:46:07 2025 GMT; NotAfter: May 16 11:46:06 2025 GMT
 1 s:C=US, O=Let's Encrypt, CN=R11
   i:C=US, O=Internet Security Research Group, CN=ISRG Root X1
   a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256
   v:NotBefore: Mar 13 00:00:00 2024 GMT; NotAfter: Mar 12 23:59:59 2027 GMT
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIFMzCCBBugAwIBAgISA5P3+oblpLleVpcbszj2CTdmMA0GCSqGSIb3DQEBCwUA
MDMxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQwwCgYDVQQD

この証明書ではCN=example.comとなっていますが、これはここで使った証明書がワイルドカード証明書で、マッチするドメイン名は証明書のSAN(Subject Alternative Name)で指定されています。
例えば、この証明書が置かれたホスト上で以下のように証明書の内容を確認することで、SANでどのようなホスト名をマッチさせているかを確認することができます。

openssl x509 -text -in /etc/letsencrypt/live/example.com/cert.pem
            X509v3 Subject Alternative Name:
                DNS:*.example.com, DNS:example.com

ちゃんと最後のベリファイ結果もOKとなっているようです。

    00d0 - 79 f3 2a bf 8f 9f 06 2c-23 a8 9f 92 b2 67 ee be   y.*....,#....g..
    00e0 - 6a fd 60 ab 2b 90 ab b2-b5 52 33 4f 1f e8 61 a7   j.`.+....R3O..a.

    Start Time: 1740557291
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
    Extended master secret: no
    Max Early Data: 0
---
read R BLOCK
closed

ここまで散々と弄って何なのだけど、さらに色々と試しているうちにもっと単純なルールを発見!
実は!(も何も、当たり前と言われれば当たり前なのだけど…)諸々の設定は早い者勝ち的に先に定義されたものが有効になるようなので、/etc/httpd/conf.d/の中から読み込まれる設定ファイルの順番をファイル名を工夫することで調整してssl.confよりも前に追加したバーチャルホストの設定ファイル(今回の例では/etc/httpd/conf.d/webserver.example.com.conf)が読み込まれるようにすればこれが有効になるので全て解決です。

この/etc/httpd/conf.d/ディレクトリの中から設定ファイルが読み込まれる順番はディレクトリをlsコマンドでリストした際の表示順(アルファベット順)で読み込まれます。

例えば今回の例では/etc/httpd/conf.d/ディレクトリをlsコマンドでリストすると
(-1オプションはファイル名を1行に1件表示するオプションです)

ls -1 /etc/httpd/conf.d/*.conf
/etc/httpd/conf.d/autoindex.conf
/etc/httpd/conf.d/ssl.conf
/etc/httpd/conf.d/userdir.conf
/etc/httpd/conf.d/webserver.example.com.conf
/etc/httpd/conf.d/welcome.conf

のようにssl.confwebserver.example.com.confより先に読み込まれることが判ります。

これを例えば以下のようにファイルwebserver.example.com.confの前に文字列00_を付けることで00_webserver.example.com.confssl.confより先に読み込ませることができます。

ls -1 /etc/httpd/conf.d/*.conf
/etc/httpd/conf.d/00_webserver.example.com.conf
/etc/httpd/conf.d/autoindex.conf
/etc/httpd/conf.d/ssl.conf
/etc/httpd/conf.d/userdir.conf
/etc/httpd/conf.d/welcome.conf

その様子をhttpd -Sで確認しても先にバーチャルホストwebserver.example.comの設定として/etc/httpd/conf.d/00_webserver.example.com.confが使われているのが判ります。

/usr/sbin/httpd -S
VirtualHost configuration:
*:80                   webserver.example.com (/etc/httpd/conf.d/00_webserver.example.com.conf:1)
*:443                  is a NameVirtualHost
         default server webserver.example.com (/etc/httpd/conf.d/00_webserver.example.com.conf:8)
         port 443 namevhost webserver.example.com (/etc/httpd/conf.d/00_webserver.example.com.conf:8)
         port 443 namevhost webserver.example.com (/etc/httpd/conf.d/ssl.conf:40)
ServerRoot: "/etc/httpd"
Main DocumentRoot: "/var/www/html"
Main ErrorLog: "/etc/httpd/logs/error_log"

でも、このディレクトリの中の設定ファイルの読み込み順で動作が変わっちゃうやつに関しては、大抵の場合はここでの対処のように最初から(パッケージ化する時点で!)nn_ファイル名みたいになっていて、それを見てみんな「ああそうか!そんな感じでファイル名には注意が必要なのか!」と気がつくんだと思うんで「先にそれを言ってよ〜」な感じはする…

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?