DHCPv6 登場
IPv6 アドレスの自動割り当ては、
- RA による stateless
- DHCPv6 による statesful
の2つがあるのは、IPv6 の解説には必ず登場する有名な話です。で、DHCPv6 を使って stateful な IPv6 アドレスの割り当てをする場合、RA の M フラグと O フラグを設定する事も、様々な解説で登場します。ちなみに M フラグ、O フラグのそれぞれの意味は、
- M フラグ
- Management の M で、アドレスを管理(Management)する事を意味する。
- O フラグ
- Other の O で、アドレス以外の情報(DNS サーバのアドレス等)を提供する事を意味する。
で、実際にアドレスを管理したり、アドレス以外の情報を提供するのが DHCPv6 サーバ、という事になります。RA を受信したホストは、受信した RA の情報で M フラグが on になっていたら、DHCPv6 サーバからアドレスの割り当てを受ける、O フラグが on だったら、DHCPv6 から DNS サーバのアドレス等を取得する、という挙動をすることになります*1。
で、このときにふと気になったのは、前回の段階では、ルータの RA にグローバルなアドレスのプレフィックスと ULA のプレフィックスを広告する様に設定していますが、問題になったのは、
- グローバルなアドレスは一時アドレスを使う。
- ULA は一時アドレスを使わない。
と、2つのプレフィックスで違うポリシーでアドレスを割り当てたい、ということでした。で、グローバルなアドレスは今まで通りでよく、ULA のプレフィックスだけ、DHCPv6 でコントロールできないか、という発想だったのですが、ルータのマニュアルをどう読んでも、先の M フラグ、O フラグをプレフィックス毎に設定できません。
実際に、RA のパケットをキャプチャしてみると、一つの RA の中に2つのプレフィックス情報があり、M フラグ、O フラグは、そのプレフィックス情報の外側にあるので、RA の仕様として、プレフィックス毎にこれらのフラグを使い分ける事ができないことが分かりました。
で、しょうがないので、
- M フラグ、O フラグを on にする。
- だけど、広告する2つのプレフィックスのうち、DHCPv6 サーバが提供しているのは片方(当然、ULA のプレフィックス)。
- 結果、ULA の方は DHCPv6 から割り当てられるけど、グローバルな方は、仕方なく、自動構成アドレスになる。
という挙動を期待するしかありません。
実際に設定してみる
実際に、M フラグ、O フラグを on にした NVR500 の設定は、こんな感じになります*2。
ipv6 prefix 1 dhcp-prefix@lan2::/64 ipv6 prefix 2 fdfe:dcba:9876:5432::/64 ipv6 lan1 address dhcp-prefix@lan2::1/64 ipv6 lan1 address fdfe:dcba:9876:5432::1/64 ipv6 lan1 rtadv send 1 2 m_flag=on o_flag=on
前回との違いは「m_flag=on o_flag=on」が付いている部分です。
DHCPv6 サーバの方は、おうちサーバの CentOS で動かしました。/etc/dhcp/dhcpd6.conf の主な内容は下記の通りです。
# T2, the delay before Rebind (if Renews failed) # (default is 3/4 preferred lifetime) # (set to 2 hours) option dhcp-rebinding-time 7200; # Enable RFC 5007 support (same than for DHCPv4) allow leasequery; # Global definitions for name server address(es) and domain search list option dhcp6.name-servers fdfe:dcba:9876:5432::35; option dhcp6.domain-search "july.example.com"; # Set preference to 255 (maximum) in order to avoid waiting for # additional servers when there is only one ##option dhcp6.preference 255; # Server side command to enable rapid-commit (2 packet exchange) ##option dhcp6.rapid-commit; # The delay before information-request refresh # (minimum is 10 minutes, maximum one day, default is to not refresh) # (set to 6 hours) option dhcp6.info-refresh-time 21600; # The path of the lease file dhcpv6-lease-file-name "/var/lib/dhcpd/dhcpd6.leases"; # The subnet where the server is attached # (i.e., the server has an address in this subnet) subnet6 fdfe:dcba:9876:5432::/64 { # Two addresses available to clients # (the third client should get NoAddrsAvail) range6 fdfe:dcba:9876:5432::1:1 fdfe:dcba:9876:5432::1:ffff; }
CentOS Ver 6.5 に含まれる dhcp パッケージでは、/usr/share/doc/dhcp-4.1.1/dhcpd6.conf.sample というサンプルファイル*3があるので、これを参考にして /etc/dhcp/dhcpd6.conf を作成しました。
ポイントになるのは、まず、
- option dhcp6.name-servers
- option dhcp6.domain-search
で、これで DHCPv6 で渡す DNS サーバとドメイン名を定義しています。RA で O フラグを on にした時に、クライアントに渡される情報です。
あとは、
- subnet6 fdfe:dcba:9876:5432::/64 { 〜
の部分で、ここで、DHCPv6 で割り当てるアドレスの範囲を指定します。RA で M フラグ on にしていると、subnet6 で指定されているプレフィックスと RA が広告するプレフィックスで一致する物があれば、DHCPv6 からアドレスが割り当てられることになるはずです。
結果は、あと一歩
この状態で PC を起動すると、
- グローバルなアドレスは、RA による自動構成アドレス。
- ULA のアドレスは、DHCPv6 サーバが割り当てるアドレス。
となる事が確認できました。ところが、予定外の事が一つありました。
グローバルなアドレスに関しては狙ったとおり、RA だけの時と何ら変わりありません。ところが、ULA は、
- DHCPv6 から割り当てられたアドレス
- ULA のプレフィックスを持つ一時アドレス
の2つのアドレスが割り当てられました。前回見つかった問題は、
- 一時アドレスは、ダイナミック DNS の対象外
- ソースアドレスとして一時アドレスが使われると、DNS 上には存在しないホストからの接続になる。
- Windows で Linux の Kerberos 認証を使っていると、host プリンシパルの問題があるので、これでは都合が悪い。
という問題です。DHCPv6 を使っても、ULA の一時アドレスが使われるのであれば、問題は解決しません。
調べると、Windows で「そもそも、IPv6 の一時アドレスを使わない」という方法を推奨している記事を見かけます。
上記の Technet フォーラムでの話は Windows 8 での話ですが、Windows 7 だと netsh コマンドで設定できるようです。
http://www.ipnet-lab.jp/post/2012/04/12/ipv6-ra-privacyaddress-disable.aspx
しかし、これだとグローバルなアドレスでも一時アドレスが付かなくなってしまいます。
先の Technet フォーラムでの回答には
Windows Vista / Windows Server 2008 以降の OS は、ステートレス RA 環境に置かれると、IPv6 アドレスを自動構成がデフォルト ON になっていますので、手動で IPv6 アドレスを設定しても、RA で自動構成された IPv6 アドレスも付いてしまいます。
とあります。DHCPv6 でアドレスが払い出されるプレフィックスは stateful ではないのか? と思うのですが、実際には一時アドレスが付いてしまいます。
う〜ん、どうしたものか... ということで次回に続きます。
*1:O フラグだけ on であれば、アドレス自体は RA に含まれるプレフィックスから自動構成で、DNS サーバのアドレス等は DHCPv6 サーバから受け取る、ということになります。その逆(アドレスは DHCPv6 だけど、DNS サーバ等は手動設定するとか、IPv4 まかせ)にするケースは、ほとんど無いかな。
*2:前回から引き続き、ULA のプレフィックスを fdfe:dcba:9876:5432::/64 としています
*3:このサンプルファイル。おそらく、ISC のオリジナルパッケージに含まれている物をそのまま収録したものだと思いますが、RHEL / CentOS で SELinux を有効にしている場合、dhcpv6-lease-file-name の指定をサンプルから変更しないと、リース状態を出力するファイルの書き込みに失敗するはずです。/var/lib/dhcpd の下に設定すれば、SELinux 有効で問題ありません。
IPv6 でアドレスの手動設定はイヤ
IPv6 では、ルータが送信する RA(Router Advirtisement:ルータ広告)の情報に含まれるプレフィックスから、自動的にアドレスが決定される、という話は有名です。実際、個人向け IPv6 で、ご家庭用ブローバンドルータを使った時に、端末側に振られる IPv6 アドレスは、まさにこの仕組みによるものです。
前回、おうちサーバには、IPv4 のプライベートアドレスに該当する ULA を手動で、グローバルなアドレスに関しては RA を受信した上で自動構成する、としました。
同じ事は、クライアント側にも当てはまります。
グローバルなアドレスは、ルータが広告する RA によって自動構成アドレスが付与されます。サーバと同様に ULA を手動で設定する、という事も可能ですが、はっきり言って、128 bit のアドレスを手入力するのは拷問です。まぁ、一度設定すれば変える事はないかもしれませんが、例えばノート PC のように、自宅外のネットワークにつなぐ事を考えた時、自動でアドレスが割り当てられるようにしていた方がベターです*1。
IPv4 で自動的にアドレスを割り当てるのは DHCP の役目ですが、IPv6 では基本的に RA の役目です。ところが、RA が IPv4 での DHCP の役割を置き換える事ができるかというと、
- あくまでも、RA が通知しているのは「ネットワークアドレスはこれですよ」と「デフォルトゲートウェイはこれですよ」だけ。
- 故に DNS サーバやドメイン名は範囲外。
- 特定のホストに、毎度、同じアドレスを付与するようなまねはできない。
といった具合に、完全に力不足です。DNS に関して言えば、IPv4 の DHCP で付与すれば、それでも OK ですが、IPv6 の話をしている時に、DNS は IPv4 で、というのもちょっとどうかなぁ、と。IPv4 が全く使われない、という時代は遥か先*2なので、実用上はこれでも OK なのですが、そんなところで妥協するぐらいなら、ULA を付けてまで、おうちネットワークを IPv6 化する意味がない!
ということで、DHCPv6 の登場です。
DHCPv6 は単独では生きられない
正直、IPv4 の DHCP のように、DHCP サーバを正しく設定しておけば OK、という方が楽です。が、なぜか、DHCPv6 は、単独では意味を成しません。DHCPv6 はあくまで RA を補佐する役割であって、RA がすでに広告されている事が大前提です。
という事は、ULA で IPv4 での DHCP のような環境を作ろうとしたら、まずは、ULA のプレフィックスを RA で広告する必要があります。
実は、IPv6 のインターネット接続環境がやってくる前に、おうちサーバで RA と DHCPv6 をやらせていました。もっとも、おうちネットワークの外に IPv6 の経路が無いのに広告している、というイリーガルな状態でしたが、DHCPv6 でのアドレス配信はできていました。
IPv6 のインターネット接続環境がやってきた、ということは、RA を送出するのはルータの役目でなので、まずは、ルータに ULA のアドレスを認識させて、RA で広告するプレフィックスに ULA のプレフィックスを追加する、という必要があります。
RA に ULA プレフィックスを追加
具体的な手順は、ルータによって様々だと思います。実際は分かりませんが、LAN 側にルータの IPv6 アドレスを手動で追加すると、そのプレフィックスを自動的に広告してくれるかもしれません。
我が家のルータは Yamaha の NVR500 ですが、Web インタフェースからの設定方法が分からず、結局、テキストベースで config を投入することになりました。
ipv6 prefix 1 dhcp-prefix@lan2::/64 ipv6 prefix 2 fdfe:dcba:9876:5432::/64 ipv6 lan1 address dhcp-prefix@lan2::1/64 ipv6 lan1 address fdfe:dcba:9876:5432::1/64 ipv6 lan1 rtadv send 1 2
1行目、3行目は、lan2、すなわち、WAN 側から与えられたアドレスに関するもので、フレッツ光ネクストでひかり電話を使いつつ、IPoE で IPv6 を使えるように設定すれば入っている設定です*3。
2行目で ULA のプレフィックスを定義し、4行目でルータ自身の LAN 側の IPv6 アドレスに、ULA のアドレスを手動設定(ULA のプレフィックスで、末尾が 1 のアドレス)しています。
5行目が、RA を送信するための設定で、ULA を追加する前は「ipv6 lan1 rtadv send 1」となるところに、2行目で追加したプレフィックスも送信するように設定しています。
ひとまず、ここまでやれば、RA による自動構成アドレスがクライアントに付与される格好になります。
実は、ここまでで、いったん満足してました。とりあえず、ULA のアドレスが自動的に付与されるようになったし、これで、DNS を除けば、おうちのネットワーク IPv6 だし、と。
ところが、「ダメだ。やっぱり DHCPv6 が必要だ!」という事が発覚します。
きっかけは、おうちサーバで運用している Kerberos の認証で不具合が発生したことでした。
自分で書いておいて、すっかり忘れていたのですが、MIT の Kfw を使わずに、Windows のネイティブの機能で非 Active Directory の Kerberos に対応させようとすると、Windows がクライアントのホストプリンシパルを要求する関係で、事実上、Dynamic DNS 必須になります。
RA で ULA を配信するようになると、ULA のプレフィックスを持つ IPv6 アドレスが2つ作られます。一つは MAC アドレスから生成される EUI-64 を使ったアドレス。もう一つは、一時アドレス(もしくは、匿名アドレス、プライバシーアドレス)と呼ばれる、都度、変化するアドレスです。
IPv6 の一時アドレス
もともと、IPv6 で「DHCP サーバを用意しなくても、自動的に IP アドレスが設定されます。」というのは、RA のプレフィックス情報と EUI-64 の組み合わせで生成する、というものでした。
EUI-64【イーユーアイ・ロクジュウヨン】 | 日経 xTECH(クロステック)
EUI-64 がどういう物かは上記ページに詳しく書かれていますが、ポイントは、MAC アドレスから生成される点で、MAC アドレスから生成されるということは、IPv6 アドレスの下位 64 bit を見れば、同じ PC から接続されている事が分かる*4ことになります。自宅から繋ごうが、ホテルから繋ごうが、公共のネットワークから繋ごうが、IPv6 アドレスの下位 64 bit が同じならば、同じ PC から繋いでると考えられます。
で、これはプライバシー上問題だ、ということで、一時アドレスと呼ばれるアドレスが作られます。こっちはランダムに割り当てられるので、どこへ行っても下位 64 bit が同じ、という事はありません。
で、この一時アドレスは、Dynamic DNS による DNS サーバへの登録から除外されます。
Windows ツールを使用して IPv6 構成情報を取得する
通常、一時的 IPv6 アドレスは、Web ブラウザなどの通信を開始するときにクライアント アプリケーションによって使用され、DNS に登録されません。
ということは、クライアント側が一時アドレスを送信元のアドレスとして選択してサーバへ接続すると、その IPv6 アドレスは DNS 上、存在しないことになり、前述の Kerberos の問題が発生します。
これ、おうちネットワークで問題が発生するのは私のところぐらいかもしれませんが、普通に企業内ネットワークを IPv6 で作った時に、一時アドレスが送信元では都合が悪い、という事は結構、ありそうです。
一時アドレスを一切使わない、という手段はあるようですが、目指すところは、
- グローバルなアドレスは一時アドレスを使う。
- ULA は一時アドレスを使わない。
です。で、手動でアドレスを設定すれば、この条件を満たせるのは分かるのですが、IPv6 で手動設定は、出だしにも書きましたが「拷問」です。
ということで、おうちネットワークで IPv6 アドレスを自動で割り当てたかったら、単に ULA を RA に乗せるだけでは事が足りない事が分かりました。
で、実際にどうするかは、次回へ*5。
*1:といっても、ULA のアドレス割り当てを自動にする明確なメリットは、せいぜい、「アドレス入力の間違いを無くせる」ぐらいですが。
*2:少なくとも、私が生きている間には、そんな時代は来ないかな、と。
*3:http://jp.yamaha.com/products/network/solution/flets/next/flets-next-ipv6_ipoe-nvr500/
*4:厳密には、同じ NIC から接続されていることが分かる。なので、例えば、同じ PC からでも有線と無線では違う。
*5:途中、「ということで、DHCPv6 の登場です。」と書いておいて、RA の話で終わってしまった...
IPv6 に NAT が無いことの影響
我が家に IPv6 環境がやってきました。フレッツ光ネクストの回線にして、ISP 側に申し込んで、IPoE(いわゆるネイティブ方式)で IPv6 のインターネットにつながりました。
おそらく、多くの一般家庭であれば、今時のルータさえ用意すれば、特段、意識することなく、IPv6 につながる環境になると思います。おうちサーバがあっても、「自宅内は IPv4 で十分」と考えれば、何も問題はありません。
が、一応、北の大地で技術系サラリーマンの端くれとして生活の糧を得ている身としては、おうち LAN でも IPv6 を使わないでどうする、と、無駄な努力をするのでした(^^;
IPv6 とは
なんて話はどうでもよいですね。立派な専門家の方々がたくさん解説を書いています。
NAT が無い
これも割と有名な話だと思います。そもそも、NAT によってグローバルアドレスを延命する、というアイデアが出る前に、「次世代の IP を作らなければ、アドレスが枯渇する」と言われてました*1。
故に、かどうかは分かりませんが、IPv6 には NAT がありません。という事はどういう事かというと、「インターネットに繋ぎたければ、末端の端末、一つ一つに、グローバルアドレスを持つべし」という事になります。
実際、IPv6 に対応したご家庭用ブロードバンドルータで、IPv6 でつながる環境になると、IPv6 のアドレスの自動構成によって、おうちの中の PC にグローバルな IPv6 アドレスが付与されます。その状態で、例えば Google に繋ぐと、どこにも NAT が入らず、グローバルな IPv6 アドレスでつながっています。
個人向けの接続サービスだと、IPv4 アドレスがルータの WAN 側に一個、割り当てられるのが普通ですが、IPv6 の場合は、プレフィックス、つまり、アドレスのネットワーク部が割り当てられます。128 bit の IPv6 アドレスのうち、上位 64 bit のアドレスが ISP から割り当てられ、下位 64 bit は「勝手に付けてよし」という状態です*2。
プライベートアドレス
NAT が無く、普通にグローバルアドレスが PC に割り当てられているのだから、IPv4 のプライベートアドレスのような物は必要ないか、というと、そういうわけでもありません。例えば、サーバのアドレスは固定したい訳ですが、もし、個人向けの安い契約で済ませようとすれば、割り当てられるプレフィックスが変わるたびに、サーバのアドレスを変更する必要があります。固定の契約を結んでも、ISP を変えたら、サーバのアドレスを変更しなければいけません。インターネット側からアクセスするためのサーバならしかたないですが、内部だけで使うサーバもこれではたまりません。
当初は、「NAT は悪だ! だからプライベートアドレスなんて不要だ!」みたいな雰囲気があったのですが、結局、上記のような事を考えると、プライベートアドレスに該当する物が必要、ということで、IPv6 では ULA と呼ばれるアドレス帯が用意されました。
ULA は「fd」で始まるアドレスで、組織統合などがあった場合にも衝突しないように、計算方法を提示されています。また、実際に計算するための Web ページが、BSD 系 OS の IPv6 を実装した Kame プロジェクトにあります。
http://www.kame.net/~suz/gen-ula.html
自分が持っている PC や機器の MAC アドレスを入れれば、48bit のプレフィックスを生成してくれます。これで、IPv4 でのプライベートアドレスを使う事ができ、内部サーバに固定の IPv6 アドレスを割り当てる事ができます。
再び、NAT が無い
IPv4 のプライベートアドレスで内部のネットワークを作り、それでインターネットに接続できたのは、NAT のおかげです。しかし、IPv6 では NAT がありません。このままだと、
- グローバルのアドレスが割り当てられた物は、インターネットにはつながるけど、ULA を付けた内部のサーバに、直接はつながらない*3。
- ULA を付けたものは、内部のサーバにはアクセスできるが、インターネットにはつながらない。
ということになります。そもそも、内部のサーバは ULA を付ける訳ですから、じゃぁ、このサーバのアップデートとかはどうするんだ? という事になります。
で、どうするかというと、「どっちのアドレスも付ける」という事になります。IPv6 が「複数のアドレスを持つのが普通」と言う時、通常使うアドレスとリンクローカルアドレスの事を指して解説している事が多いのですが、ULA を使う場合には、「ULA とグローバルの両方のアドレスを持つ」という事になります。IPv4 だと内部のネットワークがプライベートアドレスだけで済んでいたのと、大きな違いになります*4。
Windows 上で、ipconfig で見ると、こんな感じになります。
自動構成有効. . . . . . . . . . . : はい IPv6 アドレス . . . . . . . . . . . : 240b:1234:5678:9abc:def0:1234:5678:9abc(優先) IPv6 アドレス . . . . . . . . . . . : fdfe:dcba:9876:5432::beef(優先) 一時 IPv6 アドレス. . . . . . . . . : 240b:1234:5678:9abc:fedc:ba09:8765:4321(優先) リンクローカル IPv6 アドレス. . . . : fe80::123:4567:890a:bcde%11(優先)
上記の例では、プレフィックスは 64 bit で、
- グローバルなアドレスは、「240b:1234:5678:9abc:def0:1234:5678:9abc」と「240b:1234:5678:9abc:fedc:ba09:8765:4321」
- ULA は「fdfe:dcba:9876:5432::beef」
- リンクローカルアドレスは「fe80::123:4567:890a:bcde」
になります。
おうちサーバにグローバルな IPv6 と ULA を付ける
ということで、おうちサーバを IPv6 対応にするには、グローバルなアドレスと ULA の両方を持つように設定する必要があります。
が、ここで落とし穴が。
私のおうちサーバは CentOS なのですが、CentOS で普通に GUI でアドレス設定をしようとすると、
- 手動で設定すれば、自動構成のアドレスは生成されない。
- 自動構成のアドレスが生成されるようにすると、手動でアドレスを設定できない。
という状態になります。サーバなので、ULA のアドレスを手動で設定したいのですが、そうすると、自動構成で割り当てられるはずのグローバルなアドレスは付与されない、という状態になります。企業のように、グローバルのプレフィックスが固定の場合は問題ないですが、おうちサーバだと、プレフィックスが変わる可能性があるので、グローバルのアドレスを手動で設定する訳にはいきません。
IPv6 の自動構成アドレスは、ルータが送信する RA(Router Advertisement:ルータ広告)のメッセージを受信し、そのメッセージに含まれるプレフィックスの情報を使って、自分のアドレスを決定します。RA を受信して自動構成アドレスを生成しつつ、固定で ULA を付けたい。IPv4 で例えれば「DHCP と手動設定の両方のアドレスを付けたい」といった感じです。
いろいろ嗅ぎまわったところ、「/proc/sys/net/ipv6/conf/eth0/accept_ra」が 0 な事が分かりました。おそらく、これを 1 にする必要があるだろうと思って、
# echo 1 >/proc/sys/net/ipv6/conf/eth0/accept_ra
として「service network restart」としてみると、0 に戻ってしまいます。
# find /etc/sysconfig/network-scripts/ -type f -exec grep -Hn accept_ra {} \;
とすると、確かに、この値を書き換えているところが見つかります。
これを追いかけると、どうやら IPV6_AUTOCONF が yes なら accept_ra が 1 になるようです。GUI から IPv6 アドレスを手動すると、/etc/sysconfig/network-scripts/ifcfg-eth0 に IPV6_AUTOCONF が no に設定されます。これを yes にすれば良いのですが、今度は NetworkManager が上書きしてしまいます。
ということで、
- NetworkManager を無効にする。もしくは、インタフェースを NetworkManager の管理外にする*5。
- /etc/sysconfig/network-scripts/ifcfg-インタフェース名 のファイルで「IPV6_AUTOCONF=yes」とする。
とした上で、ULA の固定アドレスを設定すると、両方のアドレスが割り当てられるようになります。ifcfg-インタフェース名で IPv6 に必要な設定内容はこんな感じになります。
IPV6INIT=yes IPV6_AUTOCONF=yes IPV6ADDR=fdfe:dcba:9876:5432::1234/64
NetworkManager が残念
NetworkManager が動いていると、あるとき、意図しない設定になっている目に遭って、「サーバだから、そんな物は必要ない」みたいな事を言う人は多いのですが、とはいえ、無線 LAN や VPN のように、ユーザのデマンドで接続する場合には、NetworkManager がある方が便利だし、慣れていない人でも、それなりに設定できるので、個人的には「なら、手なずけてやる」と思って使ってきたのですが、NetworkManager を止めざるを得なかったのは、KVM のホストでブリッジを作った時と合わせて2度目。う〜ん、ちょっと残念。
*1:その頃は IPng (IP next generation) と言われていました
*2:フレッツ光ネクストのネイティブ接続で個人契約の場合。サービスメニューによって変わる可能性が無いわけではないですが、基本、64 bit のプレフィックスと思っていて間違いないと思います。
*3:ルータを介してルーティングすれば接続可能。ルータは割り当てを受けているプレフィックスを持つホストが、内部にいる事を知っているので、ルータの LAN 側に ULA のアドレスを付けるれば、基本的には OK
*4:まぁ、インターネットへの接続は全部、Proxy 経由、という構成もあり得ますが、その Proxy は少なくとも、グローバルなアドレスと ULA の両方を持つ必要があります。
*5:/etc/sysconfig/network-scripts/ifcfg-インタフェース名のファイルで NM_CONTROLLED=no とします。
「パスワードを保存する」ということ
Chrome のパスワード管理で、平文のパスワードが見えると大騒ぎになっているようです。
Chromeでは自動保存のパスワードが丸見え。サーッと血の気が引いたわ | ギズモード・ジャパン
これ、2つの論点が混ざっていて、
- 平文のパスワードが表示できる。
- 平文のパスワードが表示できることに、なんら警告がない。
という話になります。
で、前者は原理的には驚くことはなにもありません。パスワードをユーザの代わりに送信しようとするソフトウェアは、平文のパスワードを知らないと「ユーザの代わり」はできません。私はKeePass というオープンソースのソフトを使っていますが、これでも表示することはできます*1。
ただ、その平文のパスワードをどうやって保存するか、という事に関しては千差万別です。
最も、ユーザの手を煩わさない方法は、ソフトウェアが他の秘密情報無しに復元できる形で保存する方法で、一番単純なのは、そのままの形でファイルに保存する方法で、ちょっと気を利かせるなら暗号化もどきをした上で保存します。ここで大切なのは、しょせんもどきなことです。
暗号化もどきは、プログラムにハードコーディングされているアルゴリズム、情報さえ分かってしまえば、誰でも複合化できます。これが問題になった典型的なケースは Gumblar というマルウェアで、FFFTP で保存したパスワードが抜き取られた事件です。
当時の FFFTP では「簡易的な暗号化」と呼ばれる処理で、一見、元のパスワードが分からないように処理してから保存していたのですが、FFFTP が行っている処理をそっくりまねれば、元のパスワードは誰でも復元できます。Gumblar は FFFTP が保存している「簡易的な暗号化」したパスワードを見つけると、それを復号化する仕組みをもっていたため、ここから FTP 用のアカウント情報を収集し、Web サイトの書き換えを行いました。
FFFTP はその後、マスターパスワードを使うように修正されました。
ウイルス感染したPCにおける保存パスワードの窃取問題へ対策した「FFFTP」v1.97 - 窓の杜
マスターパスワードを使って、AES で暗号化する、といった対策が取られましたが、これだと、マルウェア等が自動的にパスワードを復元する事は不可能になります。これは、AES で暗号化したからではなく、マスターパスワードを鍵として*2暗号化しているからです。
今時の暗号化の大前提は、暗号化処理のアルゴリズム自体は既知であり、その暗号化処理の鍵を秘密にする、というものです*3。FFFTP が行った対策は、AES という既知のアルゴリズムで暗号化しているのですが、鍵をマスターパスワードという形でユーザに入力させることで、自動的には復号化できないようにしました。
逆に、マスターパスワードに該当する入力を必要としない場合には、マルウェア等が自動的にパスワードを復元できる可能性があります。
例えば、暗号化処理に AES を使っていたとしても、その鍵が、レジストリのどこかに保存されている、ということであれば、ユーザはマスターパスワードのような物を入力する必要はありませんが、同時に、その鍵のありかさえ分かれば、自動的にパスワードを復元できることになります。
元記事に戻ると、そもそも Safari から Chrome にパスワードを引き継げた、という事は、Safari で保存していたパスワードも自動的に復元できる、ということですから、その点では Safari も Chrome も同じです*4
で Google 側の言い分としては、Chrome を実行しているアカウントが第三者に盗られた時点でアウトなんだから、しょうがないだろう、というものです。
これはこれで「アリ」だとは思います。Windows Vista 以降であれば、管理者権限を持ったアカウントであっても、UAC が有効であれば、プログラムがアクセス、コントロールできる範囲は狭くなっているので、マルウェアが好き勝手できない状態にはなります。ただ、アカウント情報を盗もうとするのであれば、特に管理者権限も必要なく、通常のユーザの権限で動くマルウェアで十分です。
という事を考えると、最初の方に書いた論点の後者「平文のパスワードが表示できることに、なんら警告がない」という点に関しては不満が残ります。また、マスターパスワードの設定のような、保存されたパスワードを保護するための機能が無い、という点では、Firefox よりも劣っていることにはなります。
どうも元記事を読んでると、「パスワードが見えた!」という事に反応してしまって、本来、取り上げるべき
- ユーザに対する警告が無い。
- マスターパスワードのような保護機構が用意されていない。
という点に焦点があまり当たっていない感じがします。それは、
UPDATE:FireFoxも見れますね…ご指摘ありがとうございます。「Are you sure you wish to show your passwords?(本当に表示していいですか?)」の警告は出ますけど、そこでYESを押すと一括表示です。
というところに現れています。Firefox は警告もしているし、マスターパスワードも設定できます。
個人的には、マスターパスワードに該当するものがないパスワードの保存、というのは、基本的にあまり信用してません。マルウェアに感染したら抜かれる情報だ、という認識でいます。抜かれてもクリティカルではなく、利便性が向上するならかまわないと思いますが、「パスワードの保存」というのはそれに該当することはないでしょう。
自転車の交通事故
自転車通行可の標識を減らしているそうです。
http://www3.nhk.or.jp/news/html/20130525/k10014839591000.html
自転車乗りの端くれとして、この「自転車は車道を走れ」は悲しい物があるものの、明らかに酷い走り方をしている人がいるのは事実だし、個人的にはそういった、我が物顔で歩行者の脇をすり抜けていく人に怒りを感じます。車道を走る時には車の交通状況を常に気にした上で車道走り、車の通行の妨げになると思った場合には、十分に歩行者との間隔を空けた上で、いつでも危険を回避できるように気を配って走っていている人までも、そういったマナーの悪い人たちと同じように見られるのはつらいです。
で、気になったのは、上記のニュースのこの部分です。
警察庁によりますと、自転車が歩行者に接触する事故は去年、全国で2625件と、10年連続で2000件を超え、死亡事故も5件起きるなど深刻な状態が続いています。
警察庁によると、
- 10 年連続で 2000 件を超えている。
- うち、死亡事故が 5 件ある。
という事は「深刻な事態である」として、自転車専用道の整備もできていない現状で、自転車を車道へ追い出している点です。
「じゃぁ、自転車と自動車の間の事故は、これよりも深刻ではないの?」と疑問に思いました。
「警察庁」「自転車」「自動車」「事故」でググって見つかったのが下記の資料です。
この資料は、平成 13 〜 23 年の統計で、前述の報道で言われている資料より一年古いものだと思いますが、平成 23 年の事故は、
- 自転車と自動車の事故件数: 121,004 件(うち死亡事故 556 件)
- 自転車と歩行者の事故件数: 2,801 件(うち死亡事故 6 件)
とあります。少なくとも、「点」の数字としては、自転車と自動車の事故の方が圧倒的に多いし、事故全体に占める死亡事故の割合も、
- 自転車と自動車: 556 / 121,004 = 0.46%
- 自転車と歩行者: 6 / 2,801 = 0.21%
と、自動車との事故の方が高くなります。ちょっと単純すぎる比較*1ですが、自転車に乗る者にとって車道の方が危険である事には違いないでしょう。
ただ、トレンドとしては自動車と自転車の事故件数はこの 10 年あまりで 15 % 程度減少し、死亡事故に関しては 40% 近く減っているのに対し、歩行者との事故は 50 % 以上増加していて、死亡事故も 4〜6 人で推移している、ということから、自転車と歩行者の事故を減らすための対策が必要、という事に異論はありません。
自転車の信号無視の取り締まりをやっているニュース映像を見たことがありますが、こういった取り締まりの強化には賛成します。自転車の飲酒運転とか、夜間無灯火とか、傘を差したままの運転とか、明らかに危険な行為に対して、もっと厳しくて良いと思います。
ただ、歩行者との事故を減らすために「車道を走れ」というのは、自転車を乗る人に対して、より危険な選択を迫っていることになります。
「車道が原則」である事は分かりますが、そのために、より危険な方へ追い込むのは本末転倒な気が...
せきゅぽろ 15
標的型攻撃に関して
一つ目のテーマは、トレンドマイクロの平原 伸昭さんによる、標的型攻撃に関するお話でした。
2、3年前までは遠い海の向こうの話、というイメージだった標的型攻撃も、最近は日本でも標的型攻撃だと思われる事例が報道されたり、すっかり身近(?)になってきました。
もっとも印象に残った言葉は「注目すべきは不正な活動ではなく、不審な活動」。
そもそも「不正な活動」なら、アンチウィルス製品や IPS の類で検知できるけど、本当に標的型攻撃が行われている時は、明確に不正な活動ではなく、管理者がやっているのと同じ事が行われている、ということです。
例えば、管理共有を使ったファイルのコピーや PsExec などのツールの利用など、それ自身は不正とは言えない処理があり、それらを「点」として捉えると、白黒つかないけど、じゃぁ、それらが「どこから」「いつ」といったところまできちんと調べると、「それはおかしい」ということに気づくことになります。
逆に、そういった動きが「怪しい」と分かるように、普段の運用環境をきちんと整理しておく事が大切だよなぁ、と思いました。
あと、実際のマルウェア解析の一端を紹介してくれました。
いろいろツールはあるんだけど、まずは OllyDbg を使って、どんな動きなのかを知る、というところからスタートするそうです。特に、インシデント対応の場合は、通常のマルウェアのようには時間をかけられないので、どんなファイルを保存するのか、どんなレジストリを書き込むのか、といった辺りを、OllyDbg で Win32API にブレークポイントをはって確認し、被害範囲を把握する、といった感じだそうです。
プライバシー周りのお話
二つ目のテーマは、北大大学院教授の町村 泰貴先生による「オンライン・プライバシー」でした。
まずは、そもそもの話、ということで「個人情報」と「プライバシー」の違いといったところで、個人情報に関しては個人情報保護法によって規定されているけど、プライバシーに関しては法的に明確に規定されてない、ということでした。故に、プライバシー侵害に関しては、民法 709 条に基づく民事での損害賠償はあっても、刑事罰はありません。ただ、プライバシー侵害の中に名誉毀損も含まれる場合もあって、名誉毀損になれば刑事罰の対象になるそうです。
で、私も最近聞いたキーワードで「忘れられる権利」というのがあるのですが、さすがに広まってしまった情報を全て回収して消すことは不可能だけど、一度与えた情報をいつでもオプトアウトできるようにすべきだろう、という話でした。
プライバシー保護の必要条件として、町村先生が挙げていた項目は、上記の「忘れられる権利」の他に、情報の収集・理由に関して十分な情報が与えられる「インフォームド・コンセント」と、収集・利用した側が漏洩防止に十分なセキュリティを施す「データ漏洩防止の保証」を挙げられていました。
現在、この辺をまとめた「パーソナルデータの利用・流通に関する研究会の報告書」に関して、総務省がパブリックコメントを募集中だそうです*1。
次回は
次回は 7/20 に Yahoo の 戸田 薫さんが、ワンタイムパスワードに関してお話してくれるそうです。...って、今、大変な時じゃないのかなぁ。
ちなみに、次回は「気象兵器」のまっちゃさんもいらっしゃるようです(^^;。
セキュアな rsync - 実践編
理論編(id:JULY:20111127)を書いてから、既に1年半も経ってしまいましたが*1、ようやく実践編です。
2台のホストは
ホスト名 | |
---|---|
コピー元 | master.example.com |
コピー先 | slave.example.com |
とします。SSH は slave.example.com から master.example.com へ接続します。
rsync 用のユーザの作成
双方のホスト上で rsync 用のユーザを作ります。このユーザは普通にログインする必要が無いので、ログインシェルを /sbin/nologin にしておきます(後述しますが、とりあえずこの時点では、ログインシェルは /sbin/nologin としておきます)ホームディレクトリも必要ありません。
あとは基本的には任意ですが、多くのディストリビューションで 500 番以上の UID を一般ユーザに割り当てる習慣があるので、500 番未満が良いでしょう*2
# groupadd -g 480 rsync # useradd -d / -M -u 480 -s /sbin/nologin -g rsync rsync
ssh のホストベース認証
これについて書いているサイトは多いので、省略... でも良いのですが、一応、書いてみます。
鍵の用意
多くの Linux ディストリビューションでは、最初に sshd が起動された時に生成するようになってます。/etc/ssh の下に、下記のようなファイルがあると思います。
秘密鍵 | 公開鍵 | 対応プロトコルバージョン |
---|---|---|
ssh_host_key | ssh_host_key.pub | Version 1 |
ssh_host_dsa_key | ssh_host_dsa_key.pub | Version 2 |
ssh_host_rsa_key | ssh_host_rsa_key.pub | Version 2 |
今時、SSH1 のプロトコルを使う必要は無いので、「ssh_host_dsa_key、ssh_host_dsa_key.pub」か「ssh_host_rsa_key、ssh_host_rsa_key.pub」のどちらかを使う事になります。違いは公開鍵暗号として、DSA を使うか、RSA を使うか、ということですが、現在は RSA を使うのが一般的です*3。
これらの生成済みのファイルは、「このホストを認証してもらうための鍵」です。今回の例では、slave.example.com 側のファイルです。
$ ssh-keygen -f ssh_host_rsa_key -t rsa -N '' Generating public/private rsa key pair. Your identification has been saved in ssh_host_rsa_key. Your public key has been saved in ssh_host_rsa_key.pub. The key fingerprint is: ......
-f は出力するファイル名で、このファイル名で秘密鍵が生成されます。このファイル名に「.pub」が付いた名前で、対応する公開鍵のファイルが保存されます。-t は公開鍵暗号の暗号方式で rsa をして、-N はパスフレーズの指定で、「-N ''」とする事で、パスフレーズ無しの鍵が作られます*5。
鍵の配備
今回は、slave.example.com が、master.example.com に認証してもらう立場なので、slave.example.com のホスト公開鍵を、master.example.com にコピーします。
認証する側では、/etc/ssh/ssh_known_hosts ファイルに公開鍵の内容を追記します。認証される側で ssh サーバが動いているのであれば、認証する側のホスト上で下記のようにすると、/etc/ssh_known_hosts ファイルに公開鍵を追記できます。
# ssh-keyscan slave.example.com >>/etc/ssh/ssh_known_hosts
ホストとユーザの制限
認証する側の/etc/ssh/shosts.equiv で、許可するホストとユーザ名を指定できます。ただし、sshd_config の次の項目を確認する必要があります。
今回は master.example.com の管理者による制限を行いたいので、IgnoreRhosts は yes にします*6
shosts.equiv の記述自体は簡単で、認証を許可するホスト名とユーザ名を記述します。ユーザ名は省略可能で、その場合は記述したホストの全てのユーザを許可します*7。slave.example.com から master.example.com へ接続するユーザを user1 とすると、shosts.equiv の内容は以下のようになります。
slave.example.com user1
しかし、今回の場合、
ユーザ | ホスト | ユーザ | ホスト | |
---|---|---|---|---|
rsync | slave.example.com | → | rsync | master.example.com |
という接続になります。shosts.equiv に書くのは、あくまでも、認証してもらう側のホスト上のユーザなので、
slave.example.com rsync
となります。
sshd_config の設定
先述の IgnoreRhosts に加えて、残っているのは、そもそもホストベース認証を有効にするための設定です。
ssh_config の設定
ssh クライアント側の設定で必要なのは、下記の2項目です。
ssh_config は、Host キーワードを使って、特定の接続先にだけオプションを適用する事ができます。ssh のサーバになるホスト名をして、
Host master.example.com HostbasedAuthentication yes
とすれば、ホストベース認証を使う相手先を master.example.com だけに絞る事ができます。ただしEnableSSHKeysign は、この Host キーワードによる指定の外側に記述する必要があります。
EnableSSHKeysign yes Host master.example.com HostbasedAuthentication yes
sudo の設定
rsync コマンドを cron に設定して実行する側は、
- 一般ユーザの cron で実行する。
- 面倒だし、分かりづらくなるので、/etc/cron.daily とかに設定してしまう。
の2つの方法が考えられます。後者であれば、rsync をスケジュール起動する側(ここでは、slave.example.com)では、sudo の設定は不要です。
前者であれば、/etc/sudoers で、rsync を実行する時のユーザで、rsync を root 権限で実行できるようにする必要があります。仮にその時のユーザアカウントが rsync-user であれば、
rsync-user localhost=(root) NOPASSWD:/usr/bin/rsync
と記述し、後述の requiretty に関する記述を追加した上で、「sudo rsync 〜」と、sudo 経由で rsync を実行します。
逆に、ssh 経由で rsync を呼び出される方は sudo の設定は必須です*8。
rsync ALL=(root) NOPASSWD:/usr/bin/rsync
これで、rsync を root 権限で呼び出せるようにはなったのですが、多くのディストリビューションの /etc/sudoers で、
Defaults requiretty
という行が入っています。これは、sudo を使ってコマンドを呼び出す際、端末としてログインしている状態である事を要求します。リモート側で実行される sudo rsync は、ログインセッションでは無いので、除外してやる必要があります。ここで、
Defaults !requiretty
としたり、コメントアウトする例を見かけますが、これだと、全ての sudo の実行で、ログインセッション以外での sudo 実行を許可する事になります。rsync ユーザだけ、ログインセッション以外での実行を許可するためには、
Defaults:rsync !requiretty
という行を追加します。
rsync のコマンドライン
ポイントは2つです。
通信を、sudo 付きの ssh 経由にする
「-e」オプションで ssh を指定するのですが、その際、「-e 'sudo -u rsync ssh -l rsync'」と、sudo 経由で、かつ、ユーザ名付きにする必要があります。こうすることで、
という動作になります*9。この時の「-u」で指定するユーザ名は、master.example.com の shosts.equiv で指定したユーザ名と合致している必要があります。今回は、ssh を実行するユーザ(-u rsync)と、接続先のユーザ(-l rsync)が同じなので、「-l rsync」は省略可能です。
リモート側で実行される rsync を sudo 付きにする。
「--rsync-path='sudo rsync'」とします。これを忘れると、リモート側の rsync は「rsync アカウント権限で rsync を実行」する事になり、読み出しに root 権限が必要なファイルが取得できなくなります。
全体としては、こんな感じのコマンドラインになります。
rsync -a -e 'sudo -u rsync ssh -l rsync' --rsync-path='sudo rsync' master.example.com:コピー元 コピー先
非 root ユーザでこれを実行する場合は、この手前に sudo を付けます。
コピー元の rsync ユーザの制限
実は、動作を確認している中で、ここでつまずきました。目指す rsync の実行は、
- slave.example.com 側で rsync を実行。
- その rsync が「ssh -l rsync」として ssh を実行。
- 繋がれた master.example.com では「sudo rsync 〜」を実行。
となります。冒頭、rsync ユーザのログインシェルを「/sbin/nologin」としていますが、そうするとすると、ssh でつながった直後に切断され、3 番目の「sudo rsync 〜」を実行できなくななります。
なので、普通の「/bin/bash」などをログインシェルにしてしまえば、これまでの設定で目的が果たせます。
... ただ、なんとかしたい。rsync 実行の為だけに用意したアカウントで、slave.example.com から繋いだ時だけとは言え、rsync ユーザに許可されている任意のプログラムが実行できる、というのは、どうにも格好悪い。rssh を使えば、scp や sftp、rsync 用に絞ることも可能だけど、今回は sudo 経由で rsync を呼び出すので、これは使えない...。
で、あれこれ調べまわった挙句*10、ログインシェルを自作しました*11。
#!/bin/sh if [ "$1" != "-c" ]; then exit 0 fi if (expr "$2" : 'sudo *rsync *--server *--sender ' >/dev/null); then echo -n "$2" | egrep -q '[^\][;<>]|&&|\|\|' if [ $? -eq 0 ]; then exit 0 fi /bin/sh "$@" else exit 0 fi
ssh 経由でリモート側で呼び出される rsync コマンドには「--server」というオプション付きで呼び出されます。で、今回のケースのように、リモート側からローカル側へダウンロードする場合、「--sender」というオプションも付きます。
これらのオプション付きの rsync が sudo 経由で呼ばれ、かつ、途中に「;」や「&&」「||」といった、更に別のコマンドを呼び出す事が可能なメタ文字や、リダイレクト用の文字が含まれていない場合にのみ、処理を実行できるようにしています。
このシェルスクリプトを例えば「/usr/local/bin/rsyncshell」というファイル名で保存し、
usermod -s /usr/local/bin/rsyncshell rsync
といった具合に、ログインシェルに設定すれば完成です。
但し、このログインシェルスクリプト、通常のシェルをログインシェルにするよりは制限がかかっている状態になっていますが、
- 他のプログラムを実行するための回避策が他に無いか?
- メタ文字に対するチェックが、同期対象のパス名に ASCII 以外の文字が含まれているケースで問題がないか?
といったところは十分に検証されていません。後者の問題が無ければ、通常のシェルを指定するよりはマシ、といった程度に考えて下さい。
また、あくまでも「マスターからダウンロードする rsync」という前提にしています。というのは、不用意にリモート側の $(HOME)/.bashrc などを書き換えないように、と思うと、ダウンロードする方が安全ではないかと思ったからです。
まとめ
全体をまとめると、こんな感じになります。
- コピー先の root ユーザで rsync を実行し、コピー元からダウンロードする方向にする。
- rsync の通信は ssh 経由とし、ssh の認証はホストベース認証にする。
- コピー元となるホストの sshd の設定で、相手のホストと、その時の相手ホスト上のユーザにのみ、ホストベース認証を許可する。
- コピー元のとなるホストでは、rsync 用のユーザに対して sudo 経由で rsync 実行を許可する。
- できれば、コピー元の rsync 用ユーザは、制限付きのログインシェルにする。
- コピー先での rsync の実行は、-e で特定のユーザ名を使った ssh 接続を実現し、--rsync-path で相手側の rsync を sudo 付きで呼び出すようにする。
*1:時間がかかった理由は、本文中にもあるログインシェルの問題と、sudo の requiretty をまるごと無効にしないための方法、あと、我が家の中途半端な IPv6 環境のせい、とか、いろいろありまして...
*2:RHEL / CentOS Ver.6 以降、490 番台に、特定のプログラムが使うアカウントが配置されるケースがあるので、ちょっと気を付ける必要があります。
*3:OpenSSH の ssh-keygen が DSA の鍵を生成する場合、鍵長が 1024 bit に制限される、というのが、RSA が推奨される理由になってます。DSA 自体は 1024 bit より長い鍵長もあるけど、現状、実際に使えるのは 1024 bit のみ、ということのようです。参照: DSA鍵の鍵長 | dodaの日記 | スラド
*4:sshd が動作しているホストであれば、必ずあるはずです。
*5:この時、生成した秘密鍵を置くパスに関して、どこにどんな名前で保存しても、設定ファイルで指定できそうな気もするのですが、ちょっと分かりませんでした。秘密鍵を読み出すのは、ssh クライアントプログラムから呼び出される ssh-keysign というプログラムなのですが、ssh-keysign の manpage を見ても、固定のパス名しか書いてありません。ちょっと謎...
*6:一応、デフォルトは yes です。
*7:ただし、root は除く
*8:「ALL」のところをホスト名で制限したいのですが、ssh 経由の時にこの sudoers のホストによる制限は効かないようです。参照:HowTO: Sudoers Configuration - 「Note: This applies to computers running application services. If you connect to a machine via ssh, this does not apply.」
*9:/etc/sudoers に、「root ALL=(ALL) ALL」といった行があることが前提です。普通、入っていると思います。
*10:もちろん、rbash や rksh などの Restricted Shell も検討しました。
*11:元ネタは http://www.oreillynet.com/linux/blog/2006/05/restricting_rsync_over_ssh.html