DNSがもっと良かったらという思考実験
概要
IPv4 アドレスが枯渇しヴァーチャルドメインが普及する現代では,DNSとWell Known Portで構成されるアプリケーションの通信にTLS証明書問題などの不都合が生じることが稀に良くある.DNSとportmapを組み合わせたような賢いサービスディスカバリーシステムを導入していたらスマートなインターネットができたのではないのか,という考察を思考整理を兼ねて行ってみる.
DNS
ドメイン名とIPアドレスを対応付ける仕組み.10.0.0.1 だとわかりにくいから www.yuyarin.net という名前を付ける.名前を「解決」するとIPアドレスが得られ,アプリケーションは解決で得られたIPアドレスを使って通信を行う.
ドメイン名からIPアドレスを解決することを正引き,IPアドレスからドメイン名を解決することを逆引きという.正引きは違うドメインが同一のIPアドレスを指すことができるのでn対1のマッピングである.
Well Known Port
よく知られた通信を行うためのポートは基本的に固定されている.例えばHTTPはTCP80,メールはTCP25など.TCP/UDP のポート1~1024はこれに該当する.Well Known PortはIANAによって管理されている.Well Known Portに当てはまるプロトコルとポート番号のマッピングは/etc/services に記述されており,getservbyname でプロトコル名からポート番号を得られるのだが,大体の場合ポート番号は数値でハードコーディングされる.
struct sockaddr_in s; struct servent *service; // まちがい s.sin_port = htons(80); // せいかい service = getservbyname("http", "tcp"); s.sin_port = service->s_port;
IPv4
過去のインターネットプロトコルであるが何故か現在も大量に利用されている.アドレス空間が狭く,1人1つグローバルアドレスが持てれば御の字である.
想定する状況
ドメインを複数持っているが,グローバルIPv4アドレスを1つしか持ってない時に,Well Known なサービスを提供する状況を考える.
DNSとWell Known Portで困ったこと
httpdを複数立てたい
例えば,このドメインではApache httpd,このドメインではLight httpdを動かしたいという場合や,ドメインごとにApache自体を分けて使いたい場合.TCP80でコンフリクトしてしまう.
場当たり的な解決としては,片方をTCP8080のように別のポートで動かし,www.yuyarin.net:8080 のようにアクセスする.どうしても同じポートを使いたい場合はトランスパレントプロキシを噛ませる.前者はカッコ悪いし後者は面倒である.バッドノウハウ.
postfixのバーチャルドメインとTLS
1つのサーバで複数のドメインのメールを使う場合.postfixのバーチャルドメインで複数のドメインのメールを扱えるのであるが,複数のドメインに対してTLSを提供することができない.これはTLSで暗号化された後にドメイン名の交換が行われるから,ヴァーチャルドメインの証明書を正しく取得できないからである.ちなみにHTTPでは一部のブラウザと一部のhttpdが協調することで,この問題を解決している.このTLS問題はどのプロトコルでも生じる問題である.
解決方法としては,postfixのインスタンスを分けて,それぞれ別のポート番号をアサインし,MUAでのSMTPサーバの設定を変更する.例えばsmtp.yuyarin.net のSMTPは TCP25, smtp.yuyar.in のSMTPはTCP2525 などと.
ただし,他のMTAの設定を変更することはできないので,この場合,smtp.yuyar.in に対して外から飛んでくるメールはどうしようもないのだが,現状はMTA間のTLSなんてほぼ動いていないので TCP25 を使えばよく,問題になってないだけである.
何が悪かったのか
結論から言うと
の2つが原因だと思う.
学校では「ドメイン名はホストを区別してポート番号はプロセスを区別する」と習う.1つのホストに注目した場合これは真であるが,Well Known Portに関して言えば,彼らが区別するのはプロセスではなくプロトコルである.HTTPやFTPは柔軟に別のポート番号が指定できるが,DNSやSMTPはそれができない.
ポート番号が変えられないという状況で,先に述べた問題をスマートに解決するためには新しいIPv4アドレスが必要になる.恐らくDNSができた当時はCIDRが提案される10年も前の話でIPv4アドレスはたくさんあったし,バーチャルドメインなんてものも存在しなかった.だからその解決策が使うことができた,というかそもそも問題が起きなかったのだろう.
ただ,こうした歴史はデプロイを考えた当時のエンジニアリング的には最適な解決策だったことには間違いはない.
どうすれば良かったのか
もし今からインターネットを作り直すとしたらどうするのがいいのだろうか.ドメイン名からアドレスを解決するだけではなく,サービスとドメイン名からアドレスとポート番号を解決できる,もっと賢いサービスディスカバリシステムがあればスマートになるのではないのかと思う.
従来
www.yuyarin.net => 10.0.0.1 www.yuyar.in => 10.0.0.1 http => tcp80 (決め打ち)
http://www.yuyarin.net と http://www.yuyar.in がコンフリクトする.
提案
http://www.yuyarin.net => 10.0.0.1:80 http://www.yuyar.in => 10.0.0.1:8080
http://www.yuyarin.net と http://www.yuyar.in がコンフリクトしない.
Winnyの話
P2Pネットワークは似たような仕組みを実現している.Winnyの初期ノードリストはIPv4アドレスとポート番号の情報が含まれている.実はこの初期ノードリストを検索するという行為は winny というプロトコルを話す(サービスを提供している)ホストを探すという行為である.そこを人間が自分の手でやっていることになる(なんかズレてる例かも).
portmapの話
サービスからポート番号を知ることができるのがportmapである.サービス名を渡すとアプリケーションレイヤプロトコルのバージョン,セッションレイヤのプロトコルとポート番号がわかる.
提案で書いたような DNS+portmap みたいな仕組みが初めからあればスマートだったのかなと思う.
SRVレコード
DNSにはSRVレコードってのがあって,ドメインの提供するサービスの情報が引けるのだけど,対応してないライブラリが多かったり,実際にSRVレコードを参照するアプリケーションがほとんどないと思う(ActiveDirectoryぐらい?).ここで話してきたことは「初めっからSRVレコードがメインだったら良かったのに」ということになる.
ついでに言うとMXレコードの存在は気持ちが悪すぎる.
結局は救えないもの
portmap でも portmap 用のポートが固定されているように,こういうサービスディスカバリのシステムがあったとして,そのポート番号は固定になってしまう.なので3つめのDNSの複数インスタンス問題のようなものはどっちみち救えない.うまくやる方法はないのかな.
LSN
LSNが運用され始めるとポート番号のコンフリクトはより深刻になるだろうと思うが,多分LSNに収められるのはそういうことをしない人なので問題ないのか.