Windows で sudo のようなことがしたい

Windows 環境で、権限昇格をしようとする場合、一般的に以下の2種類のやりかたが知られていると思います。

  • 右クリック(または Ctrl+Shift+Enter)して「管理者として実行」
  • runas コマンドで起動

私はよく Cygwin を使いますが、残念ながら Cygwin 上では runas のパスワード入力ができないため、前者の方法で別のプロンプトを立ち上げる必要があります。

これが面倒くさいなーと前から思っていました。

(コマンドプロンプトでも、runas の使い方が慣れないのでだいたい Ctrl+Shift+Enter で起動していました)

そこで、ふとぐぐってみると、 superuserのスレッド の回答にソースコードが投稿されていました。

コンパイルしてみたらさらっと使ってみた感じ問題ないようなので、同じように少し困ってる人が居るかな?と思い、 GitHub にバイナリ込みで置いておきました。

Cygwin から使うときは、以下の注意が必要です。

  • bash -c でコマンド実行しようとするとうまくいかない
    • あんまり調べずに断念しました
  • sudo hoge.sh のようなことは普通はできない
    • *.sh を bash に関連づけると可能かもしれませんが試していません
  • sudo bash hoge.sh すると、Windowsのパスが優先されるため、 find などが使えない
  • sudo bash --login hoge.sh すると、カレントパスが通常ホームディレクトリになるため、引数のファイル名を見つけられない
    • sudo bash --login $(readlink -m hoge.sh) すると回避できます
  • そもそも、Unix 環境の sudo とは異なり、新たにプロンプトが立ち上がる
    • スクリプトを実行すると終了後にウィンドウが閉じてしまうため、それが困る場合は read -p などで止める必要あり
    • sudo で実行したコマンドの出力は新たに起動した管理者権限のコンソールに出力されるため、たとえば sudo rsync -av src/ dest/ のようなことをすると期待通りに行かない

git config color.ui always はダメ、ゼッタイ

はまったのでメモ

color.ui

皆さんご存知のとおり git の diff や status を ANSI カラー出力するようにする設定です。

一般的に、以下のようにすることが多いかと思います。

git config --global color.ui auto

これを、「git status などを less に渡したときにも色づけしたい!」ということで、自分は以下のようにしていました。

git config --gobal color.ui always
続きを読む

TDDBC横浜#3 参加レポート

TDDBC横浜#3 に参加してきました

2013/10/05(土)に行われた、 TDD Boot Camp 横浜 3rd に参加してきました。

前々から TDDBC には興味があったのですが、今回初参加させていただきました。

結論から言うと、非常に楽しく、また収穫の多い勉強会でした。

続きを読む

覚えられない自己署名証明書作成

自己署名証明書はわりとよく作るのですが、自分の場合だいたいいつも何かしら忘れていて、調べるのに時間を要するパターンが多いです。

ので、メモを書けばいいのですが、それもわりとめんどい(おい)ので、GitHubに一式をあげておきました。未来の自分のために。

openssl.cnf をCA作成/CSR作成/自己署名ごとに書き換えなくてもいいように、 misc/CA を修正してあります。

あと、証明書はワイルドカード証明書サブドメイン無しを subjectAltName に設定)するようにしています。

一応、 diff をのっけておきます。

CA証明書作成用 openssl.cnf.ca

--- openssl.cnf 2012-05-15 19:44:23.000000000 +0900
+++ openssl.cnf.ca      2013-03-10 01:36:45.951053900 +0900
@@ -58,7 +58,7 @@
 private_key    = $dir/private/cakey.pem# The private key
 RANDFILE       = $dir/private/.rand    # private random number file

-x509_extensions        = usr_cert              # The extentions to add to the cert
+x509_extensions        = v3_ca         # The extentions to add to the cert

 # Comment out the following two lines for the "traditional"
 # (and highly broken) format.
@@ -73,7 +73,7 @@
 # crlnumber must also be commented out to leave a V1 CRL.
 # crl_extensions       = crl_ext

-default_days   = 365                   # how long to certify for
+default_days   = 3650                  # how long to certify for
 default_crl_days= 30                   # how long before next CRL
 default_md     = sha1                  # which md to use.
 preserve       = no                    # keep passed DN ordering
@@ -133,18 +133,18 @@

 [ req_distinguished_name ]
 countryName                    = Country Name (2 letter code)
-countryName_default            = GB
+countryName_default            = JP
 countryName_min                        = 2
 countryName_max                        = 2

 stateOrProvinceName            = State or Province Name (full name)
-stateOrProvinceName_default    = Berkshire
+stateOrProvinceName_default    = Hokkaido

 localityName                   = Locality Name (eg, city)
-localityName_default           = Newbury
+localityName_default           = Hakodate

 0.organizationName             = Organization Name (eg, company)
-0.organizationName_default     = My Company Ltd
+0.organizationName_default     = Comutt is not a compnay

 # we can do this but it is not needed normally :-)
 #1.organizationName            = Second Organization Name (eg, company)
@@ -155,6 +155,7 @@

 commonName                     = Common Name (eg, your name or your server\'s hostname)
 commonName_max                 = 64
+commonName_default      = Comutt Auhority

 emailAddress                   = Email Address
 emailAddress_max               = 64
@@ -247,10 +248,10 @@
 # Key usage: this is typical for a CA certificate. However since it will
 # prevent it being used as an test self-signed certificate it is best
 # left out by default.
-# keyUsage = cRLSign, keyCertSign
+keyUsage = cRLSign, keyCertSign

 # Some might want this also
-# nsCertType = sslCA, emailCA
+nsCertType = sslCA, emailCA

 # Include email address in subject alt name: another PKIX recommendation
 # subjectAltName=email:copy

自己署名用 openssl.cnf.sign

--- openssl.cnf 2012-05-15 19:44:23.000000000 +0900
+++ openssl.cnf.sign    2013-03-10 01:37:22.158717500 +0900
@@ -66,14 +66,14 @@
 cert_opt       = ca_default            # Certificate field options

 # Extension copying option: use with caution.
-# copy_extensions = copy
+copy_extensions = copy

 # Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
 # so this is commented out by default to leave a V1 CRL.
 # crlnumber must also be commented out to leave a V1 CRL.
 # crl_extensions       = crl_ext

-default_days   = 365                   # how long to certify for
+default_days   = 3650                  # how long to certify for
 default_crl_days= 30                   # how long before next CRL
 default_md     = sha1                  # which md to use.
 preserve       = no                    # keep passed DN ordering
@@ -181,7 +181,7 @@
 # the certificate can be used for anything *except* object signing.

 # This is OK for an SSL server.
-# nsCertType                   = server
+nsCertType                     = server

 # For an object signing certificate this would be used.
 # nsCertType = objsign
@@ -196,7 +196,7 @@
 # keyUsage = nonRepudiation, digitalSignature, keyEncipherment

 # This will be displayed in Netscape's comment listbox.
-nsComment                      = "OpenSSL Generated Certificate"
+nsComment                      = "Certificate issued by Comutt Authority"

 # PKIX recommendations harmless if included in all certificates.
 subjectKeyIdentifier=hash

CSR作成用 openssl.cnf.req

--- openssl.cnf 2012-05-15 19:44:23.000000000 +0900
+++ openssl.cnf.req     2013-03-10 01:39:11.967310300 +0900
@@ -73,7 +73,7 @@
 # crlnumber must also be commented out to leave a V1 CRL.
 # crl_extensions       = crl_ext

-default_days   = 365                   # how long to certify for
+default_days   = 3650                  # how long to certify for
 default_crl_days= 30                   # how long before next CRL
 default_md     = sha1                  # which md to use.
 preserve       = no                    # keep passed DN ordering
@@ -129,22 +129,22 @@
 # the resulting certificates are compatible with Netscape
 string_mask = MASK:0x2002

-# req_extensions = v3_req # The extensions to add to a certificate request
+req_extensions = v3_req # The extensions to add to a certificate request

 [ req_distinguished_name ]
 countryName                    = Country Name (2 letter code)
-countryName_default            = GB
+countryName_default            = JP
 countryName_min                        = 2
 countryName_max                        = 2

 stateOrProvinceName            = State or Province Name (full name)
-stateOrProvinceName_default    = Berkshire
+stateOrProvinceName_default    = Hokkaido

 localityName                   = Locality Name (eg, city)
-localityName_default           = Newbury
+localityName_default           = Hakodate

 0.organizationName             = Organization Name (eg, company)
-0.organizationName_default     = My Company Ltd
+0.organizationName_default     = Comutt is not a company

 # we can do this but it is not needed normally :-)
 #1.organizationName            = Second Organization Name (eg, company)
@@ -155,6 +155,7 @@

 commonName                     = Common Name (eg, your name or your server\'s hostname)
 commonName_max                 = 64
+commonName_default             = *.comutt.jp

 emailAddress                   = Email Address
 emailAddress_max               = 64
@@ -181,7 +182,7 @@
 # the certificate can be used for anything *except* object signing.

 # This is OK for an SSL server.
-# nsCertType                   = server
+nsCertType                     = server

 # For an object signing certificate this would be used.
 # nsCertType = objsign
@@ -196,7 +197,7 @@
 # keyUsage = nonRepudiation, digitalSignature, keyEncipherment

 # This will be displayed in Netscape's comment listbox.
-nsComment                      = "OpenSSL Generated Certificate"
+nsComment                      = "Certificate issued by Comutt Authority"

 # PKIX recommendations harmless if included in all certificates.
 subjectKeyIdentifier=hash
@@ -224,7 +225,9 @@
 # Extensions to add to a certificate request

 basicConstraints = CA:FALSE
+nsCertType = server
 keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+subjectAltName = DNS.1:*.comutt.jp, DNS.2:comutt.jp

 [ v3_ca ]

CA作成/CSR作成/自己署名用スクリプト misc/CA

--- a/misc/CA
+++ b/misc/CA
@@ -32,8 +32,8 @@

 if [ -z "$OPENSSL" ]; then OPENSSL=openssl; fi

-DAYS="-days 365"       # 1 year
-CADAYS="-days 1095"    # 3 years
+DAYS="-days 3650"      # 10 years
+CADAYS="-days 3650"    # 10 years
 REQ="$OPENSSL req $SSLEAY_CONFIG"
 CA="$OPENSSL ca $SSLEAY_CONFIG"
 VERIFY="$OPENSSL verify"
@@ -53,13 +53,13 @@ case $i in
     ;;
 -newcert)
     # create a certificate
-    $REQ -new -x509 -keyout newkey.pem -out newcert.pem $DAYS
+    $REQ -config ../openssl.cnf.req -new -x509 -keyout newkey.pem -out newcert.pem $DAYS
     RET=$?
     echo "Certificate is in newcert.pem, private key is in newkey.pem"
     ;;
 -newreq)
     # create a certificate request
-    $REQ -new -keyout newkey.pem -out newreq.pem $DAYS
+    $REQ -config ../openssl.cnf.req -new -keyout newkey.pem -out newreq.pem $DAYS
     RET=$?
     echo "Request is in newreq.pem, private key is in newkey.pem"
     ;;
@@ -87,9 +87,9 @@ case $i in
            RET=$?
        else
            echo "Making CA certificate ..."
-           $REQ -new -keyout ${CATOP}/private/$CAKEY \
+           $REQ -config ../openssl.cnf.ca -new -keyout ${CATOP}/private/$CAKEY \
                           -out ${CATOP}/$CAREQ
-           $CA -out ${CATOP}/$CACERT $CADAYS -batch \
+           $CA -config ../openssl.cnf.ca -out ${CATOP}/$CACERT $CADAYS -batch \
                           -keyfile ${CATOP}/private/$CAKEY -selfsign \
                           -infiles ${CATOP}/$CAREQ
            RET=$?
@@ -97,31 +97,31 @@ case $i in
     fi
     ;;
 -xsign)
-    $CA -policy policy_anything -infiles newreq.pem
+    $CA -config ../openssl.cnf.sign -policy policy_anything -infiles newreq.pem
     RET=$?
     ;;
 -sign|-signreq)
-    $CA -policy policy_anything -out newcert.pem -infiles newreq.pem
+    $CA -config ../openssl.cnf.sign -policy policy_anything -out newcert.pem -infiles newreq.pem
     RET=$?
     cat newcert.pem
     echo "Signed certificate is in newcert.pem"
     ;;
 -signcert)
     echo "Cert passphrase will be requested twice - bug?"
-    $X509 -x509toreq -in newreq.pem -signkey newreq.pem -out tmp.pem
-    $CA -policy policy_anything -out newcert.pem -infiles tmp.pem
+    $X509 -config ../openssl.cnf.sign -x509toreq -in newreq.pem -signkey newreq.pem -out tmp.pem
+    $CA -config ../openssl.cnf.sign -policy policy_anything -out newcert.pem -infiles tmp.pem
     cat newcert.pem
     echo "Signed certificate is in newcert.pem"
     ;;
 -verify)
     shift
     if [ -z "$1" ]; then
-           $VERIFY -CAfile $CATOP/$CACERT newcert.pem
+           $VERIFY -config ../openssl.cnf.ca -CAfile $CATOP/$CACERT newcert.pem
            RET=$?
     else
        for j
        do
-           $VERIFY -CAfile $CATOP/$CACERT $j
+           $VERIFY -config ../openssl.cnf.ca -CAfile $CATOP/$CACERT $j
            if [ $? != 0 ]; then
                    RET=$?
            fi

OpenSSH を使って簡易拠点間 VPN

概要

少し業務とは違うところで、NAT配下のマシン同士で、拠点間のVPNもどきが必要になる場面がありました。

単純な Dynamic Port Forwarding で、 SOCKS プロキシを使う手もあったのですが、やはり、ローカルアクセス相当がしたいと。
調べてみると、 OpenSSH 4.3 から、 tun/tap デバイスを使ったレイヤー2およびレイヤー3のトンリングがサポートされていることがわかりました。
OpenSSH を使った簡易 VPN の構築

そこで、実際にやってみたので、メモ代わりに残しておいてみることにしました。
なお、今回はレイヤー2のトンネリングを行うことにしました。

今回の環境

拠点のサーバ側、およびクライアント側は両方とも CentOS 6.3 を使用しました。

また、ネットワーク構成は以下のものとします。
ネットワーク図

クライアント側での準備

鍵生成

鍵認証を行うため、専用の鍵を生成します。
今回は、ブランクパスフレーズの鍵を生成しました。

また、あえて 1024 ビットの鍵を作っています。
(2048ビットだとトンネリングの負荷が少し高くなりそうだなあと思ったのと、そもそも、要件的に暗号強度は求められていないため)

[root@vpn-client ~]#  ssh-keygen -t rsa -b 1024
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
ee:ae:76:b8:e5:d1:ca:ac:ea:a5:a7:e1:62:ca:55:3d root@vpn-client
鍵をサーバに転送

作った鍵の公開鍵を、サーバ側に転送します。

[root@vpn-client ~]#  scp -p ~/.ssh/id_rsa.pub user@vpn-server:/tmp

サーバ側の設定

OpenSSH で tun/tap デバイスによるトンネリングを行うためには、いくつか下準備が必要です。

/etc/ssh/sshd_config を修正
## レイヤー2、およびレイヤー3 の両方のトンネリングを許可する場合
#PermitTunnel yes
## レイヤー2 のみを許可する場合(今回はこれを使用)
PermitTunnel ethernet
## レイヤー3 のみを許可する場合
# PermitTunnel point-to-point
## トンネリングには root ログインが必要なため、
## authorized_keys で記述したコマンドのみ実行できるように制限する
PermitRootLogin forced-commands-only
tun/tap デバイスを使えるように
  • /etc/modprobe.d/tun.conf を作る
[root@vpn-server ~]# cat /etc/modprobe.d/tun.conf
alias netdev-tun0 tun
alias netdev-tap0 tun
  • # modprobe tun
[root@vpn-server ~]# lsmod | grep tun
tun                    17127  0
クライアント側の公開鍵を登録

転送しておいた root ログイン用の公開鍵を登録します。

[root@vpn-server ~]# mkdir ~/.ssh
[root@vpn-server ~]# chmod 0700 ~/.ssh
[root@vpn-server ~]# echo -n 'tunnel="0",command="ifup tap0" ' > ~/.ssh/authorized_keys
[root@vpn-server ~]# cat /tmp/id_rsa.pub >> ~/.ssh/authorized_keys
[root@vpn-server ~]# chmod 0600 ~/.ssh/authorized_keys
[root@vpn-server ~]# rm -f /tmp/id_rsa.pub
[root@vpn-server ~]# cat ~/.ssh/authorized_keys
tunnel="0",command="ifup tap0" ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAvM58cRnLjQQf+MniEtflzj4oAQNBCxeYv/NW0lsj/UnwYaX0H1z6kWpCWv/jI79zENVsJZ8VQUEa533tEBYZHLGh6xbKSpefFzWCUIEcxTT2Tm18ZJafnyrL36z1kWbnTzATxPkYrsaFeBQoVdmL9PGrTYiUwnoAyBqcLPwpErE= root@vpn-client
ifcfg-tap0 を作成

authorized_keys に記述している通り、 tap0 を ifup できるようにするため、
/etc/sysconfig/network-scripts/ifcfg-tap0 を作成します。

[root@vpn-server ~]# cat /etc/sysconfig/network-scripts/ifcfg-tap0
DEVICE="tap0"
BOOTPROTO="none"
MTU="1500"
NM_CONTROLLED="no"
ONBOOT="no"
TYPE="Ethernet"
IPADDR="192.168.11.254"
NETMASK="255.255.255.0"
route-tap0 を作成

さらに、サーバ側 → クライアント側の通信を可能とするため、ルーティングを追加する必要があります。
ifup tap0 時に自動的にルーティングが追加されるように /etc/sysconfig/network-scripts/route-tap0 を作成します。

[root@vpn-server ~]# cat /etc/sysconfig/network-scripts/route-tap0
192.168.12.0/24 via 192.168.11.253 dev tap0
パケット転送を有効化

CentOS のデフォルトでは、パケット転送が無効化されています。
トンネリングされたパケットは tap0 を通して送受信されますが、配下のネットワークに接続されているホストとは ethN を通して通信するため、パケット転送を有効化する必要があります*1

  • /etc/sysctl.conf を編集し、net.ipv4.ip_forward を 1 にする
--- /etc/sysctl.conf.org        2012-02-22 23:47:27.000000000 +0900
+++ /etc/sysctl.conf    2012-11-03 18:54:26.000000000 +0900
@@ -4,7 +4,7 @@
 # sysctl.conf(5) for more details.

 # Controls IP packet forwarding
-net.ipv4.ip_forward = 0
+net.ipv4.ip_forward = 1

 # Controls source route verification
 net.ipv4.conf.default.rp_filter = 1
  • # sysctl -p で設定を再読み込み

クライアント側の設定

クライアント側でも、おおよそ同じことを設定します。

tun/tap デバイスを使えるように
  • /etc/modprobe.d/tun.conf を作る

OpenSSH を使った簡易 VPN の構築 の解説では、

alias tun0 tun
alias tap0 tun

という設定例が掲載されていますが、CentOS6.3 で設定し、 /etc/init.d/network restart したところ、

Loading kernel module for a network device with
CAP_SYS_MODULE (deprecated).  Use CAP_NET_ADMIN and alias netdev-tap0 instead

といわれてしまったため、下記のように設定しました。

[root@vpn-client ~]# cat /etc/modprobe.d/tun.conf
alias netdev-tun0 tun
alias netdev-tap0 tun
  • # modprobe tun
[root@vpn-client ~]# lsmod | grep tun
tun                    22849  0
ifcfg-tap0 を作成

サーバと違うのは、IPADDR。

[root@vpn-client ~]#  cat /etc/sysconfig/network-scripts/ifcfg-tap0
DEVICE="tap0"
BOOTPROTO="none"
MTU="1500"
NM_CONTROLLED="no"
ONBOOT="no"
TYPE="Ethernet"
IPADDR="192.168.11.253"
NETMASK="255.255.255.0"
route-tap0 を作成

クライアント側 → サーバ側のルーティングを設定

# cat /etc/sysconfig/network-scripts/route-tap0
192.168.10.0/24 via 192.168.11.254 dev tap0
パケット転送を有効化

同様にパケット転送を有効化します*2

  • /etc/sysctl.conf を編集し、net.ipv4.ip_forward を 1 にする
--- /etc/sysctl.conf.org        2012-02-22 23:47:27.000000000 +0900
+++ /etc/sysctl.conf    2012-11-03 18:54:26.000000000 +0900
@@ -4,7 +4,7 @@
 # sysctl.conf(5) for more details.

 # Controls IP packet forwarding
-net.ipv4.ip_forward = 0
+net.ipv4.ip_forward = 1

 # Controls source route verification
 net.ipv4.conf.default.rp_filter = 1
  • [root@vpn-client ~]# sysctl -p
接続設定を作成

/root/.ssh/config に接続設定を記述します。

  • [root@vpn-client ~]# vi ~/.ssh/config
  • [root@vpn-client ~]# chmod 0600 ~/.ssh/config
[root@vpn-client ~]#  cat ~/.ssh/config
Host vpn-server
    HostName server.global
    Port 12345
    User root
    IdentityFile ~/.ssh/id_rsa
    Tunnel ethernet
    TunnelDevice 0:0
    PermitLocalCommand yes
    LocalCommand (echo "Waiting for 10 seconds..."; sleep 10; ifup tap0; ifconfig tap0; route; ping -c3 192.168.10.254; echo; if [ $? -eq 0 ]; then echo "Connect to 192.168.254: SUCCESS"; else echo "Connect to 192.168.10.254: FAILURE"; fi; echo; echo "Press Control-C to exit") &

OpenSSH を使った簡易 VPN の構築 で解説されているとおり、
クライアント側の tap0 は LocalCommand 実行後に作成されます

そのため、LocalCommand はバックグランドで実行し、スリープを挟むようにします。
リンク先では、3秒にされていますが、一度接続すればコネクションが切れない限りは放っておくものであることを鑑みて、安全側に倒して10秒にしています。

実際に接続してトンネリングを確認する

あとは ssh コマンドで接続するだけです。
上記のように設定した場合、ローカル側で ifup tap0 まで自動で行われます。

[root@vpn-client ~]# ssh vpn-server
Waiting for 10 seconds...
tap0      Link encap:Ethernet  HWaddr 5E:63:33:30:1E:AA
          inet addr:192.168.11.253  Bcast:192.168.11.255  Mask:255.255.255.0
          inet6 addr: fe80::5c63:33ff:fe30:1eaa/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:11 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:500
          RX bytes:678 (678.0 b)  TX bytes:448 (448.0 b)

Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
192.168.12.0    *               255.255.255.0   U     0      0        0 eth1
192.168.10.0    192.168.11.254  255.255.255.0   UG    0      0        0 tap0
192.168.11.0    *               255.255.255.0   U     0      0        0 tap0
192.168.1.0     *               255.255.255.0   U     0      0        0 eth0
link-local      *               255.255.0.0     U     1002   0        0 eth0
link-local      *               255.255.0.0     U     1003   0        0 eth1
link-local      *               255.255.0.0     U     1008   0        0 tap0
default         192.168.1.254   0.0.0.0         UG    0      0        0 eth0
PING 192.168.10.254 (192.168.10.254) 56(84) bytes of data.
64 bytes from 192.168.10.254: icmp_seq=1 ttl=64 time=4.06 ms
64 bytes from 192.168.10.254: icmp_seq=2 ttl=64 time=2.16 ms
64 bytes from 192.168.10.254: icmp_seq=3 ttl=64 time=2.23 ms

--- 192.168.10.254 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 2.168/2.823/4.067/0.880 ms

Connect to 192.168.10.254: SUCCESS

Press Control-C to exit
サーバマシンの ifconfig, route の結果
[root@vpn-server ~]# ifconfig
eth0      Link encap:Ethernet  HWaddr 00:0C:29:C1:3B:3D
          inet addr:192.168.1.100  Bcast:192.168.1.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fec1:3b3d/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:1370 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1179 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:157592 (153.8 KiB)  TX bytes:169069 (165.1 KiB)

eth1      Link encap:Ethernet  HWaddr 00:0C:29:C1:3B:47
          inet addr:192.168.10.254  Bcast:192.168.10.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fec1:3b47/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:117 errors:0 dropped:0 overruns:0 frame:0
          TX packets:152 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:12439 (12.1 KiB)  TX bytes:13373 (13.0 KiB)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:14 errors:0 dropped:0 overruns:0 frame:0
          TX packets:14 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:819 (819.0 b)  TX bytes:819 (819.0 b)

tap0      Link encap:Ethernet  HWaddr 7E:C4:3C:1A:B9:A3
          inet addr:192.168.11.254  Bcast:192.168.11.255  Mask:255.255.255.0
          inet6 addr: fe80::7cc4:3cff:fe1a:b9a3/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:32 errors:0 dropped:0 overruns:0 frame:0
          TX packets:31 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:500
          RX bytes:2456 (2.3 KiB)  TX bytes:2414 (2.3 KiB)
[root@vpn-server ~]# route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
192.168.1.0     *               255.255.255.0   U     0      0        0 eth0
192.168.12.0    192.168.11.253  255.255.255.0   UG    0      0        0 tap0
192.168.10.0    *               255.255.255.0   U     0      0        0 eth1
192.168.11.0    *               255.255.255.0   U     0      0        0 tap0
link-local      *               255.255.0.0     U     1002   0        0 eth0
link-local      *               255.255.0.0     U     1003   0        0 eth1
link-local      *               255.255.0.0     U     1008   0        0 tap0
default         192.168.1.254   0.0.0.0         UG    0      0        0 eth0
クライアントマシンの ifconfig, route の結果
[root@vpn-client ~]# ifconfig
eth0      Link encap:Ethernet  HWaddr 00:1E:C9:6B:5A:C2
          inet addr:192.168.1.100  Bcast:192.168.10.255  Mask:255.255.255.0
          inet6 addr: fe80::21e:c9ff:fe6b:5ac2/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:4626 errors:0 dropped:0 overruns:0 frame:0
          TX packets:3220 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:506625 (494.7 KiB)  TX bytes:413131 (403.4 KiB)
          Interrupt:21 Memory:fe9e0000-fea00000

eth1      Link encap:Ethernet  HWaddr 00:A0:B0:A5:84:A4
          inet addr:192.168.12.254  Bcast:192.168.12.255  Mask:255.255.255.0
          inet6 addr: fe80::2a0:b0ff:fea5:84a4/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:22 errors:0 dropped:0 overruns:0 frame:0
          TX packets:12 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:7454 (7.2 KiB)  TX bytes:828 (828.0 b)
          Interrupt:16 Base address:0x6f00

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)

tap0      Link encap:Ethernet  HWaddr 5A:F1:0D:F0:93:27
          inet addr:192.168.11.253  Bcast:192.168.11.255  Mask:255.255.255.0
          inet6 addr: fe80::58f1:dff:fef0:9327/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:31 errors:0 dropped:0 overruns:0 frame:0
          TX packets:32 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:500
          RX bytes:2414 (2.3 KiB)  TX bytes:2456 (2.3 KiB)

[root@vpn-client ~]# route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
192.168.12.0    *               255.255.255.0   U     0      0        0 eth1
192.168.10.0    192.168.11.254  255.255.255.0   UG    0      0        0 tap0
192.168.11.0    *               255.255.255.0   U     0      0        0 tap0
192.168.1.0     *               255.255.255.0   U     0      0        0 eth0
link-local      *               255.255.0.0     U     1002   0        0 eth0
link-local      *               255.255.0.0     U     1003   0        0 eth1
link-local      *               255.255.0.0     U     1009   0        0 tap0
default         192.168.1.254   0.0.0.0         UG    0      0        0 eth0
クライアント側ネットワークのホスト (192.168.12.100) から、サーバ側ネットワークのホスト (192.168.10.100) に tracert してみる
C:\Documents and Settings\user>tracert 192.168.10.100

Tracing route to 192.168.10.100 over a maximum of 30 hops

  1    <1 ms    <1 ms    <1 ms  192.168.12.254
  2     2 ms     2 ms     2 ms  192.168.11.254
  3     2 ms     2 ms     2 ms  192.168.10.100

Trace complete.
サーバ側ネットワークのホスト (192.168.10.100) から、クライアント側ネットワークのホスト(192.168.12.100) に traceroute してみる
[root@server-host ~]# traceroute -T 192.168.12.100
traceroute to 192.168.12.10 (192.168.12.10), 30 hops max, 60 byte packets
 1  192.168.10.254 (192.168.10.254)  0.212 ms  0.135 ms  0.104 ms
 2  192.168.11.253 (192.168.11.253)  5.481 ms  6.058 ms  6.755 ms
 3  192.168.12.100 (192.168.12.100)  7.385 ms  8.171 ms  8.408 ms

トンネリングを終了するには

入力待ちとなっているコンソールで、 Control-C を入力することで終了します。
tap0 デバイスは自動的に削除されるので、特に後始末をする必要はありません。

Connect to 192.168.10.254: SUCCESS

Press Control-C to exit
^CKilled by signal 2.

やってないこと

  • パフォーマンスの測定
    • そもそも一時用途なので、あまりまじめに使おうとしていません。通常のVPNよりは当然パフォーマンスは落ちるはずです。鍵の長さによる、負荷比も未確認です。
  • 安定性
    • SSHと同程度の安定性だと思いますが、こちらも未確認です。

*1:vpn-server にあたるマシンが、すでにルータを担っている場合、この設定は有効化されてるはずです。

*2:vpn-client にあたるマシンが、すでにルータを担っている場合、この設定は有効化されてるはずです。

Jenkins で Jasmine

大変久しぶりにエントリを書いてみます。

JasmineJenkins で動かそうとしたら少しはまりました。
はまったので、メモ代わりに書いておきます。

Jasmine を Jenkins で動かすにあたり、以下の構成で動かすことに落ち着きました。

以下のものを採用しなかった理由も書いておきます。

  • rake jasmine:ci
    • ブラウザが必要になるので、重い
    • reporter を自由に変えられなさそうだった*1
  • PhantomJS
    • JavaScript を読み込むのに、 http:// じゃないとだめそうだった(file:// 形式が読めなさそうだった)
      • あまりちゃんと調べなかったので、実はできるかも。

さて、準備方法です。

上に、4つ必要だと書きましたが、実は、 jasmine-reporters に必要なものが全部入ってます。

今回は、これのディレクトリ構成を変えてやってみることにしました。

  • jasmine-reporters v0.2.1 をダウンロードし、適当なディレクトリに展開します
  • 私は、以下のように構成を変えました
    • test/envjs.runner.sh -> bin/envjs.runner.sh
    • lib -> extlib
    • src -> lib
    • test/envjs.bootstrap.js -> lib/envjs.bootstrap.js
    • src/JUnitXmlReporterSpec.js -> spec/JUnitXmlReporterSpec.js
    • test/junit_xml_reporter.html -> ./junit_xml_reporter.html
    • PhantomJS 用ファイルなど、不要なものを削除
      • LICENSE, README.md, test/phantomjs*, test/*.html
  • bin/envjs.runner.sh を、カスタマイズ
--- a/bin/envjs.runner.sh	2012-03-09 14:00:12.000000000 +0900
+++ b/bin/envjs.runner.sh	2012-09-16 18:26:59.000000000 +0900
@@ -1,7 +1,53 @@
 #!/bin/bash
 
+MYDIR=$(dirname "$0")
+MYDIR_FULLPATH=$(cd ${MYDIR} && pwd)
+ROOTDIR_FULLPATH=$(cd ${MYDIR}/../ && pwd)
+EXTLIB_DIR="${ROOTDIR_FULLPATH}/extlib"
+LIB_DIR="${ROOTDIR_FULLPATH}/lib"
+BOOTSTRAP_JS="${LIB_DIR}/envjs.bootstrap.js"
+MAIN_CLASS=org.mozilla.javascript.tools.shell.Main 
+
+# If your path doesn't include java, or you wanna to specify the other one, 
+# please customize the following lines.
+
+### If you wanna to override JAVA_HOME, it's required to unset current JAVA_HOME
+
+# unset JAVA_HOME
+
+SYSTEM=$(uname -s)
+
+CYGWIN=0
+MAC=0
+LINUX=0
+
+if [[ "${SYSTEM}" =~ ^CYGWIN ]]; then
+    CYGWIN=1
+    MY_JAVA_HOME="C:\\Program Files (x86)\\Java\\jdk1.6.0_35"
+elif [[ "${SYSTEM}" =~ ^Darwin ]]; then
+    MAC=1 
+    MY_JAVA_HOME=/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
+else
+    # Assume the other is Linux, yes I know this is too rough.
+    LINUX=1
+    MY_JAVA_HOME=/usr/java/jdk1.6.0_35
+fi
+
+
+export JAVA_HOME=${JAVA_HOME:-${MY_JAVA_HOME}}
+
 # cleanup previous test runs
-rm -f *.xml
+rm -f TEST-*.xml
+
+if [ ${CYGWIN} -eq 1 ]; then
+    CLASSPATH=$(cygpath -w "${EXTLIB_DIR}/js.jar")
+    ROOTDIR_FULLPATH_WIN=$(cygpath -w "${ROOTDIR_FULLPATH}")
+    JAVA=$(cygpath "${JAVA_HOME}\\bin\\java")
+    BOOTSTRAP_JS=$(cygpath -w "${LIB_DIR}/envjs.bootstrap.js")
+    "${JAVA}" -cp "${CLASSPATH}" ${MAIN_CLASS} -opt -1 "${BOOTSTRAP_JS}" "${ROOTDIR_FULLPATH_WIN}" "$@"
+else
+    CLASSPATH=${EXTLIB_DIR}/js.jar
+    JAVA=${JAVA_HOME}/bin/java
+    ${JAVA} -cp "${CLASSPATH}" ${MAIN_CLASS} -opt -1 "${LIB_DIR}/envjs.bootstrap.js" "${ROOTDIR_FULLPATH}" "$@"
+fi
 
-# fire up the envjs environment
-java -cp ../ext/js.jar:../ext/jline.jar org.mozilla.javascript.tools.shell.Main -opt -1 envjs.bootstrap.js $@
  • envjs.bootstrap.js をカスタマイズ
--- a/lib/envjs.bootstrap.js	2012-03-09 14:00:12.000000000 +0900
+++ b/lib/envjs.bootstrap.js	2012-09-15 05:04:23.000000000 +0900
@@ -1,10 +1,11 @@
-load('../ext/env.rhino.1.2.js');
+var baseDir = arguments[0];
+load(baseDir + '/extlib/env.rhino.1.2.js');
 
 Envjs.scriptTypes['text/javascript'] = true;
 
 var specFile;
 
-for (i = 0; i < arguments.length; i++) {
+for (i = 1; i < arguments.length; i++) {
     specFile = arguments[i];
     
     console.log("Loading: " + specFile);
  • env.rhino.1.2.js に、 Windows 用 file:// 処理パッチを当てる(Windowsで使用する場合)
--- a/extlib/env.rhino.1.2.js	2012-03-09 14:00:12.000000000 +0900
+++ b/extlib/env.rhino.1.2.js	2012-09-15 05:04:22.000000000 +0900
@@ -1118,7 +1118,12 @@
     // if base is still empty, then we are in QA mode loading local
     // files.  Get current working directory
     if (!base) {
-        base = 'file://' +  Envjs.getcwd() + '/';
+        var prefix = 'file://';
+        var cwd = Envjs.getcwd();
+        if (cwd.match('^[a-zA-Z]:\\\\')) {
+            prefix = prefix + '/'
+        }
+        base = prefix + cwd + '/';
     }
     // handles all cases if path is abosulte or relative to base
     // 3rd arg is "false" --> remove fragments
  • envjs.runner.bat を作成 (Windows で使用する場合)
new file mode 100644
index 0000000..00ded11
--- /dev/null
+++ b/bin/envjs.runner.bat
@@ -0,0 +1,26 @@
+@echo off
+
+set MYDIR=%~dp0
+echo %MYDIR%
+set ROOT_DIR=%MYDIR%..
+set EXTLIB_DIR=%ROOT_DIR%\extlib
+set LIB_DIR=%ROOT_DIR%\lib
+set BOOTSTRAP_JS=%ROOT_DIR%\envjs.bootstrap.js
+set MAIN_CLASS=org.mozilla.javascript.tools.shell.Main
+
+REM Uncomment if you wanna to override JAVA_HOME
+REM set JAVA_HOME=
+
+REM Change the following to match your environment
+if "%JAVA_HOME%" == "" set JAVA_HOME=C:\Program Files (x86)\Java\jdk1.6.0_35
+
+del /q TEST-*.xml
+
+set CLASSPATH="%EXTLIB_DIR%\js.jar"
+set JAVA=%JAVA_HOME:"=%\bin\java
+set JAVA="%JAVA%"
+set BOOTSTRAP_JS="%LIB_DIR%\envjs.bootstrap.js"
+set ROOT_DIR="%ROOT_DIR%"
+
+echo %JAVA% -cp %CLASSPATH% %MAIN_CLASS% -opt -1 %BOOTSTRAP_JS% %ROOT_DIR% %*
+%JAVA% -cp %CLASSPATH% %MAIN_CLASS% -opt -1 %BOOTSTRAP_JS% %ROOT_DIR% %*
  • ここまでで、 bin/envjs.runner.sh junit_xml_reporter.html を実行し、カレントディレクトリに TEST-JUnitXmlReporter.xml が生成されれば成功です。
  • あとは、Jenkins を設定します
    • Freestyle Project を作成
    • リポジトリは適切に設定
    • Add build step にて、以下の内容で Execute shell ステップを追加
bin/envjs.runner.sh junit_xml_reporter.html
      • Windows の場合、 以下の内容で Execute windows batch command ステップを追加
bin\envjs.runner.bat junit_xml_reporter.html*2
    • Add post-build action で Publish JUnit test result report を追加し、「TEST-*.xml」を設定する
      • このとき、ファイルが見つからないとエラーを吐く場合(一度ビルドに失敗してる場合など)がありますが、無視します
    • Let's build, and enjoy!

自分の Github に上記状態のがおいてありますので、ご自由にお使いください。

実行結果

ビルド成功時の Jenkins の結果を張っておきます。
jasmine-reporters により、圧倒的に結果が分かりやすくなっています。

*1:ソースを hack すればいけそうでした。が、ブラウザ起動が必要なことにはかわりないので、そこまでしませんでした。

*2:htmlへのパスの区切りは / を使用してください。また、どうやら Envjs の内部挙動により、Windows においては、hoge/junit_xml_reporter1.html hoge/junit_xml_reporter2.html のように複数ファイルを指定した場合、2つめ以降が正常に Envjs に読み込まれません。その場合は、 envjs.runner.bat を起動する前に、 cd hoge して実行してください。