2014年8月22日 (金)

複数台WEBサーバのラウンドロビンをサブドメインで固定振り分け

負荷分散構成で、通常はWEBサーバをLBでラウンドロビンさせているけど、特定の操作のあいだはずっと同じサーバに振り分けたいというニーズは、ままあるかと思います。

例えば課金処理のセッションの開始から終了までとか、XSS対策でワンタイムトークンの突き合わせをおこなうとか。

同じIPであれば、L4のLBでもIPのセッション維持で片側に倒すことはできますが、スマホの移動中やテザリングなど、途中でIPが変わる場合は対応できません。

かといってL7のLBでクエリから接続するサーバを振り分けるにも、L7は高いので、予算的に難しいことも多いと思います。

ここでは、Apacheのmod_proxyを使って、サブドメインによって接続するサーバを切り替える、という方法を書きとめておきます。
アプリケーション層から制御できるため、柔軟で、しかも費用がかかりません。

環境:
CentOS 6.5
Apache 2.2.7

構成:
WEBサーバ1 web1 (192.168.1.11)
WEBサーバ2 web2 (192.168.1.12)

ルール:
ドメイン test.com(サブドメインなし) 宛 => web1かweb2のいずれか
ドメイン www1.test.com宛 => web1
ドメイン www2.test.com宛 => web2

イメージ:
Image_2

設定:
@web1


vi /etc/hosts
192.168.1.12 web2 www2.test.com

vi /etc/httpd/conf/httpd.conf
    <VirtualHost 192.168.1.11:80>
        (略)
        <IfModule mod_rewrite.c>
            #プライベートネットワーク以外からのアクセスでwww2.test.com宛の接続はweb2へ
            RewriteCond %{REMOTE_ADDR} !^192\.168\.1\.\d+$
            RewriteCond %{HTTP_HOST} ^www2\.test\.com$
            RewriteRule ^(.+)        %{HTTP_HOST}$1 [C]
            RewriteRule ^www2\.test\.com(.*) http://www2.test.com$1$2 [P,L]
        </IfModule>
    </VirtualHost>

@web2


vi /etc/hosts
192.168.1.11 web1 www1.test.com

vi /etc/httpd/conf/httpd.conf
    <VirtualHost 192.168.1.12:80>
        (略)
        <IfModule mod_rewrite.c>
            #プライベートネットワーク以外からのアクセスでwww1.test.com宛の接続はweb1へ
            RewriteCond %{REMOTE_ADDR} !^192\.168\.1\.\d+$
            RewriteCond %{HTTP_HOST} ^www1\.test\.com$
            RewriteRule ^(.+)        %{HTTP_HOST}$1 [C]
            RewriteRule ^www1\.test\.com(.*) http://www1.test.com$1$2 [P,L]
        </IfModule>
    </VirtualHost>


これで、www1.test.comへのアクセスは常にweb1へ、www2.test.comへのアクセスは常にweb2へ、サブドメイン指定なしのtest.comはラウンドロビン、になります。

デメリットとしては、LBではなくWEBサーバが振り分けるため、リダイレクトによるネットワークコストがかかります。
また、未検証ですが、LB側で重み付けやIPでセッション維持をおこなっていると、LBはweb1へ振り続け、WEBサーバはweb2に振り続け、といったリダイレクトのループが発生してしまうかもしれません。

| | コメント (0) | トラックバック (0)

2014年5月27日 (火)

WordPressを使ったブログやサイトの制作代行はじめました

告知ですいません。
WordPressを使ったブログやサイトの制作代行を始めました。
なかなかの安価だと思いますので、よろしくお願いいたします。

http://soulproj.com/wp/

| | コメント (0) | トラックバック (0)

2014年3月 2日 (日)

目に見えない文字に注意 ~ 制御コードについて

この文章をコピーしないで


↑直上の文章はなんて入力してあるか分かりますか?
ただ「この文章をコピーしないで」と入力したわけじゃありません。

試しにこの文章をコピーして、ちゃんとしたテキストエディタ(メモ帳じゃないやつ)にペーストしてみてください。
あら不思議。
ブログ上では目に見えない特殊な文字が現れましたね。


この文字のことを、正確にはエスケープシーケンスや制御コードと呼びます。
これは"文字"というより、テキストの扱い方を制御する"命令"に近く、この制御コードによって文字は動きや振る舞いを変えます。


一番よく親しんでいるのは、改行コードではないかと思います。
エンターキーによってこれを与えられると、文字は「折り返す」という振る舞いを取ります。
折り返し自体は文字ではないですが、文字に含まれる制御コードという文字によって、動きが与えられるわけですね。


さて、この制御コードが影響するのは、ブログだけでしょうか?

いいえ、ブログやホームページのようなWEBアプリからスマホアプリ、ゲームアプリ、メーラー、プリンタ、テキストエディタなど、テキストを扱うもの全般に及びます。

そしてもちろん、プログラム言語におけるテキストを扱う関数も同様です。
文字列関数を扱う際、この制御コードによってセキュリティを突かれるヌルバイト攻撃やヘッダー分割攻撃などが知られていて、開発者はその対策を考慮したコーデイングが求められています。

これは制御コードによって振る舞いを変えた文字が、文字としての意味以外を持って、プログラムに期待しない動きを与えてしまうことで、これをハッキングされることを防止するものになります。

そのため、ちゃんとしているサイトでは、文字列がバイナリで扱えないようなバイナリセーフでない関数を制御コード付のテキストが通ることで予期せぬ動きをとらないよう、ヌルバイトコードなどは共通入力処理などで一括除去などしていると思いますが、文頭の制御コードはいかがでしょうか。

このブログもそうですが、facebookなどでも除去しておらず、混入させることが可能となっています。


はい。
で、ここからが本題です。

先日facebookからブログへ記事を転送させようとして、パースエラーが発生して転送に失敗しました。
原因がなかなかつかめなかったのですが、この目に見えない制御コードが含まれていたためエラーとなっていました。

この制御コードは「ユニット区切り(US)」と呼ばれるエスケープシーケンスです。
facebookでは以前「ファイル区切り(FS)」というエスケープシーケンスも混ざってきていました。
混入工程は分かりませんが、こういう余計なものはしっかり除去しておくか、検知した時点でエラーにするのが賢明かと思います。

というわけでfacebookフィードをどこかに投稿する際に「parse error. not well formed」とか出て、文字コードに問題ない場合はこれを疑ってみてください。
除去する場合、PHPならこんなコードをいれておいた方がいいと思います。

$feed = str_replace(array(chr(28), chr(31)), '', $feed);

たぶん↓このあたりも改行コード以外は追加しておいた方がいいかもしれません。
http://e-words.jp/p/r-ascii.html

一個一個の制御コードがどんな意味を持つのか調べて個別に対策するコストを考えると、必要な制御コード以外は一律除去しておくのが現実的な落としどころではないかと。はい。


後記) facebookアプリでは文頭の制御コードはパース不能な文字としてマスク文字に置き換えられて、目に見えるようになってますね。親切。

| | コメント (0) | トラックバック (0)

2013年10月14日 (月)

UPSに監視ツールを導入する(Network UPS Tools)

安くて頼れる無停電電源装置の、CLASSIC PRO UPS1500LXを買いまして、備えあれば憂いがないという日々を送りはじめてみます。
中にUPSilon2000という電源管理ソフトウェアが同梱されていますが、CUIでは使えないのでスルーします。
代わりにNetwork UPS Toolsという、フリーウェアの汎用的な電源管理ツールを使います。

以下、導入時のコマンド履歴です。誰かのお役に立てば。

$ sudo /sbin/lsusb
	Bus 005 Device 001: ID 0000:0000
	Bus 003 Device 003: ID 0001:0000 Fry's Electronics
	Bus 003 Device 001: ID 0000:0000
	Bus 004 Device 001: ID 0000:0000
	Bus 001 Device 001: ID 0000:0000
	Bus 002 Device 001: ID 0000:0000
 
$ sudo /sbin/lsusb -v -s 003:003
	Bus 003 Device 003: ID 0001:0000 Fry's Electronics
	Device Descriptor:
	  bLength                18
	  bDescriptorType         1
	  bcdUSB               1.00
	  bDeviceClass            0 (Defined at Interface level)
	  bDeviceSubClass         0
	  bDeviceProtocol         0
	  bMaxPacketSize0         8
	  idVendor           0x0001 Fry's Electronics
	  idProduct          0x0000
	  bcdDevice            1.00
	  iManufacturer           1 MEC
	  iProduct                2 MEC0002
	  iSerial                 0
	  bNumConfigurations      1
	  Configuration Descriptor:
	    bLength                 9
	    bDescriptorType         2
	    wTotalLength           34
	    bNumInterfaces          1
	    bConfigurationValue     1
	    iConfiguration          0
	    bmAttributes         0x80
	    MaxPower              100mA
	    Interface Descriptor:
	      bLength                 9
	      bDescriptorType         4
	      bInterfaceNumber        0
	      bAlternateSetting       0
	      bNumEndpoints           1
	      bInterfaceClass         3 Human Interface Device
	      bInterfaceSubClass      0 No Subclass
	      bInterfaceProtocol      0 None
	      iInterface              0
	        HID Device Descriptor:
	          bLength                 9
	          bDescriptorType        33
	          bcdHID               1.00
	          bCountryCode            0 Not supported
	          bNumDescriptors         1
	          bDescriptorType        34 Report
	          wDescriptorLength     624
	         Report Descriptors:
	           ** UNAVAILABLE **
	      Endpoint Descriptor:
	        bLength                 7
	        bDescriptorType         5
	        bEndpointAddress     0x81  EP 1 IN
	        bmAttributes            3
	          Transfer Type            Interrupt
	          Synch Type               None
	          Usage Type               Data
	        wMaxPacketSize     0x0008  1x 8 bytes
	        bInterval              10
$ sudo yum install -y libusb.i386
$ sudo yum install -y libusb-devel.i386
$ cd /usr/local/src/
$ wget http://www.networkupstools.org/source/2.6/nut-2.6.5.tar.gz
$ tar zxvf nut-2.6.5.tar.gz
$ cd nut-2.6.5
$ ./configure --with-user={UPS管理ユーザ名} --with-group={UPS管理グループ名} --with-usb --with-cgi --prefix=/etc/nut
$ make
$ sudo make install
$ sudo mv /etc/nut/nut.conf.sample /etc/nut/nut.conf
$ sudo vi /etc/nut/nut.conf
	MODE=standalone
 
$ sudo mv /etc/nut/ups.conf.sample /etc/nut/ups.conf
$ sudo vi /etc/nut/ups.conf
	[ups1500lx]
	    driver = blazer_usb
	    vendorid = 0001
	    productid = 0000
	    subdriver = krauler
	    desc = "SoundHouse CLASSIC PRO UPS 1500LX"
	    port = auto
 
$ sudo mv /etc/nut/upsd.users.sample /etc/nut/upsd.users
$ sudo vi /etc/nut/upsd.users
	[{UPS管理ユーザ名}]
	password = {UPS管理ユーザ パスワード}
	allowfrom = localhost
	upsmon master
 
$ sudo mv /etc/nut/upsmon.conf.sample /etc/nut/upsmon.conf
$ sudo vi /etc/nut/upsmon.conf
	MONITOR ups1500lx@localhost 1 {UPS管理ユーザ名} {UPS管理ユーザ パスワード} master
	RUN_AS_USER root
 
	NOTIFYCMD /etc/nut/notify.sh
 
	NOTIFYMSG ONLINE   "UPS %s on line power"
	NOTIFYMSG ONBATT   "UPS %s on battery"
	NOTIFYMSG LOWBATT  "UPS %s battery is low"
	NOTIFYMSG SHUTDOWN "Auto logout and shutdown proceeding"
	NOTIFYMSG REPLBATT "UPS %s battery needs to be replaced"
 
	NOTIFYFLAG ONLINE  SYSLOG+EXEC
	NOTIFYFLAG ONBATT  SYSLOG+EXEC
	NOTIFYFLAG LOWBATT SYSLOG+EXEC
	NOTIFYFLAG SHUTDOWN SYSLOG+EXEC
	NOTIFYFLAG REPLBATT SYSLOG+EXEC
 
$ sudo vi /etc/nut/notify.sh
#!/bin/bash
 
/usr/sbin/sendmail -t << EOF
From: {送信元メールアドレス}
Subject: Alart form Network UPS Tools
To: {送信先メールアドレス}
 
This is auto mail from Network UPS Tools about UPS1500LX
 
${1}
EOF
 
$ sudo chmod 0755 /etc/nut/notify.sh
 
$ sudo mv /etc/nut/upsd.conf.sample /etc/nut/upsd.conf
$ sudo mv /etc/nut/upssched.conf.sample /etc/nut/upssched.conf
$ sudo blazer_usb -u root -a ups1500lx -DDD
   0.001111     Checking device (0001/0000) (003/003)
   2.012263     - VendorID: 0001
   2.012302     - ProductID: 0000
   2.012323     - Manufacturer: MEC
   2.012333     - Product: MEC0002
   2.012342     - Serial Number: unknown
   2.012353     - Bus: 003
   2.012364     Trying to match device
   2.012412     Device matches
   2.018275     Trying megatec protocol...
   2.018307     send: Q1
   2.635094     received 47 (40)
   2.635127     read: (098.0 000.0 100.0 027 50.0 25.8 29.0 00001000
   2.635274     Status read in 1 tries
   2.635290     Supported UPS detected with megatec protocol
	...
 
$ upsdrvctl start ups1500lx
	Network UPS Tools - UPS driver controller 2.6.5
	Network UPS Tools - Megatec/Q1 protocol USB driver 0.09 (2.6.5)
	Supported UPS detected with megatec protocol
	Vendor information read in 1 tries
	No values provided for battery high/low voltages in ups.conf
	
	Using 'guestimation' (low: 20.800000, high: 26.000000)!
	Battery runtime will not be calculated (runtimecal not set)
 
$ upsdrvctl stop ups1500lx
$ sudo vi /etc/rc.d/init.d/nut
	#!/bin/bash
	# chkconfig: 2345 60 99
	# description: NUT ups daemon
	# processname: nut
	# config: /etc/nut/ups.conf
	# pidfile: /var/state/ups
	
	source /etc/rc.d/init.d/functions
	
	RETVAL=0
	prog="nut"
	upsdrv="/bin/upsdrvctl"
	drv="blazer_usb"
	upsd="/sbin/upsd"
	ups="upsd"
	upsmon="/sbin/upsmon"
	mon="upsmon"
	desc="NUT daemon"
	
	start() {
	        echo -n $"Starting $desc : "
	        daemon $upsdrv -u root start
	        $upsd -u root
	        $upsmon -u root
	        RETVAL=$?
	        echo
	        [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$prog
	        return $RETVAL
	}
	
	stop() {
	        echo -n $"Shutting down $desc: "
	        killproc $drv
	        killproc $ups
	        killproc $mon
	        RETVAL=$?
	        echo
	        [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$prog
	        return $RETVAL
	}
	
	restart() {
	        stop
	        start
	}
	
	reload() {
	        echo -n $"Reloading $desc: "
	        killproc $drv -HUP
	        killproc $ups -HUP
	        killproc $mon -HUP
	        RETVAL=$?
	        echo
	        return $RETVAL
	}
	
	case "$1" in
	  start)
	        start
	        ;;
	  stop)
	        stop
	        ;;
	  restart)
	        restart
	        ;;
	  reload)
	        reload
	        ;;
	  condrestart)
	        [ -e /var/lock/subsys/$prog ] && restart
	        RETVAL=$?
	        ;;
	  status)
	        status $prog
	        RETVAL=$?
	        ;;
	  *)
	        echo $"Usage: $0 {start|stop|restart|reload|condrestart|status}"
	        RETVAL=1
	esac
	
	exit $RETVAL
 	
$ sudo chmod 0755 /etc/rc.d/init.d/nut
$ sudo /sbin/chkconfig --add nut
$ sudo /sbin/chkconfig --list nut
	nut             0:off   1:off   2:on    3:on    4:on    5:on    6:off
 
$  sudo /sbin/service nut start
$ ps aux | grep ups | grep -v cupsd | grep -v grep
	root     31066  0.0  0.0   2160   692 ?        Ss   23:22   0:00 //bin/blazer_usb -a ups1500lx -u root
	root     31068  0.0  0.0   5240   736 ?        Ss   23:22   0:00 /sbin/upsd -u root
	root     31070  0.0  0.0   5120   828 ?        Ss   23:22   0:00 /sbin/upsmon -u root
	root     31071  0.0  0.0   5120   784 ?        S    23:22   0:00 /sbin/upsmon -u root
 
$  sudo /sbin/service nut stop

| | コメント (0) | トラックバック (0)

2013年5月24日 (金)

yum曰く「セグメンテーション違反です」

敷布団にクールシーツを使うようになって、寝返りが極上に気持ちよくなったなあと思っていたある日、yumを叩くと「セグメンテーション違反です」と怒ってくるようになった。
ググるとすでにソリューションはいろいろあがっている。
https://www.google.co.jp/search?q=yum+セグメンテーション違反です

しかし


$ sudo rm -f /var/lib/rpm/__db.00*
$ sudo rpm –rebuilddb
$ sudo yum clean all



$ sudo yum –disablerepo=* –enablerepo=base install

もダメだった場合、libz.so.1.2.5が悪さをしているので1.2.3にダウングレードする、というソリューションがあります。
結果から言うと、これでうちも解決しました。
クールシーツに取り替えるタイミングのいつかどこかでかに、何かをインストールした際にlibzが1.2.5にあがってしまったのでしょう。依存性するライブラリを暗黙でインストールとかしちゃったのでしょうか。そら恐ろしいです。もはやうすら寂しいです。

ではなぜここであらためて記事にして書いているのかというと、「libzは1.2.8の上位バージョンではダメ」でした。
上位バージョンであれば、不具合解消はおのずと務まっているはずだというのは、少なくとも本件においては幻想でした。
環境によるかもしれません。うちはこうです。

$ cat /proc/version
	Linux version 2.6.18-308.13.1.el5 (mockbuild@builder10.centos.org) 
	(gcc version 4.1.2 20080704 (Red Hat 4.1.2-52)) #1 SMP Tue Aug 21 17:10:06 EDT 2012
$ cat /etc/redhat-release
	CentOS release 5.5 (Final)
$ yum --version
	3.2.22

仕方ないので、以下を参考に1.2.3にして解決です。
http://serverfault.com/questions/256385/yum-segmentation-fault-in-centos

それと蛇足かもしれませんが一点注意を。
libzがなくなるとsudoコマンドが使えなくなります。1.2.5を削除してからインストールしようなどとすると、


$ sudo make install
error while loading shared libraries: libz.so.1:cannot open shared object file :
No such file or directory

となってインストールできなくなってしまいます。

素直に以下のように、シンボリックを元に戻して対応が吉。

$ ls -l /usr/lib | grep zlib
	-rw-r--r--  1 root root  101462  7月 25  2010 libz.a
	lrwxrwxrwx  1 root root      13  7月 25  2010 libz.so -> libz.so.1.2.5
	lrwxrwxrwx  1 root root      13  7月 25  2010 libz.so.1 -> libz.so.1.2.5
	-rwxr-xr-x  1 root root   75028  1月 10  2007 libz.so.1.2.3
	-rwxr-xr-x  1 root root   96572  7月 25  2010 libz.so.1.2.5
$ sudo rm libz.so libz.so.1
$ sudo lin -s libz.so.1.2.3 libz.so
$ sudo lin -s libz.so.1.2.3 libz.so.1

これでまた気持ちのいい寝返りを打てる安息の日々が戻ってきましたとさ。

| | コメント (0) | トラックバック (0)

2013年5月 9日 (木)

続・クリックジャッキング対策

こんにちは、シーチキンマヨネーズは史上最強ですね。時代を超越して、今後もごはんに塗られ続けることでしょう。
ところで、クリックジャッキング対策の続きです。

もし"X-FRAME-OPTIONS"ヘッダに対応していないブラウザを利用していて、変えることができないユーザーを考慮するなら、以下の代替策もいいと思います。


if (window.self != window.top)
window.top.location = window.self.location;


「もし自身のページと親ページのウインドウが異なれば、自身のページでリダイレクトする」の意


これ、なにげに強力です。
"X-FRAME-OPTIONS"は許容条件外のフレーム使用がおこなわれると、自身のページを表示しなくなりますが(それによって不正利用を防止する)、これは自身のページでリダイレクトし直すという、もっとアグレッシブでサディスティックでアトミックな挙動になります。

ユーザーに不正利用の温床の存在すら知らせない(すぐにリダイレクトしてフレーム内のサイトが親ページとして表示され直されるので)という、ユートピア追求型です。

場合によっては、不正利用しようとしている側からすれば、何度アクセスしてもリダイレクトされて存在をニフラムされてしまうことにいきり立って、攻撃心が刺激され、オームの赤い目をしながら、あの手この手でハックしにかかってくるかもしれません。
この場合、つまり相手のサディズムが「強制リダイレクト」というサディズムをうわまわるかどうかにかかってきますね。

まあ、滅多にいないと思いますが。
そういうサリーちゃんのパパみたいなひとの対策も踏まえるなら、やはり"X-FRAME-OPTIONS"ヘッダで、つつましく大和撫子型の防衛網を敷くのがよろしいのではないかと存じます。

| | コメント (0) | トラックバック (0)

2013年4月26日 (金)

クリックジャッキング対策

先日、仕事の取引先から「クリックジャッキングを仕込めないか」と相談があった。
クリックジャッキング?
初めて聞く用語だったので調べてみると・・・、わは!これはダメだ!


技術的には簡単に仕込めそうですが、負の技術ですね。


リンク先にエロサイト貼っておいて、iframeでレイヤー構造にして透明のfacebookのいいねボタン仕込んどけば、これ性癖みんなに教えちゃうね。


根本的な対策はユーザーにはできなくて、サイト管理者が自サイトにX-FRAME-OPTIONSヘッダを取り入れることで、はじめてブラウザ側のセキュリティ機能で検知できるようですが、ユーザー側で対策できないところはXSSみたいで超怖い。

しかも2013年3月時点の調査状況では、対策済サイトはわずか5%強しかないとのこと。
サイトを公開しているひとは、ここらへんのソリューション少し抑えておいた方がよさそうです。
X-FRAME-OPTIONS によるクリックジャッキング対策


・・・


というわけで、あなたのブラウザがちゃんとクリックジャッキング対策が施されているか、チェックするツールをつくってみました。
クリックジャッキング対策ブラウザチェッカー


サイト管理者のみなさんも、ユーザーが安心して訪れられるように対策していきましょう。
95%近くのサイトは未対策だそうですから、いくらブラヴザが最新のものでも防げないようです(noscriptなどのアドオンを導入するなど、別の対策もあるそうですが)。

| | コメント (0) | トラックバック (0)

2011年6月18日 (土)

php swigでフィボナッチしてみた

PHPはスクリプト言語なので、書いたらすぐ実行できますが、そのつど逐次解釈しながら実行されるので、あまりパフォーマンスを要求される処理には向かないなどと言われます。
そうは言っても、PHPで重い処理を書かないとならないこともあるでしょう。そんなときにも、こなれたPHPでパフォーマンスを確保しつつコーディングすることができれば、将来は今よりもう少し明るいものになるかもしれません。

SWIGはそんなときに使えそうです。売りはどちらかといったら「C/C++のコードをさまざまなスクリプト言語で利用できること」にあるようですが、「C/C++のもつパフォーマンスの恩恵に預かること」もできて、上述のようなケースではとても役に立ちます。
しかもC/C++のコードを組み込むのにPHPのリコンパイルは不要という、使い勝手もとてもいい感じです。

ためしにSWIGのインストールから、フィボナッチ数列の計算式をSWIGで実装し、通常のユーザー定義関数のそれと比べてどの程度差が出るか、はっけよいのこったしてみました。

環境:CentOS 5.3、 PHP 5.3.1

さっそくまずはSWIGのインストール


$ cd /usr/local/src/
$ wget "http://sourceforge.net/projects/swig/files/swig/swig-2.0.4/swig-2.0.4.tar.gz/download"
$ tar zxvf swig-2.0.4.tar.gz
$ cd swig-2.0.4
$ ./configure
$ make
$ sudo make install
$ which swig
/usr/local/bin/swig
$ cd ~/

簡単です。


次に、Cでフィボナッチ


$ vim fibo.i
/* fibo.i */
%module Fibonacci
%{
unsigned long calc(int n);
%}
unsigned long calc(int n);

$ vim fibo.c
/* calc.c */
unsigned long calc(int n) {
unsigned long f;
switch (n) {
case 1:
case 2:
f = 1L;
break;
default:
f = calc(n - 1) + calc(n - 2);
break;
}
return (f);
}

で、フィボナッチをPHPに組み込む


$ swig -php fibo.i
$ cc -fpic -c fibo.c
$ gcc `php-config --includes` -fpic -c fibo_wrap.c
$ gcc -shared *.o -o fibo.so
$ sudo cp fibo.so `php-config --extension-dir`
$ echo 'extension=fibo.so' >> /etc/php.ini

これで完了です。
実行する前に、比較用に普通のPHPも書いておきます。


PHPでフィボナッチ


$ vim fib.php
function fibonacci($n) {
switch ($n) {
case 1:
case 2:
$n = 1;
break;
default:
$n = fibonacci($n-1) + fibonacci($n-2);
break;
}
return $n;
}


では比べてみましょう。40番目のフィボナッチ数を求めてみます。

まず普通のPHPから


$ time php -r 'require "fib.php";
> for($i=1; $i<=40; $i++) {
> var_dump(fibonacci($i));
> }'
...
int(102334155)

real 5m52.546s
user 5m52.369s
sys 0m0.107s

6分弱かかりました。

次にSWIGで組み込んだ方


$ time php -r 'require "fibonacci.php";
> for($i=1; $i<=40; $i++) {
> var_dump(Fibonacci::calc($i));
> }'
...
int(102334155)

real 0m5.444s
user 0m5.434s
sys 0m0.010s

6秒弱でした。

なんと実に約65倍のパフォーマンス。想像以上の速さです。KVSとか全文検索とかとの連携やバッチなどで、ここぞというときには積極的に使ってみたいですね。

ref)
SWIG

SWIG を使って PHP 拡張機能を作成する

フィボナッチ数でベンチマーク(サーバーサイド言語)

C言語講座:フィボナッチ数列

| | コメント (0) | トラックバック (0)

2010年12月26日 (日)

ssh不正アクセスを国別に監視する(muninプラグイン)

Munin_2
監視サーバにnagiosやmuninをいれている方も多いかと思います。
うちの自宅サーバにはmuninをいれているのですが、使い慣れてくると標準実装のプラグインだけじゃ物足りなくなって、いろんなプラグインを追加したくなりますね。
そんなときはMunin Exchangeというプラグインのレポジトリサイトで、がさごそします。


前回の記事で、外部開発者にSVNサーバへアクセスさせるための方法を書きましたが、そのときsshのポートを公開したら、途端に不正侵入行為がわんさとやってきました。
シスログを眺めていると、ひとつのIPで大量のアクセスを試行するケースが多く、これがいわゆるブルートフォースアタックかと、感慨深い気持ちになりました。


で、シスログを眺めるのも手間なので、こないだsshd_logという、ssh経由のログイン要求を監視するプラグインを導入しました。
これは検知されたログイン要求を手段別にグラフ化するというもので、不正侵入行為もグラフに表示されます(以下画像)。
Graph_image_00_2

これを見ると、某日15:30から16:00のあいだの僅か30分ほどのあいだに200超のInvalid user login attempt(Potential Breakin Attempts)が検知されているのが分かります。要はなりすましユーザによる不正侵入行為です。

このやろめ、と思いつつ、思いつつもうちはssh認証をがっちり組んでいるので、怒りよりも睥睨するような気持ちが立って、いったい「どこ」の「どいつ」がそんなことをしてるのか知りたくなりました。

「どいつ」はIPみれば分かりますね。でもこの場合、fail2banなんかで監視させておげは自動でアク禁にできるし、最小単位をIPベースにしても情報量が多くなり過ぎるし、知ったところであんまり意味がないかなあと思いました。

じゃあやっぱり「どこ」を最小単位として監視してもらえれば、僕の要求は満たされる気がしました。
google analyticsの「国/地域からのセッション数」のsshポート版みたいなものですね。


Munin Exchangeにはそういったプラグインがまだなかったので、じゃあと思って作ってみました。
幸い、geoIPという、IPやFQDNから地域情報を返す仕組みが無償で提供されているので、それを利用することで簡単にグラフ化できるようになりました(以下画像)。
Graph_image_2

上述のsshd_logで監視された200超の不正侵入行為はインドからのものでした。
という感じでモニタリングできます。ちなみにgeoIP cityを使えば都市レベルで地域情報を返してくれるようです。すごいですね。


というわけで、以下「ssh不正アクセスを国別に監視する」ためのmuninプラグインです。
php版
ruby版
# muninなくてもコマンドとして叩けば国別で不正アクセス数を標準出力します。

| | コメント (0) | トラックバック (0)

2010年11月23日 (火)

外部非公開のSVNサーバ(Subversion)に、公開サーバを踏み台にしてアクセスする

なんていうんでしょう、えーと。こんにちは。
mogwai超カッコいい。それより。

ソースの世代管理にバージョン管理システムを導入しました。

当初は普通にリモートサーバ(以降の呼称は開発サーバ)にリポジトリサーバ(以降の呼称はSVNサーバ)をインストールしようと思ったのですが、昔買った玄箱がほとんど働いていないので、この際そっちにいれることにしました。

うちでは開発サーバは本番サーバを兼ねているので(本当は開発兼本番サーバと言うべきかもしれないけど)外部公開していますが、玄箱は公開していません。閉じたネットワーク内でファイルサーバとして使っていましたが、今後もそのかたちは変えたくありません。

ところが、今回の開発は僕以外に外部の共同開発者も開発に参加しているため、外側からもSVNサーバにアクセスできるようにしなければなりません。

相関図にするとこうなります。
Relationship

図の右下の「管理者」が僕です。リレーションが薄くひいてあるのは外部非公開の意味で、外からは「管理者」のPCと「SVNサーバ」はアクセスできないようになっています。
外部の開発者がいなければ「管理者」が直接プライベートネットワーク内の「SVNサーバ」にアクセスすれば事足りるのですが、このままだと「外部開発者A」と「外部開発者B」が「SVNサーバ」にアクセスできません。
じゃあ「SVNサーバ」を公開しよう、というのはなしです。「SVNサーバ」を玄箱に置かずにやっぱり「開発」サーバに置こう、というのの100場合くらいない話です。
なので、「SVNサーバ」は引き続き外部非公開のまま、公開済の「開発サーバ」を踏み台にして、「SVNサーバ」を使用できるように構築します。

経路にするとこうなります。
Routing


では以下その手順です。

■前提条件
管理者のアカウント名は「inner」とする。すでに作成済
外部開発者のアカウント名は「outer」とする。未作成
開発サーバのホスト名は「devel」
SVNサーバのホスト名は「svn」


■SVNサーバの構築
・概要
SVNサーバにはSubversionをインストール
Subversionは開発サーバのTrac「redmine」からHTTPアクセスできるようにするため、apacheもインストール
Subversion操作ユーザのアカウント名は「repos」
SVNプロジェクトは「project」

・手順
apache操作ユーザの作成


inner@svn:~$ sudo /usr/sbin/groupadd -g 500 www
inner@svn:~$ sudo /usr/sbin/useradd -g www -s /sbin/nologin apache
inner@svn:~$ sudo /usr/bin/passwd -d apache

apacheのインストール


inner@svn:~$ sudo apt-get install apache2
inner@svn:~$ sudo vi /etc/apache2/apache2.conf
# ↓追加
ServerName svn:80

inner@svn:~$ sudo vi /etc/apache2/envvars
# ↓変更
export APACHE_RUN_USER=apache
export APACHE_RUN_GROUP=www

inner@svn:~$ sudo /etc/init.d/apache2 restart

# apache操作ユーザで起動していることを確認
inner@svn:~$ ps aux | grep apache | grep -v grep

# HTTPステータスコード200が返ることを確認
inner@svn:~$ wget http://localhost -O -

SSHの鍵認証と許可ユーザの設定


inner@svn:~$ sudo vi /etc/ssh/sshd_config
# ↓変更
PermitRootLogin no
PermitEmptyPasswords no
PasswordAuthentication no
# ↓追加
AllowUsers inner repos

inner@svn:~$ sudo /etc/init.d/ssh reload

Subversionのインストール(SVN用のapacheモジュールもインストール)


inner@svn:~$ sudo apt-get install subversion
inner@svn:~$ sudo apt-get install subversion-devel
inner@svn:~$ sudo apt-get install subversion-tools
inner@svn:~$ sudo apt-get install subversion-mod_dav_svn
inner@svn:~$ sudo apt-get install libapache2-svn
# dav_svnがapacheモジュール配下に置かれているか確認
inner@svn:~$ ll /usr/lib/apache2/modules | grep dav
inner@svn:~$ find /etc/apache2/ -name dav_svn.conf
# 置かれていなかったのでシンボリックリンクで回避
inner@svn:~$ sudo ln -s /usr/lib/apache2/modules /etc/apache2/modules
inner@svn:~$ sudo vi /etc/apache2/mods-enabled/dav_svn.conf
# ↓追加
<Location /repos>
DAV svn
SVNPath /usr/local/repos/
</Location>

inner@svn:~$ sudo /etc/init.d/apache2 reload

Subversion操作ユーザの作成


inner@svn:~$ sudo /usr/sbin/useradd -g www -d /home/repos -m repos
inner@svn:~$ sudo /usr/bin/passwd -d repos

リポジトリの作成


inner@svn:~$ sudo -u apache mkdir /usr/local/repos
inner@svn:~$ sudo chmod 0775 /usr/local/repos
inner@svn:~$ sudo -u repos svnadmin create /usr/local/repos
inner@svn:~$ ll /usr/local/repos

Subversion操作ユーザによる動作確認


inner@svn:~$ svn mkdir file:///usr/local/repos/project -m "create project"
inner@svn:~$ svn mkdir file:///usr/local/repos/project/trunk -m "create trunk"
inner@svn:~$ svn mkdir file:///usr/local/repos/project/tags -m "create tags"
inner@svn:~$ svn mkdir file:///usr/local/repos/project/branches -m "create branches"
inner@svn:~$ svn list file:///usr/local/repos/project/branches
inner@svn:~$ svn list http://svn/repos/project/trunk


■外部開発者のアカウント用意
・概要
鍵はSSH-2 RSA形式。管理者PCのPuTTYで事前に作って、秘密鍵だけ開発サーバにアップしておく
操作は開発サーバ「devel」でおこなう (途中からSVNサーバに乗り換えます)

・手順

外部開発者用のユーザ作成
# 外部開発者×nはすべてグループ「mate」に属する。パスワードは適当に。いずれも省略。


[inner@devel~]$ sudo /usr/sbin/useradd -g mate -d /home/outer -m outer
[inner@devel~]$ sudo /usr/bin/passwd outer
[inner@devel~]$ sudo mkdir /home/outer/.ssh
[inner@devel~]$ sudo mv ~/id_rsa.pub /home/outer/.ssh/authorized_keys
[inner@devel~]$ sudo chown -R outer:mate /home/outer/.ssh
[inner@devel~]$ sudo chmod 0700 /home/outer/.ssh
[inner@devel~]$ sudo chmod 0600 /home/outer/.ssh/authorized_keys

管理者PCからouterユーザで、PuTTYの鍵認証でログインできることを確認しておく(略)

開発サーバからSVNサーバに、SSH鍵認証でログインできるようにする


[inner@devel~]$ sudo scp -p /home/outer/.ssh/authorized_keys svn:~/
[inner@devel~]$ ssh -A svn
inner@svn:~$ sudo mkdir /home/repos/.ssh
# 管理者の公開鍵をSubversion操作ユーザに渡しておく
inner@svn:~$ sudo cp -p ~/.ssh/authorized_keys /home/repos/.ssh/authorized_keys
# 外部開発者の公開鍵をSubversion操作ユーザに渡しておく
inner@svn:~$ sudo mv ~/authorized_keys /home/repos/.ssh/authorized_keys2
inner@svn:~$ sudo chown -R repos:www /home/repos/.ssh
inner@svn:~$ sudo chmod 0700 /home/repos/.ssh
inner@svn:~$ sudo chmod 0600 /home/repos/.ssh/authorized_keys*
inner@svn:~$ sudo -u repos mkdir /home/repos/workspace

ログインできることの確認(SVN操作の確認も)


inner@svn:~$ exit
[inner@devel~]$ ssh -A repos@svn
repos@svn:~$ exit
[inner@devel~]$ sudo -u outer ssh -A repos@svn
repos@svn:~$ cd workspace
repos@svn:~/workspace$ svn co file:///usr/local/repos/project/trunk .
repos@svn:~/workspace$ vi test.txt
repos@svn:~/workspace$ svn add test.txt
repos@svn:~/workspace$ svn commit -m "test"
repos@svn:~/workspace$ exit

本当は、外部開発者はjailkitでホームDIR配下に閉じ込めて、使用できるコマンドもsshとsvnとその他適当なコマンドに制限させたかったのですが、ログイン後のsshがどうしても利用できるようにできず、断念しました。
詳細は省きますが、今後の課題として残りました。


■開発サーバにSVNクライアントをインストール
・概要
SVNクライアントにはSubversionを利用
開発サーバのTrac「redmine」がSVNサーバのリポジトリにHTTP経由でアクセスするための設定
なお、HTTP経由となっていますが、開発サーバがHTTPでアクセスするのはサーバサイドなので、開発サーバからSVNサーバの80番ポートがプライベートネットワーク内で見えていれば、SVNサーバは引き続き外部非公開で問題ありません(特にTracアプリケーションを使わなければ必要ないと思います)。

・手順
SVNクライアントをインストール


[inner@devel~]$ cd /usr/local/src
[inner@devel~/src]$ wget http://subversion.tigris.org/downloads/subversion-1.6.13.tar.gz
[inner@devel~/src]$ tar zxvf subversion-1.6.13.tar.gz
[inner@devel~/src]$ wget http://subversion.tigris.org/downloads/subversion-deps-1.6.13.tar.gz
[inner@devel~/src]$ tar zxvf subversion-deps-1.6.13.tar.gz
[inner@devel~/src]$ cd subversion-1.6.13
# SVNクライアントのみインストールします
[inner@devel~/subversion-1.6.13]$ ./configure --without-berkeley-db --without-apxs \
[inner@devel~/subversion-1.6.13]$ --without-swig --without-serf --with-ssl
[inner@devel~/subversion-1.6.13]$ make
[inner@devel~/subversion-1.6.13]$ sudo make install
[inner@devel~/subversion-1.6.13]$ cd ~/
[inner@devel~]$ svn list http://svn/repos/project


■クライアントPCにSVNクライアントをインストール
・概要
SVNクライアントにはTortoiseSVNを利用
クライアントPCはWindowsVISTA

・手順
TortoiseSVNのインストール
http://www.gside.org/Gentoo/subversion/subversion_client.html

上記の手順を元に以下をインストールしました
http://sourceforge.net/projects/tortoisesvn/files/Application/1.6.11/TortoiseSVN-1.6.11.20210-win32-svn-1.6.13.msi/download
http://sourceforge.net/projects/tortoisesvn/files/Language%20Packs/1.6.11/LanguagePack_1.6.11.20210-win32-ja.msi/download


■クライアントPCから開発サーバ経由でSVNサーバにアクセス
・概要
PuTTY(ごった煮版)はインストール済
トンネルに利用するポートは20022とする
手順では開発者アカウント「outer」を設定(管理者「inner」は省略)

・手順

まず、開発サーバのSSHログイン設定をおこないます。
putty.exeをダブルクリック、「PuTTY設定」を以下のように設定

セッション
Putty_devel_session


ホスト名:devel
ポート:22
接続タイプ:SSH
セッション一覧:任意の名前(例:devel)

接続=>データ


自動ログインのユーザ名:outer

SSH=>認証
Putty_devel_auth


Pagentを使って認証する:チェックをいれる
"keybord-interactive"認証を試みる:チェックをいれる
エージェントフォワーディングを認める:チェックをいれる

SSH=>トンネル
Putty_devel_tunnel


源ポート:20022
送り先:svn
ローカル:チェックをいれる
「追加」ボタンを押して、フォワードするポート一覧に「L20022 svn:22」と追加

セッションの項目に戻って「保存」ボタンを押します。
「開く」ボタンを押してログイン(パスワード認証ダイアログが出たら、鍵生成時のパスフレーズを入力)。
ログインできたら、開いたターミナルはそのままにしておきます。


次にSVNのSSHログイン設定をおこないます。
上記のトンネル経由でログインできるようにします。
putty.exeをダブルクリック、「PuTTY設定」を以下のように設定

セッション
Putty_svn_session


ホスト名:localhost
ポート:20022
接続タイプ:SSH
セッション一覧:任意の名前(例:svn)

接続=>データ
Putty_svn_data


自動ログインのユーザ名:repos

SSH=>認証


Pagentを使って認証する:チェックをいれる

セッションの項目に戻って「保存」ボタンを押します。
「開く」ボタンを押してログイン(パスワード認証ダイアログが出たら、鍵生成時のパスフレーズを入力)。
ログインできたら成功です。


TortoiseSVNの設定
適当な「作業フォルダ」の下で右クリックしてコンテキストメニューを開き、TortoiseSVN→「ここにリポジトリを作成」を選択します。
再び右クリックからTortoiseSVN→「設定」で、「一般」欄の「編集」ボタンを押下。設定ファイルがテキストで開くので、


[tunnels]
testssh(SVNトンネル用の適当なスキーマ名) = "<PuTTYのインストール先フォルダのパス>/plink.exe" -agent -load svn(PuTTYのセッション名) -i "<秘密鍵を保存したフォルダのパス>/id_rsa.ppk"


として保存。
例) testssh = "E:/tools/PuTTY/plinkw.exe" -agent -load svn -i "E:/tools/PuTTY/outer/id_rsa.ppk"

再び右クリックからTortoiseSVN→「SVNチェックアウト」で、


リポジトリのURL:svn+testssh:///usr/local/repos/project/trunk
チェックアウトDIR:<作業フォルダのパス>\project


として「OK」押下。これでチェックアウト完了です。

失敗する場合は設定ファイルを


[tunnels]
donassh(SVNトンネル用の適当なスキーマ名) = "<PuTTYのインストール先フォルダのパス>/plinkw.exe" -v -agent -load svn(PuTTYのセッション名) -i "<id_rsa.ppkを保存したフォルダのパス>/id_rsa.ppk"


として、再び「SVNチェックアウト」をおこないます。plinkw.exeはplink.exeのGUI版で、-v(verbose)をつけることでデバグコンソールから内容を確認できます。


これですべての設定が完了です。
外部開発者にアカウント名とパスワード、秘密鍵とパスフレーズを渡して、PuTTYとTortoiseSVNの設定をしてもらえば今回の用件を満たした開発がおこなえます。


■補足
ちなみにTrac「redmine」を使うと上で書きましたが、ついでにredmine側の設定で、SVN連携に必要な部分も書いておきます。

・開発サーバのredmineURL
設定=>リポジトリ
Redmine_repos_2


バージョン管理システム:Subversion
URL:http://svn/repos/project


として保存。プロジェクトの「リポジトリ」をクリックすると「リポジトリに、エントリ/リビジョンが存在しません」となる場合は開発サーバのSVNコマンドのパスがredmineに見えてないことが多いようなので、以下を修正

[inner@devel redmine]$ which svn
/usr/local/bin/svn
[inner@devel redmine]$ vi scm/adapters/subversion_adapter.rb
#SVN_BIN = "svn"
SVN_BIN = "/usr/local/bin/svn"

ついでにruby ver1.9.1でredmine ver1.0.3を動かす場合は以下を修正

[inner@devel redmine]$ vi vendor/rails/actionpack/lib/action_controller/request.rb
#value.each { |k, v|h[k] = normalize_parameters(v) }
value.each { |k, v|
v.force_encoding("UTF-8") if v.class==String
h[k] = normalize_parameters(v)
}

| | コメント (0) | トラックバック (0)