Python の導入(root 権限なし)

Let’s Encrypt のクライアントを動かすのに python が必要らしいので、導入。

perlbrew などのように、ローカルに切り替えられるようにしたい。
python の場合は pyenv と virtualenv を使うらしい。

とりあえず、前準備をします(※ここだけ root 権限が必要ですすみません)

1
$ sudo apt-get install -y make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils

まず、pyenv を入れる: https://github.com/yyuu/pyenv

1
2
3
4
5
$ git clone https://github.com/yyuu/pyenv.git ~/.pyenv
$ echo >> ~/.bash_profile
$ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile
$ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(pyenv init -)"' >> ~/.bash_profile

続いて、pyenv-virtualenv を入れる: https://github.com/yyuu/pyenv-virtualenv

1
2
$ git clone https://github.com/yyuu/pyenv-virtualenv.git ~/.pyenv/plugins/pyenv-virtualenv
$ echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bash_profile

ここで新しいログインシェルを起動すると pyenv コマンドが使えるようになる。
とりあえず 2.x 系の最新版を入れておく:

1
2
3
4
5
6
7
8
$ pyenv install -l
$ pyenv install 2.7.13
$ pyenv rehash
$ pyenv versions
$ pyenv global 2.7.13
$ pyenv versions
$ python -V
$ pyenv exec pip install --upgrade pip

以上。

HTTPS (SSL/TLS) の導入

独自ドメインを独自サーバに移転するに際し、
全面的に HTTPS (SSL/TLS) を導入することにしました。

参考:


SSL/TLS のバージョンはどうする?

SSL/TLS 1.0 はいつまでに無効化しなければならないか?
http://www.intellilink.co.jp/article/pcidss/18.html

SSL and TLS Deployment Best Practices
https://github.com/ssllabs/research/wiki/SSL-and-TLS-Deployment-Best-Practices
↑TLS 1.0 は “shouldn’t be used”

とりあえず h2o.confminimum-version: TLSv1.1 としてみた。

その後、SSL Server Test (Powered by Qualys SSL Labs)
でチェックしてみたところ、全部 TLS 1.2 でアクセスできていた
(あるいはアクセス不可だった)。
TLS 1.1 だけに対応しているクライアントはいない模様。

暗号スイート ChaCha20-Poly1305 は AEAD なので TLS 1.2 が必須。

HTTP/2 は TLS 1.2 が必須。

最終的に、h2o.confminimum-version: TLSv1.2 とすることとした。


Diffie-Hellman key の生成

デフォルトでは鍵長が短くて危険らしいので、手動で生成して設定しました。

What is the current security status of Diffie-Hellman key exchange? - StackExchange
http://security.stackexchange.com/questions/112313/what-is-the-current-security-status-of-diffie-hellman-key-exchange
↑とりあえず鍵長は 2048bit にしておけば良いらしい。

まずは Diffie-Hellman key を生成(数分間かかりました):

1
$ nice -19 openssl dhparam -out dhparam.pem 2048

h2o.confssl: エントリに dh-file: dhparam.pem の行を追加:

1
2
3
4
5
6
7
listen:
port: 443
ssl:
certificate-file: letsencrypt/fullchain.pem
key-file: letsencrypt/key.pem
minimum-version: TLSv1.1
dh-file: dhparam.pem

最後に H2O の master プロセスに kill -HUP すれば OK。


暗号スイートの選択

OpenSSL 式のスイート名は openssl コマンドで翻訳できます。
ただし、最新の OpenSSL でない場合は一部のスイート名が無視されてしまいます。
たとえば、ChaCha20-Poly1305 とか。

1
$ openssl ciphers -v 'HIGH:!ADH:!MD5' | less

2017/Jan/14 時点で、次のような選択をしました:

  • プロトコルは TLS 1.2 以上に限定する。
  • “Security/Server Side TLS - MozillaWiki” の「Intermediate compatibility (default)」をベースとする。
  • AES-GCM と ChaCha20 とでは、確実性をとって ChaCha20 を優先させる。
  • AEAD (Authenticated Encryption with Associated Data) を優先させる。すなわち、AES-GCM と ChaCha20 を優先させる。
  • -SHA (SHA1) は削除。(異議もあるけど、PFS の点でなんとなく不安が残るので)
  • ECDHE-DHE- 以外は PFS じゃないので削除。RSA- も不可。
  • ECDHE-ECDSA-AES256-SHA384ECDHE-RSA-AES256-SHA384 の順序を逆に(単に他と揃えたかっただけ)
  • 仕様上は DHE-RSA-CHACHA20-POLY1305 もあるので追加。OpenSSL/LibreSSL は実装しているみたいだけど、ブラウザには実装されなさそう。 https://tools.ietf.org/html/rfc7905
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ECDHE-ECDSA-CHACHA20-POLY1305
ECDHE-RSA-CHACHA20-POLY1305
ECDHE-ECDSA-AES128-GCM-SHA256
ECDHE-RSA-AES128-GCM-SHA256
ECDHE-ECDSA-AES256-GCM-SHA384
ECDHE-RSA-AES256-GCM-SHA384
DHE-RSA-CHACHA20-POLY1305
DHE-RSA-AES128-GCM-SHA256
DHE-RSA-AES256-GCM-SHA384
ECDHE-ECDSA-AES128-SHA256
ECDHE-RSA-AES128-SHA256
ECDHE-ECDSA-AES256-SHA384
ECDHE-RSA-AES256-SHA384
DHE-RSA-AES128-SHA256
DHE-RSA-AES256-SHA256

なお、DHE-RSA-AES256-SHA256DHE-RSA-AES256-SHA384 の typo じゃないの?
 と思ったので、調べてみた。しかしググってもよくわからない。
openssl コマンドは前者しか知らないと言うので、少なくとも typo ではない。謎。

==========

以下、参考にしたサイト:

PSF (Perfect Forward Secrecy) を満たす鍵交換方式は DHE と ECDHE だけらしい。

httpsだからというだけで安全?調べたら怖くなってきたSSLの話!? - Qiita
http://qiita.com/kuni-nakaji/items/5118b23bf2ea44fed96e
↑暗号スイートの日本語解説記事としてわかりやすいです

我々はどのようにして安全なHTTPS通信を提供すれば良いか - Qiita
http://qiita.com/harukasan/items/fe37f3bab8a5ca3f4f92
↑Mozilla 推奨設定の日本語での解説記事。
↑「優先順位付けのロジック」は必読
↑「AES 128はAES 256よりも優先される」AES256はコストに見合うか議論があるらしい
↑最新の Mozilla SSL Configuration Generator と差異があったので要検討 ←原文は改版されていた
↑「OCSP Stapling」の解説もある

その原文:
Security/Server Side TLS - MozillaWiki
https://wiki.mozilla.org/Security/Server_Side_TLS
↑設定内容は Mozilla SSL Configuration Generator と一致。
↑「Modern compatibility」の「Rationale:」は必読。
↑「Intermediate compatibility (default)」の「Rationale:」も必読。
↑モダンなデバイスは AESNI 命令を使えるので AES256 を ChaCha20 より優先する、というのは確かにそうかも?

NginxでHTTP2を有効にする - Qiita
http://qiita.com/Aruneko/items/8c11f9e45a33457c3c1f
cipher-suite: ECDHE+AESGCM:DHE+AESGCM:HIGH:!aNULL:!MD5

自社WebサイトをHTTP/2対応しました。
https://inaba-serverdesign.jp/blog/20160511/website_http2_nginx.html
cipher-suite: AESGCM:HIGH:!aNULL:!MD5
↑HTTP/2で必須となる暗号スイート TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 を優先、とのこと

Let’s Encryptを使用したkame.photosのSSL Server Test結果をA+にした
http://tapira.hatenablog.com/entry/2016/01/31/232130

SSLCipherSuite を変更し perfect forward secrecy にも対応してみる - さくらVPS CentOS 6.5
http://impov.hatenablog.com/entry/2014/04/29/010108

本当は怖いAES-GCMの話
http://d.hatena.ne.jp/jovi0608/20160524/1464054882

Do the ChaCha: better mobile performance with cryptography
https://blog.cloudflare.com/do-the-chacha-better-mobile-performance-with-cryptography/
↑AES の代わりに ChaCha20-Poly1305 を推奨
↑H2O 公式ドキュメントでもこの記事を参照している
↑Google も ChaCha20-Poly1305 を使っている(Windows 7 Google Chrome で確認)←これはうちの PC が AES 命令を実装していないからです
↑iPhone5s 以降は AES 命令を実装しているらしい
↑たしかにクライアントからのリクエストを参考にして切り替えるのは賢いかも ←これが “equal preference cipher groups” 機能

新しいTLSの暗号方式ChaCha20-Poly1305
http://d.hatena.ne.jp/jovi0608/20160404/1459748671
仕様上は DHE- も定義されているが Chrome では DHE- は deprecate なので実装されないらしい
↑AES-GCM よりメッセージ長が短くなるのはうれしい。
↑「Chrome は、端末がAES-NIとAVXをサポートしている時のみChaCha20-Poly1305よりAES-GCMを優先します。」やはり。
↑「BoringSSLには equal preference cipher groups機能を実装していました。」でも H2O は LibreSSL なんですよねー

https://github.com/libressl-portable/portable/issues/66
↑LibreSSL では Issue は上がっているけど equal preference cipher groups は未実装。

SSL/TLSの暗号スイートは何を基準に優先すべきか?(1) ~鍵長と安全性~
https://blogram.net/2016/07/18/securitybits/
↑DHE鍵交換方式はリスクがあるらしい

SSL/TLSの暗号スイートは何を基準に優先すべきか?(2) ~考慮する要素~
https://blogram.net/2016/07/20/ciphersuites-2/

Windowsが対応している暗号スイートの一覧表
https://blogram.net/2016/07/12/ciphersuites-3/
↑MAC は SHA1 でも問題ない、という意見(←でも PFS を考えたらどうなのかな?)

暗号スイートの暗号強度と、公開鍵のビット数の設定
https://http2.try-and-test.net/ecdhe.html
↑暗号スイートごとの処理の重さについて解説している
ECDHE-ECDSA-AES256- のほうが ECDHE-RSA-AES128- より軽い?
↑Let’s Encrypt の中間証明書が RSA 2048bit なので、RSA 4096bit のサーバ証明書はムダでは? ←正解っぽい

ちなみに、H2O では ECDHE (ECDH) の鍵長 (curve) を指定できないのですが、
SSL Server Test のレポートによると、
ECDH sect571r1 (eq. 15360 bits RSA) となっているとのこと。
ムダに長い鍵を使っているようです。
DHE (DH) は dhparam ファイルの鍵長と同じ 2048 bits でした。
また、実際のクライアントとの通信では
ECDH secp384r1 となっているケースもありました。

ECDSA対応CSRを生成して、Let’s Encryptをつかう場合
https://http2.try-and-test.net/letsencrypt_ec_csr.html
↑「証明書と、暗号スイートの関係」は必読。
↑今回の独自ドメイン構築では RSA 4096bit のサーバ証明書を取得したので、暗号スイートとして ECDHE-ECDSA- を指定しても無意味らしい。

サーバ負荷をRSAとECDSAで比較
https://http2.try-and-test.net/ecdsa.html
↑サーバ証明書を ECDSA にすれば TAT が短縮できるかも?(モバイル相手だと微妙?

SSL and TLS Deployment Best Practices
https://github.com/ssllabs/research/wiki/SSL-and-TLS-Deployment-Best-Practices
↑とても参考になる記事です。必読。

Mozilla SSL Configuration Generator
https://mozilla.github.io/server-side-tls/ssl-config-generator/
cipher-suite: ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256

iPhone 5s A7 arm64 専用命令の速度 (2) (ARMv8 AArch64)
http://wlog.flatlib.jp/item/1652
↑iPhone 5s では AES 命令が実装されている

Nexus 9 Tegra K1 と ARM 64bit Denver
http://wlog.flatlib.jp/item/1739
↑Nexus 9 (ARM64 (AArch64), NVIDIA Denver) では AES 命令が実装されている

The Nexus 5X And 6P Have Software-Accelerated Encryption, But The Nexus Team Says It’s Better Than Hardware Encryption
http://www.androidpolice.com/2015/09/30/the-nexus-5x-and-6p-have-software-accelerated-encryption-but-the-nexus-team-says-its-better-than-hardware-encryption/
↑Nexus 5X and 6P は AES ハードウェアを使わずに ARMv8 命令を使っているらしい

ハードウェアの AES 命令サポート状況:

  • iPhone5s 以降は AES 命令を実装している
  • Nexus 9 (ARM64, NVIDNA Denver) は AES 命令を実装している
  • ARM の 64bit アーキテクチャ ARMv8 の AArch64 は AES 命令を実装している
  • x86 プロセッサについては Wikipedia 参照: https://ja.wikipedia.org/wiki/AES-NI
  • GCM を高速化する CLMUL 命令も x86 プロセッサに実装されている: https://ja.wikipedia.org/wiki/CLMUL_instruction_set
  • 構築中の VPS のプロセッサは AES 命令を実装していた /proc/cpuinfo
  • Seaoak の自宅 PC は AMD Phenom II X4 970 プロセッサなので未サポート(だから Google Chrome で Google にアクセスすると ChaCha20 が選択されるのかもしれない)←正解

サーバ設定のチェック

SSL Server Test (Powered by Qualys SSL Labs)
https://www.ssllabs.com/ssltest/

とりあえず試してみたら、seaoak.jp は “Grade B” 判定でした。

cipher-suite:“SSL and TLS Deployment Best Practices”
の推奨に変えたら “Grade A” 判定になりました。IPv4 と IPv6 の両方とも。
Forward Secrecy も “Yes” でした。
IE10 を除いて、それなりに幅広く対応できている模様。
IE11, Android 4.4.2 以上、Safari7 (iOS7.1) 以上、Java 8 以上、に対応です。
すべて TLS 1.2 接続でした。
この結果を見る限り、TLS 1.1 を有効にするメリットは無いのかもしれない。

TLS 1.2 以上に変更して、暗号スイートも変更して、再チェック。
結果は変わらず “Grade A” でした。
ほとんどのクライアントで ECDHE-RSA-AES128-GCM-SHA256 が選択されており、
一部 (Chrome/Firefox) で ECDHE-RSA-CHACHA20-POLY1305 が選択されるという、
期待したとおりの結果になっています。
古い Windows Phone + IE11 と、古い Safari で、
GCM でない ECDHE-RSA-AES128-SHA256 が選択されてしまっていましたが、
まぁ、許容範囲でしょう。
なお、Windows 7/8.1 + IE11 で DHE-RSA-AES128-GCM-SHA256 が選択されていたので、
やはり DHE 鍵交換方式は必要と思われます。


リザンプションとは?

TLS Session Resumption のこと。
http://www.slideshare.net/kazuho/http-58452175/66

これを有効にしておかないと、パフォーマンスに影響があるらしい。
https://github.com/ssllabs/research/wiki/SSL-and-TLS-Deployment-Best-Practices#32-use-session-resumption

とりあえず H2O はデフォルトでよろしくやってくれるみたい。
https://h2o.examp1e.net/configure/base_directives.html#ssl-session-resumption

Yahoo! JAPAN Tech Blog に実コードを用いた詳しい解説記事があります:
細かすぎて伝わらないSSL/TLS
https://techblog.yahoo.co.jp/infrastructure/ssl-session-resumption/


おまけ: ESET Smart Security 9 が悪いことをする

Windows PC からのアクセスが HTTP/2 にならない場合は ESET Smart Security 9 の「SSL/TLSプロトコルフィルタリング機能」のせいかも?
https://inaba-serverdesign.jp/blog/20160511/website_http2_nginx.html

Seaoak はつい先日 ESET Smart Security 9 にアップデートしたところです。
で、実際、自分のサイトに Google Chrome 56.0.2924.59 beta (64-bit)
でアクセスしたらこの現象を踏みました。
Web サーバへのアクセスは HTTP/1.1 になっていて、
証明書の発行元は ESET SSL Filter CA になってしまっていました。
ESET Smart Security 9 の設定を変更したら、
無事に HTTP/2 アクセスになって「青いイナズマ」アイコンが見られました。


追記:
別に借りている CentOS 7 のサーバは、TLS 1.0 までしか対応していなかった。
さらに、暗号スイートの候補を Mozilla の “Intermediate compatibility”
相当にする必要がありました。

追記:
ガラケー (docomo) は TLS 1.0 までしか対応しておらず、
さらに、暗号スイートの候補を Mozilla の “Intermediate compatibility”
相当にしたところ、安全ではないと言われてしまった。
(無視してアクセスすることはできた)

一般ユーザ権限で特権ポートを bind したい

Web サーバ H2O
root 権限の無い一般ユーザで運用したいと考えました。

通常、root 権限で h2o を実行すると、
TCP port 80 (HTTP) と port 443 (HTTPS) を bind して、
その後 nobody ユーザに setuid で切り替えます。
nobody ユーザはシェルにログインできない一般ユーザなので、安全。

なぜ先に TCP ポートを bind するのかというと、
1024 番未満のポートは “privileged port”
well-known port numbers, 予約ポート, 特権ポート)
と呼ばれるポートなので一般ユーザ権限では触れないからです。
HTTP プロキシとかでよく使われる port 8080 なら root 権限は不要なのですが。

ところで、実は Seaoak はこの nobody ユーザが好きではありません。
たとえば、あるファイル/ディレクトリについて、
CGI プログラムとふつうの一般ユーザの両方が書き込めるようにすると、
chmod o+w が必要になるのが気持ち悪い。
あと、perlbrew とか nvm とかも利用したいです。
「いまさら CGI かよ」という意見もあるとは思いますが……。

もちろん、ふつうの一般ユーザ権限で Web サーバを運用すると、
万一、プロセスを乗っ取られた時に、被害が大きくなります。
sudo できるユーザだったりしたら最悪です。

いちおうそういうリスクは認識した上で、トライ。

ちなみにサーバ環境は Ubuntu 16.04.1 LTS (Xenial Xerus) x86_64 です。

事前準備

ファイアウォールで標準のポートを開けておきます:

1
2
3
$ sudo ufw allow 80
$ sudo ufw allow 443
$ sudo ufw status

なお、いちおう UDP ポートも開けておきました。(後述)

案1: Linux Capabilities (setcap)

root 権限が無くても特権ポートにアクセスできるようにする仕組みがありました。

特定の実行ファイルに CAP_NET_BIND_SERVICE capability を設定すると、
そのプログラムは特権ポートにアクセスできるらしいです。

さっそく試してみます:

1
2
$ sudo setcap CAP_NET_BIND_SERVICE+ep /usr/local/bin/h2o
$ sudo getcap /usr/local/bin/h2o

これで h2o -m worker はうまく動きました。

しかし、h2o -m daemonh2o -m master はダメでした。
/usr/local/bin/h2o から呼ばれているシェルスクリプト
/usr/local/share/h2o/start_server の中で、特権ポートが
open できないと言われてしまいます。
capability に +i (inheritable) を加えて
setcap CAP_NET_BIND_SERVICE+eip と指定してもダメでした。
capability がうまく継承されないようです。

とりあえず、元に戻します:

1
2
$ sudo setcap -r /usr/local/bin/h2o
$ sudo getcap /usr/local/bin/h2o

案2: ポートのリダイレクト

ファイアウォール (iptables) で特権ポートから非特権ポートにリダイレクトして、
Web サーバはその非特権ポートを bind する、という手段がありました。

2番目の記事の「Ubuntu*/Debian systems can use ufw as a firewall for port redirection:」を参考にしました。
また、3番目の記事の「成功その3」を採用させていただきました。

まず、リダイレクト先のポート番号は以下を参考に適当に選びます:

本来はエフェメラルポートを使うべきなのかもしれませんが、なんとなく気分で、誰も使っていなさそうなポート番号を適当に選びました:

port 5210 : redirect from  80 (HTTP)
port 5211 : redirect from 443 (HTTPS)
port 5212 : always drop

次に、ufw の設定ファイル /etc/ufw/before.rules および /etc/ufw/before6.rules
に以下の行を追加します:

1
2
3
4
5
6
7
8
9
10
11
*nat
:PREROUTING ACCEPT [0:0]
-A PREROUTING -p tcp --dport 5210 -j REDIRECT --to-port 5212
-A PREROUTING -p udp --dport 5210 -j REDIRECT --to-port 5212
-A PREROUTING -p tcp --dport 5211 -j REDIRECT --to-port 5212
-A PREROUTING -p udp --dport 5211 -j REDIRECT --to-port 5212
-A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 5210
-A PREROUTING -p udp --dport 80 -j REDIRECT --to-port 5210
-A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 5211
-A PREROUTING -p udp --dport 443 -j REDIRECT --to-port 5211
COMMIT

Seaoak はとりあえず設定ファイルの先頭部分(コメント行
# Don't delete these required lines, otherwise there will be errors
の前)に上記の行を挿入しました。

具体的な手順としては、次のようになります:

1
2
3
4
5
6
7
$ sudo vi /etc/ufw/before.rules
$ sudo vi /etc/ufw/before6.rules
$ sudo ufw allow 5210
$ sudo ufw allow 5211
$ sudo ufw deny 5212
$ sudo ufw disable
$ sudo ufw enable

ufw deny 5212 は念のためです。
なお、今回試した限りではサーバのリブートは不要でした。

次に、H2O の設定ファイル h2o.conf を変更します。
listen: ディレクティブで指定するポート番号を “80” から “5210” に、
“443” から “5211” に、それぞれ変更します。
ここで、hosts: ディレクティブに書く host:port:port 部分は
:80 とか :443 とかのまま変更してはいけません。

なぜなら、クライアントがリクエストした URL は、
あくまでも port 80 / 443 に対するものだからです。
ここを :5210 とか :5211 とかに変えてしまうと、
クライアントがリクエストした URL (の host:port 部分)
にマッチしなくなってしまいます。

ちなみに、困ったことに、h2o.conflisten: ディレクティブを
global-level に記述している場合、上記のようなミスをすると、
無条件に hosts: ディレクティブの先頭のエントリにマッチしたものとして扱われてしまいます。
公式ドキュメントにも明記されています。
エラーにはなりません。
この罠に Seaoak はハマりました。
今後も同じようなミスをする可能性があるので、対策として、
Seaoak の h2o.conf では hosts: ディレクティブの先頭に
「番兵」(ダミー)を置くことにしました:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
hosts:
"sentinel.example.com":
paths:
"/":
file.dir: /dev/null
access-log: "| rotatelogs -l access-log.sentinel.%Y%m%d 86400"
"seaoak.jp:443":
paths:
"/":
file.dir: /path/to/doc-root
access-log: "| rotatelogs -l access-log.root.%Y%m%d 86400"
"seaoak.jp:80":
paths:
"/":
redirect:
url: "https://seaoak.jp/"
status: 301
access-log: "| rotatelogs -l access-log.root-nossl.%Y%m%d 86400"

ちなみに、file.dir: /dev/null とすると “404 Not Found” が返ります。

閑話休題。

以上の状態で h2o を動かしてみると、h2o -m workerh2o -m daemon
h2o -m master も動きました。特にエラーメッセージも出ません。

ブラウザで http://seaoak.jp とか https://seaoak.jp
とかにアクセスしてみると、問題なくアクセスできました。
また、http://seaoak.jp:5210 とか https://seaoak.jp:5211
とかにアクセスしてみると接続エラーになります。
すばらしい!!

また、別に借りているサーバから IPv6 でアクセスすると無事成功しました。

カンペキです。

案3: Linux Capabilities (Ambient capabilities)

案1で試した setcap による capabilities 機能は、正確には
“File capabilities” と呼ぶものらしく、それとは別に、
“Ambient capabilities” というものがあるらしいです。

Seaoak は試していませんが、もしかするとうまい手があるかもしれません。

ご参考まで。

How about UDP ports ?

とりあえず UDP のポート 80 / 443 も開けておいたほうが良さそうです。

いちおう H2O でも QUIC を実装しようという Issue が上がっています。
https://github.com/h2o/h2o/issues/275

オチ?

h2o プロセスがダウンしたときに自動的に再実行したい、とか言うと、
結局 systemd のお世話になるので、上記の話はすべて無駄になりそうです。

Web サーバ H2O の導入

独自ドメインを独自サーバに移転するに際し、
Web サーバをどれにしようかと考えました。

ゼロから新しく構築するので、当然ながら HTTP/2 前提です。

Apache は以前から使っていたのですが、
設定ファイルが複雑で、あまり好きではありませんでした(自由度の高さは認めます)。
nginx は触ったことがないので以前から興味がありました。
本も2冊買いました。

そんなある日、HTTP/2 に最適化されているとうたう
H2O というサーバの存在を知りました。
DeNA のひとがメインで開発している OSS です。

せっかくの機会なので、試してみることにしました。

ちなみにサーバ環境は Ubuntu 16.04.1 LTS (Xenial Xerus) x86_64 です。

追記:デフォルトで IPv6 にも対応していました。

インストール

ソースコードを git clone してビルドしました。

1
2
3
4
5
6
7
8
9
$ sudo apt-get install -y cmake zlib1g-dev
$ sudo apt-get install -y bison ruby ruby-dev
$ git clone https://github.com/h2o/h2o.git
$ cd h2o
$ git tag
$ git checkout v2.0.4
$ nice -19 cmake -DWITH_BUNDLED_SSL=on -DWITH_MRUBY=on .
$ nice -19 make
$ sudo make install

一発でビルドに成功しました。

なお、RPM なひと(CentOS とか)では ruby-dev の代わりに ruby-devel かも。

とりあえず添付されているサンプルを実行してみる:

1
2
3
4
5
6
7
8
9
動作確認用のポートを開ける
$ sudo ufw allow 5210
$ sudo ufw allow 5211
$ sudo ufw status
自動的に IPv6 のポートも開けてくれる。

h2o.conf のポート番号だけ書き換えて実行
$ vi examples/h2o/h2o.conf
$ h2o -c examples/h2o/h2o.conf

使ったポート番号は適当です。
1024 番未満のポートは特権ポートなので使えません。
port 8080 と port 8081 はポートスキャンがうざいので避けたほうがいいです。

ブラウザで http://hogehoge.example.com:5210 とか
https://hogehoge.example.com:5211 とかにアクセスすれば、
“Welcome to H2O” のページが見られるはずです。

バージョンアップ

1
2
3
4
5
6
7
8
9
$ cd h2o
$ make clean
$ git fetch
$ git tag
$ git checkout v2.0.6
$ nice -19 cmake -DWITH_BUNDLED_SSL=on -DWITH_MRUBY=on .
$ nice -19 make clean
$ nice -19 make
$ sudo make install

あとは H2O の master プロセスに kill -HUP するだけ。

設定ファイル

設定ファイルは h2o.conf です。
書き方は公式ドキュメント参照。
Apache に比べてはるかにシンプルです。

1
$ vi h2o.conf

編集が終わったら、問題が無いかチェック:

1
$ h2o -t

OK なら H2O の master プロセスに kill -HUP すると再読み込みしてくれる。

ログのローテーションは rotatelogs コマンドで

公式ドキュメントでは
error-log や access-log をローテーションさせるのに
rotatelogs コマンドを使っています。

rotatelogs コマンドは apache2-utils パッケージに含まれています。

1
$ sudo apt-get install -y apache2-utils

以上。

光学ドライブが消えた

Windows 7 64bit です。

昨日、突然、エクスプローラー上で DVD/CD ドライブが消えました。
2台とも。
仮想ドライブも2個あったのですが、それも消えました。

WindowsUpdate とかしていないので、原因不明。

デバイスマネージャを見ると、「DVD/CD-ROM ドライブ」配下の4個のデバイスすべてに黄色いビックリマーク(エクスクラメーションマーク)が出ていました。デバイスを右クリックしてプロパティを見てみると、「デバイスの状態」欄に「レジストリ内の構成情報が不完全または破損しているため、Windowsはこのハードウェア デバイスを開始できません。(コード19)」と書かれていました。デバイスを「削除」して「ハードウェア変更のスキャン」をすると、デバイスドライバが正常に組み込めませんでしたと言われる。

結局、Microsoft の KB314060 に従って regedit でレジストリを修正したら直りました。

CD ドライブまたは DVD ドライブが存在しないか、Windows または他のプログラムでは認識されません。
https://support.microsoft.com/ja-jp/kb/314060

トラブルシューティングツールでは直らず、「方法3」に従って
regedit でレジストリを修正しました。
具体的には、キー HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E965-E325-11CE-BFC1-08002BE10318} のエントリ UpperFiltersLowerFilters を削除して、リブート。

ちなみに、この現象は iTunes が悪さをしているという噂がありますが、ここしばらく iTunes のアップデートはしていません。謎です。

茂木健一郎さんのブログ

なかなかおもしろいです。

http://lineblog.me/mogikenichiro/

日本人の英語の学び方にある誤解
http://lineblog.me/mogikenichiro/archives/8306639.html

夏目漱石だったら、TOEICなんて受けないよね
http://lineblog.me/mogikenichiro/archives/8306624.html

脳なんでも相談室。東大生と米国のハーバード大学や英国のケンブリッジ大学の世界の大学ランキングにおける違いは何でしょうか?
http://lineblog.me/mogikenichiro/archives/8306407.html

LINE BLOGとツイッターから、ネットの発言の自由について考える
http://lineblog.me/mogikenichiro/archives/8306213.html

算数の「順序問題」が象徴するもの。
http://lineblog.me/mogikenichiro/archives/8305841.html

小学校の算数にまかり通っている「奇習」は、子どもたちに対する「虐待」である
http://lineblog.me/mogikenichiro/archives/8305779.html

Firefox の Vertical Tabs アドオンの代替

Seaoak はひとつのブラウザのウインドウでタブをたくさん開くので、
ウインドウ上辺に小さなタブがたくさん並ぶことになってしまいます。
タイトル文字列は読めないし、クリックもしにくい。

Opera や Sleipnir などは、タブバーをウインドウ右辺 or 左辺に縦置きできます。
1タブ1行なので、タイトル文字列もちゃんと読めるし、とても便利。
最近のディスプレイはみんな横長なので、
ウインドウの横幅がちょっとくらい増えても全然問題ありません。
実のところ、Windows のタスクバーも縦置きしているくらいです。

Firefox でもタブバーの縦置きをするために、
Vertical Tabs というアドオンを使っていました。
https://addons.mozilla.org/ja/firefox/addon/vertical-tabs/

しかし、このアドオンは最終更新日が2014年3月で、もうメンテされていません。
しばらく前から動作がおかしかった(Firefox を起動したら一度アドオンを無効化して
有効化しなおさないとダメだった)のですが、
最近の Firefox 本体のアップデートでついに完全にお亡くなりになってしまいました。
ウインドウのレイアウトが崩れてページ表示領域がほとんど無くなり、
操作不能になってしまったりとか。
ちなみに、こうなったときは、Alt キーをひと押ししてメニューバーを表示させて、
「ヘルプ」の「アドオンを無効にして再起動」を選択すれば復活できます。

Vertical Tabs アドオンのソースコードは GitHub で公開されています。
https://github.com/philikon/VerticalTabs

そこで、Fork して自作するという手も考えられたのですが、
当然ながら同じようなことを考えている人がすでにいました。

Vertical Tabs Reloaded
https://addons.mozilla.org/ja/firefox/addon/vertical-tabs-reloaded/
https://github.com/Croydon/vertical-tabs-reloaded

インストールしてみたら、期待通り動作しました。すばらしい!

作者様に感謝!

Sysinternals の pslist が動かなくなった

Windows Sysinternals (http://www.sysinternals.com) は
Microsoft 謹製の Windows システムツール群です。
タスクマネージャの高機能版である Process Explorer や、
余計なプログラムが勝手に起動されるのを無効化できる Autoruns など、
高機能なものからマニアックなものまで幅広いツールが揃っています。
すべて無償で利用できます。
コマンドライン上で使う CUI プログラムが多いのも特徴。

Sysinternals に含まれるツールのひとつである pslist (https://technet.microsoft.com/ja-jp/sysinternals/pslist) は、
UNIX の ps コマンドのようなもので、
コマンドライン上で実行するとプロセスの一覧が取得できたりする CUI プログラムです。

Seaoak は Firefox のプロファイルをコピーするシェルスクリプトで
pslist を使っています。
安全のため、実行中の Firefox プロセスがいないことを pslist で確認しています。

1
2
3
4
if /c/sysinternals/pslist -e 'firefox'; then
trueecho 'ERROR: firefox is still running.' 1>&2
trueexit 2
fi

だいぶ前から問題なく使えていたのですが、つい先日、突然動かなくなりました。

1
2
3
4
5
6
7
8
9
10
11
C:\sysinternals>pslist

PsList v1.4 - Process information lister
Copyright (C) 2000-2016 Mark Russinovich
Sysinternals - www.sysinternals.com

Processor performance object not found on HOGE-PC
Try running Exctrlst from microsoft.com to repair the performance counters.


C:\sysinternals>

環境は Windows 7 Professional 64bit です。

ググってみたら、Sysinternals のフォーラムに答えが投稿されていました。

http://forum.sysinternals.com/pslist-process-performance-object_topic71_post128749.html#128749

単にコマンドライン上で LODCTR /R を実行するだけでした。

1
2
3
4
5
C:\sysinternals>LODCTR /R

情報: パフォーマンス カウンターの設定をシステムのバックアップ ストアから正常に再
構築しました
C:\sysinternals>

フォーラム投稿者に感謝!

Git for Windows でシェルスクリプトが動かなくなった

Git for Windows (https://git-for-windows.github.io/) を愛用しています。
Windows 7 上で bash が動きます。
本来は分散型バージョン管理システム git を Windows 上で使うための環境ですが、
UNIX の基本的なコマンド群がそろっており、
perl やシェルスクリプトが使えるので、
ファイル処理やテキスト処理など、かなりいろいろできます。
Windows 10 の Bash on Ubuntu on Windows は触ったことがないので比較はできませんが、
少なくとも Windows 7 上では日本語も含めて全く問題なく使えています。

Git for Windows をインストールすると、拡張子 .sh に関連付けが設定されます。
エクスプローラー上でシェルスクリプトファイルをダブルクリックするだけで実行できます。
もちろん bash を起動してコマンドライン上で実行させることもできますが、
ダブルクリック一発で動くので、完全にバッチファイルの代わりに使えます。
とても便利。

ずっと問題なく使えていたのでアップデートもせずに使っていたのですが、
ふと気が向いて Git for Windows を最新版にアップデートしたところ、
ダブルクリックで起動したシェルスクリプトが意図しない挙動を示すようになってしまいました。

  • 旧: Git-1.9.5-preview20150319.exe
  • 新: Git-2.10.2-64-bit.exe

bash を起動してコマンドライン上でそのシェルスクリプトを実行すると問題なく動きます。
ダブルクリックして実行したときだけダメ。

原因

いろいろ試行錯誤して調べた結果、原因が判明しました。
シェルスクリプトをダブルクリックして起動すると、
シェルの alias 設定が有効な状態で実行されてしまい、
ls コマンドの出力がカラー化&ファイルタイプ記号付きになってしまうのでした。
つまり余計なエスケープシーケンスや記号が付与されてしまうわけです。
この出力を生のファイル名一覧だと思ってパイプにつないで処理しようとすると、
当然ながら意図しない挙動になってしまいます。

具体的には、~/.bashrc での以下の alias 設定が有効になってしまっていました:

1
alias ls='ls -F --color --show-control-chars'

ややこしいことに、旧バージョンでも alias 設定が有効な状態だったのですが、
なぜかシェルスクリプト内でパイプに出力する時だけ ls コマンドの出力がカラー化&ファイルタイプ記号付きにならなかったため、
問題が顕在化しなかったのでした。
この挙動は --color=auto オプション指定時の挙動に似ていますが、

  • --color とだけ指定した場合は --color=always と解釈されるのが仕様 by man ls(1)
  • -F オプションについては出力先による自動 ON/OFF 機能がない

ということから予期しない挙動だと言えます。
詳しくは以前の記事「ls コマンドの --color オプション」を参照してください。

なお、~/.bashrc などで上記の alias 設定をしていなかったとしても、
/etc/profile.d/aliases.sh により以下の alias がデフォルトで設定されます。

1
alias ls='ls -F --color=auto --show-control-chars'

すなわち、カラー化は回避できますが、-F オプションによる余計な記号の追加は避けられません。

なぜ alias 設定が有効になってしまうのか

/usr/bin/bash --login -i というオプション付きで bash にシェルスクリプトが渡されるから。

man bash(1) によれば、

1
2
3
4
5
6
7
8
A login shell is one whose first character of argument zero is a -,  or
one started with the --login option.

An interactive shell is one started without non-option arguments and
without the -c option whose standard input and error are both connected
to terminals (as determined by isatty(3)), or one started with the -i
option. PS1 is set and $- includes i if bash is interactive, allowing
a shell script or a startup file to test this state.

とのことなので、bash は “interactive login shell” として起動されたことになります。
そして、さらに man を読むと、

1
2
3
4
5
6
7
When bash is invoked as an interactive login shell, or as a  non-inter‐
active shell with the --login option, it first reads and executes com‐
mands from the file /etc/profile, if that file exists. After reading
that file, it looks for ~/.bash_profile, ~/.bash_login, and ~/.profile,
in that order, and reads and executes commands from the first one that
exists and is readable. The --noprofile option may be used when the
shell is started to inhibit this behavior.

とのことなので、bash は /etc/profile を読み込みます。
/etc/profile の中では /etc/profile.d/*.sh を順次読み込みます。
/etc/profile.d/aliases.sh を読み込むと、次のように ls コマンドの alias が設定されます:

1
alias ls='ls -F --color=auto --show-control-chars'

また、~/.bash_profile 経由で ~/.bashrc も読み込まれるので、
個人的に ls コマンドの alias 設定をしていれば上書きされます。

bash に --login -i オプションを指定しなければ、これらのファイルは読み込まれず、
ls コマンドの alias 設定はされません。
bash のコマンドライン上でシェルスクリプトを実行した場合はこちらの挙動になります。

誰が bash に余計なオプションを付けているのか

git-bash.exe です。

Windows のレジストリを見ると、拡張子 .sh に対して
C:\Program Files\Git\git-bash.exe が関連付けられています。

git-bash.exe の詳細と、拡張子の関連付け設定については、
それぞれ後述の「おまけ」を参照してください。

とりあえず結論を言うと、git-bash.exe必ず --login -i オプション付きで
bash を起動するようにハードコーディングされています。
https://github.com/seaoak/MINGW-packages/blob/master/mingw-w64-git/git-bash.rc

--login -i オプション無しに bash を起動させるオプションは git-bash.exe にはありません。

対策

git-bash.exe を使わない。

Git for Windows のコミッターいわく:

Please understand that git-bash.exe is the executable that is intended to open the interactive Git Bash.

What you most likely wanted to do was to call C:\Program Files\Git\bin\bash.exe instead (which is still not the real Bash, but a redirector that sets up appropriate environment variables first).

とのことなので、そもそもシェルスクリプトに git-bash.exe を関連付けるのが妥当なのか疑問。

しかし、bash.exe は Windows で言うところの「コンソールプログラム」なので、
bash.exe を直接実行すると mintty ではなく cmd.exe が立ち上がってしまいます。
そのため、日本語表示が化ける。

そこで、明示的に mintty 経由で bash.exe を呼んでシェルスクリプトと引数を渡します:

1
"C:\Program Files\Git\usr\bin\mintty.exe" --dir "%W" "C:\Program Files\Git\bin\bash.exe" "%L" %*

このコマンドラインを拡張子 .sh に関連付けしてあげれば OK.

なお、C:\Program Files\Git\bin\bash.exe の代わりに「素の bash」である
/usr/bin/bash を mintty に渡しても実行してくれますが、
最低限の PATH 設定も無い状態なので、使い物になりません。
上記のコミッターのコメントの言うとおりです。
git-bash.exe/usr/bin/bash を直接 mintty に渡しても(一見)ちゃんと使えているのは、
--login -i オプションのおかげです(しかし alias 設定という副作用があるのは上述の通り)。

おまけ: ファイルの関連付けの設定方法

Windows のレジストリを見ると、拡張子 .sh に対して
(正確には拡張子 .sh に結びつけられた “sh_auto_file” タイプに対して)
ただひとつのアクション “open” が設定されており、そのコマンドラインは、

1
"C:\Program Files\Git\git-bash.exe" --no-cd "%L" %*

となっています。

レジストリのキーは HKEY_CLASSES_ROOT\sh_auto_file\shell\open\command です。

これを上述の mintty 呼び出しのコマンドラインに書き換えてあげれば OK です。

%L とか %W とかの意味はこちらのページが参考になります:
http://pf-j.sakura.ne.jp/program/winreg/classes.htm

レジストリを直接いじるのは怖いので、
Seaoak は FileTypesMan というフリーソフトを使わせていただいています(感謝!)。
Windows 7 Pro 64bit 版で使えていますが、Windows 10 で使えるかはわかりません。
http://www.nirsoft.net/utils/file_types_manager.html

なお、シェルスクリプトに別のファイル/フォルダをドラッグ&ドロップすると、
“open” アクションが実行される模様(実際の挙動から)。
ただし、このとき、カレントディレクトリは /c/Windows/system32 (C:\Windows\system32)
となり、シェルスクリプト自身およびドラッグ&ドロップされたファイル/フォルダは
ともに絶対パス(フルパス)で bash に渡されます。

おまけ: git-bash.exe について

git-bash.exe に関するドキュメントはこの世に存在しないっぽい。
GitHub の Issue を見ても、コミッターが「無い」と言ってる:
https://github.com/git-for-windows/git/issues/130#issuecomment-98724488

git-bash.exe の実体は git-wrapper.c です:
https://github.com/seaoak/MINGW-packages/blob/master/mingw-w64-git/PKGBUILD#L152

git-wrapper.c のコマンドラインオプション処理部分は 389 行目付近:
https://github.com/seaoak/MINGW-packages/blob/master/mingw-w64-git/git-wrapper.c#L389

git-bash.exe 内部で mintty / bash を呼ぶ時のコマンドライン文字列はこちらで指定:
https://github.com/seaoak/MINGW-packages/blob/master/mingw-w64-git/git-bash.rc

git-bash.exe が誕生するに際して議論があった模様(全部は読んでません):
https://github.com/git-for-windows/git/pull/42

おまけ: Git for Windows のインストーラーの設定?

https://github.com/git-for-windows/build-extra/blob/master/installer/install.iss

git-bash.exe“ で検索!

おまけ: mintty について

Windows 標準のコマンドライン cmd.exe の代わりに使えるターミナル。
日本語表示 (UTF-8) に対応していたりするので、mintty のほうが便利。

リポジトリ:
https://github.com/mintty/mintty

マニュアル:
https://mintty.github.io/mintty.1.html

なお、Git for Windows のリポジトリには mintty のパッケージが2個ある。

実際に Git for Windows をインストールすると
/usr/bin/mintty (C:\Program Files\Git\usr\bin\mintty.exe)
しか無いのですが、どちらのパッケージのものかは不明(調査不足)。
ただし、どちらのパッケージも上記の mintty リポジトリを参照している様子なので、
とりあえず実用上問題はないでしょう。

おまけ: MinGW と MSYS と Mingw-w64 と Git for Windows の関係

http://d.hatena.ne.jp/m-hiyama/20151013/1444704189

ls コマンドの --color オプション

ls コマンドの --color オプションの挙動が
自分の認識と違っていたので、メモ。

ls コマンドに --color オプションを指定すると、出力時に、
ファイルタイプ(ディレクトリとかシンボリックリンクとか)
によって色づけ(ハイライト)してくれます。

普段の作業にはとても便利な機能なので、
シェルの alias 設定で指定している人も多いのではないでしょうか。

ただ、この --color オプションを使うにあたって、
ひとつだけ注意しなければなりません。
ls コマンドの出力をパイプにつないでワンライナーを書く時に、
ハイライト機能が有効になっていると正しく動きません。

1
/bin/ls --color | xargs -d '\n' -n 1 hogehoge.pl

この対策として、--color=auto と指定しておくと、
出力先が端末(ターミナル)でない場合にハイライト機能を無効化してくれます。
とても便利。

で、Seaoak の認識では、単に --color と指定した場合は
--color=auto になると思っていました。
しかしながら、Ubuntu 16.04.1 LTS や Git for Windows (2.10.2) では、
--color--color=always と解釈されるのでした。
ちゃんと man にも明記されています。

1
2
3
--color[=WHEN]
colorize the output; WHEN can be 'always' (default if omitted),
'auto', or 'never'; more info below

おそらくですが、ls | less -R としたときにハイライト機能を有効にしたい、
というのが理由と推測されます。

以上の結論として、
「ワンライナーを書く時は \ls または /bin/ls と書きましょう」
ということになります。

おまけ

ちなみに、似たような便利機能で -F オプションがあります。
ファイルタイプによって末尾に / とか @ とか * とか付けてくれる機能です。
こちらは出力先が端末か否かに関係なく常に有効になります。
よって、ワンライナーを書く時は、
やっぱり \ls または /bin/ls と書くのが確実です。

おまけ2

grep コマンドにも --color オプションがあります。
こちらは単に --color と指定すると --color=auto と解釈されるようです。
man やヘルプには明記されていませんが、
Ubuntu 16.04.1 LTS や Git for Windows (2.10.2) で試すとそうなります。
統一感が無いですね・・・・。

おまけ3

git コマンドの grep--color と指定した場合は --color=always
解釈されます。man git-grep 参照。