朝から昼寝

ITや身近な小ネタをちょこちょこ整理

default VirtualHostのTLS設定が他のVirtualHostに反映される動作

概要

Apache HTTP Serverでdefault VirtualHostのTLS設定が他のVirtualHostに反映される動作がありました。
適切にVirtualHost設定すればほぼ遭遇しない動作ですが、気になったので留意点として整理しておきます。
本記事は、そのあたりの複数VirtualHost構成時のTLS設定の留意点についてまとめたものです。
対象はhttpd2.4系です。動作確認は主にRHEL系のLinuxで行っていますが、httpdの話なのでUbuntuBSD系でも同様です。

複数のVirtualHostを構成した際の動作全般については、別記事にまとめてあります。

※表記統一のため、本記事では"a virtual host"のことをconfig上の表記と同じ"VirtualHost"と記載します。

本記事の目的

  • Apache HTTP Serverで複数VirtualHost構成時のTLS設定の留意点を把握する。


目次

基本

留意点まとめ

今回、整理しておく留意点は以下です。

  1. Listen 443の場合、SSLEngine onが未定義でもTLS化されるケースがある

  2. default VirtualHostのTLS設定が他のVirtualHostに反映されるケースがある

いずれも、適切にVirtualHost設定すればほぼ遭遇しない動作のはずですが、検証などで色々試す際に再現することがあるかもしれません。

構成例と動作

  • 前提
    • Listen 192.168.0.1:443 (<VirtualHost>外のmain server設定(グローバル))
      IPアドレスを指定せずListen 443とした場合も同様の動作となる
    • 192.168.0.1:443で動作するVirtualHostは2つ (どちらも<VirtualHost 192.168.0.1:443>)
      • VirtualHost #1 (ServerName vhost1.nap)
        default VirtualHost(定義順が先)
      • VirtualHost #2 (ServerName vhost2.nap)
        non-default VirtualHost(定義順が後)
    • main serverにはTLS設定無し

httpd -SでVirtualHostの構成を表示すると、以下のようになる構成です。

# httpd -S
VirtualHost configuration:
192.168.0.1:443        is a NameVirtualHost
         default server vhost1.nap (/etc/httpd/conf.d/ssl.conf:42)
         port 443 namevhost vhost1.nap (/etc/httpd/conf.d/ssl.conf:42)
         port 443 namevhost vhost2.nap (/etc/httpd/conf/vhost.conf:1)
…

VirtualHostのTLS設定を変更しながら試したところ、以下の動作となりました。
"証明書"はHTTPSに用いるサーバ証明書のことです。

No. <VirtualHost> #1
(default)
TLS設定
<VirtualHost> #2
(リクエストにマッチ※)
TLS設定
適用されるTLS設定
1 証明書1を指定
SSLEngine未定義
証明書2を指定
SSLEngine未定義
TLSあり
(証明書2使用)
2 証明書1を設定
SSLEngine未定義
無し TLSあり
(証明書1使用)
3 無し 証明書2を指定
SSLEngine未定義
(SSLEngine onでも同様)
TLS無し

※クライアントからのリクエストがVirtualHost #2にマッチする際の動作です。リクエストのHost:ヘッダやSNIが"vhost2.nap"のケースです。

1. Listen 443の場合、SSLEngine onが未定義でもTLS化されるケースがある

前述のNo.1、No.2では、どこにもSSLEngine onが定義されていないにも関わらず、Listen 443が定義されているとhttpdのサービスポートはHTTPS化されます。
SSLEngineを省略した場合の初期値は"off"のはずなのですが、TLSが有効化されるということです。

細かく確認していませんが、おそらく条件としては、TLS設定上必須となるSSLCertificateFileSSLCertificateKeyFileがdefault VirtualHostに定義してあれば、このようにTLS化されるようです。
これは、SSLEngineを省略した場合の初期値"off"をオーバーライドする動作になっているように見えます。

なお、明示的にSSLEngine offを定義したり、443番以外のポートでListenした場合は、TLS化されないようです。

httpdのマニュアルに、SSLEngine onを省略してもTLS化されるケースがあるといった記載は見つけられませんでした。
Listenディレクティブの説明に、"https is the default for port 443 and http the default for all other ports. "という記載はあります。

2. default VirtualHostのTLS設定が他のVirtualHostに反映されるケースがある

複数のVirtualHostが同じIPアドレスの443/tcpポートを使用する場合(ネームベース)、default VirtualHostのTLS設定が使用されるケースがあります(3つ以上のVirtualHost構成については未確認)。
リクエストにマッチするVirtualHostにTLS設定が無くても、configの記載順で最初に定義されたdefault VirtualHostのTLS設定が使用されるというものです。

前述のNo.2のケースでは、<VirtualHost>#2にTLS設定が一切無いにも関わらず、<VirtualHost>#2宛のリクエストに対して<VirtualHost>#1のTLS設定(証明書1)が使用されます。

またNo.1のケースでは、<VirtualHost>#1と#2それぞれで異なる証明書を指定していますが、リクエストにマッチするVirtualHostの方の証明書が優先的に使用されます。SNIは機能しているようです。

逆に、No.3のケースではTLS化されませんでした。default VirtualHostにTLSの必須設定(SSLCertificateFileSSLCertificateKeyFile)が定義されていない場合は、non-default VirtualHost側でTLS設定してもTLS化されないようです。
ネームベースのVirtualHostをTLS化する場合は、default VirtualHostのTLS設定が必須なのでしょう。

なおTLS設定以外の設定については、本記事で説明したTLS動作とは異なり、(マニュアル通りに)マッチしたVirtualHostのものが反映されます(Header Set等)。

詳細

アクセスログ

前述の動作について、httpdデバッグログを記載しておきます。コメントは私の推測です。

前述のNo.1

  • main serverのログ
[ssl:info] [pid 269582:tid xxxxx] [client xxx.xxx.xxx.xxx:52273] AH01964: Connection to child 0 established (server vhost.vhost1.nap:443)
↑VirtualHost #1がコネクション確立
[ssl:debug] [pid 269582:tid xxxxx] ssl_engine_kernel.c(2382): [client xxx.xxx.xxx.xxx:52273] AH02043: SSL virtual host for servername vhost2.nap found
[core:debug] [pid 269582:tid xxxxx] protocol.c(2429): [client xxx.xxx.xxx.xxx:52273] AH03155: select protocol from , choices=h2,http/1.1 for server vhost2.nap
↑SNIによりVirtualHost #2にマッチング
[ssl:debug] [pid 269582:tid xxxxx] ssl_engine_kernel.c(2266): [client xxx.xxx.xxx.xxx:52273] AH02041: Protocol: TLSv1.3, Cipher: TLS_AES_256_GCM_SHA384 (256/256 bits)
↑TLSネゴシエーション完了
  • VirtualHost #2のログ
[ssl:debug] [pid 269582:tid xxxxx] ssl_engine_kernel.c(422): [client xxx.xxx.xxx.xxx:52273] AH02034: Initial (No.1) HTTPS request received for child 0 (server vhost2.nap:443)
↑VirtualHost #2がリクエストを処理

前述のNo.2

  • main serverのログ
[ssl:info] [pid 269887:tid xxxxx] [client xxx.xxx.xxx.xxx:52349] AH01964: Connection to child 73 established (server vhost.vhost1.nap:443)
[ssl:debug] [pid 269887:tid xxxxx] ssl_engine_kernel.c(2382): [client xxx.xxx.xxx.xxx:52349] AH02043: SSL virtual host for servername vhost2.nap found
[core:debug] [pid 269887:tid xxxxx] protocol.c(2429): [client xxx.xxx.xxx.xxx:52349] AH03155: select protocol from , choices=h2,http/1.1 for server vhost2.nap
↑ここまではNo.1と同じ

※VirtualHost #1のログを記録し忘れたせいか、"AH02041: Protocol: TLSv1.3, Cipher: TLS_AES_256_GCM_SHA384 (256/256 bits)"のログが見当たらなかった?(No.1ではmain serverのログに記録されているが)

  • VirtualHost #2のログ
[ssl:debug] [pid 269887:tid xxxxx] ssl_engine_kernel.c(422): [client xxx.xxx.xxx.xxx:52356] AH02034: Initial (No.1) HTTPS request received for child 75 (server vhost2.nap:80)
↑VirtualHost #2がリクエストを処理

前述のNo.3

httpdのreload時に、以下の警告あり

[ssl:warn] [pid 268640:tid xxxxx] AH01916: Init: (vhost.vhost1.nap:443) You configured HTTP(80) on the standard HTTPS(443) port!

HTTPSアクセス不可なので、アクセス時のログは省略。

その他の構成1(default VirtualHost無し、IPベース)

参考として、別構成の場合です。

  • 前提
    • 基本的に前述の構成例と同じだが、以下の差異あり
      • VirtualHost #1(vhost1.nap)が<VirtualHost _default_:443>
        (前述の構成例では<VirtualHost 192.168.0.1:443>だった)
      • この<VirtualHost _default_:443>は、Apache HTTP Serverのデフォルトのssl.confに定義されているものと同等

httpd -SでVirtualHostの構成を表示すると、以下のようになる構成です。

# httpd -S
VirtualHost configuration:
192.168.0.1:443       vhost2.nap (/etc/httpd/conf/vhost.conf:1)
*:443       vhost1.nap (/etc/httpd/conf.d/ssl.conf:42)
…

この場合も、VirtualHost #1のTLS設定の有無や、VirtualHost #2のSSLEngineの有無に関わらず、VirtualHost #2でSSLCertificateFileSSLCertificateKeyFileが定義してあれば、httpdのサービスポートはTLS化されます。
細かい条件までは未確認ですが、明示的にSSLEngine offを定義した場合には、TLS化されません。

なお、VirtualHost #1のTLS設定がVirtualHost #2に反映されることはありません。VirtualHostのIPアドレスとポート番号の組合せを超えて反映されることは無いようです。

その他の構成2(main serverでTLS設定)

  • 前提
    • 基本的に前述の構成例と同じだが、以下の差異あり
      • VirtualHost #1(vhost1.nap)が無し(main server)
        (前述の構成例では<VirtualHost 192.168.0.1:443>だった)
      • これは、Apache HTTP Serverのデフォルトのssl.confに定義されている<VirtualHost _default_:443>コメントアウトした構成と同等

httpd -SでVirtualHostの構成を表示すると、以下のようになる構成です。

# httpd -S
VirtualHost configuration:
192.168.0.1:443       vhost2.nap (/etc/httpd/conf/vhost.conf:1)
…

この構成はマニュアル通りの動作として、main serverの設定がVirtualHostに引き継がれます。VirtualHostにある設定はmain serverの設定をオーバーライドします。TLS設定も同様です。

ただし、SSLEngineの有無に関わらず、SSLCertificateFileSSLCertificateKeyFileが定義してあれば、httpdのサービスポートはTLS化されるという点は同様です。
細かい条件までは未確認ですが、明示的にSSLEngine offを定義した場合には、TLS化されません。

(参考)VirtualHostの構成確認とconfigダンプの方法

基本的にhttpd -SでVirtualHostの構成を確認できますが、configから確認したい場合は、httpd -t -D DUMP_CONFIG 2>/dev/null | grep -v '#'でconfigをダンプしてVirtualHostの定義順を確認できます。

参考URL