ラベル ipsec の投稿を表示しています。 すべての投稿を表示
ラベル ipsec の投稿を表示しています。 すべての投稿を表示

2010/03/05

MTU設定時の注意点

以前、 IPsecを試していた時にIPのフラグメントが原因と思われる通信障害が発生しました。 どうやらAlixで作成したブロードバンドルータのMTU設定がまずかったようです。

現状でも参考にした資料を全部読めていないのですが、いまの理解でまとめておこうと思います。

問題となった現象について

エラーメッセージ

VMWare上でTeamを組んで2つのサブネットをlinuxをルータにして接続してみました。 MTUをいろいろ変更している最中にsyslog経由で記録されたログは次のようになっています。

linuxでのメッセージ

...
Mar  3 10:05:40 lipsec01 kernel: [ 1778.358003] pmtu discovery on SA AH/09dd8188/0a000001
Mar  3 10:07:40 lipsec01 kernel: [ 1898.356663] pmtu discovery on SA AH/09dd8188/0a000001
...

netbsdでのメッセージ

...
wm0: discarding oversize frame (len=1502)
wm0: discarding oversize frame (len=1502)
...

どちらもMTUが適切に設定されていない事を示唆していますが、この時点ではまだその点にちゃんと気がついていませんでした。

設定を確認

実際に問題が発生したAlixブロードバンドルータを中心にしたネットワークは次のようになっていました。

Alixを中心にしたHomeLANの概要

192.168.1.0/24と192.168.10.0/24のサブネットのゲートウェイになるeth1, eth2のMTU値が1454になっていました。 MTU値1454はBフレッツで接続する際にNTTから指定されている値で、PPPデバイスに対しては正しいのですが、家の中で各サブネットに接続しているPCはMTU値1500が設定されているため、サブネット単位で不整合が発生しています。

対応策

サブネット単位でちゃんとMTUを揃えて対応は完了。

通常はこれで気になる事もなかったのですが、どういうわけかIPsecでAH+ESPヘッダを付与したところで顕著に影響が出てしまったようです。 IPsecはヘッダが付与される分パケットが長くなりがちですが、どうしてこれでIPsec以外の通信では問題にならなかったんだろう。

ああ、外部に出ていく時にはPPPのMTU値1454に合わせるから問題がなくて、家の中ではそもそも大きなファイルの転送をしなかったとかかな。いや、そんなことはない。

Path MTU Discoveryについて

今回の原因はMTUの不整合でした。 それとは別にMTU関連についていろいろ調べていく中で、alixを通した時にPath MTU(PMTU) Discovery(PMTUD)がちゃんと動いているのか確認する事にしまいた。

サブネット単位でMTU値を同じ値に設定した後に、ICMPパケットをtcpdumpを使って眺めてみました。 外部へ接続する時にはppp0に設定されているMTU値1454に合わせるよう、PMTUDが動いている様子がわかります。

$ sudo tcpdump -p -i eth0 icmp -vv -n

tcpdump出力ログ

...
21:26:29.868484 IP (tos 0xc0, ttl 64, id 18704, offset 0, flags [none], proto ICMP (1), length 576) 192.168.1.1 > 192.168.1.xxx: ICMP 210.xxx.yyy.zzz unreachable - need to frag (mtu 1454), length 556
	IP (tos 0x0, ttl 64, id 19526, offset 0, flags [DF], proto TCP (6), length 1499) 192.168.1.xxx.59612 > 210.xxx.yyy.zzz.80: P 3195263263:3195264710(1447) ack 810452849 win 90 <nop,nop,timestamp 89692924 674447780>[|icmp]
...

今回の反省

サブネット単位でMTUが揃っていなかったのは痛いミスでした。

今回の調査の中でPMTUDについても勉強しなおしてAlixの設定に反映させました。 まぁAlixでルータを作った時にはPMTUの事を考えずにICMPパケットを全て落していましたからね。

現在のiptables設定

現在ではいろいろな経緯で変更を加えて state モジュールをUDPとICMPにも適用した状態になっています。 またネットワーク内部を探査されないようにecho-requestとredirectについては明示的にDROPする設定にしています。

現在のiptables設定スクリプト

...
## [inppp] default rules
## - disallow some icmp inbound messages
iptables -A inppp -p icmp --icmp-type echo-request -j logdrop
iptables -A inppp -p icmp --icmp-type redirect -j logdrop
## - allow already used connections
iptables -A inppp -m state --state ESTABLISHED,RELATED -p tcp -j ACCEPT
iptables -A inppp -m state --state ESTABLISHED,RELATED -p udp -j logaccept
iptables -A inppp -m state --state ESTABLISHED,RELATED -p icmp -j logaccept

## [outppp] default rules
iptables -A outppp -m state --state ESTABLISHED,RELATED -p tcp -j ACCEPT
iptables -A outppp -m state --state ESTABLISHED,RELATED -p udp -j logaccept
iptables -A outppp -m state --state ESTABLISHED,RELATED -p icmp -j logaccept
iptables -A outppp -m state --state NEW -p tcp -j ACCEPT
iptables -A outppp -m state --state NEW -p udp -j logaccept
iptables -A outppp -m state --state NEW -p icmp -j logaccept
...

意図せず侵入を許したパケットは通過してしまうので、outboundに"ESTABLISHED,RELATED"を設定するのは少し問題なのかもしれません。 外部からのSSH接続などinboundを追加するタイミングで個別にoutboundを設定するよう変更するかな。

icmpは到達不可をちゃんと伝えるには必要そうだけど利便性の問題でたぶん必須ではないのだと思います。

2010/03/01

NetBSD 5.0.1によるIPsec環境の構築

VMWare Workstation 6.5を使って、NetBSD 5.0.1同士でTeamを作成して、2つのVMの間でIPsec環境を構築することにしました。

これはDebian lennyで同じことをした時にMTUを片側で少し減らした場合、PMTUが発生し、端末がハングしたようにみえる現象がNetBSDと連携した時にどうなるか確認したかったからです。

とりあえずNetBSD 5.0.1同士でのIPsec環境を構築したので、その時の差分の手順をメモしておきます。

はじめに

作業手順は大きく分けて次のような流れになっています。

ipsec.confファイルはipsec-tools.confと読み替えれば、手順もファイルの内容もLinuxの時と同じものがそのまま使えます。 そのため今回はNetBSD固有の部分で、特に気になった設定周りについてだけメモをまとめていきます。

NetBSDカーネルの再ビルド

5.0.1ではIPSECが標準で有効になっていないため、これを有効にしたカーネルを作成します。 手順自体は通常のようにGENERICファイルをコピーして編集しますが、その際に次のオプションを有効にしてください。

名前を変更したGENERICファイル編集個所

...
options IPSEC
options IPSEC_ESP
...
IPsec用のNICの追加

VMWare Workstationの機能でネットワークセグメントを一つ追加しています。 今回はwm1に対して"10.0.0."1と"10.0.0.2"のIPアドレスを割り当て、その2点間でIPsecが有効になるように設定を行ないました。

システムの設定

NetBSD固有の設定は/etc/rc.confに集中しています。

/etc/rc.confファイルの追加内容

sshd=YES
ipsec=YES
racoon=YES

念のためにipsec.confとraccon.confファイルの内容もコピーしておきます。

server2側の/etc/ipsec.confファイル

#!/usr/sbin/setkey -f
#
flush;
spdflush;

## out rule (local -> remote)
## IPv4
spdadd 10.0.0.2 10.0.0.1 any -P out ipsec
           esp/transport//require
           ah/transport//require;

## in rule (remote -> local)
## IPv4
spdadd 10.0.0.1 10.0.0.2 any -P in ipsec
           esp/transport//require
           ah/transport//require;

ipsec.confファイルはもう片側では、IPアドレスを入れ替えています。 実際には"out", "in"を入れ替えた方が楽でしょう。

server2側の/etc/racoon/racoon.confファイル

path certificate "/etc/racoon/certs";

remote 10.0.0.1 {
        exchange_mode main;
        my_identifier asn1dn;
        peers_identifier asn1dn;

        certificate_type x509 "newcert.pem" "newkey.pem";
        peers_certfile x509 "server1.newcert.pem";
        verify_cert on;
        proposal {
                encryption_algorithm aes;
                hash_algorithm sha1;
                authentication_method rsasig;
                dh_group modp1024;
        }
        generate_policy on;
        passive off;
}

sainfo anonymous {
        pfs_group modp768;
        encryption_algorithm rijndael, 3des;
        authentication_algorithm hmac_sha1, hmac_md5;
        compression_algorithm deflate;
}

racoon.confファイルは、もう片側では10.0.0.1を10.0.0.2に、server1.netcert.pemはserver2.newcert.pemに変更しています。

さいごに

システムを再起動すると2つのIPアドレスの間では無事にIPsecによる通信が始まりました。 最初はracoonを有効にせずに再起動し、後からracoonを起動したせいか、linuxの時と同じようにphase2でtimeoutが発生しました。

当初発生したエラー抜粋

Mar  1 13:21:00 server2 racoon: ERROR: phase1 negotiation failed due to time up. e60ee11ca211d3b2:0000000000000000 
Mar  1 13:21:03 server2 racoon: ERROR: phase2 negotiation failed due to time up waiting for phase1. ESP 10.0.0.1[500]->10.0.0.2[500] 

やはり同じように両方で"racoonの停止"→"setkeyのクリア"を行なった後に、"racoonの起動"、"setkey -f ipsec.confによる設定の反映"を行なうと無事に通信が始まりました。

やはりMTUをいじってもちゃんと動きますね。まぁ当然か。 Linuxの新しいkernelでは直ってるのかなぁ…。

2010/02/28

racoonによるIPsec環境の構築

freeradiusを触った時に経路の暗号化をしないと潜在的なリスクがあると思ったので、IPsecをtransport modeで試してみる事にしました。

今回の鍵作成の作業はApache2用にSSLキーを作成した時と同じで、Ubuntu 8.04 LTSで行なっています。作成する鍵ファイルは次の3種類です。

  • newreq.pem - リクエストファイル。秘密鍵ファイルを作成するために使用
  • newkey.pem - 秘密鍵ファイル。newreq.pemを元にCAが署名し、生成される
  • newcert.pem - 公開鍵ファイル。newkey.pemと対になる

opensslやCA.plを使った鍵作成の手順を説明したサイトはいくつかありますが、独自のファイル名を使うため鍵ファイルの用途がわかりずらいと感じました。 この記事ではCA.plが作成するファイル名を踏襲しました。

今回の作業全体の流れは7.3.3.2 公開鍵を使った方法にある流れに従っています。 ただ、これだけでは不十分だったので、より正確な情報をIPsec HowtoThe PKIX certificate with racoon、それにipsec-tools.sourceforge.netからのリンク先にあるAutomatic keyingから補完しました。

なおIPsecが稼働するサーバはAlixとWRAPで稼働するDebian lennyです。

鍵ファイルの作成

CAとしてはApacheサーバ用にSSL用鍵ファイルを作成した時に使ったdemoCAをずっと使っていますが、手を広げ過ぎたので鍵ファイルの管理が煩雑になってきました。

そこで今回はIPsec用に中間CAを作成するところから始めました。 必要がなければ飛して既にあるdemoCAディレクトリを使えば良いと思います。

中間CA用demoCAの作成

まずは/usr/lib/ssl/misc/CA.plを使って、土台となるdemoCAディレクトリを準備します。 いま親CAのdemoCAディレクトリがみえるディレクトリにいるとして、中間CA用にサブディレクトリを作成する事にします。

$ mkdir demoCA.ipseq_ca
$ cd demoCA.ipseq_ca
$ /usr/lib/ssl/misc/CA.pl -newca

入力項目は任意なのでパスワードだけ忘れないようにしておきます。 この作業が終るとカレントディレクトリにdemoCAディレクトリが作成されています。

親CAによる中間CA用reqファイルの署名

いまはdemoCA.ipseq_caディレクトリ直下にいて、そこには"demoCA"ディレクトリだけが存在している状態を仮定します。

このディレクトリの中で単独のCAとして必要なものは揃っていますが、中間CAとなるために親CAに署名をしてもらう必要があります。 通常CAを作成するときに使うCA.pl -newcaコマンドでは、リクエストファイルを作成して、リクエストファイルへの署名をdemoCA内にある自身の秘密鍵で行なっています。

今回はその署名作業を親CAに行なってもらう必要があるので、いちど作成したcacert.pemを捨てて、改めてcareq.pemファイルを親CAに署名してもらいます。

$ cp demoCA/careq.pem ../newreq.pem
$ cd ..
$ /usr/lib/ssl/misc/CA.pl -signCA

ここまでの作業で"newreq.pem"ファイルが新たに作成されました。 これを中間CAのdemoCAディレクトリにコピーします。

$ cd demoCA.ipseq_ca
$ rm demoCA/cacert.pem 
$ mv ../newcert.pem demoCA/cacert.pem

ここでcacert.pemを削除せずに上書きしても良いのですが、作業内容をはっきりとさせるために削除するようにしています。

作業が終ったらnewreq.pemファイルは削除しておきます。

$ rm ../newreq.pem

これで中間CAを作成する作業は完了です。

IPsec用の鍵ファイルの作成

中間CAはできたので、IPsecで2点間の通信を暗号化するための鍵ファイルを作成します。 ここでは最終的に"newkey.pem", "newcert.pem"の2つのファイルを1セットとして作成します。 サーバ2台でテストをするために、合計で4ファイル、2セットを作成する事になります。

$ /usr/lib/ssl/misc/CA.pl -newreq
$ /usr/lib/ssl/misc/CA.pl -sign
$ mkdir server1
$ mv new* server1/

作業ではCommon Name(CN)をサーバのFQDNにしておきます。 作業の結果作成された"newkey.pem", "newcert.pem"ファイルをサーバ毎のディレクトリに分けておき、必要に応じて使う事にします。

続けて"server1"を"server2"と読み替えて、もう一度、この手順を繰り返します。

最後にserver1ディレクトリにserver2のnewcert.pemファイルをコピー(server2.newcert.pem)し、server2ディレクトリにも同様にserver1のnewcert.pemファイルをコピー(server1.newcert.pem)します。

ここまででserver1, server2ディレクトリにそれぞれ"newkey.pem"と"newcert.pem"ファイルと相手のnewcert.pemファイルが存在しているはずです。 treeコマンドを使うと、次のようなディレクトリ構造になっているはずです。

treeコマンド出力

|-- demoCA ## 親CA
|   |-- cacert.pem
.   .
.   .
.   `-- careq.pem
.   
`-- demoCA.ipsec_ca ## 中間CA
    |-- demoCA
    .
    .
    |-- server1/
    |   |-- newcert.pem
    |   |-- server2.newcert.pem
    |   `-- newkey.pem
    `-- server2/
       |-- newcert.pem
       |-- server1.newcert.pem
       `-- newkey.pem

実際のところはnewcert.pemとnewkey.pemが2組作れていれば良いので、この構造は参考に留めておいてください。 いろいろな鍵ファイルを作る手順をみていると、どこまでがMUSTで、どこからがSHOULDなのか判らないですよね…。

公開鍵方式を使ったIPsec接続

今回はracoonを使った鍵交換を行なうため、双方のシステムにracoonをインストールしておきます。

$ sudo apt-get install racoon
鍵ファイルの配置

サーバにserver1, server2ディレクトリをそれぞれコピーし、作成した鍵ファイルを/etc/racoon/certsディレクトリにコピーしておきます。 なお本当の環境では、サーバの秘密鍵"newkey.pem"はネットワークを経由する事はありません。相手側に渡さないように注意しましょう。

$ sudo mkdir /etc/racoon/certs
$ sudo cd server1
$ sudo cp newkey.pem newcert.pem server2.newcert.pem /etc/racoon/certs/

他方のserver2サーバにログインして、同様にファイルを配置します。

racoon.confファイルの作成

ファイルの設定方法は本家サイトのドキュメントが信頼性が高そうです。 この他のドキュメントはKAMEの流れを組む*BSD用だったり、少し古いとそのまま使えないものもありました。

exchange_mode main;を使う場合には、IPアドレスでの指定だけが使えるため、IPv6とIPv4の設定を分けていますが、内容は同じです。

server2側の/etc/racoon/racoon.confファイル

path certificate "/etc/racoon/certs";
remote 192.168.1.xx {
        exchange_mode main;
        my_identifier asn1dn;
        peers_identifier asn1dn;

        certificate_type x509 "newcert.pem" "newkey.pem";
	peers_certfile x509 "server1.newcert.pem";
	verify_cert on;
        proposal {
                encryption_algorithm aes;
                hash_algorithm sha1;
                authentication_method rsasig;
                dh_group modp1024;
        }
        generate_policy on;
	passive off;
}
remote 2001:03xx:xxxx:1::xxxx {
        exchange_mode main;
        my_identifier asn1dn;
        peers_identifier asn1dn;

        certificate_type x509 "newcert.pem" "newkey.pem";
        peers_certfile x509 "server1.newcert.pem";
	verify_cert on;
        proposal {
                encryption_algorithm aes;
                hash_algorithm sha1;
                authentication_method rsasig;
                dh_group modp1024;
        }
        generate_policy on;
        passive off;
}

sainfo anonymous {
        pfs_group modp768;
        encryption_algorithm rijndael, 3des;
        authentication_algorithm hmac_sha1, hmac_md5;
        compression_algorithm deflate;
}
CRLファイルの取り扱いについて

いまのところは、CRLやcacert.pemファイルの取り扱いについて、ちゃんとしたドキュメントをみつけられていません。 racoon.confのオプションには関連しそうなca_typeパラメータがありますが、使い方について明確な指示はみつけられませんでした。verify_certは相手方から送られてくるDNと手元のcertファイルのDNを比較するもののようで関係なさそうでした。

コードを少し追った限りではCRLのチェックはしているようにみえました。 しかしrevokeしたnewcert.pemファイルはpeers_certfileに指定した状態で、CRLファイルを配置してもhashファイルへのリンクを作成しても通信が拒否される事ありませんでした。

ハッシュファイルへのシンボリックリンクはc_rehashコマンドで行なうことができます。

$ sudo c_rehash /etc/racoon/certs

ここら辺の動きについてはもう少し確認が必要そうです。

/etc/racconディレクトリの内容

treeコマンドを使うと、現在の/etc/racoonディレクトリの構造は次のようになっています。

tree /etc/racoonの実行結果

/etc/racoon/
|-- certs
|   |-- server2.newcert.pem
|   |-- newcert.pem
|   `-- newkey.pem
`-- racoon.conf
ipsec-tools.confファイルの編集

少し古いドキュメントには/etc/setkey.confを編集する手順が載っていますが、Debian Lennyでは/etc/ipsec-tools.confファイルを編集します。 起動時に/etc/init.d/setkeyスクリプトが、/etc/rc2.d/S??setkey経由で実行されて、ipsec-tools.confの内容が反映されることになります。

/etc/ipsec-tools.confファイル

#!/usr/sbin/setkey -f
#
flush;
spdflush;

## out rule (local -> remote)
## IPv4
spdadd 192.168.10.xx 192.168.1.yy any -P out ipsec
           esp/transport//require;
## IPv6
spdadd 2001:03xx:xxxx:10::xxxx 2001:03xx:xxxx:1::yyyy any -P out ipsec
           esp/transport//require;

## in rule (remote -> local)
## IPv4
spdadd 192.168.1.yy 192.168.10.xx any -P in ipsec
           esp/transport//require;
## IPv6
spdadd 2001:03xx:xxxx:1::yyyy 2001:03xx:xxxx:10::xxxx any -P in ipsec
           esp/transport//require;
稼働確認

両方のサーバで次のようにプロセスを再起動する事で設定が完了します。

$ /etc/init.d/setkey restart
$ /etc/init.d/racoon restart

さいごに

IPsecを使ってみて気になったことをまとめておきます。

煩雑な設定

相手方を指定するところでは個別にpeers_certfileなどを指定する必要があるため、サイト全体に拡張しようとするとかなり煩雑になりそうです。 ファイルを互いに交換する仕組みだと鍵交換がn*(n-1)回必要になるので公開鍵方式の利点を殺したような状態になっています。

まぁネットワーク層レベルの仕組みですから、何を信頼するかという議論もあると思うんですけどね。

とはいえracoon.confファイルはincludeで、外部ファイルを読み込む事ができるので、他で検証した設定ファイルとpemファイル一式を展開してあげれば、だいたい安全に自動化ができそうな気配です。

しかしpeers_certfileはdnssecを引数に取るので、これが使えるかもしれないと思っています。 時間をみつけて検証していこうと思います。

おかしな挙動

試したdebian lenny同士の接続では、ipsec-tools.confでAHを有効(ah/transport//require;付き)にした状態で、しばらく使っていると反応がなくなりハングアップしてしまいました。

うまく動かなかったipsec-tools.conf設定

...
## IPv6
spdadd 2001:03xx:xxxx:10::xxxx 2001:03xx:xxxx:1::yyyy any -P out ipsec
    esp/transport//require
    ah/transport//require;
...

これはpmtu関連のメッセージが大量にでていて、どうやら途中の経路にあるルータのMTUが1500よりも低いことが原因のようでした。 このルータをはさまないようにスイッチングハブのみを経由して互いを接続したところ、この問題は発生しませんでした。

ただIPv4は無事に接続できたのですが、IPv6だけはエラーになってしまいました。

racoonのエラーメッセージ

...
Mar  1 08:59:26 localhost racoon: ERROR: phase1 negotiation failed due to time up. ac92e25b5eb92084:0000000000000000
Mar  1 08:59:37 localhost racoon: ERROR: phase2 negotiation failed due to time up waiting for phase1. ESP 2001:3e0:a31:10:20d:b9ff:fe12:f284[500]->2001:3e0:a31:10:20d:b9ff:fe03:4154[500]
...

これはracoonが正常に動いていないようにみえるのですが、ともかく設定を再び行ないます。

$ sudo /etc/init.d/racoon stop
$ sudo /usr/sbin/setkey -F
$ sudo /usr/sbin/setkey -FP

2,3分ほどしたら再び起動します。

$ sudo /etc/init.d/racoon start

両方のサーバでracoonが起動した事を確認します。 次にsetkeyでSPDを更新します。

$ sudo /etc/init.d/setkey restart

これで無事にIPv4, IPv6ともにtransport modeでIPsecを動かす事ができました。 たぶん/etc/ipsec-tools.confでAHを有効したり、無効にしたり編集している間におかしくなったんだと思います。

それにしても元々の問題はpmtuが正常に動けば問題ないはずなのですが、この直結に近い状態でもmtuの高い側から低い側へ接続すると比較的データ量の多そうな通信の応答がなくなってしまいます。 これはVMWare上でも同様の動きをしています。 tcpdumpでみるとARPやらICMPv6 Neighbor Discoveryには正常に反応しているので、接続は切れずにこのまま画面が固まり続けるんでしょうね。

試しにこの状態で$ sudo setkey -F$ sudo setkey -FPを実行すると、反応は戻ってきました。 /etc/init.d/setkeyをリスタートしてみると、やはりまた元に戻ってしまいます…。 う〜ん、困ったなぁ…。

racoonプロセスの稼働状況

sshで相手方に接続している最中にどちらかのracoonプロセスを再起動すると、次のログのようにSADが再構成されるため、その間は通信ができなくなります。 切断はしませんでしたが、入力した文字はバッファに入ったまま応答がなくなってしまうので、使い方によってはクリティカルかもしれません。

/var/log/daemon.logから抜粋

...
Feb 28 13:38:16 localhost racoon: INFO: respond new phase 2 negotiation: 192.168.10.yy[500]<=>192.168.1.xx[500]
Feb 28 13:38:16 localhost racoon: INFO: Update the generated policy : 192.168.1.xx/32[500] 192.168.10.yy/32[500] proto=any dir=in
Feb 28 13:38:16 localhost racoon: INFO: IPsec-SA established: ESP/Transport 192.168.1.xx[0]->192.168.10.yy[0] spi=250681865(0xef11a09)
Feb 28 13:38:16 localhost racoon: INFO: IPsec-SA established: ESP/Transport 192.168.10.yy[500]->192.168.1.xx[500] spi=221824184(0xd38c4b8)
Feb 28 13:38:16 localhost racoon: ERROR: such policy does not already exist: "192.168.1.xx/32[500] 192.168.10.yy/32[500] proto=any dir=in"
Feb 28 13:38:16 localhost racoon: ERROR: such policy does not already exist: "192.168.10.yy/32[500] 192.168.1.xx/32[500] proto=any dir=out"
...