世の中にはTLS v1.0も許可しない業界があるようで
TLS v1.2のみ有効なサイトのWEB監視ができません!
お客様より問い合わせを頂いた内容はまさしくこんな感じでした。
HTTPサーバの暗号化通信において、SSLプロトコルに対してTSL v1.2のみを有効にしたサイトの監視を行ったところ、「SSL connect error」となり監視を正しく行えないとのことです。
今までOpenSSL関連の脆弱性により、いくつかのSSLプロトコルを禁止することは今や常識だったりしますが、ここまで限定することはありませんでした。(ちなみに、SSL v2とSSL v3は使っちゃだめですよ!)
なんでこんなに制限するかな~と思いつつ、ぐ~ぐる様に問い合わせてみると、PCI DSSという基準があり、ここではTLS v1.2しか許されないとのことです。きっ、厳しいですね。しかし、ごにょごにょあって移行期間を延長するとかごにょごにょ… まあ、触れないようにしておきましょう、MIRACLE ZBXには関係ないし。
ちなみに、今回はソースコードの解説というより、汎用性の高い修正の仕方な感じです。
調査開始
Zabbix内でのWeb監視はlibcurlを使用しているので、cURLから調査です。
テスト用のオレオレ証明書を仕組んだHTTPサーバをTLSv1.2のみを有効にします。
その後、手元のAsianux 4でコマンドを打ってみると…
$ curl https://...
curl: (35) SSL connect error
ダメじゃん!
curlのマニュアルを見ると、こんなオプションがあります。
-1/--tlsv1
(SSL) Forces curl to use TLS version 1.x when negotiating with a remote TLS server.
You can use options --tlsv1.0, --tlsv1.1, and --tlsv1.2 to control the TLS version more
precisely (if the SSL backend in use supports such a level of control).
--tlsv1.0
(SSL) Forces curl to use TLS version 1.0 when negotiating with a remote TLS server.
(Added in 7.34.0)
--tlsv1.1
(SSL) Forces curl to use TLS version 1.1 when negotiating with a remote TLS server.
(Added in 7.34.0)
--tlsv1.2
(SSL) Forces curl to use TLS version 1.2 when negotiating with a remote TLS server.
(Added in 7.34.0)
-2/--sslv2
(SSL) Forces curl to use SSL version 2 when negotiating with a remote SSL server.
-3/--sslv3
(SSL) Forces curl to use SSL version 3 when negotiating with a remote SSL server.
ということで、-1や--tlsv1.2を使用すれば良さそうなので再テスト。
$ curl -k --tlsv1.2 https://...
WEBページのソース
$ curl -k -1 https://...
WEBページのソース
上手く行きました。
cURL側での実装は問題はなさそうなので、Zabbix側の修正をすれば問題は解決しそうです。
libcurlのオプション curl_easy_setopt()
libcurlを用いるときには、curl_easy_setopt()を使用してアクセスのオプションを設定します。
今回は、Zabbix側のソースコードは割愛します。curl_easy_setopt()のマニュアルに下記のようにあります。
CURLOPT_SSLVERSION
Pass a long as parameter to control what version of SSL/TLS to attempt to use. The
available options are:
CURL_SSLVERSION_DEFAULT
The default action. This will attempt to figure out the remote SSL protocol ver-
sion, i.e. either SSLv3 or TLSv1 (but not SSLv2, which became disabled by
default with 7.18.1).
CURL_SSLVERSION_TLSv1
Force TLSv1.x
CURL_SSLVERSION_SSLv2
Force SSLv2
CURL_SSLVERSION_SSLv3
Force SSLv3
CURL_SSLVERSION_TLSv1_0
Force TLSv1.0
CURL_SSLVERSION_TLSv1_1
Force TLSv1.1
CURL_SSLVERSION_TLSv1_2
Force TLSv1.2
Zabbixのコードを確認すると、このCURLOPT_SSLVERSIONはセットされていません。すなわち、デフォルトの値(SSL v3とTLS v1)が使用されます。ただし、上述のテストの挙動をみるとTLS v1は1.0に限定のようです。
そして、このCURLOPT_SSLVERSIONを上手いこと設定すれば問題なくなるでしょう。実際、CURL_SSLVERSION_TLSv1_2をセットすれば問題なく監視を行うことができました。
ただし、汎用性を持たせるためには、CURL_SSLVERSION_TLSv1とすべきです。というのも、古いパッケージを確認しましたが、v1_1とv1_2は実装されていません。このような状況でv1_2をセットしたコードをコンパイルしようとしたり、他でコンパイルしたバイナリを実行しようとした場合問題が発生します。v1としておけばこれらの問題を回避することができます。
既に報告済みです
当社のパッチ投稿リストにあるように、すでに投稿済みです。
実は、このZabbix LLCでの報告もお客様が問い合わせ時に教えてくれたものです(ありがたいことです)。
詳しくは、リンク先を見ていただくとして、二種類のパッチを作成しました。
本当の問題は実はcURLなのです
先ほどのcurl_easy_setopt()のマニュアルを読んで違和感を覚えた方もいるでしょう。
「SSL v3 と TLS v1系全般でアクセスする」と記述されているにもかかわらず、TLS v1.1とv1.2で失敗しているのです。cURLのコード(curl-7.19.7-46)を調査していくと、バグらしきところを発見しました。
lib/nss.c :
1173 static CURLcode nss_init_sslver(SSLVersionRange *sslver,
1174 struct SessionHandle *data)
1175 {
1176 switch (data->set.ssl.version) {
1177 default:
1178 case CURL_SSLVERSION_DEFAULT:
1179 sslver->min = SSL_LIBRARY_VERSION_3_0;
1180 return CURLE_OK;
...
1256 static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex)
1257 {
...
1267 SSLVersionRange sslver = {
1268 SSL_LIBRARY_VERSION_TLS_1_0, /* min */
1269 SSL_LIBRARY_VERSION_TLS_1_0 /* max */
1270 };
...
1328 if(nss_init_sslver(&sslver, data) != CURLE_OK)
1329 goto error;
これでは、確かにSSLv3とTLSv1.0でしか接続をしません。
最新のcURL(7.48.0)のコードを見ると、既にSSLv3は使用されなくなり、このコードもTLSv1.2まで使用するように修正されています。
今のところ、当社ではどのように修正するかは未定です。ただし、RedHatさんにcURLを修正してもらうのが一番正しいのですが、どうしましょう?
2016.5.6追記
Asianux Server 4 == MIRACLE LINUX V6では、7.19.7-46.0.1.AXS4にて修正がされました。
これにより、デフォルト設定での、TLS v1.1, v1.2への接続ができるようになったので、libcurlをアップデートすることにより、問題なく監視が行えるようになったと思います。