postgres 9.3 でjson

postgres 9.3にはjsonがネィティブにサポートされている。json内の値をアクセスしたりインデックスできる。

psql (9.3.0)
Type "help" for help.

# JSON型!
yoyodb=> CREATE TABLE publishers(id INT, info JSON);
CREATE TABLE

# JSON型をインデックス!!
yoyodb=> CREATE INDEX ON publishers( ( info->>'name' ) ) ;
CREATE INDEX

yoyodb=> insert into publishers (id,info) values (1, '{"name":"foo"}');
INSERT 0 1
yoyodb=> insert into publishers (id,info) values (2, '{"name":"bar"}');
INSERT 0 1
yoyodb=> insert into publishers (id,info) values (3, '{"name":"baz"}');
INSERT 0 1
yoyodb=> select * from publishers
yoyodb-> ;
id | info

                                        • -

1 | {"name":"foo"}
2 | {"name":"bar"}
3 | {"name":"baz"}
(3 rows)

# col->'key'でJSON内のフィールドをアクセス
yoyodb=> select info->'name' from publishers ;
?column?

                  • -

"foo"
"bar"
"baz"
(3 rows)

yoyodb=> select info from publishers where info->>'name'='bar';
info

                              • -

{"name":"bar"}
(1 row)

yoyodb=> select info->'name' from publishers where info->>'name'='bar';
?column?

                  • -

"bar"
(1 row)

debianでのインストール

http://www.postgresql.org/download/linux/ubuntu/

echo 'deb http://apt.postgresql.org/pub/repos/apt/ precise-pgdg main' | sudo tee

/etc/apt/sources.list.d/postgres.list

https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -

sudo apt-get update

sudo apt-get install postgresql-9.3

DBとユーザの設定

コマンドでこうする


createuser --no-adduser --no-createdb --pwprompt --encrypted yoyota
createdb --owner=ams --encoding=UNICODE yoyodb
あるいは

su postgres
psql
CREATE ROLE yoyota WITH LOGIN NOSUPERUSER NOCREATEROLE ENCRYPTED PASSWORD 'xxxx';
CREATE DATABASE yoyodb WITH OWNER yoyota;

クライアント権限

/etc/postgresql/9.3/main/pg_hba.conf
にこのようなラインを追加
local all all md5


これは超画期的ですよ。スキーマレスなJSONSQLをうまく融合させている。Postgresを優秀なRDBからno-sqlまでカバーする万能DBにしてしまう機能なのかもしれない。これからの開発はpsqlにきまり。

htmlでもありjpegでもあるファイル

このURLを開いてソースを見てもらいたい:
http://lcamtuf.coredump.cx/squirrel/

HTMLソースのコメントの中にJPEGデータが埋め込まれている。
そして、画像リンクは同じURL…

ソースをよく見てみるとHTMLタグの前に短いバイナリシーケンスがある。多分JPEGのヘッダーのコメント開始までの部分だろう。だとすると、このHTML・JPEG兼用ファイルはJPEGのコメントのなかにHTMLが入っていて、そのコメントの中にJPEGのデータが入っている二つのフォーマットが混った入れ子型ファイルってことになる。可愛いハックだ。

VIペダル

VIのモード切り替えを足でやって編集の高速化を可能にするペダルを自作したやつがいる:
https://github.com/alevchuk/vim-clutch
ペダルを踏むと「i」が入力され編集モードに。上げると「ESCAPE」でコマンドーモードに。素晴しいハックだ。

emacs用の「Controlペダル」が出たら使うかも。

被害妄想症の友人: Pythonでssh

paramikoというpythonsshライブラリで遠隔マシンの操作が自動化できる。

hello paramiko


import paramiko
transport=paramiko.Transport( ( 'takao3' , 22 ) )
transport.connect(username='tengu', password='xxxx')
c=transport.open_session()
c.exec_command('ls hoge > /dev/null')
if c.recv_exit_status()==0:
print c.makefile('rb', -1).readlines()
else:
print c.makefile_stderr('rb', -1).readlines()

接続


transport=paramiko.Transport( ( 'takao3', 22 ) )
transport.connect(username='tengu', password='xxxx')
pkeyを使うようにすべきだが、passwordを使わなきゃいけないときもある。ところで、TCPソケット以外のTransportって何がるんだろう…

セッション・チャンネル

遠隔コマンドとはChannelと呼ばれるデータストリームでデータをやり取りする。チャンネルを開いてデータを書き込んだり、返答を読み出したり、プロセスの終了を待ったりexit statusを得たりできる。subprocessのPopenみたいな感覚だ。
http://www.lag.net/paramiko/docs/ (help(paramiko.Channel)) を参照。


# チャンネル(セッション)はコマンド毎に開く
c=transport.open_session()
c.exec_command('ls hoge > /dev/null')

# コマンドが終了するまでブロックする。wait(2)に相当する。
if c.recv_exit_status()==0:
# 出力が少いのでバッファさせた出力を読む。
# 出力が多いと読んだ後にrecv_exit_status()しないといけないだろう。
print c.makefile('rb', -1).readlines()
else:
print c.makefile_stderr('rb', -1).readlines()

サイトのドキュメンテーション(http://www.lag.net/paramiko/docs/)もしっかりしていて、よく管理されたライブラリという印象が得られる。ちなみにparamikoという名は「被害妄想症の友人」という意味のエスペラント語の造語だそうだ。

Flameが世界最高水準の技術によるサイバー兵器であることを証明する機能リスト

f-secureがFlameの凄さを指摘している:
http://www.f-secure.com/weblog/archives/00002383.html

ユーザ監視

keyloggerとscreengrabberをもつ

暗号化通信

SSHSSLLUAライブラリを装備

フルテキスト監視

オフィス、PDF、Autodeskファイルなどのドキュメントをディスク・ネットワーク上で発見、テキストを抽出しそれをsqliteデータベースに保存。

盗聴機能

マイクロフォンをオンにして会話をオーディオファイルとして保存。

地域情報

ディスク・ネットワーク上で画像ファイルを探しそこからGPS情報を抽出。 写真がとられた場所を保存。

電話も

Bluetoothで接続されたスマホからアドレス帳を盗み出す

隔離されたマシンからも…

このように集められた情報は暗号化されたSQLiteデータベースとしてUSBスティックに保存され ネットワークのある環境に辿り着いたら送り返されるようになっている。ネットワークと接続されていないハイセキュリティー環境からも情報を盗み出せるようになっている。

...

証明書検証も打ち破る

上記の工作はスーパーコンピュータを使いこなすことが前提となる。このような高度な国家レベルのスパイ活動が2010年から行われていることになる。2010がサイバー戦争元年ということになる。

StuxnetとFlameの関係

Kasperskyの研究者がSuxnetとFlameの関連性を発見した

http://www.securelist.com/en/blog/208193568/Back_to_Stuxnet_the_missing_link

長々と証拠と解説が続くが最後の要約を見るとこういうことらしい:

  • Flameはstuxnet以前にプラットフォームとして存在していた
  • 初期(2009年版)のstuxnetはFlameのプラグインアーキテクチャによるモジュールを使用していた
  • このモジュールはその時点で知られていなかったzero-day exploitにりよ特権エスカレーションを可能にした
  • 2009年以降、stuxnetとFlameは別々に進化していった

つまり、協力関係にある二つのチームがサイバー兵器を開発していたということになる。

nginxで広告(HTML断片)をランダムに入れ替える

このようなHTMLで広告を表示するとしよう:


<div class="ad"><a href="..."><img src="..." /></a></div>
この画像リンクの部分をページロードする度に次のHTML断片の一つに入れ替えたい。


<a href="/redirect?dest=FOO"><img src="/img/FOO.png"></a>
<a href="/redirect?dest=BAR"><img src="/img/BAR.png"></a>
<a href="/redirect?dest=BAZ"><img src="/img/BAZ.png"></a>

高速化のためにキャッシュしておいたHTMLを、一部を書き換えるために再び生成するのは効率が悪い。HTMLの一部を前もって用意された幾つかのHTML断片の一つにおきかえるという簡単な操作なので、スクリプト言語によるバックエンドでなく、高速なCコードにやらせたい。幸いにnginxはこれを可能にしてくれる。
ssiとhttp_random_index_moduleを組み合わせれば、nginx内でページの一部をランダムに入れ替えることができる。

nginxをhttp_random_index_moduleを組み込んで構築する

 ./configure --with-http_random_index_module 
ssiはディフォルトで有効になっているので、ここで指定する必要はない。

random_index_moduleの設定

random_ads.conf


location /ad/banner/ { # このURLのリスポンスがランダムに入れ替わる。
random_index on;
alias /var/www/hoge/ad/html/banner/; # ここに表示するHTML断片ファイルを置いておく。
}

HTML断片管理

/var/www/hoge/ad/html/banner/にHTML断片を置く設定になっているが、そこにこのようにHTML断片ファイルを入れておく。ファイルの内容は上のHTML断面だ。


html/banner/foo.html
html/banner/bar.html
html/banner/baz.html
/ad/banner/からは foo.html, bar.html, baz.html のうちの一つの内容が返される。

この例ではHTML断片から参照される画像はこのように置いてある。nginx設定にこれに相当するalias文を入れておく。


img/banner/foo.jpg
img/banner/bar.jpg
img/banner/baz.jpg

nginx設定

  • HTML断片を組込むURLのロケーションでssiを有効にする


location / {
...
ssi on;
...

HTMLにSSI文を入れる


<div class="ad"><!--# include virtual="/ad/banner/" --></div>

random index URLの作動を確認


$ curl http://localhost:8000/ad/banner/
<a href="/redirect?dest=foo"><img src="/ad/img/banner/foo.jpg" /></a>
$ curl http://localhost:8000/ad/banner/
<a href="/redirect?dest=bar"><img src="/ad/img/banner/bar.jpg" /></a>
$ curl http://localhost:8000/ad/banner/
<a href="/redirect?dest=foo"><img src="/ad/img/banner/foo.jpg" /></a>
何度もリクエストするとランダムに入れ替わるはず。

ページレベルでの確認


$ curl -s http://localhost:8000/ | grep banner
<a href="/redirect?dest=foo"><img src="/ad/img/banner/foo.jpg" /></a>
$ curl -s http://localhost:8000/ | grep banner
<a href="/redirect?dest=foo"><img src="/ad/img/banner/foo.jpg" /></a>
$ curl -s http://localhost:8000/ | grep banner
<a href="/redirect?dest=bar"><img src="/ad/img/banner/bar.jpg" /></a>
広告HTML断片がランダムに入れ替わっていることを確認。

結論

nginx+ssi+http_random_index_moduleで高速、効率的そして安定した広告ロテーションができるようになった。

背景

安く高速に多大なトラフィックを処理するウェブアプリの秘訣はとにかくスクリプト言語によるバックエンドをトラフィックに直接さらさないことだと思う。一般的にはmemcacheなどでこの問題を解決しているようだが、この方法は用いていない。単独サーバの設定だとファイルシステムが十分キャッシュの役割をはたす。あとキャッシュにおいてはリクエストを受けたページがないとバックエンドにクライアントが行くことになる。資源の乏しい環境ではこれが問題をおこす。(これについては後に詳しく書きたい) そこでバックエンドのウェブアプリはモデルの更新毎に静的なドキュメントをファイルシステムに書き込むようにしてある。それをnginxがクライアントに送る。つまり、ウェブアプリはリクエストに応えるサーバではなく、イベントに反応して静的なサイトを更新するdaemonとして機能している。アプリケーションとしてのウェブサービスでは無理だが、比較的安定したコンテンスを多くの人に見せる出版系サイトならこの手法が使える。

このように静的なファイルとして管理されたサイトで広告ローテーションを導入しようとした際にこのssi+http_random_index_module方をみつけた。せっかくnginxだけでページを送り返せるようにしたのに、広告の入れ替えのためにpython/djangoのような重いプロセスをその過程にかかわらせたくなかった。幸い同じような問題に直面した人がいたらしく、http_random_index_moduleがnginxの標準モジュールとして存在していた。