About the Site

This weblog is edited and run by members of reallyenglish, a company offering a total English learning solution based in London, Beijing, Shanghai and Tokyo. Visit our corporate site to know more about what we do.

Notes are posted by members from various cultural and geographical backgrounds, and the topics range from education, business and international communication to software development, the internet culture, and more.

Staff

Masatomo Nakano http://twitter.com/masatomon /m/mt-static/support/assets_c/userpics/userpic-2-100x100.png simonl davida jeremyw Go Kameda gavin b

 

Recent Comments

  • Tomoyuki Sakurai: VRRP is not open nor free by our standard. I read more
  • affleck: nice! one small note: vrrp is an open standard as read more

One of cryptic warnings found in ModSecurity audit.log is

  [msg "ModSecurity does not support content encodings"]

Clearly, it says ModSecurity doesn’t support content encodings. That is, it cannot inspect gzipped content. This is problematic especially when ModSecurity is running on reverse proxy. The question is, “how can I inspect, then?” There are some very useful advice from the developer in the list archive. Bad news is, it doesn’t work. Some claims it worked, but they skip deflate process by SetEnvIfNoCase, which is not an option for me.

My goal is:

  • Compress by Content-Type, not by file extension because sometime file extension is not available (/foo.php?imageid=1234). You cannot use SetEnvIfNocase because it cannot access to response headers.
  • Do not compress images, video and other pre-compressed files (compress all except a few others).
  • Do not compress before ModSecurity inspect the content (the goal).

After hours and days to find why it doesn’t work, I finally found the problem. In modextfilter.c, it looks for ext filter by f->frec->name, which is not the name of ext filter but FilterProvider’s name when it should have looked for it by “nodeflate” in my case.

583     /* look for the user-defined filter */
584     ctx->filter = find_filter_def(f->r->server, f->frec->name);

I’m not an apache guru in any way, I might be wrong.

Three tricks:

  • Even though configtest doesn’t complain, ExtFilterDefine is only valid in server config context
  • DO NOT “SetOutputFilter DEFLATE”. It compresses all contents, ignoring nodeflate
  • Define two ext filters

Here is the WORKING config.

    LoadModule ext_filter_module libexec/apache22/mod_ext_filter.so
    LoadModule filter_module libexec/apache22/mod_filter.so
    ...
    # Netscape 4.x has some problems...
    BrowserMatch ^Mozilla/4 gzip-only-text/html
    # Netscape 4.06-4.08 have some more problems
    BrowserMatch ^Mozilla/4\.0[678] no-gzip
    # IE is ok, but looked like Netscape, so we reset it
    BrowserMatch \bMSIE !no-gzip !gzip-only-text/html

    # We need to force compression since we will remove the
    # request Content-Encoding/TE headers which mod_deflate
    # looks for.
    SetEnvIfNoCase Accept-Encoding gzip force-gzip
    SetEnvIfNoCase TE gzip force-gzip

    # Disable compression from backend
    RequestHeader unset Accept-Encoding
    RequestHeader unset TE

    # Make sure caching still works
    Header append Vary User-Agent env=!dont-vary

    # XXX be sure to define ExtFilterDefine in server config context!
    # not in virtual host, not directory, etc
    ExtFilterDefine nodeflate mode=output \
        cmd=/usr/bin/true \
        enableenv=SomeVarThatWillNeverBeSet
    # XXX workaround a bug in mod_ext_filter.
    # it looks up extfilter by FilterProvider's name, not user defined ext filter.
    # it complains "couldn't find definition of filter 'compress'"
    # Define the same name of FilterProvider.
    ExtFilterDefine compress mode=output \
        cmd=/usr/bin/true \
        enableenv=SomeVarThatWillNeverBeSet

    # Setup the filter to compress the response if we saw
    # an Accept-Encoding/TE header and marked it as force-gzip
    FilterDeclare compress CONTENT_SET
    FilterProvider compress DEFLATE env=force-gzip =1

    # Overwrite the "deflate" filter with the "nodeflate"
    # for various content types so they will not be compressed.
    # This is done as a hack because there is no way to
    # use more than one condition with FilterProvider above.
    FilterProvider compress nodeflate Content-Type $image/
    FilterProvider compress nodeflate Content-Type $application/
    FilterProvider compress nodeflate Content-Type $video/
    FilterProvider compress deflate Content-Type $application/javascript
    FilterProvider compress deflate Content-Type $application/json
    FilterProtocol compress "change=yes"

    FilterDeclare  modsec CONTENT_SET
    FilterProvider modsec modsecurity_out env=modsec-ignore !=1

    FilterChain modsec compress

I tested this on FreeBSD, so, use “cmd=/bin/true” on Linux and others (“/bin/sh” should work and is more portable, if you prefer).

When Apache 2.4 is released, it will be much simpler. See “Expression parser for Apache

Batch image exporting from .fla

| No Comments

Just wanted to post a small .jsfl code snippet to export .png images from each frames in the current timeline (with the currenrt .png export settings).

_ExportImages.jsfl
Download Demo Files

Although the same is possible by "Export" -> "Export Movie..." then choosing "png sequence" as the file format, jsfl can be more flexible as;

  1. It can export images from any (current) timeline
    Correction: It is also possible from "Export Movie", sorry for my ignorance.. orz
  2. It can possibly perform more complex batch processing (with a bit of code modification)
  3. The direct keyboard shortcut (with no dialog) can be assigned to it

Basic Usage

  1. Open the .fla from which you want to export the image (In the Demo, it is "ExportImagesTest.fla").
  2. From the menu, "Commands" -> "Run Command..." and open "_ExportImages.jsfl".

And, here is the Adobe Reference for how to run .jsfl in different ways;
Running JSFL files (Adobe Livedocs)

The image file names or location can be easily modified by editing the line with the comment. It saved me quite a lot of time so hopefully it could help be some help for any of you in any way.

現在開いている.flaの各フレームから連番で.pngを書き出す超シンプルな.jsflスニペット、メモとして一応あげておきますね(Flash CS3とCS4で動作確認)。

_ExportImages.jsfl
Download Demo Files

実際"Export" -> "Export Movie"から.pngを連番で書き出せばいい話なのですが、jsflでならどのタイムラインからでも書き出せたり(Export Movieでもできました、知らなかったorz)、名前のコントロールが柔軟だったり、ダイアログなしで一発で終わるようにショートカットをあてがえたり、より複雑なバッチ処理にも使えたりするのがいいんじゃないかと思います。

使い方

  1. イメージ書き出し元の.flaを開く(デモでは "ExportImagesTest.fla")。
  2. "コマンド" -> "コマンドの実行"を選び、"_ExportImages.jsfl"を開く。

あとAdobeのリファレンスにいろんな方法でコマンド(.jsfl)を走らせる方法が書いてあります(一応です)
Running JSFL files (Adobe Livedocs)

超簡単なわりにわりと重宝したのでもし良ければという感じです。(とはいえjsflはしくじるとこわいのでどうぞ前もってお試しの上使ってください。。)

The other day I wanted to emphasize all the numbers in HTML text dynamically with JavaScript for some reason. First it was quite simple enough even to my RegEx-dummy brain. I just wrote something like the following;

(JavaScript)
function emphasizeNumbers(){
  var elmText = document.getElementById("text");
  elmText.innerHTML = elmText.innerHTML.replace(/(\d)/g, "<em>$1</em>");
  return false;
}

(HTML)
<p id="text">You have finished 3 lessons. Your score was 80/100. This is end of Stage 1. Please proceed to Stage 2. (By the way my number is 012-345-6789).</p>

So far it was simple enough. However when I looked at the consequence, I noticed that we did not want to emphasize the numbers followed by the word "Stage", as in "1" in "Stage 1" and "2" in "Stage 2". And I did not know the way how to exclude only these from the RegEx matches to be replaced.

Without too much consideration, first I tried the following;

function emphasizeNumbers(){
  var elmText = document.getElementById("text");
  elmText.innerHTML = elmText.innerHTML.replace(/[^(Stage )](\d)/g, "<em>$1</em>");
  return false;
}

Here I tried to mean "match with any digits which are preceded by anything other than the string 'Stage ' (as well as trying to exclude the grouped characters 'Stage ' from back-references)". However the result was miserable. Honestly I still cannot even explain what exactly happened here. Anyways, I understood that grouping with parenthesis ("()") in character class square bracket("[]") does not work (does it?).

After a while of googling, I found that there is an RegEx expression called "negative lookbehind". It took me about an hour to even vaguely understand the concept, but this seemed to be the answer. So anyways, I tried the following just mimicking the tutorial page;

function emphasizeNumbers(){
  var elmText = document.getElementById("text");
  elmText.innerHTML = elmText.innerHTML.replace(/[^(?<!Stage )(\d)/g, "<em>$1</em>");
  return false;
}

It did not work at all and ended up in a JavaScript error. Soon after that, I found that I missed out one important line in the tutorial page;

Finally, flavors like JavaScript, Ruby and Tcl do not support lookbehind at all, even though they do support lookahead.
OK great, (negative) lookbehind is NOT supported in current JavaScript (as of 2010 Jan, JavaScript 1.8.1) RegEx.

After all, I resolved the problem by doing the following.

function emphasizeNumbers(){
  var elmText = document.getElementById("text");
  elmText.innerHTML = elmText.innerHTML.replace(/(?:Stage\s)?(\d)/g, function(str, p1){
    if(str.indexOf("Stage ")!=-1) return str;
    else return "<em>" + p1 + "</em>";
  });
  return false;
}

This finally worked. I did not know that the String.replace method accepts a function instead of a replacing string as the second parameter.
String.replace (MDC Core JavaScript 1.5 reference) - Specifying a function as a parameter

However I'm not sure if this is the best solution - is there better and easier way to do the same?

Almost all serious project require redundancy, reallyenglish is no exception. carp(4), developed by OpenBSD project, is a free implementation of VRRP, which is patented by Cisco. carp(4) has load balancing based on L2 and L3, but here, I'm going to focus on its fail over feature.

            | 192.168.100.254/24
     .------+------.
 vr0 |         vr0 |
 .---+---. vr3 .---+---.
 | core1 +-----+ core2 |
 `---+---'     `---+---'
 vr1 |          vr1|
     `------+------'
            | 192.168.200.254/24

Firstly, enable IPv4 forwarding. Also, enable preemption to failover all carp interfaces altogether when one interface fails.

# sysctl net.inet.ip.forwarding=1
# echo net.inet.ip.forwarding=1 >> /etc/sysctl.conf
# sysctl net.inet.carp.preempt=1
# echo net.inet.carp.preempt=1 >> /etc/sysctl.conf

Using carp(4) is fairly simple. Here is what hostname.if(5) looks like.

/etc/hostname.vr0:     up
/etc/hostname.vr1:     up
/etc/hostname.vr3:     inet 172.16.0.1/30
/etc/hostname.carp0:   192.168.100.254 vhid 1 carpdev vr0 pass mypass
/etc/hostname.carp1:   192.168.200.254 vhid 2 carpdev vr1 pass mypass
/etc/hostname.pfsync0: syncdev vr3

Bring up the interfaces. You can also use ifconfig(8), but I always prefer startup script to make sure that the configuration will not be lost.

# sh /etc/netstart vr0
# sh /etc/netstart vr1
# sh /etc/netstart vr3
# sh /etc/netstart carp0
# sh /etc/netstart carp1
# sh /etc/netstart pfync0

If you insists on using ifconfig(8):

# ifconfig vr0 up
# ifconfig vr1 up
# ifconfig vr3 inet 172.16.0.1/30
# ifconfig carp0 create
# ifconfig carp1 create
# ifconfig carp0 192.168.100.254 vhid 1 carpdev vr0 pass mypass
# ifconfig carp1 192.168.200.254 vhid 2 carpdev vr0 pass mypass
# ifconfig pfsync0 syncdev vr3

Repeat almost same configuration on core2. The difference is each carp interface has advskew keyword and vr3 for pfsync device has different IP address. A router with higher advskew value becomes slave.

/etc/hostname.vr3:     inet 172.16.0.2/30
/etc/hostname.carp0: 192.168.100.254 vhid 1 carpdev vr0 pass mypass advskew 100
/etc/hostname.carp1: 192.168.200.254 vhid 2 carpdev vr1 pass mypass advskew 100

Now you have redundant firewalls. Easy!

As explained in the earlier entry, we use OSPF for internal routing. How do I establish OSPF neighbor? Because ospfd(8) cannot be enabled actively on carp'ed interface, you need real interface that connects OSPF cloud and core routers. Additionally, real interface cannot preempt carp interface. That means when the real interface fails, you cannot failover. To workaround this problem, you need two OSPF-enabled routers and two links on each core. Things get messy from now on.

                                     carp0
           udav1   vr1    sis0      /
-----.      .------.     .-------. / 
     +------+  r1  +-----+ core1 |<
     |      `------'     `-------' \
     |     udav0|vr2\   / sis1  \   \
     |          |    \ /         \   carp1
OSPF |          |     X        pfsync0
     |          |    / \         /   carp0
     |     udav0|vr1/   \ sis0  /   /
     |      .------.     .-------. / 
     +------|  r2  +-----+ core2 |<
-----'      `------'     `-------' \
           udav1   vr2     sis1     \
                                     carp1

In this setup, when one of the link from core to OSPF cloud fails, traffic will be routed via the link between r1 and r2. When carp'ed interface fails, preemption kicks in and ospfd(8) on core1 will remove the routes and r1 will not route traffic to core1. claudio@, one of "the networking guy" in OpenBSD admitted it's not optimal. Hopefully, it will be fixed in the future release.

ALIX 2D3 board, which is used as r1 and r2, has only three ports. And one port is used for diskless boot in this testing. So, i added two USB ethernet adopters, deteced as udav(4). Make sure the right patch goes to the right port. Each link between the routers has a /30 subnet. If confused, draw a diagram on paper.

Let's enable OSPF on all interface on every router, but not on real interfaces used for carp. ospfd.conf(5) looks like:

# core1 and core2
auth-type crypt
auth-md 1 "mypass"
auth-md-keyid 1

area 0.0.0.0 {
  # link to r1/r2
  interface sis0 { metric 10 }
  interface sis1 { metric 20 }
  # you can omit "passive" on carp interface. it's always passive anyway
  interface carp1 { passive }
  interface carp0 { passive }
}

# r1 and r2
auth-type crypt
auth-md 1 "mypass"
auth-md-keyid 1

area 0.0.0.0 {
  # link to core
  interface vr1 { metric 10 }
  interface vr2 { metric 20 }
  interface udav0 { }
  interface udav1 { }
}

Run ospfd(8) on every router. The output of ospfctl(8) on core1 looks like this.

# ospfctl sh int
Interface   Address            State  HelloTimer Linkstate  Uptime    nc  ac
carp0       192.168.100.254/24 DOWN   -          master     00:00:00   0   0
carp1       192.168.200.254/24 DOWN   -          master     00:00:00   0   0
sis1        192.168.0.5/30     BCKUP  00:00:00   active     1d06h19m   1   1
sis0        192.168.0.1/30     BCKUP  00:00:00   active     1d06h19m   1   1

# ospfctl sh ne
ID              Pri State        DeadTime Address         Iface     Uptime
172.16.254.202  10  FULL/DR      00:00:33 192.168.0.6     sis1      1d06h20m
172.16.254.201  1   FULL/DR      00:00:32 192.168.0.2     sis0      1d06h20m

Now the fun (and boring) part. While pinging from a host in OSPF cloud to the other end of core, pull out the cable randomly. You can shutdown one of the router, too. Nothing should happen, other than normal ping output. If it doesn't work, tcpdump(8) is your friend. You can manually fail over the master by:

# ifconfig -g carp carpdemote 10

This setup requires no additional package and works out of the box. If you need more redundancy, i.e. redundancy at application level, than this setup, OpenBSD also provides relayd(8). If you need redundant DHCP servers, modified version of dhcpd(8) has synchronization support. OpenBSD makes redundant networking fairly easy.

Happy redundancy!

前回に引き続き、今回はFreeBSDのJailの簡単な説明と、実際にどのような感じで使えるのか、簡単なサンプルを交え紹介します。

「プログラマのための」について

前回書き忘れてしまったのですが、なぜ「プログラマのための」というのをタイトルに入れたかというと、

  • プログラマは自分の開発環境/テスト環境の構築ぐらい自分ですべし
  • でも、環境構築に時間をかけすぎるのはプログラマとしては本末転倒だよね

この二つをあっさりと解決してくれるのがFreeBSDのJailだと感じているからです。

それと、自分自身そこそこ長いことプログラマをやってますが、Jailは自分にとってなかなか衝撃的だった、というのもこのエントリを書くきっかけになっているというのもあります。

FreeBSDのJailとは

さて、まず基本的なFreeBSDのJailについて簡単に触れておきます。

FreeBSDのJailは仮想環境の一つの実装です。仮想環境にも色々ありますがJailはその中でもとてもシンプルな物です。一つの物理的なサーバ上で動いている一つのFreeBSD(以下Jailホスト)の上で、複数のFreeBSD(以下Jailゲスト)が動いているように見えるようにする仕組です。「見えるようにする」だけというのがポイントで、実際にはJailホスト上のカーネルの上ですべてのプロセスが動いているだけです。

また、Jailとchrootを同じようなもの、と考えている人もいるようですが、chrootがファイルシステムだけが対象なのに対し、Jailはファイルシステムに加えネットワークやプロセスといったものも管理下になるのが違います。Jailゲスト一つが独立したIP addressを持ちます。

外からネットワーク越しに見ると、Jailゲストは一つのFreeBSD OSのように見えます。しかし、実際には、Jailホスト上の1つのカーネルで、Jailゲスト上のすべてのプロセスが動いています。このことは、Jailホスト上のカーネルからJailゲスト上の各プロセスを普通にpsやtopコマンド等で確認することができます。

また、この仕組みがそのままいくつかのJail自体の制限になります。たとえば、違うバージョンのFreeBSDや、違うOSを仮想環境として入れるといったVMWareやXenといった他の仮想環境でできることができません。そして、(今のところ)各Jailゲストで動くプロセスに対してリソースの割当や制限をすることができません。ですので一つのJailゲストがリソースを食い過ぎてしまうと他のJailゲストに大きく影響を与えます。この辺は将来的には何かできるようになるかも、という話です。

実際に動いている環境での例を示します。

host-top

これは、実際に弊社で稼働中のJailホストでtopした結果です。”j”を押すと左から2列目にJID (Jail ID)というものが表示されます。これはそのプロセスがどのJailゲストで動いているかということを示します。このようにJailホストからは、Jailゲストのプロセスが普通に見ることができますし、またJailホスト上からJailゲストのプロセスのkillなどは普通にできます。

では、その中の一つのJailゲストに入り、同じようにtopコマンドを実行してみます。すると、以下のようにそのJailゲストのプロセスのみが表示されます。

guest-top

このJailゲストはPostgreSQL用のものなのでPostgreSQLに関連するプロセスとcronなどの基本的なプロセスだけが走っているのがわかります。Jailゲストの中からは、他のJailゲストやJailホストの情報は見えません。これはプロセスだけではなくてファイルシステムなどもそうです。

とてもシンプルです。そして、シンプルなだけにに仮想環境を動かすこと自体のオーバヘッドが少なく、無駄なく一台のハードウェアの資源を使うことができます。また9年前にリリースされたFreeBSD 4.0Rからの機能なので安定度も非常に高いです。

資源という面では、Jailゲスト同士でFreeBSDのユーザランド部分をファイルレベルで共有するのでディスクスペースもそれほど無駄になりません。どう作るかにもよりますが、大雑把に言うと、/etc, /usr/local, /var あたりはJailゲスト毎ですが、/usr 以下は共有されます。また、もちろんパッケージからインストールしたものはJail毎になります。これにより資源を節約できるだけなく、Jailゲストの数をたくさん増やしたとしてもFreeBSDとしてのベース部分のメンテナンスは1カ所で済むのも楽なところです。

また、Jailゲストで使うファイルもJailホストと同じファイルシステム上にあるので、簡単に直感的に扱うことができます。例えば、あるJailゲスト上で「/etc/pam.d/systemを壊してしまって、仮想環境に入れなくなってしまった」というトラブルが起きたとします。Jailホスト上から見ると、このファイルは、/foo/bar/jails/hogehoge.example.com/etc/pam.d/system にあります。Jailホストにとっても普通のファイルなのでお使いのエディタで簡単に修復できます。

Jailの導入

Jail自体はFreeBSDに標準で組み込まれているのですが、その他にいくつか設定が必要です。また、Jail単体だと管理が大変なので、packageまたはportsから管理ツールであるsysutils/ezjailも導入してください。

さて、普通ならここでJailシステムのインストールの仕方等を紹介するべきなのかもしれませんが、ちょっと大変だし、世の中にあるものより劣化したドキュメントを垂れ流すだけになりそうなので、どこか適当な情報を探してください

また、Jail自体についてはこの辺が詳しいです。

Jailを使用した運用例

以下の例ではJailの管理ツールであるezjailを使っています。私はezjailができる前のJail事情を知らないのですが、その時代は色々と大変だったみたいです。

例1: Jailの新規作成

Jailゲストを新しく作ります。JailゲストはFQDNをディレクトリ名として作成され、その下がそのJailゲストの/(root)ディレクトリになります。

$ sudo ezjail-admin create newjail.example.com 192.168.4.25
/usr/local/jails/newjail.example.com/./bin/usr/local/jails/newjail.example.com/./boot
/usr/local/jails/newjail.example.com/./dev
/usr/local/jails/newjail.example.com/./etc
/usr/local/jails/newjail.example.com/./etc/X11
/usr/local/jails/newjail.example.com/./etc/bluetooth
/usr/local/jails/newjail.example.com/./etc/bluetooth/hcsecd.conf
/usr/local/jails/newjail.example.com/./etc/bluetooth/hosts
/usr/local/jails/newjail.example.com/./etc/bluetooth/protocols
/usr/local/jails/newjail.example.com/./etc/defaults
(中略)
/usr/local/jails/newjail.example.com/./.cshrc
/usr/local/jails/newjail.example.com/./COPYRIGHT
/usr/local/jails/newjail.example.com/./basejail

この例では、192.168.4.25 というIP addressがこのJailゲストに設定されます。(Jailホスト側にも192.168.4.25というIP addressをaliasとしてふる必要もあります。)

作成したJailゲストを起動してみます。

$ sudo ezjail-admin start newjail.example.com
Configuring jails:.
Starting jails: newjail.example.com.

Consoleに入ります。

$ sudo ezjail-admin console newjail.example.com
Copyright (c) 1992-2009 The FreeBSD Project.
Copyright (c) 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994
     The Regents of the University of California. All rights reserved.

FreeBSD 7.2-STABLE (GENERIC) #0: Fri Aug  7 20:52:37 BST 2009

Welcome to FreeBSD!
(中略)
You may also use sysinstall(8) to re-enter the installation and
configuration utility.  Edit /etc/motd to change this login announcement.

newjail#

ここれでJailゲストが一つ作成され起動しました。newjail# のプロンプトが表示され、この以下の操作はJailゲストの仮想環境内の操作になります。この中からは他のJailゲストやJailホストの情報は全く見えません。この先は普通のFreeBSDと同じように好きなようにパッケージを入れたり設定したりできます。もちろんsshdを起動すれば外から直接newjailに入ってこれます。

このようにコマンド2、3個、所要時間1分未満で簡単に1つの仮想環境を作成することができます。

例2: Jailゲストのコピー

似たような環境を作るときにいちいち0から設定するのは大変です。こういうときは既にあるJailを簡単にコピーできます。

先ほど作成したnewjailをコピーし、newjail2を作成してみましょう。

いったんnewjailを停止。

$ sudo ezjail-admin stop newjail.example.com

コピー

$ sudo cp -a /usr/local/jails/newjail.example.com /usr/local/jails/newjail2.example.com

単純なファイルコピーなので、rsyncでもtarでもお好きなものでどうぞ。(パーミッションとかに気をつけて)

コピーしたファイルを元にnewjail2を作成します。(すでにあるファイルを使う場合は-xをつける)

$ sudo ezjail-admin create -x newjail2.example.com 192.168.4.26

これで、完了です。

先ほどと同じようにコンソールに入りコピーされていることを確認します。(起動していない場合にはconsoleコマンドでstartもします。)

$ sudo ezjail-admin console newjail2.example.com
Configuring jails:.
Starting jails: cms3-build.uk.reallyenglish.com.
Copyright (c) 1992-2009 The FreeBSD Project.
Copyright (c) 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994
     The Regents of the University of California. All rights reserved.

FreeBSD 7.2-STABLE (GENERIC) #0: Fri Aug  7 20:52:37 BST 2009

Welcome to FreeBSD!
(中略)
You may also use sysinstall(8) to re-enter the installation and
configuration utility.  Edit /etc/motd to change this login announcement.

newjail2#

これでコピーされたJailゲストが動きだしました。

ファイルのコピーにかかる時間は、もちろんその中のファイルの量によるので何とも言えませんが、実作業時間はこちらも数分といったところでしょう。

同じようなやり方で複数のJailホスト間でJailゲストをコピー(または、元のを消せば移動)することもできます。この場合コピー先のJailホストは同一のアーキテクチャのサーバ、同一のFreeBSDのバージョンのものにしておいた方が無難です。

今回のまとめ

とにかくJailは簡単です。よほど深いことをするのでない限りJail自体の知識はあまり必要となりません。普通のUNIX的な知識とFreeBSD的な知識があれば普段使いとしては十分です。

個人的な経験では、他の仮想環境はメンテナンス含め色々と手間がかかります。気づいたら動くなくなってた仮想環境というのも目にします。またメモリやディスクスペースのオーバヘッドも大きいので簡単にいくつも作ったりもできません。Jailはそういう意味でもとても軽いし堅いです。もし何らかの理由で動かなくなったとしても、上記で説明したようにとてもシンプルなのでわりと簡単に修復できますし、仮想環境の中のファイルを外から取り出すのも簡単です。

次回はもう少しだけ運用のTipsなどを紹介して最後にしたいと思っています。

それでは!

reallyenglish.com is geographically diverse. The HQ in UK, a few branches in Asia, maybe more in the future. Except for few exceptions, the employees expect universal services, such as source code repository, project management system and wiki. All of them must be protected by strong encryption. IPsec is a standard protocol designed for encrypting packets from one place to another. We extensively use IPsec between our offices.

As a sysadmin, whenever someone says "the Internet is down", I say "no, it isn't". The problem would be caused by networking issue, misconfiguration by someone, maybe his own fault or whatever. The Internet is fine. But, partially, he is right. The Internet is the network of networks. Different network is managed by different entity. As such, links between networks
cut off for various reason every day somewhere on the Internet. You just don't know it is happening. The Internet as a whole works somehow thanks to "dynamic routing".

What dynamic routing does is, in short, "if there are multiple paths to the destination, use the best path" and "tell neighbors about your directly connected networks so that others can understand the whole network map". Without dynamic routing, you have to teach every possible route to every single router. For our intranet, we're using OSPF.

Okay, for robust intranetworking, all you need is multiple meshed IPsec tunnels with OSPF. IPSec is a standard and mature protocol, and so is OSPF. Sounds easy? Not quite. What I don't really like about IPsec is, it wasn't designed with routing in mind. As Security Association is statically configured, you cannot specify multiple path to a destination. Moreover, it doesn't handle multicast packet. OSPF is a routing protocol and uses multicast for its communication.

One more thing to make this problem complicated is proprietary products, Netscreen security devices to be specific, we're still using. If you stick with a specific vendor, you will not have interoperability problem, but you're successfully vendor locked-in. What you can do is what that particular vendor implemented. If they don't, you cannot do anything with it. Anyway, I strongly prefer open standards and open platforms. The choice in this case is OpenBSD. However, there wasn't documentation about how to implement meshed, dynamically routed IPsec with OpenBSD and ScreenOS.

IPsec doesn't like multicast pakcet. Okay, use GRE, Generic Routing Encapsulation. By encapsulating multicast packet into GRE unicast packet, IPsec happily forward the packet to its peer. When the link to the peer goes down, ospfd(8) marks the link as unavailable and use alternative path to the destination. Sounds simple, isn't it? Again, not quite. You have to understand all the network layers involved. There would be 6 endpoints, or IP addresses, involved in this setup. IPsec endpoints, GRE tunnel endpoints and a /30 network for OSPF communication.

Let's configure OpenBSD first. Make sure both packet forwarding and GRE are enabled. Also, you better to disable pf(4) during the testing. You can enable it again after everything works. OpenBSD 4.6 and later enable pf by default.

# sysctl net.inet.gre.allow=1
# sysctl net.inet.ip.forwarding=1
# pfctl -d

Next, create GRE tunnel.

# ifconfig gre0 create
# ifconfig gre0 tunnel myglobal.ip.addre.ss peerglobal.ip.addre.ss (outer header)
# ifconfig gre0 inet mygre.ip.addre.ss peergre.ip.addre.ss netmask 0xffffffff link0 up (inner header)

The packets between mygre.ip.addre.ss and peergre.ip.addre.ss will be encapsulated by GRE unicast packets with the global IP addresses of the both endpoints as source and destination header. GRE packets are not encrypted, yet.

Then, configure IPsec. OpenBSD's newer ipsec.conf(5) makes IPsec configuration much easier than ever. The security policy would be "encrypt all packets between the endpoints with their global address". Due to performance limitation of the hardware, we use SHA1 and 3DES.

# vi /etc/ipsec.conf
mypeer = "peerglobal.ip.addre.ss"
me = "myglobal.ip.addre.ss"
mypassword = "password"
ike esp from $me to $mypeer peer $mypeer \ main auth hmac-sha1 enc 3des group modp1024 \ srcid $me/32 dstid $mypeer/32 \ psk $mypassword

Start isakmpd and tell it to read security policy from /etc/ipsec.conf.

# isakmpd -K
# ipsecctl -f /etc/ipsec.conf


NS 5GT's config is not so straight forward, but almost same you did above on OpenBSD.

set interface tunnel.1 zone untrust
set interface tunnel.1 ip mygre.ip.addre.ss/30 (inner header)
set interface tunnel.1 tunnel encap gre
set interface tunnel.1 tunnel local-if untrust dst-ip global.ip.addre.ss (outer GRE header, GRE endpoints)
set interface tunnel.1 tunnel keep-alive (optional, but useful to see GRE packet on the wire)
set ike gateway mypeer address peerglobal.ip.addre.ss main outgoing-interface "untrust" preshare password sec-level compatible
set vpn mypeer gateway jp proposal "nopfs-esp-3des-sha" 
set vpn mypeer bind interface tunnel.1
set vpn mypeer proxy-id local-ip global.ip.addre.ss/32 remote-ip peerglobal.ip.adre.ss/32 ANY

The gotcha was, I thought it wasn't possible for OpenBSD and 5GT to talk to each other in a setup like this because OpenBSD's GRE tunnel is point-to-point, 5GT's is a normal /30 subnet. I even didn't give it a try during the initial testing. In fact, it is. Now, you can ping the peer using GRE /30 IP address each other.


# ping peergre.ip.addre.ss
PING peergre.ip.addre.ss (peergre.ip.addre.ss): 56 data bytes
64 bytes from peergre.ip.addre.ss: icmp_seq=0 ttl=64 time=277.665 ms
...


You'll see ICMP packets on the GRE interface.
# tcpdump -tnei gre0 -c 10 icmp 
tcpdump: listening on gre0, link-type NULL
mygre.ip.addre.ss > peergre.ip.addre.ss: icmp: echo reply
peergre.ip.addre.ss > mygre.ip.addre.ss: icmp: echo request
...


Make sure the traffic is encrypted. Replace vr0 with your egress interface's name.
# ipsecctl -s all
FLOWS:
flow esp in from myglobal.ip.addre.ss to peerglobal.ip.addre.ss peer peerglobal.ip.addre.ss srcid myglobal.ip.addre.ss/32 dstid peerglobal.ip.addre.ss/32 type use
flow esp out from peerglobal.ip.addre.ss to myglobal.ip.addre.ss peer peerglobal.ip.addre.ss srcid peerglobal.ip.addre.ss/32 dstid myglobal.ip.addre.ss/32 type require

SAD:
esp tunnel from myglobal.ip.addre.ss to peerglobal.ip.addre.ss spi 0x1275cf8c auth hmac-sha1 enc aes
esp tunnel from peerglobal.ip.addre.ss to myglobal.ip.addre.ss spi 0x20eec763 auth hmac-sha1 enc aes

# tcpdump -tni vr0 -c 10
tcpdump: listening on vr0, link-type EN10MB
esp myglobal.ip.addre.ss > peerglobal.ip.addre.ss spi 0x61a862f2 seq 45586 len 372
esp peerglobal.ip.addre.ss > myglobal.ip.addre.ss spi 0x84cd621d seq 45973 len 132
Now that both IPsec and GRE tunnel worked, the last thing you need is enabling OSPF. ospfd.conf is very simple. Replace vr1 with your internal network interface.

# vi /etc/ospfd.conf
password="mypassword"
auth-md 1 $password
auth-type crypt
auth-md-keyid 1
area 0.0.0.0 {
 interface gre0 { }
 interface vr1 { passive }
}

Start ospfd(8) in debug and verbose mode.

# ospfd -dv
... lots of debug information ...

Do the same thing on 5GT.

set vr trust-vr protocol ospf enable
set interface tunnel.1 protocol ospf area 0.0.0.0
set interface tunnel.1 protocol ospf authentication md5 mypassword key-id 1
set interface tunnel.1 protocol ospf authentication active-md5-key-id 1
set interface tunnel.1 protocol ospf enable
set interface trust protocol ospf area 0.0.0.0
set interface trust protocol ospf passive
set interface trust protocol ospf enable

Now you'll see OSPF neighbor relationship is established.

# ospfctl show neighbor
ID                      Pri State      DeadTime Address                Iface     Uptime
peergre.ip.addre.ss     1   FULL/P2P   00:00:30 peergre.ip.addre.ss    vr1       00:00:23

run "ospfctl show rib" to see RIB and "ospfctl show fib" to see FIB. It should show you the network at the peer's end. If it doesn't work for you, make sure IPsec flow is established,  OSPF packet is flowing between both hosts. To make a meshed IPsec, simply repeat this procedure. Tedious? Well, for that matter, you have to replace all the proprietary boxes with OpenBSD and use some kind of automated configuration management system.

How I made my Flash dead silent

| No Comments

どうでも良い失敗談なのですが、先日.flaからswfをPublishしてもActionScriptが全く認識されずtraceもエラーも出力されず、本当にうんともすんともいわないという状態に陥りました。(Publishは成功するがActionScriptが全く認識されないという状態)

本当にうんともすんとも言わないので原因の手がかりが掴めず、classpath、document classのリンケージのチェック、ASO削除、TextMateでASファイルにBOMを加えてみるなどいろいろ試したんですが全然駄目で、数時間を無駄にしようとしていたそのとき、自分のミスに気がつきました。

Killer.png

画像のように、どうやら何かの拍子で誤ってLibraryのMovieClip Symbolのひとつに、(Base Classにではなく直接)flash.display.Spriteをリンケージしてしまっていたようです。

何故自分でもこんなことをやってしまったのかわかりませんが、これをやってしまうと上記のようにCS4(Mac版)だとpublishはするものの出力が完全沈黙するので、下手をするとはまります(因みにCS3で試したところIDEがクラッシュしました)。たぶんこんなアホなことをやるのは自分だけだろうと思いつつも、もし同じ症状に出くわしたら、一応各Symbolのリンケージをチェックしてみてください。。

This is one of my trivial horror stories - the other day I made changes on some Flash stuff I was working on and published the .fla as usual. However, it suddenly stopped working and even worse, it also stopped spitting out any trace/error messages at all. It published fine, but it's broken and dead silent. It seemed to stop recognizing all the ActionScript class files suddenly.

Since everything is dead silent I had no clue why it went wrong, I just blindly tried a couple of things I could think of, like checking class pathes/document class linkage, deleting ASOs, and trying to add UTF-8 BOM to the AS files (via TextMate). None of them worked. After wasting a couple of hours like this, I found my mistake.

Killer.png

Like the image above, somehow I mistakenly made a linkage to flash.display.Sprite from one of my hundreds of MovieClip symbol. I typed in flash.display.Sprite not in its Base Class field, but in the Class field. This was the cause of everything.

I had no idea why I did this, but anyway I wasted a couple of hours because of the mistake. Usually you don't do something like this but once you did it it may be hard to locate as Flash goes completely silent (In the case of CS4 Mac - when I tried the same on CS3 for testing, it simply crashed). I wrote this note hoping it might help someone who got into the same trap as I did. Perhaps no one else is ever gonna be as dump as me but anyways...

On Having Layout

| No Comments

いまさらかもしれんですが感動しました、これを知っているだけでいままで「なぜ?」と戸惑いながらいろいろ思考錯誤してみるしかなかったIE6(と7)のCSSの問題が結構な確率で解決するように思います;  

On Having Layout (http://www.satzansatz.de/cssd/onhavinglayout.html)

ものすごくおおざっぱに理解したところを反芻させてもらうと;

  • IEにおいてはHTMLの要素に専用の「隠し」パラメータ "hasLayout"というものが存在する
  • これがある要素に対してOn(true)になっているかなっていないかで、その要素の挙動が大きく変化する
  • hasLayoutはデフォルトでは決められた少数の要素でしかOnになっていない(詳しくは本文参照)。何といっても、一番よく使われると思われるdiv要素でOnになっていない
  • hasLayoutは特定のCSSのプロパティを指定してやることでOnにできるので、hasLayoutがOffになっている要素でこれを恣意的にやってやることによってIEにおける他のブラウザとの表示不一致の問題の矯正が可能な場合がある(その逆も真)
といった話だと思います。hasLayoutのOn/Offが実際にどのような影響を及ぼすかについては本文にいくつかの例がリストとしてあがっています(まだincompleteな状態とのことです、具体例を全てあげていくのは困難なのではないでしょうか)。自分も良くわからないままに実際に試してみただけなのですが、div要素やp要素のhasLayoutをOnにしてやることで、IE6/7でのFirefox3との表示の不一致の問題の多く(margin, float関連)が解決したので驚きました。今では、CSSの編集時にIEで何か問題が起こると、ひとまずこのhasLayoutのOn/Offを試すようにしています。

reallyenglishでは、開発環境、また一部のプロダクション環境でFreeBSDのJail機能を使っています。このJailを使うことによりシンプルで柔軟な開発環境が構築できます。

“FreeBSD”と聞いて、「ああ、関係ないや」と思われた方へ。

ちょっとだけでも読んでみてください。JailのためだけでもFreeBSDを導入する価値があると思っています。もし、FreeBSDの導入が無理だとしても考え方自体は他のFreeBSDのJail以外の仮想環境でも使えるはずです。

前提

Webシステム開発ではたくさんのサーバソフトウェアが必要です。システムの中心部であるPHPやRuby On Railsといったアプリケーションサーバや、データを格納するデータベースサーバは最低限必要でしょう。またロードバランサやメールサーバといったものも必要なることもあります。

また開発時にはGitやSubversionのようなSCMや、コミュニケーションのためのIRCサーバなども必要かもしれません。同じような環境を開発者やテスター毎、開発拠点毎に設置する必要があることも珍しくないでしょう。さらに、一つのWebアプリケーションでも、プロダクション用や、開発用と複数のバージョンを持ちつつ同時に開発するのもよくあることです。

さらに、予算やスペースの関係で、プロダクションサーバでは多くの物理的なサーバで行っていることを開発環境では少ない台数のサーバで賄わないといけない、ということもよくあることでしょう。

そのような様々なことから、ちょっとしたWebサービスの開発でも、かなりの複雑な環境が必要になることがわかります。

そう言った複雑な環築をなるべくシンプルに構築するためにreallyenglishではFreeBSDのJailを使用した仮想環境を使っています。

基本的な方針

まず、基本的な方針として、1サービス=1仮想環境にします。これはプロダクション環境でも、開発環境でも同じです。つまり、Apacheでも、PostgreSQLでも、Memcacheでも、Postfixでも、Gitでも、とにかくサーバ的なものは必ず一つの仮想環境にしてしまいます。

つまり「このサービスは軽いから、他のサービスと同じところで動かしちゃおう」的なことをやめました。何か新しく必要になったら、仮想環境を新規で作成しそこにインストールします。ちょっとした実験的なサービスを入れる場合でも同じです。「Nginx試したい。じゃー、Jail作ってそこでやろう。」と言った感じです。

また、同じサービスでも、用途が違えば別に仮想環境を作ります。例えば、Apache等のWebサーバはVirtual Host機能を使うことにより1つのWebサーバを複数の用途として使えますが、そういうこともやめました。

狙いは2つです。

  • 設定をシンプルにする。一つのサーバで、あれもこれもしようとすると、どうしても設定ファイルが複雑にいなってしまいます。1サービス1Jailとすると必然的に設定はかなりシンプルになります。また、設定をシンプルにすることでPuppetと言ったツールも導入しやすくなります。
  • ハードウェアとサービス、サービス間を疎結合にする。粗結合にしておくと色々なことがとてもフレキシブルになります。例えば、一つのサービスの負荷が非常に高くなり他のサービスに影響を与え始めた。こういう場合、別のハードウェアを用意し、そのサービスだけ移動する、と言ったことがよく行われます。このときに1サービス = 1 仮想環境ならその仮想環境をコピー(移動)するだけで完了します。同一のネットワーク内で移動するだけなら、仮想環境のIP addressの付け替えさえ必要ありません。

いちいち仮想環境を作る。というのは一見めんどくさいようにも思えますし、リソース的にも無駄が多そうだ、と思われるかもしれません。しかし、FreeBSDのJailとその管理ツールを使用するとその辺は驚くほど問題になりません。

次回、実際にどう使っているかを紹介します。

続く

FAQ for SWFObject is a must-read

| No Comments

SWFObjectのFAQはいけてますね、FlashをHTMLにembedするにあたっての陥りやすい罠が網羅されている印象です。実質、FAQの項目の多くが SWFObject自体の問題というよりはブラウザのバグ等が原因のため、同ライブラリを使う使わないに関わらず起こるもののような気がするのですが、SWFObjectが非常に多くの人達に使われているため、ここにそのあたりの質問とその応答の結果として蓄積されたノウハウが一番端的に集まっているように思います。日々様々な(時にはいわれのない)「おい、これ動かんぞ」の問い合わせに開発者の方やコミュニティーのみなさんが反応してくれている状況を想像するに、様々なライブラリ一般を公開して維持してくれている全てのみなさんへの尊敬の念が深まります。