UT-VPNとhttpsを同時に使用するときの注意点

UT-VPNApacheを同時に動かしているサーバで少しはまったので一応残しておく。

UT-VPNサーバを起動すると、デフォルトでは443、5555、992の3つのポートが開く。
その際、どれか1つのポートが使われている場合、当然ながらそのポートは使われない。
一番多いのは、既にApache等のWebサーバが動いており、https(443)を既に使っている場合だろう。

しかし、ここでApacheを停止すると、UT-VPNが443ポートの状況を常に監視しているのか、即座にUT-VPNが443ポートを占有するのである。
今回現象が起こったサーバの場合、週に1回ログをローテーションするためにApacheを再起動しているのだが、Apacheが停止して再び起動する際、既にUT-VPNが443を占有しており、起動に失敗した。

正確にはこのサーバは少しおかしくて、apachectl restartするとなぜかたまにApacheが停止したまま上がらないことがあるので、数分後に明示的にapachectl startが動くようにしてあるのだが、そのタイミングでこの現象が起こった。
(apachectl restartがちゃんと動いたときはそういったことは起こらなかった。)

対処としては、UT-VPNが余計なポートを使わないようにすること。
(そもそも5555しか使っていないので、992とかが空いてるのもセキュリティ上よろしくない。)
utvpncmdから、ListenerListでポートの使用状況を確認する。

VPN Server>ListenerList
ListenerList コマンド - TCP リスナー一覧の取得
ポート番号|状態
----------+----------
TCP 443   |エラー発生
TCP 5555  |動作中
TCP 992  |動作中
コマンドは正常に終了しました。

なるほど、空いていなかったポートは「エラー発生」となっていたのか。
ListenerDeleteコマンドで余計なリスナーを削除する。

VPN Server>ListenerDelete    
ListenerDelete コマンド - TCP リスナーの削除
削除する TCP/IP リスナーのポート番号: 443

コマンドは正常に終了しました。

VPN Server>ListenerDelete    
ListenerDelete コマンド - TCP リスナーの削除
削除する TCP/IP リスナーのポート番号: 992

コマンドは正常に終了しました。

これで余計なポートは全て閉じた。

まあ、ちゃんと運用するサーバであれば最初から使うポートだけに絞るはずなので、こんな現象に遭遇することはまず無いとは思うのだけれど。

MacPortsでインストールしたTwitter GemでMultiJson::DecodeError

MacPorts-1.9.2を使ってインストールしたrubygemsTwitter Gemを入れたところ少しハマったので残しておく。
MacPortsはパッケージインストーラで入れただけ。
その後rubygemsをインストール。

$ sudo port install rb-rubygems

ここで

$ which gem ruby

すると両方/opt/localの下を指しているが、

$ gem --version
$ ruby --version

の出力を見るとどうもMac標準の/usr/binの下が呼ばれていそうなので、シェルを一回再起動する。
次にTwitter Gemsをインストール。

$ sudo gem install twitter

動作を確認するため、サンプルを書く。

require 'rubygems'
require 'twitter'

p Twitter.user('junji_furuya').location

これを実行したところ、MultiJson::DecodeErrorになった。

/opt/local/lib/ruby/gems/1.8/gems/multi_json-0.0.5/lib/multi_json.rb:66:in `decode': #<RuntimeError: Did not recognize your engine specification. Please specify either a symbol or a class.> (MultiJson::DecodeError)
	from /opt/local/lib/ruby/gems/1.8/gems/faraday_middleware-0.3.2/lib/faraday/parse_json.rb:22:in `register_on_complete'
	from /opt/local/lib/ruby/gems/1.8/gems/faraday-0.5.7/lib/faraday/response.rb:47:in `call'
	from /opt/local/lib/ruby/gems/1.8/gems/faraday-0.5.7/lib/faraday/response.rb:47:in `finish'
	from /opt/local/lib/ruby/gems/1.8/gems/faraday-0.5.7/lib/faraday/response.rb:47:in `each'
	from /opt/local/lib/ruby/gems/1.8/gems/faraday-0.5.7/lib/faraday/response.rb:47:in `finish'
	from /opt/local/lib/ruby/gems/1.8/gems/faraday-0.5.7/lib/faraday/builder.rb:19:in `inner_app'
	from /opt/local/lib/ruby/gems/1.8/gems/faraday-0.5.7/lib/faraday/response.rb:17:in `call'
	from /opt/local/lib/ruby/gems/1.8/gems/faraday-0.5.7/lib/faraday/response.rb:17:in `call'
	from /opt/local/lib/ruby/gems/1.8/gems/faraday-0.5.7/lib/faraday/adapter/net_http.rb:57:in `call'
	from /opt/local/lib/ruby/gems/1.8/gems/twitter-1.1.2/lib/faraday/multipart.rb:16:in `call'
	from /opt/local/lib/ruby/gems/1.8/gems/faraday-0.5.7/lib/faraday/request.rb:85:in `run'
	from /opt/local/lib/ruby/gems/1.8/gems/faraday-0.5.7/lib/faraday/request.rb:27:in `run'
	from /opt/local/lib/ruby/gems/1.8/gems/faraday-0.5.7/lib/faraday/connection.rb:177:in `run_request'
	from /opt/local/lib/ruby/gems/1.8/gems/faraday-0.5.7/lib/faraday/connection.rb:66:in `get'
	from /opt/local/lib/ruby/gems/1.8/gems/twitter-1.1.2/lib/twitter/request.rb:28:in `send'
	from /opt/local/lib/ruby/gems/1.8/gems/twitter-1.1.2/lib/twitter/request.rb:28:in `request'
	from /opt/local/lib/ruby/gems/1.8/gems/twitter-1.1.2/lib/twitter/request.rb:6:in `get'
	from /opt/local/lib/ruby/gems/1.8/gems/twitter-1.1.2/lib/twitter/client/user.rb:23:in `user'
	from /opt/local/lib/ruby/gems/1.8/gems/twitter-1.1.2/lib/twitter.rb:21:in `send'
	from /opt/local/lib/ruby/gems/1.8/gems/twitter-1.1.2/lib/twitter.rb:21:in `method_missing'
	from hello_world.rb:5

どうも何か設定が足りない様子。
ググってもよく分からなかったので、/opt/local/lib/ruby/gems/1.8/gems/multi_json-0.0.5/lib/multi_json.rbの中身を読と、12〜36行目付近に次のような記述があった。

  REQUIREMENT_MAP = [
    ["yajl", :yajl],
    ["json", :json_gem],
    ["active_support", :active_support],
    ["json/pure", :json_pure]
  ]

  # The default engine based on what you currently
  # have loaded and installed. First checks to see
  # if any engines are already loaded, then checks
  # to see which are installed if none are loaded.
  def default_engine
    return :yajl if defined?(::Yajl)
    return :json_gem if defined?(::JSON)
    return :active_support if defined?(::ActiveSupport::JSON)

    REQUIREMENT_MAP.each do |(library, engine)|
      begin
        require library
        return engine
      rescue LoadError
        next
      end
    end
  end

どうもREQUIREMENT_MAPの各要素の1番目の要素の文字列を順番にrequireしている様子。
要はJSONのエンジンがインストールされていないのが原因らしい。
そこでyajlをrubygemsからインストール。

$ gems search --remote yajl
benofsky-yajl-ruby (0.7.7)
filipegiusti-yajl-ruby (0.6.4)
sprout-yajl-library (0.0.1)
yajl-ruby (0.8.1)
$ sudo gems install yajl-ruby

これで、サンプルを動かすことができた。

$ ruby -Ku location.rb
"東京都北区"

IDEのHDDに入ったLinuxをVMWare仮想マシンに変換してみたが…

RedHat ES 4 が入ったサーバの電源が故障してしまい、起動しなくなってしまった。
内蔵HDDのデータは無事のようなので、この際VMWare仮想マシンに変換することにした。
作戦としては
http://d.hatena.ne.jp/mitszo/20080403/p1
を参考にさせていただき(つーかパクり)、

  1. HDDをddコマンドが使えるマシンに接続し、ディスクイメージを抽出
  2. 仮想マシンを作成
  3. CD Linuxから仮想マシンを起動
  4. CD Linux上のddコマンドを使い、仮想マシンのディスクに元環境のディスクイメージを流し込む
  5. 仮想マシンを普通に起動する

といった感じ。

HDDをddコマンドが使えるマシンに接続し、ディスクイメージを抽出

元のマシンから内蔵HDDを取り出した後、こういうやつを使い、Linux等、ddコマンドが利用可能なマシンのUSBポートに接続する。
最初はWindows版のddコマンドを利用しようと思ったのだが、ext3がどうしても見えなかったので諦め、Linux機(Ubuntu 10.04 LTS)を使うことにした。
接続したHDDから、ddコマンドでディスクイメージを抽出する。
HDDは/dev/sdcとして認識された。保存先はSambaで共有されたディレクトリとした。

$ sudo su -
# dd if=/dev/sdc of=/var/smb-share/ide-disk.img

仮想マシンを作成

VMWare ESXi上で仮想マシンを新規作成する。
ディスクのサイズは、変換元HDDのサイズに合わせる。

CD Linuxから仮想マシンを起動

Ubuntu 10.04 LTSのisoイメージがあったため、それを使うことにする。
isoイメージを仮想マシンに接続して起動し、「インストールせずにUbuntuを試す」的な選択肢を選ぶ。

CD Linux上のddコマンドを使い、仮想マシンのディスクに元環境のディスクイメージを流し込む

起動後、先ほどディスクイメージを抽出したディレクトリをsmbfsを使ってマウントし、仮想マシンのディスクにddコマンドで流し込む。(この前にネットワークの設定は必要。)
Ubuntu上のディスクユーティリティで確認したところ、仮想マシンのディスクは/dev/sdaとして認識されているようであった。

$ sudo su -
# apt-get install smbfs
# mkdir /mnt/smb-share
# mount -t smbfs //xxx.xxx.xxx.xxx/smb-share /mnt/smb-share
Password:
# dd if=/mnt/smb-share/ide-disk.img of=/dev/sda

仮想マシンを普通に起動する

デスクトップ上の電源ボタンから再起動を選ぶと、メディアを取り除いてEnterを押すようメッセージが表示されるので、isoイメージを切断し、Enterを押す。
すると、想定通りRedHat ES 4のgrubの画面が出た!
喜び勇んでEnterボタンを押すと、早々とkernel panicになった。
エラーメッセージを読んでみると、「No volume groups found」とある。
ここからが地獄の始まりであった。
initrdがVolumeGroupを認識できないのだろうということは分かるのだが、Ubuntuから起動してvgscanすると普通に見えるし、元のマシンで動いていたときも/はLVMでマウントされていたはずなので、なぜ認識できないのかが分からない。
色々ネットで調べていたところ、
http://chiji.atnifty.com/topics/?p=68
というエントリに当たり、解決した。
考えてみれば当たり前なのだけど、VMWareのディスクはSCSIエミュレーションなので、IDEで動いていた元のマシンとは別のカーネルモジュールを読み込ませてやらないと、カーネルはディスクを認識できない。

/etc/modprobe.confの記述内容の決定

上のエントリで紹介されている方法に従い作業することにする。(手順はほとんど同じなのだが、一応記録しておくことにする。)
まずは/etc/modprobe.confに正しいカーネルモジュールのエントリを記述する必要がある。
これについてはいい方法が思いつかなくて、別の仮想マシンに新規でRedHat ES 4 をインストールし、その/etc/modprobe.confの内容をひかえておくことにした。
ただし、RedHat ES 4 のメディアが既に手元に無い状況だったため、CentOS 4.8で代用することにする。

仮想ディスク上の/etc/modprobe.confを編集した後、initrdを再生成する

こちらも作業にはRedHat ES 4 のメディアのレスキューモードを使いたかったのだけど、CentOS 4.8のisoイメージで代用することにする。
CentOS 4.8のisoイメージを仮想マシンに接続して起動する。
boot: プロンプトでlinux rescueを入力してEnterを押す。
すると、必要なドライバが読み込まれ、勝手に/mnt/sysimageに仮想ディスクのパーティションがマウントされるので、

# vi /mnt/sysimage/etc/modprobe.conf

として、上でひかえたmodprobe.confの内容に書き換える。NICのドライバも当然ながら元環境とは異なっていたため、合わせて記述を変えた。
コメントアウトされているエントリは、元々記述されていた箇所。

#alias eth0 e1000
#alias snd-card-0 snd-intel8x0
#options snd-card-0 index=0
#install snd-intel8x0 /sbin/modprobe --ignore-install snd-intel8x0 && /usr/sbin/alsactl restore >/dev/null 2>&1 || :
#remove snd-intel8x0 { /usr/sbin/alsactl store >/dev/null 2>&1 || : ; }; /sbin/modprobe -r --ignore-remove snd-intel8x0
#alias usb-controller ehci-hcd
#alias usb-controller1 uhci-hcd
alias eth0 pcnet32
alias scsi_hostadapter mptbase
alias scsi_hostadapter1 mptscsi
alias scsi_hostadapter2 mptspi
alias scsi_hostadapter3 mptsas
alias scsi_hostadapter4 mptscsih
alias scsi_hostadapter5 ata_piix

そうした上で、initrdを再生成。chrootし、/usr/local/src/initrdで作業することとする。

# chroot /mnt/sysimage
# cd /usr/local/src
# mkdir initrd
# cd initrd
# mkinitrd initrd-2.6.9-5.EL.img 2.6.9-5.EL

しかし、「No module mptscsi found for kernel 2.6.9-5.EL, aborting.」となって怒られる。
この辺りは、RedHat ES 4 とCentOS 4.8の差異が影響しているかもしれない。
仕方がないので、無いと言われるモジュールはコメントアウトして再試行することにする。
最終的なmodprobe.confは下記のようになり、initrdも正常に生成することができた。

#alias eth0 e1000
#alias snd-card-0 snd-intel8x0
#options snd-card-0 index=0
#install snd-intel8x0 /sbin/modprobe --ignore-install snd-intel8x0 && /usr/sbin/alsactl restore >/dev/null 2>&1 || :
#remove snd-intel8x0 { /usr/sbin/alsactl store >/dev/null 2>&1 || : ; }; /sbin/modprobe -r --ignore-remove snd-intel8x0
#alias usb-controller ehci-hcd
#alias usb-controller1 uhci-hcd
alias eth0 pcnet32
alias scsi_hostadapter mptbase
#alias scsi_hostadapter1 mptscsi
#alias scsi_hostadapter2 mptspi
#alias scsi_hostadapter3 mptsas
alias scsi_hostadapter4 mptscsih
alias scsi_hostadapter5 ata_piix

最後に生成したinitrdを/bootにコピーして再起動する。

# cd /boot
# mv initrd-2.6.9-5.EL.img initrd-2.6.9-5.EL.img.org
# cp /usr/local/src/initrd/initrd-2.6.9-5.EL.img .
# reboot

これで、kernel panicが起こることなく、ログインプロンプトまで流れるようになった。

正直大変だった

最初は手順通りやれば簡単だと思ったのだが、予想外に大変だった。
実際にはgrubの設定いじったり、lvm.confを変えてみたり、的外れなことをたくさんやってたし。(まあ、色々勉強にはなったが…)
よく考えると、VMWare Convertorを使えばもっと簡単にできたのではないだろうか。
最初、Windows版を入れて少し試してみたのだけれども、Windowsからext3が見えないし、物理マシンとしても認識させることができないので、今回の手順の方が簡単と思い、すぐ諦めてしまったのだ。
よく見るとLinux版のConvertorも用意されているようだし、試してみる価値はある。
今後このようなケースに遭遇することを考え、本腰を入れて検証してみようと思う。

SSHポートフォワーディングによるリモートデスクトップ接続の色々

今日、自宅のLAN内のPCにVPN経由でリモートデスクトップ接続しようとしたら、なぜか繋がらなかった。
代替案を考えたのだけれども、LinuxルータにSSH接続が可能なので、ポートフォワーディングを利用すればいいんじゃないかと考えた。

接続元PCはMacなので、ターミナルから

ssh -L 3389:(接続先マシンのプライベートIPアドレス):3389 \
(ルータのユーザID)@(ルータのグローバルIPアドレス)

としてルータとのSSH接続を確立し、ポートフォワーディングが可能なようにしておく。そしてRemote Desktop Connection for Mac 2を利用して、接続先にlocalhostを指定することで、無事接続できた。なんだこれ、超簡単だな。これだけだったらわざわざVPN使う必要もない感じだ。
そこで、ちょっと思ったのが、iPadからも同じことが可能なのではということ。iOSマルチタスクをサポートしているので、SSHクライアントをポートフォワーディングのオプションをつけてバックグランドで起動しておき、RDPクライアントでlocalhostに接続すればよいはず。

早速試してみたが、やはり可能だった。使用したアプリは、SSH→iSSH、RDP→iTap RDP。iSSHで接続先の設定画面で、「Tunnels」というメニューがあるので、Add Tunnel...からポート設定を追加すればよい。(サポートされているのはローカル→サーバのポート転送のみのような感じ。)
余談だが、このiTap RDP、接続先の設定を保存した後、接続先にRDP接続が可能かチェックして緑→OK、赤→NGのアイコンで教えてくれる。SSHの接続を確立する前は、接続先がlocalhostなので当然赤なのだけれども、SSH接続後、緑に変わる。これはちょっと面白い。

繋がった瞬間はおおお!って感じで結構感動するのだが、画面の切り替えがパラパラというか、結構もっさりしており、iPadを使うようにはサクサク動かせない。(そこは、うちのインターネット回線が遅いせいかもしれないけど。)ただ、このアプリの操作性自体は結構頑張っていて、Windowsマシンのブラウザを二本指のスワイプでスクロールできたり、ダブルタップでのアイコンのドラッグ、同じくダブルタップでの右クリック動作など、Macトラックパッドに近い操作感だ。

これで日常作業するのはさすがにきついが、緊急手段としては十分実用レベルだ。

ということはiPhoneからも同じことができるということか…。
iPhoneとか、電話だしw
技術の進歩は恐ろしいな。

SQL Server 2005のデフォルト分離レベル

規定値はREAD_COMMITTEDなんですね。これは2008でも変わらないようだ。
こんなことも知らなかった自分に((((;゜Д゜)))ですわ。
試しに2005で

-- テスト用テーブルを作成。
CREATE TABLE TEST(id int, amt int)
GO

-- テスト用データを挿入。
INSERT INTO TEST VALUES (1, 100);
INSERT INTO TEST VALUES (2, 200);
INSERT INTO TEST VALUES (3, 300);
GO

BEGIN TRAN
    UPDATE TEST SET amt = 500 WHERE id = 1

した後に、別のセッションで

SELECT * FROM TEST;

とやったら、延々と待ってた。((((;゜Д゜)))
例外処理とか間違えたら死ねるな。
ていうか、デフォルトのREAD_COMMITTEDのままでは普通運用しないのか?

S2Container-2.4.42でのS2Chronos-1.0.0の利用

S2Chronos-1.0.0を使用していた環境のS2Containerを2.4.18から2.4.42にバージョンアップしたところ、Taskが起動しなくなってしまった。
Eclipse上でデバッグしてみたところ、s2chronos-coreに含まれるTaskExecuteStrategyImpl#load()の495行目、

			Object value = pd.getValue(this.task);

で例外が発生し、スケジューラスレッドが終了してしまう。
なぜかと思い調べてみると、どうやら、S2Containerに含まれるPropertyDescImpl#getValue()の動作が変更されているようだ。
2.4.18のときはsetterのみが定義されたフィールドであってもgetValue()が値を返していたが、2.4.42では例外が発生するようになっている。(同メソッドのjavadocは変更されていないので、2.4.18から2.4.42までの間に、正しい動作に修正された、ということらしい。)
事実、問題と思われるフィールドのgetterを実装したところ、正しくTaskが起動された。
ということは、Taskが使用するS2Container上のコンポーネントや、S2Chronosのドキュメントにあるexceptionフィールドに対して、getterを定義する必要がある。
このためだけにgetterを定義するのは微妙なので、なんとか回避する方法を模索してみた。
このTaskExecuteStrategyImpl#load()だが、デバッグしてみると処理のほとんどがスキップされるし、問題の例外発生部分についても、ローカル変数にPropertyDescImpl#getValue()の値を格納して終わっているので、実行されなくても問題がないように思われた。
そこで、思い切ってload()メソッドの動作をスキップしようと思い、TaskExecuteStrategyImplを継承したクラスを作成し、load()メソッドをオーバーライドして空実装とし、s2chronos-core.diconでそのクラスを使うように修正。
その結果、多少強引な手ではあるが、動作するようになった。


うーーん、ググっても全然引っかからないし、皆困ってないんだろうか?
ていうか、問題が起こった環境自体、2.3から移行した関係で設定の仕方とか2.4風でなかったりするので、その辺が原因なのかも。
なんか根本的に間違ってる予感がしてきたので、S2Containerのドキュメントを一から読みながら環境を見直してみるか。