ssh -R の応用例

– SSH サーバ A から SSH クライアント B に逆方向に SSH 接続可能にする(双方向SSH)
この方法を使うとポートが閉じている環境にも外部からSSH接続ができるようになります。

B$ ssh -R 12345:127.0.0.1:22 A # (1) 12345 は任意のポート番号
(A にログイン)
A$ ssh 127.0.0.1:12345 # (2)
(A から B に逆にログイン)
B$ echo hello

メリット:
— (2)においてクライアント(B)のIPアドレスを知らなくてもサーバからクライアントに SSH 接続ができるようになります。
— クライアント側(B)の sshd がローカルからしか接続を受けない場合でもサーバ側(A)から接続することができます。
 
デメリット:
— クライアント側(B)にも最低限ローカルから TCP で接続できる sshd (または telnetd) が必要です。
— (2)でユーザ名がサーバ側とクライアント側で異なる場合、クライアント側のログイン可能なユーザ名を知っている必要があります。
— (2)を実行するには(1)の接続を保持し続けなければなりません。

参考:

ssh を任意のポート、プロトコルの逆 proxy にする

以前「ssh を任意のポート、プロトコルの proxy にする」という記事で ssh の -L オプションを紹介しましたが、この逆バージョンについて触れていなかったので紹介しておきます。
 
ssh -L は先の記事で紹介したとおり SSHクライアントの任意のポートへの通信をSSHサーバのネットワークの任意のホスト、任意のポートに転送できるようにするためのオプションですが、ssh -L とは逆に ssh -R を使うとSSHサーバの任意のポートへの接続をSSHクライアントのネットワークの任意のホスト:ポートに転送できるようになります。
 
使い方:

ssh -R <SSHサーバ側でLISTENするポート番号>:<SSHクライアント側ネットワークの転送先ホストIPアドレス>:<転送先ホストポート番号> <SSHサーバホスト>

 
例: クライアントA(192.168.1.2)からSSHサーバB(10.0.0.1)に接続し、SSH サーバからクライアント側ネットワークのHTTPサーバC(192.168.1.3:80) に A 経由でアクセスする

 
A$ ssh -R 7777:192.168.1.3:80 10.0.0.1
–connected to B(10.0.0.1)–
B$ netstat -an | grep LISTEN | grep 7777
tcp4 0 0 127.0.0.1.7777 *.* LISTEN
B$ telnet localhost 7777
Trying 127.0.0.1…
Connected to localhost.
Escape character is ‘^]’.
HEAD / HTTP/1.0\n
\n\n
HTTP/1.1 200 OK
Date: Fri, 14 Mar 2008 10:15:35 GMT
Server: Apache
Last-Modified: Thu, 20 Dec 2007 11:25:38 GMT
Accept-Ranges: bytes
Content-Length: 18050
Connection: close
Content-Type: text/html
 
Connection closed by foreign host.

 
なお、このオプションは -L 同様 PuTTY でも利用可能なので Windows から ssh 接続するときにも有用です。
 
参考:
ssh を任意のポート、プロトコルの proxy にする(過去記事)
ssh(1) manpage(openbsd.org)

NULL は「値がない」のではなく「値が不明」

SQL では NULL を含む演算は IS NULL, IS NOT NULL と一部の論理演算を除き常に戻り値が NULL になります。
この結果として、NULL を値に持つレコードを IS NULL 以外で抽出できなくなります。
たとえば極端な例ですが

SELECT NULL != NULL

が true にならず NULL になったりします(つまり (NULL != NULL) IS NULL が真になる)。
 
この仕様は Java や PHP などのプログラミング言語から考えると不可解なものにうつりますが、 NULL は値が存在しないのではなくまだわかっていない(unknown)だけなのだ、と考えるとスッキリします。
 
値段のカラムに null を持つ商品について考えるとき、その商品は「無料」ではなく値段がまだ「決まっていない」だけ。携帯のメールアドレスが null のユーザがいたなら、その人は携帯電話を「持っていない」という意味ではなく、その人が携帯電話を持っているか「わからない」だけです。
 
指定した部署(department_id)以外にいる社員(employee)の名前を探す下記のようなクエリを実行しても、部署が null の社員の名前は含まれません。その社員が 1 の部署に配属されるかもしれないからです。

SELECT name FROM employee WHERE department_id != 1

null 値も結果に含めたい場合は下記のようにしなければなりません。

SELECT name FROM employee WHERE department_id != 1 AND department_id IS NULL

 
0 や に包括的なからっぽ(empty)という意味を持たせたるのも伝統的でいいですが、SQL では 0, と NULL を区別して有効に使うほうがよさそうです。
 
参考:
3値論理とNULL(CodeZine)

tips – PHP をストリームエディタとして使う

php -R オプションを使うと、perl の -ne オプションと同様に入力行単位で任意のコードを実行することができます。

php -R “実行するコード”

標準入力から一行ずつ $argn に格納されます。末尾の改行文字は取り除かれるため、必要であれば改行を自分で付加します。
 
例: HTMLの特殊文字をエスケープする

cat foo.html | php -R ‘echo htmlspecialchars($argn).”\n”;’

セッションフィクセーション問題

ITpro の記事で Session Fixation 問題について書かれていたので PHP での対策例を書いておきます。
 
PHP ではこの問題を解決するために、session_regenerate_id() という関数が提供されています。

<?php
 
session_start();
 
$old = session_id();
session_regenerate_id();
$new = session_id();
 
echo “$old -> $new<br>”;
// 結果:
// d4aa3b6cfaedca70dc09262dfbf8c39e -> 53c031b85fe89e00d3b151eddfd91733

具体的な利用方法はこんな感じです。

<?php
 
session_start();
 
$algo = “sha256”;
 
// パスワードは生で保存せずハッシュで持つ(5.1.2 以前は hash() 関数を使えないため mhash() または md5() で代用する)
$password_hash = “d74ff0ee8da3b9806b18c877dbf29bbde50b5bd8e4dad7a3a725000feb82e8f1”; // hash($algo, “pass”);
$bAuthorized = !empty($_SESSION[‘authorized’]);
 
if(!$bAuthorized){
        if(empty($_GET[‘p’]) || hash($algo, $_GET[‘p’]) !== $password_hash){
                // ログインページ表示
?>
                未認証(session_id = <?=session_id()?>)<br>
                <form action=”” method=”GET”>
                        <input type=”password” name=”p”>
                        <input type=”submit” value=”ログイン”>
                </form>
 
<?php
                exit;
        }
        $bAuthorized = $_SESSION[‘authorized’] = true;
        // セッションフィクセーション対策(Session Fixation)
        session_regenerate_id(); // ログインが成功した段階でセッションID を更新垢
 
}
 
?>
認証済み(session_id = <?=session_id()?>)

このサンプルを実行すると、パスワードに “pass” という文字列を入れて認証すると session_id が書き換わるのを確認できます。
 
この関数を使うと、透過的にセッションIDが書き換わるので関数呼び出し側はセッションフィクセーション対策を簡単に施すことができます。ただし当然ですが既に何かを

コマンドプロンプトで日本語入力をするには

Alt+[半角/全角キー]を押すとコマンドプロンプトでも日本語入力ができます。
Cygwin の場合も同様ですが、こちらは「cygwin で日本語入力をする方法(Windows Tips by pPoy)」にあるように、設定ファイルに以下の設定を追加する必要があります。
~/.inputrc:

set kanji-code sjis
set convert-meta off
set meta-flag on
set output-meta on

~/.bashrc

export JLESSCHARSET=japanese-sjis
alias ls=’ls –show-control-chars’

tips – Net_URL で URL を解決する

相対 URL から絶対 URL に変換するには PEAR::Net_URL が利用できます。
URL の要素を分解して取得することができるため、便利そうです。
 
インストール:

$ pear install Net_URL

HTTP_Request が依存しているので、ネットワーク関係の PEAR パッケージを入れたことがあれば既に入っていることが多いです。
 
例:とりあえず使ってみる

<?php
// http://example.com/foo/boo/file.php で実行する
 
require_once “Net/URL.php”;
 
$url =& new Net_URL(“next.html”);
echo “<PLAINTEXT>”;
var_dump($url);
echo $url->getURL();
?>

 
例: GET クエリで渡された URL が、自サービス内 URL かどうかを調べる
※外部サイトへの踏み台になるのは防ぎますが、内部のリファラチェック回避の踏み台にはなり得る事に注意。

$url =& new Net_URL($_GET[‘url’]);
if($url->host != $_SERVER[‘SERVER_NAME’]){ // 相対パスまたは同じドメインの絶対URLなら HTTP_HOST や SERVER_NAME と一致する。
echo “error!”;exit; //
}
echo “ok”;

 
参考:
PEAR::Net_URL(pear.php.net)

FreeBSD 7.0-RELEASE

昨日 7.0 がとうとうリリースされたようです。
 
– SMP 環境でのパフォーマンス向上(ベンチマークや DB 処理について FreeBSD 6.X 系の 350% 〜 1500%, Linux 2.6.24 の 115% の性能)
– ULE スケジューラの大幅な改善(ただし7.0では 4BSD スケジューラがデフォルトで選択される。7.1 以降は ULE スケジューラがデフォルトになる予定)
– ファイルシステム Sun ZFS の試験的サポート。
– ジャーナルファイルシステムのセットアップに gjournal が使えるようになり、仮想ストレージデバイスを扱うのに gvirstor が使えるようになった。
– ファイルシステム XFS で読み込みのみ(Read-only)モードをサポート。
– ファイルシステム unionfs をフィックス。
– iSCSI initiator
– いくつかのネットワークドライバで TSO, LRO をサポート
– SCTP(Stream Control Transmission Protocol)の試験的サポート
– ワイヤレス(802.11)サポートの多量な改善。
– OpenBSD のネットワークリンクアグリゲーション(lagg(4))を取り込んだ。
– メモリアロケータ(malloc(3))として新しく高性能な jemalloc を採用。
– freebsd-update(8) でセキュリティフィックス,不具合修正だけでなくリリースバージョンのアップグレードを正式にサポートするようになった。
– X.Org 7.3, KDE 3.5.8, GNOME 2.20.2 に対応
– GCC 4.2.1 に対応
– BIND 9.4.2 に対応
 
アナウンスに従い freebsd-update-upgrade.tgz を取得後

sh freebsd-update.sh -f freebsd-update.conf -r 7.0-RELEASE upgrade

としたら

Looking up update.FreeBSD.org mirrors… 1 mirrors found.
Fetching metadata signature for 6.3-STABLE from update1.FreeBSD.org… failed.
No mirrors remaining, giving up.

と怒られてしまった。
たしかに http://update1.freebsd.org/ には STABLE がない。
同じバージョンであれば(uname -r とか sysctl kern.version では) RELEASE = STABLE だったと思うので

$ cat freebsd-upgrade.sh.patch
534a535
> sed -E ‘s,-STABLE,-RELEASE,’ |

とパッチをあててやり、マニュアルどおり

$ uname -r
6.2-STABLE
$ su
# sh freebsd-update.sh -f freebsd-update.conf -r 7.0-RELEASE upgrade
# sh freebsd-update.sh -f freebsd-update.conf install
# shutdown -r now
(リブート後 root でログイン)
# portmanager -u -f
(ports リビルド)
(※他自分でビルドしていたものがあれば個別にリビルドする)
# sh freebsd-update.sh -f freebsd-update.conf install # もういちど。
# shutdown -r now
(リブート後 一般ユーザでログイン)
$ uname -r
7.0-RELEASE

と、これでいけました。
ただし、jail 環境がある場合、freebsd-update-upgrade スクリプトは jail 環境を考慮してないのか

# ./freebsd-update.sh -f freebsd-update.conf -r 7.0-RELEASE -b /path/to/jail upgrade
freebsd-update.sh: Cannot upgrade from 7.0-RELEASE to itself

と jail 環境を更新するときも uname -r を見てカーネルが既にアップグレード済みだから更新の必要がない、といわれてしまいました。
 
このため jail 環境を更新するのに結局 /usr/src からいつもの手順を踏んで更新しました。

# cat /etc/supfile
  
 *default host=cvsup1.jp.FreeBSD.org
 *default base=/usr
 *default prefix=/usr
 *default release=cvs tag=RELENG_7
 *default delete use-rel-suffix
 *default compress
 src-all
 
# csup /etc/supfile
# cd /usr/src
# make buildworld
# JAILDIR=/path/to/jail
# make installworld DESTDIR=$JAILDIR
# mergemaster -D $JAILDIR
 
# shutdown -r now

あとはホストと同様に portmanager -u -f で ports を更新します。

$ su
# mount_null /usr/ports /path/to/jail/usr/ports
# jls
   JID IP Address Hostname Path
     1 192.168.2.11 myjail /path/to/jail
# jexec 1 bash # jail に接続
# cd /usr/ports/ports-mgmt/portmanager/
# make install
# portmanager -u -y -f
# exit # jail から切断
# /etc/rc.d/jail restart

 
追記(2008-03-21):
portmanager では更新もれがある場合、Shared Library まわりで色々エラーが出ます。
たとえば、FreeBSD 6.2 のとき ports インストールした emacs を FreeBSD 7.0 にしてから実行しようとすると libz.so.3 がないと怒られる。あるいは新しく ports からプログラムをインストールしようとしたら lib*.so.* という共有ライブラリがないためにコンパイル中に色々エラーがでてインストールできなかった。というような現象です。
 
この場合の対策は

portupgrade -rf “問題のあるport”

でリビルドしてみる。それでだめなら

pkg_deinstall -r “パッケージ名”

でいったん該当のパッケージと依存パッケージを削除して

portinstall -r “パッケージ名”

で削除したものを再び入れなおすと解決します。

CAcert.org のセキュリティ上の問題点

いまさらネタですが、最近になってようやく CAcert.org を使ってみました。
 
しかし少し触ってみた結果、CAcert.org をルート証明書に入れるように人に勧めることはとてもできないという結論に至りました。
気をつけるべき箇所を把握した上でルート証明に加えるなら問題はないのですが。。
 
大事な点は、身分証明や本人確認を
「電子メール証明なら対象メールアドレス、サーバ証明なら対象ドメインのwhois の Administrative Contact に書かれたメールアドレスへのメールを読めるかどうか?」
だけで判断しているため、少なからず第三者がサーバ証明書を作る危険性がある点(ただし既に CAcert.org で誰かに登録されたメールアドレス/ドメインは不可)と、
「誰でも無料で登録、即時証明書発行ができる」
という点。
CAcert.org では第三者に保証人になってもらいポイントを増やすことで信頼を重ねたりもできますが、実際のところはそんなものがなくても、(CAcert.org がルート証明機関に入ったブラウザであれば) https 通信で警告が出ないサーバ証明書を作ることができます。
 
CAcert.org に限りませんが、たとえば g00gle.com みたいな、ターゲットドメインに類似した名前のドメインを正規にとり、そのサーバ証明書を作ってフィッシングサイトを作る、なんてこともありえます。
 
クライアントが証明書を適切にチェックするようになればこういった問題を考える必要はないのですが、現実にはそうもいきません。
今日日数十ドルで証明書を発行してくれるルート証明機関もいますが、CAcert.org のような無料で証明書を発行できる組織が、そういうユーザが使うブラウザのルート証明機関に入るのはリスクの増大に他ならないかなという結論に至ったわけです。
 
参考:
CAcert.org は Windows のルート証明書に含まれるようになるのか?(yamk 日記)

作業中のプロセスを残したまま端末を終了する

何かをビルドしていて、時間が予想外にかかってしまい、nohup もしていないためになかなか端末を閉じることができずに予定が狂って結局残業時間がのびる、なんてことありませんか?
 
そういうときは disown を使うと実行中のジョブを端末から切り離すことができます。
(以下は bash の例です)

$ make # 何か時間のかかる作業
## 出力は略。途中で端末接続を切りたくなった(Ctrl-Z) ##
^Z
[1]+ Stopped make
 
$ bg # バックグラウンドで実行を再開
## 略。出力再開 ##
 
$ disown # 端末を切ってもプロセスが残るようにカレントジョブ( %1 )を端末から切り離す
$ exit # 終了

nohup と違い端末を切ると標準出力、エラー出力はどこにも出力されなくなりますが、これで端末を切断してもプロセスが実行されたままになります。
 
参考:
一度 tty から起動したプロセス(PBD)
一度 tty から起動したプロセス (csh/ksh 版)(にぽたん研究所)