ApacheにはVirtualhostという便利な機能があります。 来るリクエストの種類によって、受信するディレクトリを切り分けることが出来ます。 ドメインネームベースのVhostで注意点があります。

SSL問題: 1サーバ1サービス?

基本的にネームベースのVirtualhostではSSL複数ドメインを運用することは出来ません。 本来であれば、Apacheの着信時点でドメイン情報を元に切り分けを実施します。 すなわち、IPアドレスとリクエストホスト(ドメイン)がセットになり、受信したApacheが解析して振り分けします。

しかし、HTTPSの暗号化通信の場合では、HTTP通信をするまえにSSL/TLSハンドシェイクをする必要があります。 そのため、通信を着信した段階ではどのドメインに対してのリクエストなのか分からず、振り分けることが出来ません。

Apacheのデフォルトの挙動としては、複数Virtualhostが設定されており、振り分けが不明な場合は一番最初に読み込まれた設定に 飛ばすようになっています。

<Virtualhost *:80>
  ServerName rickynews.com
  DocumentRoot /deploy/rickynews_com
  CustomLog    "|/usr/sbin/rotatelogs /var/log/httpd/rickynews_com_access_log.%Y%m%d 86400 540" combined
  ErrorLog     "|/usr/sbin/rotatelogs /var/log/httpd/rickynews_com_error_log.%Y%m%d  86401 540"
  <Directory /deploy/rickynews_com>
      AllowOverride all
      Options -MultiViews -Indexes
  </Directory>
</Virtualhost>

<Virtualhost *:80>
  ServerName michaelnews.com
  DocumentRoot /deploy/michaelnews_com
  CustomLog    "|/usr/sbin/rotatelogs /var/log/httpd/michaelnews_com_access_log.%Y%m%d 86400 540" combined
  ErrorLog     "|/usr/sbin/rotatelogs /var/log/httpd/michaelnews_com_error_log.%Y%m%d  86401 540"
  <Directory /deploy/michaelnews_com>
      AllowOverride all
      Options -MultiViews -Indexes
  </Directory>
</Virtualhost>

上記が俗にいう「1サーバ、1SSL」の問題です。上記の現象を少し理解していると、同じサーバのApacheに複数のSSLサービスが入らないことが想像つきます。

「さすがにそれは不便だ」と感じた開発者がSNI: Server Name Indicationという技術を開発しました。HTTPSを行うSSL/TLS通信を拡張し、暗号化通信と共に平文のドメインの情報をやりとりできるようにしました。

一見全て解決したように感じますが、やはりそこには問題があります。それは「特定のブラウザでは対応していないものがある」という事実です。 WindowsXPのIE8が非対応になっているため、プロダクションユースではやはり無視することは出来ません。

結局、「だから、やっぱり出来ないんだ」という考えに至ってしまう人もいます。しかし、そこには誤解があります。

そもそもの誤解の発端

社内の新卒エンジニアなどと会話していて、ApacheのVirtualhostで勘違いしているポイントがあります。 ApacheではVirtualhostの振り分けは以下の3パターンで振り分け可能です。

  1. IPアドレス
  2. ポート番号
  3. ドメイン名(ServerName)

http://httpd.apache.org/docs/2.2/ja/mod/core.html#virtualhost

インターネット上に呪文のように<Virtualhost *:80>と記載されているのが影響しているのではないか?と感じております。 さて、この組み合わせがユニークになるようにすれば、自分が意図するサービスへ転送することが可能です。

Listen 80111
<Virtualhost *:80 *:80111>
  ServerName rickynews.com
  ~

上記のように記載すれば、以下のようなURLで直接任意のプロジェクトにアクセス可能になります。

http://192.168.33.104:80111/sampladata/

ネームベースのVirtualhostしか理解していないと、リリースの事前確認などをしてもらう時に、 Hostsの設定を各自でしてもらったり、あるいはDNSに対して適当なドメインを設定をする必要があります。

それは開発者として不親切です。ポート番号であれば、ユニークなので設定が不要でそのままアクセス可能です。 付加的に80番ポートの通常の設定をしていれば、各自でhostsを設定すれば、通常と同じ振る舞いでテストも可能です。

SSLの振り分けに戻る

上記のロジックを利用すれば「サービスがユニークに振り分けられるようにすればよい」ということが簡単に導けます。

SSLの性質を考えると、SSLではport443を取り決めとして通信を開始します。 ApacheでSSLのListenポートを変更も可能ですが、Webブラウザ側もSSLポートを指定する必要があるため、現実的でありません。 そこで出てくるのがIPアドレスの固定指定です。

<Virtualhost 192.168.33.103:443>

上記のようにSSLポートとIPアドレスを指定すれば、同じIPのVhostを記載しない限り、一意に転送可能です。 そこがポイントです。

IP Aliasを利用する

LinuxマシンではデフォルトでIPエイリアス機能を利用し、簡単に1つのnicに対して複数のIPアドレスを指定可能です。

$ cd /etc/sysconfig/network-scripts

# デバイス複製
$ cp -a ifcfg-em1 ifcfg-em1:0
DEVICE="em1:0"          #1/2 Device名を変更する
IPADDR="192.168.33.104" #2/2 好きなIPアドレスを指定する
NETMASK="255.255.255.0"
GATEWAY="192.168.33.1"
ONBOOT="yes"
TYPE="Ethernet"
$ service network restart

たったこれだけです。こうすることで、いくつでもIPアドレスを複製することが可能です。もちろんグローバルIPでも可能です。 これを指定すれば、あとは対応するVirtualHost設定をApacheに記載すれば終了です。

終わりに

このポート番号とIPアドレスの原理はインフラの様々な冗長化などにも応用可能です。MySQLもbindするIPアドレスを指定することができます。 MySQL辺などまた次回の機会に記載します。