pf でルーティング

FreeBSD で pf を使ってルータを作るメモ。
 
目的:
ネットワークインターフェイスが二つあり、それぞれのインターフェイスに外部ネットワーク、内部ネットワークとして割り当てるとする。
内部→外部の通信は IP マスカレード(NAPT)で透過的に通信できるようにし、外部→内部の通信は特定のポートのみ NAT でサーバにマッピングする。
 
適用例:
A. FreeBSD をルータとして動作させる。
B. (応用) jail を仮想プライベートネットワークに入れ、外への通信を制御する。
 


設定方法(A):
…外部ネットワークインターフェイスを fxp0, 内部ネットワークインターフェイスを fxp1 としてルータとして設定する場合
/etc/rc.conf:

defaultrouter=”192.168.1.1″ # 外部のデフォルトゲートウェイ
 
# 外部インターフェイス
ifconfig_fxp0=”inet 192.168.1.2 192.168.1.255 netmask 255.255.255.0″
# 内部インターフェイス
ifconfig_fxp1=”inet 10.0.0.1 10.0.0.255 netmask 255.255.255.0″
 
# pf を有効化
pf_enable=YES
pf_rules=”/etc/pf.conf”
pf_flags=””
pflog_enable=”YES”
pflog_logfile=”/var/log/pflog”
pflog_flags=””

/etc/pf.conf:

# 外部ネットワークインターフェイス
ext_if=”fxp0″
# 内部ネットワークインターフェイス
int_if=”fxp1″
####
 
table <private> const { 10.0.0/24 }
 
# IP マスカレード
nat on $ext_if inet from ($int_if) to ! <private> -> ($ext_if)
 
# NAT 設定。外からの http(80), https(443) は内部の 10.0.0.5 に、smtp(25) は内部の 10.0.0.6 に割り当て
rdr pass on $ext_if proto tcp from any to ($ext_if) port {80, 443} -> 10.0.0.5
rdr pass on $ext_if proto tcp from any to ($ext_if) port 25 -> 10.0.0.6
 
# パケットフィルタ: 説明を簡単にするため全許可に。
pass in quick all
pass out quick all

 


設定方法(B):
…外部ネットワークインターフェイスを fxp0, 内部ネットワークインターフェイスを lo1 (ループバック) として jail を内部ネットワークインターフェイスで管理する場合。
/etc/rc.conf:

defaultrouter=”192.168.1.1″ # 外部のデフォルトゲートウェイ
 
# 外部インターフェイス
ifconfig_fxp0=”inet 192.168.1.2 192.168.1.255 netmask 255.255.255.0″
# 内部インターフェイス
ifconfig_lo1=”inet 10.0.0.1 10.0.0.255 netmask 255.255.255.0″
ifconfig_lo1_alias=”inet 10.0.0.2″
ifconfig_lo1_alias=”inet 10.0.0.3″
ifconfig_lo1_alias=”inet 10.0.0.4″
ifconfig_lo1_alias=”inet 10.0.0.5″
ifconfig_lo1_alias=”inet 10.0.0.6″
# : 以下略
 
# pf を有効化
pf_enable=YES
pf_rules=”/etc/pf.conf”
pf_flags=””
pflog_enable=”YES”
pflog_logfile=”/var/log/pflog”
pflog_flags=””
 
####
### Jail の設定 ###
jail_enable=”YES”
jail_list=”j1 j2 j3″ # 立ち上げる jail の名前のリスト
jail_set_hostname_allow=”NO”
jail_sysvipc_allow=”YES”
## jail_list で設定した jail インスタンスそれぞれについて設定 ##
# j1:
jail_j1_rootdir=”/var/jail/j1″
jail_j1_hostname=”jail_1″
jail_j1_ip=”10.0.0.2″
jail_j1_exec_start=”/bin/sh /etc/rc”
jail_j1_exec_stop=”/bin/sh /bin/rc.shutdown”
jail_j1_devfs_enable=”YES”
jail_j1_fdescfs_enable=”NO”
jail_j1_procfs_enable=”YES”
jail_j1_mount_enable=”NO”
# j2, j3 も同様に。

/etc/pf.conf:

# 外部ネットワークインターフェイス
ext_if=”fxp0″
# 内部ネットワークインターフェイス
int_if=”lo1″
####
# 以下 設定方法 A とまったく同じ
table <private> const { 10.0.0/24 }
 
# IP マスカレード
nat on $ext_if inet from ($int_if) to ! <private> -> ($ext_if)
 
# NAT 設定。外からの http(80), https(443) は内部の 10.0.0.5 に、smtp(25) は内部の 10.0.0.6 に割り当て
rdr pass on $ext_if proto tcp from any to ($ext_if) port {80, 443} -> 10.0.0.5
rdr pass on $ext_if proto tcp from any to ($ext_if) port 25 -> 10.0.0.6
 
# パケットフィルタ: 説明を簡単にするため全許可に。
pass in quick all
pass out quick all

 


参考:
FreeBSDでPacketFilter(pf)を使う

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)

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

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 日記)

tips – キャッシュあれこれ

– キャッシュの中身を見るには?
Firefox の場合, IE の Temporary Internet Files のようにキャッシュディレクトリに直接ファイルが置かれないためエクスプローラからはキャッシュの中身を見ることができません。

about:cache

を URL に指定するとメモリキャッシュ、ディスクキャッシュ両方の中身を見ることができます。
 
– キャッシュフォルダの場所を変更するには?

about:config

を URL に指定して設定画面を開き、「右クリック→新規作成→文字列」を選択して

設定名: browser.cache.disk.parent_directory
文字列: c:\path\to\cache

のように設定すると次回起動時からこの指定先がキャッシュフォルダとして利用されるようになります。
 
参考:
– about:mozilla(Mozilla Page by Yotti)
about: からはじまる擬似 URL の一覧。

ruby でランダムな文字列を生成

“ランダムなパスワードを生成する” の ruby バージョン。
[2005-04-25] (JavaScript 版)
[2005-05-31] (PHP 版)

# ランダムな文字列を生成する。
# 引数 _length_ を指定すると生成桁数を指定することができます(デフォルト 8 桁)。
def getRandomString (length = 8)
  source=(“a”..”z”).to_a + (“A”..”Z”).to_a + (0..9).to_a + [“_”,”-“,”.”]
  key=””
  length.times{ key+= source[rand(source.size)].to_s }
  return key
end
 
# 10 桁の文字列を取得
p getRandomString 10