2016年8月22日月曜日

QNAP の Web UI にログイン不可能になった場合の対処法

仮想ホストの HTTPS や証明書の設定をしていた時、何かの拍子、というか XXX.myqnapcloud.com の証明書の設定をリセットした拍子に、Web UI にログインできなくなり、困った状態になった。延々と Web 画面がリダイレクトされるループ状態になってしまう。

原因は、デフォルトでは、Web UI の HTTP ポートが 80、HTTPS ポートが 443 で、仮想ホストの Web サーバーの HTTP ポートが 8080、HTTPS ポートが 8081 のところを、Web UI と仮想ホストで入れ替えて、Web UI が HTTP(8080)と HTTPS(8081)、仮想ホストが HTTP(80)と HTTPS(443)にしていた。さらに、Web UI を HTTPS のみでログインできるようにして、HTTP ではログインできないようにしていた。ポート設定が、証明書のリセットのタイミングで Web UI のポート設定がデフォルトに戻り、HTTP はログインを拒絶して HTTPS にリダイレクトするのだが、リダイレクト先が間違っており、仮想ホストの無効なポートへのアクセスとして処理されて Web UI の HTTP へとリダイレクトを戻す、というループに陥ってしまったようだ。

最悪、NAS の設定をリセットして再起動すればいいわけだが、SSH によるログインはまだできる状態だったので、リセットが避けられればそれに越したことはない。情報を検索すると、QNAP のフォーラムにまさしく同じ状況に陥っている人がいて、達人の pwilson さんが対処法を回答していた。お蔭様で、設定リセットすることなく、復帰することができた。

pwilson さんは一連のコマンドをアドバイスしてくれているが、僕のケースでは、HTTP でのログインさえ再び有効化すればよかったので、SSH で、

setcfg System 'Force SSL' '0'
/etc/init.d/Qthttpd.sh restart
/etc/init.d/stunnel.sh restart

だけ実行して、HTTP でのログインを有効にして、あとは、HTTPd と stunnel を再起動するだけで十分だった。

それで Web UI にログインできるようになったので、ポート設定などをちゃんと設定し直して、最後に、HTTP でのログインを禁止する設定にして終了。

SSLLabs のテストで A 評価を得る

中間証明書の問題はクリアしても、SSLLabs のテストは、RC4 を使っているせいで B 評価となる。ところが、myqnapcloud の方は A 評価なのに気付いた。

これもおそらく、QNAP の中の人が、仮想ホストでの使い勝手を考慮していないためだろう。

myqnapcloud の設定は、/etc/config/apache/extra/apache-ssl.conf ではないかと推測し、その SSLCipherSuite の設定を /etc/config/apache/extra/httpd-ssl-vhosts-user.conf に移植してみた:

- SSLCipherSuite ALL:!aNULL:!ADH:!eNULL:!SSLv2:!LOW:!EXP:RC4+RSA:+HIGH:+MEDIUM
+ SSLCipherSuite ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA

すると、見事、SSLLabs の結果で、Cipher Suite のリストが myqnapcloud の場合と完全に一致するようになり、推測が大正解だったことが確認できた。当然 A 評価。

QNAP の仮想ホストで SNI

昨日はどうにか仮想ホストでちゃんと中間証明書を有効にする方法を確立した。さらに一日経って、頭が整理できて、全体的により洗練されたやり方に行き着くことができた。昨日の成功をベースにして SNI(仮想ホストのそれぞれで別々の証明書を使い分けること)も余裕でできることがわかったので、それも含めて、全体を整理し、洗練したやり方になったので、それをまとめておこうと思う。

Web サーバーのポートの設定

Web UI 用のポートは「システム設定 > 一般設定 > システム管理」で 8080(HTTP)と 8081(HTTPS)に設定した。さらに、「HTTPS のみを使用する」設定にした。

Web サーバーのポートは「Webサーバ > Webサーバ」で 80(HTTP)と 443(HTTPS)に設定した。さらに仮想ホストの設定で、 443(HTTPS)の設定の仮想ホストだけをエントリーした。

StartSSL での証明書の取得

StartSSL で、仮想ホストそれぞれの証明書と、XXX.myqnapcloud.com 用の証明書もついでに取得する。これらの中間証明書はすべて共通(StartSSL)の中間サーバーのものとなるので、中間証明書は別々に用意しなくてもいいのが洗練されたやり方となるポイント。

それぞれの Web サイト認証は、「Website Control Validation」のリンクをクリックして、認証用の HTML をダウンロードし、ウェブサイトのルートに設置してから、認証ボタンを押して、認証を成功させることができる(認証後は、認証用 HTML を削除して構わない)。ちなみに、XXX.myqnapcloud.com のルートは、/share/Web そのものである。

XXX.myqnapcloud.com 用の証明書の設定

XXX.myqnapcloud.com(Web UI)用の証明書は、Web UI から「システム設定 > セキュリティ > 証明書とプライベートキー」で行える。StartSSL からダウンロードした zip の中の さらに ApacheSever.zip の中の 2_XXX.crt を証明書としてアップロード、秘密キーには、StartSSL のツールボックスの「Decrypt Private key」で暗号化を解除した秘密キーをペーストしてアップロード、1_root_bundle.crt を中間証明書として(「発行者の中間証明書をアップロード」をチェックして)アップロードする。

この作業だけで、まず、XXX.myqnapcloud.com は https が正常に有効となる。

QNAP の Web UI は、この設定作業を通じて、中間証明書を /etc/stunnel/uca.pem に保存する。前回リバースエンジニアリングによってこのことを突き止められたので、仮想ホストの設定の際には、この中間証明書を利用する形にすればエレガントになる。

各仮想ホスト用の証明書の設定

前回のリバースエンジニアリングによって、HTTPS を有効にした仮想ホストの設定は、/etc/config/apache/extra/httpd-ssl-vhosts-user.conf にあることが判明した。この中の次の箇所を次のように修正する:

- SSLCertificateFile "/etc/stunnel/stunnel.pem"
+ SSLCertificateFile "/share/Web/mydomain.com/cert/server.pem"
+ SSLCertificateChainFile "/etc/stunnel/uca.pem"

仮想ホストの証明書のパスは好みで ok だが、SSLCertificateFile で仮想ホストの証明書を、SSLCertificateChainFile で中間証明書(のチェーン)を指すようにするのがポイント。で、中間証明書は XXX.myqnapcloud.com の設定で作成されるものと共通なので、画一的に /etc/stunnel/uca.pem を指定しておけばよい。

ちなみに、仮想ホストの証明書は、暗号化を解除した秘密キーを先頭に、続いて仮想ホストの証明書を記載する形式になっている。

仮想ホストが複数ある場合は、上記箇所が、仮想ホスト分エントリーが存在する。それぞれについて、それぞれの証明書を指すようにパスを指定することになる。これで SNI が実現する。

※ httpd-ssl-vhosts-user.conf は、Web UI で仮想ホストの設定を変更したりしたタイミングでリセットされてしまうので(ちなみに NAS の再起動ではリセットされないようだ)、その点には留意しておく必要がある。

HTTPd の再起動

/etc/init.d/Qthttpd.sh restart

リダイレクト用の index.php のカスタマイズ

XXX.myqnapcloud.com のルート(/share/Web)には、リダイレクト用の index.php が存在する。これは設定状態に応じて、HTTP を HTTPS にリダイレクトしたりする機能を提供するのだが、myqnapcloud 用には上手く機能するものの、仮想ホスト用には上手く機能しない。例えば、ここでは、仮想ホストは一律 HTTPS(443)のみを使うようにしてあるわけだが、HTTP(80)でアクセスした場合、443 ではなく、myqnapcloud 用の 8081 の方へリダイレクトされてしまい、Web UI のログイン画面が表示されてしまう。独自ドメインで運用する仮想ホストで、これはまずい。そのため、index.php をカスタマイズして、myqnapcloud 以外のドメインの場合は、443 の方にリダイレクトするように修正した index.php へと差し替えた。


<?php
function isIPv6($ip) {
    if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {
        return true;
    }
    return false;
}
function isMyCloudNAS() {
    if (isset($_SERVER['HTTP_HOST']) && strlen($_SERVER['HTTP_HOST']) > 0)
        $_http_host = $_SERVER['HTTP_HOST'];
    else
        return false;
    $mycloudnas_domains = Array(
        'mycloudnas.com',
        'myqnapnas.com',
        'qcloudnas.com',
        'myqnapcloud.com'
    );
    foreach ($mycloudnas_domains as $d) {
        if (strncasecmp(stristr($_http_host,$d),$d,strlen($d))==0) {
            return true;
        }
    }
    return false;
}
$webAccessIP = $_SERVER['SERVER_NAME'];
if (isMyCloudNAS() == true) {
    $extPort = exec('/sbin/getcfg System ExtPort -d 0');
    if (intval($extPort)>0)
        $webAccessPort = $extPort;
    else
        $webAccessPort = exec('/sbin/getcfg System "Web Access Port" -d 8080');
    if ($_SERVER['HTTPS'] && exec('/sbin/getcfg Stunnel Enable -d 1') == '1') {
        $protocol='https';
        $webAccessPort = exec('/sbin/getcfg Stunnel Port -d 443');
    }
    else
        $protocol='http';
} else {
    # mycloudnas 以外の仮想ホストは一律、https (443) のみとする。
    $protocol='https';
    $webAccessPort = 443;
}
if (isIPv6($webAccessIP))
    $webAccessUrl = $protocol.'://['.$webAccessIP .']:'.$webAccessPort.'/';
else
    $webAccessUrl = $protocol.'://'.$webAccessIP .':'.$webAccessPort.'/';
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
<meta http-equiv="expires" content="0">
<script type='text/javascript'>
    location.href = '<?=$webAccessUrl?>';
</script>
    </head>
</html>


SNI 達成!

以上で、QNAP だけで、SNI が実現できてしまう。もともと Web サーバーは、さくらインターネットのスタンダードプランを年間契約しているのだけど、来年はメールだけのプラン(さくらのメールボックス)に変更するというのも、一つの手かもしれない。

2016年8月21日日曜日

仮想ホストで中間証明書を有効にする方法

QNAP で HTTPS のために SSL 通信用のデジタル証明書をセットアップできたと思っていたら、PC 等他の環境では問題がないものの、Android の Chrome でセキュリティ警告(NET::ERR_CERT_AUTHORITY_INVALID)が出るという問題に直面した。

SSLLabs のテストでは incomplete chain や extra download と言われる。

丸一日かかって、どうやら完全に問題を克服することができた。

だいたい、こういうノウハウというのは、英語の情報(QNAP のフォーラム等)を参照すればどうにかなるもので、ほとんどのプロのエンジニアの人でも、正解となる先人の情報をどう見付けてくるかという話で、それをそのまま自分のブログに記録しているだけのケースが基本だろう(特に日本語においては)。(プロではないただのエキスパートなだけの)僕も、そういうつもりでいたのだけど、今回は、既存の情報が、低レベルなものしか見付からず、結局、自分の要求する水準のものを実現するために、自分でオリジナルに解法を確立する他なかった。

まず、何が馬鹿馬鹿しいかというと、QNAP の Web UI のセキュリティ設定があって、あそこで証明書を設定するものだと思っている人がほとんどだと思うのだが、あれがまず、騙し。あれって、myqnapcloud.com の Web UI 用の Web サーバーにしか関係がない、というかその用途しか、QNAP の中の人も念頭にない。仮想ホストで、独自ドメインを使って Web サーバーを使う場合のことは、全く蚊帳の外という……。

ほんで、英語の情報をどんなに探し回っても、Web UI 用の Web サーバーに対する証明書のセットアップについて、中間証明書を有効にする方法ばかり。それも古い情報が多いなと思ってたら、Web UI 用の Web サーバーについては、Web UI から中間証明書がコピー&ペーストで設定できるように今では対応済なんだから、当然。

もう、しょうがないので、最後は「ランボー怒りのリバースエンジニアリング」。Apache 関連のコンフィグを眺め回して、試行錯誤繰り返して、やっと仮想ホストでデジタル証明書を中間証明書を含めて適切に設定する方法を突き止めた。

httpd-ssl-vhosts-user.conf

HTTPS を有効にした仮想ホストの設定は、/etc/config/apache/extra/httpd-ssl-vhosts-user.conf にある。この中の次の箇所を修正:

- SSLCertificateFile "/etc/stunnel/stunnel.pem"
+ SSLCertificateFile "/share/Web/mydomain.com/certification/host.pem"
+ SSLCertificateChainFile "/share/Web/mydomain.com/certification/intermediate.pem"

パス自体は好みで ok だが、SSLCertificateFile で仮想ホストの証明書を、SSLCertificateChainFile で中間証明書(のチェーン)を指すようにするのがポイント。ちなみに、仮想ホストの証明書は、秘密鍵を先頭に、続いて仮想ホストの証明書を記載する形式になっている。

以上、vi が面倒臭いので、scp を使って、ダウンロード→編集→アップロードで上書きをしたのは、いつもの通り。

HTTPd の再起動

/etc/init.d/Qthttpd.sh restart で再起動すれば、設定が反映される。NAS を再起動すると、上の作業が白紙化するので、autorun.sh を使って工夫する必要があるのは、以前、述べた通り。


以上で目出度く、SSLLabs のテストで incomplete chain や extra download と言われなくなり、Android の Chrome でもセキュリティエラーが出なくなった。

そういえば、これで余裕で SNI もできるようになったわけだ。

2016年8月19日金曜日

StartSSL と QNAP

QNAP の Web サーバーで HTTPS 通信を可能にするために、StartSSL で無料の SSL 証明書を取得した。

先に、清水理史の「イニシャルB」や Qiita「新しくなったStartSSLを使ってSSLサーバ証明書を発行する」などを参照したが、清水さんの記事はやや古いためか、「正直、疲れた……」という話だったので、先入観で結構大変なのかと思い、数日間作業に着手するのを避けていたが、実際にやってみるとそうでもなかった。記事の頃よりは使い易くなったのかもしれない。

1. StartSSL アカウント

ここのアカウントは、一般のパスワードによるログイン方式は避けてあるので、基本的にはメール認証による「ワンタイムパスワード方式」となる。清水さんの記事や他の情報では、「クライアント認証方式」の設定の方法が述べられているが、必須ではない。毎度のメールでのワンタイムパスワード認証が面倒臭い場合に、クライアント認証方式も使えるようにしてある、という感じである。一旦 SSL 証明書を取得すれば、1 年間は有効なので、それほど頻繁にアクセスするわけでもないから、メール認証で十分である。

2. サイト認証

これは参照した情報では皆が、メールによるサイト認証の方法しか解説していなかったが、僕は独自ドメインのメールアドレスを設定するのが面倒臭かったので、「Website Control Validation」のリンクをクリックして、Validation 用の HTML をダウンロードし、ウェブサイトのルートに設置してから、認証ボタンを押して、認証を成功させることができた。Google のウェブサイトマスターで、そのサイトのオーナーであることを認証する時の方式と同じである。

3. 鍵の作成

これも、参照した情報では誰もやっていなかったが、僕の場合、Windows は使っていないので、OpenSSL コマンドを使って自前で作成し、StartSSL のテキストボックスに CSR をコピー&ペーストした。

openssl req -newkey rsa:2048 -keyout yourname.key -out yourname.csr

ちなみにこのコマンドでは、Email などの最後の 3 つの情報については、通常は未入力にするようである。

以上で、証明書が、zip にパッケージされた形でダウンロードして入手できるようになるので、あとは、QNAP 側での作業となる。

ただし、openssl コマンドで作成した秘密キーは、暗号化を解除したものを QNAP で使用する必要があるので、StartSSL のツールボックスで解除したものをコピーして使う(後述。また清水さんの記事でも解説されている)。

4. QNAP に証明書をアップロードする

「システム設定 > セキュリティ > 証明書とプライベートキー」で、ApacheSever.zip の中の 2_XXX.crt を証明書としてアップロード、1_root_bundle.crt を中間証明書として(「発行者の中間証明書をアップロード」をチェックして)アップロード、秘密キーには、StartSSL のツールボックスの「Decrypt Private key」で暗号化を解除した秘密キーをペーストしてアップロードする。


以上で、めでたく、QNAP 上の独自ドメインで HTTPS が使えるようになった。スマートフォンの Chrome ではセキュリティーエラーが出ていたので焦ったが、キャッシュが更新されていなかっただけで、しばらく経つと解消された。

現状では、一つのドメインしか SSL 証明書を使っていないが、SNI は QNAP の Web インターフェースでできるのだろうか?

2016年8月12日金曜日

Entware-ng で最新の Perl 環境

QNAP の AppCenter で色々と追加パッケージをインストールできるという話だけど、Perl のバージョンが 5.10 というのはやる気がなさ過ぎる。

……で、そもそも、QNAP のサーバーは初めて使うので、元々 Optware だとかのその手の類のものは何もインストールされていない状態だったのだけど、調べてみると、Entware、Optware、QNAPware、Itsy OPKG というのは、ぜ〜んぶ古くて、今は Entware-ng が活きてるものらしい。しかし、ネットで QNAP の情報を調べても、特に日本語では、古い情報が比較的多く残ってるので、この点は要注意だと思った。そこまで QNAP を使い倒しているような人が少ないせいだと思う。

Entware-ng については非常に情報が少なく、さらに、自分の使っている HS-210 で使えるかどうかも全く不明な状態だったが、今まで AppCenter も使ったことのない状況で、いきなり、apps.qnap.community というサイトから qpkg をダウンロードし、AppCenter の設定からファイルを指定して手動でインストールするという、冒険に出た。全く確信はなかったが、これ以上情報を調べるのはもっと面倒臭かった。

すると、インストールは簡単に終ったようで、AppCenter のマイアプリに Entware-ng のアイコンが加わっているので、アンインストールも簡単そうだ。

それで、次に、ちゃんと動くのかどうか、SSH でログインしてみた。Perl が目的だったから、perl -v したが、何も反応なし。

[~] # perl -v
-sh: perl: command not found

どうやら、Entware-ng がインストールされた状態で、Entware-ng を使って、Perl などの個別のモジュールをインストールするようだ。それには opkg コマンドを使うらしい。Ubuntu の apt-get みたいなものだろう。下のような流れで Perl をインストールした:

[~] # opkg update
Downloading http://pkg.entware.net/binaries/armv5/Packages.gz.
Updated list of available packages in /opt/var/opkg-lists/packages.
[~] # opkg list perl
perl - 5.22.1-3 - Perl is a stable, cross platform programming language.
 It is used for mission critical projects in the public and private sectors
 and is widely used to program web applications of all needs.
[~] # opkg install perl
Installing perl (5.22.1-3) to root...
Downloading http://pkg.entware.net/binaries/armv5/perl_5.22.1-3_armv5soft.ipk.
Configuring perl.

メッセージ内容からすると、HS-210 の CPU である Marvell 1.6GHz というのは、ARMv5 互換らしい。

Perl は比較的最新の 5.22。これなら文句なしだ。

[~] # perl -v

This is perl 5, version 22, subversion 1 (v5.22.1) built for arm-linux-glibc

Copyright 1987-2015, Larry Wall

Perl may be copied only under the terms of either the Artistic License or the
GNU General Public License, which may be found in the Perl 5 source kit.

Complete documentation for Perl, including FAQ lists, should be found on
this system using "man perl" or "perldoc perl".  If you have access to the
Internet, point your browser at http://www.perl.org/, the Perl Home Page.

ともかく未経験で、最初からストレートにゴールまで辿り着けて、ラッキーだった。

2016年8月6日土曜日

QNAP の Apache で .htaccess を有効化

QNAP の Apache では、.htaccess がデフォルトでは無効化されているので、有効にするには、apache.conf の設定を 1 ヶ所、変更する必要があります。

<Directory />
 Options FollowSymLinks
- AllowOverride None
+ AllowOverride All
 Order deny,allow
 Deny from all
</Directory>

apache.conf は /etc/config/apache/apache.conf にあります。vi を使って編集してもいいですが、僕の場合は、scp を使ってローカルにコピーし、gedit で編集して、scp でアップロードして戻すやり方で作業しています。