どうしても映画に出てくるようなハッカーになりたいのであったが、しかし技術力もなければ勇気もない、そのうえオレは無能。そこでこのツール。アイムハッカーの出番だ。これを使えば誰でもハッカーを気取れるぞ。
2010年のYAPCで映画にでてくるハッカーになりたい という発表をしましたが、なんとなく動かすには敷居が高く感じられるとともに、直接 stdin を読む仕様であったため使えないケースも多く、僕は殆ど使わなくなってしまいました。
先日 uutransfer という screen のログから出力を拾ってほげほげするみたいなツールを書いてから、そういう仕組みをもうちょい応用できないかと考えた結果上記のようなものができあがりました。
簡単にまとめると「tail -f してるアクセスログをリアルタイムで解析してなんかソレっぽく表示するツール」です。
ImHacker というふざけた名前ですが、それは置いといて、node で動くように全部 JavaScript で書いてあります。
使いかた
基本的には GNU screen 上で動かすことを考えて作ってあります。screen 上で、まぁとにかく適当なサーバに ssh で入って (後述しますが mosh だとダメです) アクセスログを tail -f しときます。
ImHacker 自体は次のようにして起動します。
git clone git://github.com/cho45/imhacker.git npm install node app.js
起動して http://localhost:2012/ にアクセスすると、まず最初は設定ダイアログが開くので、アクセスログのフォーマットを指定します。Apache の LogFormat の行をそのままコピペしたら使える感じで作ってあります。設定したら Save を押してダイアログを閉じます (localStorage に設定を保存するので、次からは開きません)
アクセスログを tail -f しているウィンドウの番号に対応する数字のボタンを押します (右上の screen window と書いてあるやつ)。するとログの解析がはじまります。
とりあえず動かしてみたいなら、http://localhost:2012/#demo とするとデモモードで動きます。
コンセプト
- イマ! この! tail -f している! まさに! この! ログを! カイセキ! したいんだ! という感じなので screen からログをひろいます
- ほとんど全ての処理をクライアントサイド (ブラウザ) で行うので、拡張しやすいです。なんかカッコイイグラフを増やしたい。
- もともと来年の YAPC (今年は応募してない) で話すネタを作るつもりだったんですが Perl が一切使われてない
現状の機能
- req/sec の時系列推移
- レスポンスタイムのグラフ化
- リクエストメソッドの統計
- レスポンスステータスの統計
- 遅いレスポンスをかえす path をリスト
- 500 エラーをかえしている path をリスト
- IP アドレスによる時系列トラッキング
- まぁまぁのアクセスがあるサイトでやると楽しいとおもうのでやってみてください
備考
- mosh だと、サーバ側で特殊なことをしていて、クライアント側では部分書き換えをするみたいなことをしているみたいで、screen のログに「見たまま」記録されないので ssh を使う必要があります
- screen 上で動かさなくても、./bin/imhacker-in というコマンドに出力をパイプすると、全クライアントにその内容が送信されるようになります。すなわち tail -f /var/log/access_log | ./bin/imhacker-in としても良いです
- bootstrap をつかってみました
- 最初は Perl で作ろうとおもったけど socket.io を使いたかったのでやめた。あと node のほうが動かすのが簡単そう
Plack::Session::Store::File でできるだけセッションファイルを作らないようにする
Plack::Session::Store::File はお手軽でちょっとしたものを作りたいときに便利なんですが、いかんせんアクセスしただけでセッションファイルを作りまくるのは嫌なのでなんとかしたい、という話です
結論からいうと以下のようにすればいいようです。ここでは MessagePack を使ってますがシリアライザ自体は別になんでもよくて、serializer に指定するサブルーチンで、$session に値がなければ書きこまないようにしただけです。
この場合、セッションに値を一切入れない場合毎回セッションIDが変化する状態 (不定) になります。CSRF 対策なんかでセッションIDを流用している場合は、セッションIDを確定させるため POST 前の画面を開いた時点では、セッションに適当な値を書きこむ必要があります (セッションを使うところではセッションに値を書くことで明示的にそれを示す)。
my $MessagePack = Data::MessagePack->new;
$MessagePack->canonical;
builder {
enable "Plack::Middleware::Session",
state => Plack::Session::State::Cookie->new(
session_key => 's',
expires => 60 * 60 * 24 * 365,
),
store => Plack::Session::Store::File->new(
dir => config->root->subdir('session').q(),
serializer => sub {
my ($session, $file) = @_;
return unless %$session;
my $fh = file($file)->openw;
print $fh $MessagePack->pack($session);
close $fh;
},
deserializer => sub {
my ($file) = @_;
eval {
$MessagePack->unpack(scalar file($file)->slurp)
} || +{}
},
);
$app;
|;
git pull したあとどのファイルが変更されたのか表示する
デフォルトだと
git config --get merge.stat
が true なので表示されるが (表示されるのは git diff --stat ORIG_HEAD 相当)、
git config --local --get branch.master.rebase
が true に設定されていたり(git config --get branch.autosetuprebase が always だったりすると設定される)、 git pull --rebase とした場合、merge ではなく rebase になるため表示されず悲しい。これは
git config --get rebase.stat
が false by default だからで、true にセットしてやればいいようだ。
git config --global --add --bool rebase.stat true
Excel が自動認識するファイルフォーマット
「UTF-16LE BOM付き タブ区切り」で出力すれば認識される (Microsoft Excel 2008 for Mac でのみ確認)
gerry++
Lion の DBD::mysql
mysql 公式パッケージの DMG を入れると、/usr/local/mysql* にインストールされる。めんどいので system 側から symlink をはる。
sudo ln -s /usr/local/mysql/lib/libmysqlclient.18.dylib /usr/lib/
あとなんか Makefile.PL 実行時に grant しろって言われるやつそのままやってもダメなことがあるけど適当にやるしかない。
gerry++
gerry++
接続済み SSH セッションからファイルをダウンロードする
別のコンピュータから手元のコンピュータへファイルを転送するというニーズはいつの時代でもあるもので、それは現代においても同じです。しかしどんなに文明が進んだとしても、そう簡単には解決しない問題も同時にあり続けます。
通常、SSH で接続できるコンピュータであれば、scp を使えば特に何も考えず、鼻クソをほじりながら、あるいは便器に座りケツニューロリンカーをバーストリンクしている状態であっても、ファイルを転送することは簡単にできます。
しかし、非常に限定されたケースであるとはいえ、踏み台にするサーバやシステム構成上、すんなりと SSH 接続を確立できないホストがあることも事実です。そこに接続してシェルを操作することはできる、でもファイルを手元に持ってくることが難しい。そんなジレンマが僕らの前に立ちはだかるわけです。
この問題について、定期的に悩んでは「そんなに量もないし、コピペして繋ぎあわせよう」だとか「既に立ってる HTTPD でプロキシ経由でとってこよう」とか、なかなかスマートとは言いがたい解決をしてきましたが、この度ようやく解決できそうだというところまできたので書き記すことにします。
ttyrec -u を使う
https://twitter.com/kzys/status/220113051536461824 で教えてもらったのですが、ttyrec には uuencode された文字列を検出して手元に書きこみ機能があり、ttyrec -u で新しいシェルを起動して、その中で SSH 接続を確立し、uuencode を実行することでファイルを転送することができます。
これは事前に ttyrec 経由で SSH のプロセスを立ち上げる必要があるので「接続済み SSH セッションからファイルをダウンロードする」方法とは言えませんが、何らかの理由があって簡単に接続を確立できないホストなら、手軽にファイルを転送できて便利そうです。
screen を使う
ttyrec -u でもいいのですが、やはり「接続済み SSH セッションからファイルをダウンロードする」方法が欲しいと思い、screen の log 機能を使って ttyrec -u 相当のこと (すなわち uuencode された文字列を検出して、手元に書きこみ) をしようと思いました。普段から手元で screen を起動しているなら、それを使えばいいだけでした。
$ uutransfer 1
のように起動します。引数は screen におけるウィンドウ番号です。ここではウィンドウ番号 1 に接続済みの SSH セッションがあるという前提です。
起動すると
Waiting for log... Watching window 1
とメッセージが起動したウィンドウに出ると同時に、ウィンドウが 1 に切り替わります (つまりメッセージは見えません)。ウィンドウ1はログを保存するモードになるので、ウィンドウステータスに (L) がつきます。
この状態で、ウィンドウ1 で
$ uuencode foo.txt foo.txt
とすると、手元に foo.txt がダウンロードされ、screen に '/Users/cho45/foo.txt was created. md5: bd5ce86cd3e2473bf32bcb5e2a824453' といった感じのメッセージが echo されます。
起動したウィンドウに戻るとこんな感じのメッセージがでています。
foo.txt: Detect uuencoded file... foo.txt: Writing... 803 bytes foo.txt: Done /Users/cho45/foo.txt 580 bytes bd5ce86cd3e2473bf32bcb5e2a824453
Ctrl-C で止めるまで動き続けるので、終わったら Ctrl-C をして止めます。止めると、screen 側のログを保存するモードも終了します。
おまけ
uuencode が意外にシステムに入っていなくてダルい
ruby -e 'puts "begin 666 #{File.basename(ARGV[0])}\n#{[File.read(ARGV[0])].pack("u")}`\nend"' fileperl -MPath::Class -e 'my $file=file(shift);printf("begin 666 %s\n%s`\nend\n",$file->basename,pack("u",scalar $file->slurp))' file
