2009/07/24

wildcard subjectaltname virtualhost

SSL と VirtualHost 関連で「どうなの?」的なことを問われたので
確か素敵まとめサイトがあったと思って検索したら
SSL/TLS で Namebase のバーチャルホスト
が出てきました
素敵!!
VhostTaskForce - CAcert Wiki
ここも素敵かな??

多分 Apache 用語ですけど
VirtualHost
1 つのウェブサーバで複数のウェブサイトを構築
IP address ベースと Name ベースのものがある
IP ベース VirtualHost
アクセスがあった IP address 毎に
サイト・コンテンツを指定して複数サイトがあるように見せる
IP address が複数必要なので場合によってはコストが高い
Name ベース VirtualHost
サーバーの名前毎にコンテンツを振り分ける VirtualHost
IP address が 1 つしかなくても複数のサイトが構築できる
HTTP 1.1 とかで Host というヘッダをクライアントは送るので
そこで判断してるんですよね?? きっと

で、こっから少し古い話

一方 SSL は TCP の接続の後にネゴシエーションするわけですが
ここにはまだ Apache 出てこないんで Host とかの内容見ません
SSL のネゴシエーションのときに分かるのは TCP までの情報なので
IP address とか port 番号とかです
なので、HTTPS でサーバが証明書を提示する際にどの証明書を提示するか
それを決める判断の基準も IP address とか port 番号しかありません

IP ベースの VirtualHost であれば IP address と port 番号毎に
1 つサイトが決まっているので証明書を提示してあげれば良いのですが
1 つの IP アドレスの下で Name ベースの VirtualHost をしてる場合
それら VirtualHost 達で 1 枚しか証明書が提示できません
なのでワイルドカードや SubjectAlternativeName などを利用して
1 枚で複数の FQDN に対応できる証明書が利用されています

古い話終わり

で、RFC 3546 で TLS 拡張で Server Name Indication ってのが定義されてるそうで
TLS レベルで接続したいサーバの名前を指定する仕組みが決められて
それ使えば Name ベースの VirtualHost でも問題無しに
1 Host 毎に 1 枚証明書を対応付けれて IP address もぉ要らないよ!
ということになっているみたいです

IE7 以降とか Fx 2 以降とか Opera 8 以上は対応してるみたいなんですが
携帯とかどうなんだろう??

更に、RFC 2817 では Upgrading to TLS という仕組みも定義されてるとか
HTTP 1.1 から TLS な世界に切り替わるみたいですが
ちょっと、ググっただけでは対応状況までは分かりませんでした

Server Name Indication が普及してくれると分かり易くていいですね

2009/07/22

epoll recv

Twisted が分からないのもそのはずで
やはり非同期ってのが良く分かっていないからなのでした
Twisted ドキュメント: Twisted による非同期プログラミング
ってのを twisted tutorial deferred で教わって
そもそも英文の方も読んだはずなのにちゃんと分かってなかったみたい

で、select とか epoll とかいう単語を
Voluntas さんに教えて頂くなどしまして
ちょっくら自分でも何か書いてみるかということで
RSS とか Atom とか取ってきてみようかなと
途中で悩んでググってみて
How epoll detect clientside close in Python? - Stack Overflow
のコードとか見たりしつつ

で、実際やってみたら HTTP の処理が面倒になっちゃったんで
まぁとりあえず epoll だけ実感できる感じで
from socket import socket, gethostbyname, error
from select import epoll, EPOLLIN, EPOLLOUT
from urlparse import urlparse

FEEDS = (
'http://d.hatena.ne.jp/Voluntas/rss2',
'http://kamo.typepad.jp/blog/index.rdf',
'http://takagi-hiromitsu.jp/diary/index.rdf',
'http://d.hatena.ne.jp/nishiohirokazu/rss2',
'http://www.nnar.org/feed/atom',
'http://d.hatena.ne.jp/CortYuming/rss2,'
'http://vim-users.jp/feed/atom/',
'http://blog.livedoor.jp/dankogai/atom.xml',
'http://d.hatena.ne.jp/Chikirin/rss',
'http://thisistanaka.blog66.fc2.com/?xml',
)

ep = epoll(1024)
sockets = []
feeds = {}

for feed in FEEDS:
feed_data = urlparse(feed)
hostname = feed_data.netloc
path = feed_data.path
s = socket()
s.settimeout(0)
try:
s.connect_ex((hostname, 80))
except:
continue

fileno = s.fileno()
feeds[fileno] = (hostname, path, s)
ep.register(fileno, EPOLLOUT)

while feeds:
for (fileno, event) in ep.poll():
s = feeds[fileno][2]
if event == EPOLLOUT:
s.send("GET %s HTTP/1.1\nHOST: %s\n\n" %
(feeds[fileno][1], feeds[fileno][0]))
ep.modify(fileno, EPOLLIN)
elif event == EPOLLIN:
s.setblocking(1)
print s.recv(1024)
del feeds[fileno]
ep.unregister(fileno)
実行する度に結果が違うはずで
それは返事が来た順番で処理できてるからなはず

FEEDS の分だけ socket 張って
epoll で監視して受けとれるようになった奴から 1KB だけデータ読んでます
Python の場合 socket object には connect_ex って method がありまして
こいつは exception ではなくて error の値を吐くんだそうです

が、connect_ex は接続先が resolve できなかったときだけ
exception 吐くという仕様となっていたので
結局 try except してしまいました

あと、ほんとは socket.fromfd とかで socket 呼んだ方がいいのかも
だけど別の場所で socket を覚えさせたりしないと
    ep.modify(fileno, EPOLLIN)
IOError: [Errno 9] Bad file descriptor
って言われたりして
unregister した瞬間に socket が葬り去られてしまってるんでしょうか
無駄に覚えさせといても動いたんですが折角なんで
socket も feeds の中に入れてみました
modify ってあったから使ってみたんだけどこれが間違ってる可能性もあり

で、更に recv するんじゃなくて socket を thread に渡したりすると
きっと凄い格好の良いことになるに違いありません
Twisted が何してるか少し分かったかも
Twisted の reactor は default だと selectreactor ってのが呼ばれてるけど
他にも色々、例えば epollreactor とかもあるみたいなので
それぞれ reactor 毎に色々してるに違いない、違いないんだけど.....

2009/07/18

"OpenSSL.SSL.WantReadError"

暫く Twisted でねばってたんですが
SSL 周りを低いところから攻めてみようと思いまして
でもちょっとは楽したいなと思いながら
from twisted.internet import reactor
from twisted.internet.protocol import Protocol, ClientFactory
from twisted.internet.ssl import CertificateOptions

class MyProtocol(Protocol):
def connectionMade(self):
self.transport.socket.do_handshake()
reactor.stop()

class MyProtocolFactory(ClientFactory):
protocol = MyProtocol

if __name__ == '__main__':
f = MyProtocolFactory()
ctx = CertificateOptions()
reactor.connectSSL('www.blogger.com', 443, f, ctx)
reactor.run()
繋がったら切るってどういうことだよ!
っていうのを先ず書いてみたら
  File "test.py", line 7, in connectionMade
self.transport.socket.do_handshake()
OpenSSL.SSL.WantReadError:
例外ってしまいました

OpenSSL ってのは pyOpenSSL のことでしたが
まぁとりあえずググってみるかっつーダメな考えで
TLS connection with timeouts (and a few other difficulties) - Stack Overflow
とても丁寧な回答が投稿されてました

Twisted なのでそもそも reactor.connectSSL の socket は
timeout=30 ってのがデフォルト設定されていて
socket は non-blocking になっているので
handshake しに client hello して返事読もうとするんだけど
せっかちだからまだ返事来てなくってエラーになってるんで
落ち着いてちょっと待てってことでした
17.2.1 socket オブジェクト でも良く読めと

なので、待つことにしたら何も無しに終わるようになりました
@@ -1,10 +1,18 @@
+from time import sleep
+
from twisted.internet import reactor
from twisted.internet.protocol import Protocol, ClientFactory
from twisted.internet.ssl import CertificateOptions
+from OpenSSL.SSL import WantReadError

class MyProtocol(Protocol):
def connectionMade(self):
- self.transport.socket.do_handshake()
+ while True:
+ try:
+ self.transport.socket.do_handshake()
+ break
+ except WantReadError:
+ sleep(0.1)
reactor.stop()

class MyProtocolFactory(ClientFactory):
なんか、ちょっと可哀そうだから sleep 入れてみた
入れないと 2000 回以上 WantReadError してました
コンピューターは速いなぁ

あ、ほんとは、
Protocol とかでなくて twisted.protocols.basic.LineReceiver とかで
line を送ったり受けとったりし始めたらいいんです
handshake なんて裏でちょちょいとやってくれます
でもほら、
Protocol と ClientFactory だけで色々試してみたいじゃないですか

2009/07/14

debian semaphore

Python で非同期だか分散だかなんかそんな辺りをうろちょろしてまして
processing とか multiprocessing とかを見てみようかなと思いました

Python 3.0 Hacks:第5回 multiprocessingモジュールによるプロセス間通信
や、まぁ、Python 2.5 とか 2.6 なんだけど試してみようとしましたら
  File "/usr/local/lib/python2.6/multiprocessing/synchronize.py", line 49, in __init__
sl = self._semlock = _multiprocessing.SemLock(kind, value, maxvalue)
OSError: [Errno 13] Permission denied
$
などと言われて普通のユーザーだと怒られます
root だと動くんだけど

エラーの文字列とかで検索してたんだけどどうもパッとしたもんが出なくって
そもそも SemLock って何だろうって思ったんだけど
そういえば semaphore ってあったな
debian で root なら OK って /dev 以下のことが permission のこと多いし
って思いながら検索してみて目についたのが
sourCEntral - fedora 7 - fedora かよっ

「POSIX セマフォの概要」を眺めはじめたわけですが

ファイルシステム経由での名前付きセマフォへのアクセス


Linux では、名前付きセマフォは仮想ファイ ル シ ス テ ム (virtual file system) 内に sem.name という形の名前で作成される。仮想ファイルシステム は通常 /dev/shm 以下にマウントされる。

おぉ、知らなかったよそんなこと
mount してないし!!

ということで mount してみたらちゃんと動きました
何で mount してなかったかって /etc/fstab で noauto になってました
ん〜、何か良く分からないから要らないと思って noauto したのかな〜と思い
"/dev/shm" fstab lenny で検索してみたら
etch/Software/Init - Debian GNU/Linux スレッドテンプレ
/dev/shm を自動で mount しない方法を紹介している...
ん〜、etch だけど、これ見てたのかなぁ......

Google 先生のおかげで解決できました、ありがとうございました...

2009/07/13

lighttpd ssl

nginx で上手くいかないので lighttpd に逃げることにしました
nginx のことは後で考えます

(2009/07/26 削除)

ググったらやっぱり色々出てきます
Webサーバ「lighttpd」でSSLを使うには - @IT
も出るし、
Lighttpd - Docs:SSL - lighty labs
も出るし。

compile は configure に --with-openssl って付けてあげれば OK
openssl の dev っぽい package があれば OK

ssl.pemfile には base64 してある証明書と秘密鍵を繋げたものを入れときます
$ cat ssl.crt ssl.key > ssl.pem
とかして ssl.pem を指定してやれば OK、絶対パスとかで
crt と key の順番はどっちでもいいらしい

ssl.ca-file には中間証明書を指定してやります
こっちも同様で複数枚にも対応してるけど
apache や nginx と違ってサーバ証明書から階層が構築できるかどうかチェックするみたい
ただ垂れ流すだけでなく、ひと手間かけてるみたいでした

redirect したいときには 設定ファイルの頭の方で mod_redirect とか呼んどけば OK
source の中に doc/lighttpd.conf って雛形があるんでそれを使いましょう

で、多分こんなんで nginx ssl と同じなはず
$SERVER["socket"] == "192.168.0.1:80" {
url.redirect = ( "^/(.*)" => "https://sample1/" )

accesslog.filename = "/www/sample1/logs/http_access.log"
}

$SERVER["socket"] == "192.168.0.1:443" {
ssl.engine = "enable"
ssl.pemfile = "/www/sample1/etc/sample1.pem"
ssl.ca-file = "/www/sample1/etc/sample1.ca-bundle"
ssl.cipher-list = "ALL:!EXP:!ADH:!LOW:!SSLv2:!MD5"
server.document-root = "/www/sample1/httpsdocs"

accesslog.filename = "/www/sample1/logs/https_access.log"
}

$SERVER["socket"] == "192.168.0.2:443" {
ssl.engine = "enable"
ssl.pemfile = "/www/sample2/etc/sample2.pem"
ssl.ca-file = "/www/sample2/etc/sample2.ca-bundle"
ssl.cipher-list = "ALL:!EXP:!ADH:!LOW:!SSLv2:!MD5"
server.document-root = "/www/sample2/httpsdocs"

accesslog.filename = "/www/sample2/logs/https_access.log"

url.redirect = ( "^/(.*)" => "https://sample3/$1" )
}

$SERVER["socket"] == "192.168.0.2:80" {
accesslog.filename = "/www/sample2/logs/http_access.log"

url.redirect = ( "^/(.*)" => "https://sample2/$1" )
}
でもなー、nginx で上手くいかないんだよなー
なんでかなー

debug internet-explorer header

nginx ssl で redirect 設定しましたが
どうやら IE だと「Internet Explorer ではこのページは表示できません」と出てしまい
上手く表示されないことが判明しました。つうか「テストちゃんとしなさい」という話ですが。
これは困りました

で、解決すべく、とりあえず IE が何を思ってるのかを知りたいなと
Firebug みたいなのがあったよなと思って検索してみました
DebugBarで快適IEデバッグ « PHPで翻訳三昧
そうそう、DebugBar

入れてみたら HTTP(S) でのやり取りは見れたんですが
ん〜、盾のマークが出てきている気がする
My DebugBar | Doc / HTTPTab によると
"Unauthorized request returned by server (401)" ということらしい
ん〜、何じゃこりゃ
error_log を見ると
2009/07/13 15:23:28 [alert] 1987#0: worker process 1990 exited on signal 11
って出てて何か根本的に間違っている様子

ん〜、困った

dpkg pin 調べる

How to install Iceweasel 3.5 on Lenny
なんてのを知ってしまったんですよね
で、testing を使ってるんですよ
まぁ、いいかな、試してみるかな、と思って試してみました

そしたら、Window Manager が動かなくなっちゃったんですね
awesome っての使ってたんですけど
確か ~nabeken/diary/ : 今日からはじめる awesome チュートリアル を参考に
自分で package を build してたんですよね
でも何か、apt の結果に出てきてて入れちゃったりして

で、Iceweasel 入れたからといって安定してるわけでもなさそうで
Google Analytics 見たら見事に落ちてしまったので
全部元に戻してやろうと思いました
とりあえず testing 全部無しにできればいいだろうと思って検索
AptGet - Debian GNU/Linux スレッドテンプレ っすか
testing/unstable から stable にダウングレードしたい
apt-show-versions って command が紹介されてたのでインストール
$ apt-show-versions | grep unstable
これで testing から貰ってるリストが出ます

例えば awesome を stable に戻したかったら
$ apt-get -ud install awesome/stable
ってすると stable な awesome を入れようとします
で、何か依存関係が壊れちゃう場合とか
失敗したりもの凄い数の package を remove しようとしたりするので
必要そうなものを一辺に stable から入れようとしたりすると上手くいくはず

これを何度か繰りかえして無事に元の状態に戻せました
Debian ったら Awesome!!
自分で build した package を入れるときは努々気をつけましょう...