スプーキーズの中の人。

スプーキーズの中の人が徒然なるままに、垂れ流します。

内部用DNSサーバのレコードをRoute 53のレコードから生成する

こんにちは。 開発の中山です。

今回は、外部用DNSサーバにAmazon Route 53、内部用DNSサーバにdnsmasqunboundを使い、内部用レコードをRoute 53のAPIを使って生成するスクリプトをご紹介します。

現在弊社では、外部向けのDNSサーバは主にAmazon Route 53を利用しています。

社内で運用しているサーバの中には、外部ネットワークと社内ネットワークの両方に繋がっていて、グローバルアドレスとプライベートアドレスを持っているものがあります。このようなサーバに社内からホスト名を指定してアクセスする場合、外部向けのRoute 53サーバを使ってホスト名を解決するとグローバルアドレスが返ってくるので、結果として社内にいるのに外部ネットワーク経由でアクセスすることになってしまいます。レスポンス速度やセキュリティ等の面も考えると、できることなら社内にいるときは内部経由でアクセスしたいものです。

そこで、内部ネットワーク用のDNSサーバを立てて、内部から直接アクセスできるサーバに関してはプライベートアドレスを返すようにします。これによって、同じホスト名を指定しても、外部にいるときと内部にいるときで最適な経路でアクセスすることが可能になります。

ここで、内部DNSサーバでのレコード管理について考えてみます。内部用サーバを動かすには、プライベートアドレスを返すためのレコード一覧を作成する必要があります。しかし、Route 53で管理しているレコードの内容を、一部レコードのみプライベートアドレスに置き換え、サーバソフトウェア用のゾーンファイルの書式で書き直す、ということを手動でやるのは非常に面倒です。できれば、内部DNSサーバで管理するのはプライベートアドレスのレコードのみにし、そのレコードも自動的に生成できると嬉しいですね。

DNSサーバソフトウェアに何を使うのが良いでしょうか。BINDは、設定ファイル内でforwardersオプションに別のキャッシュサーバを指定すれば、自身で名前解決できないゾーンへのリクエストは指定したサーバにリクエストを依頼することができますが、これはあくまでもゾーン単位での挙動であり、ゾーン内の一部レコードのみ自身で解決、それ以外のレコードは他のサーバに回す、といったことはできません。

そこでdnsmasqやunboundです。これらはBINDのようなコンテンツサーバではなく、キャッシュ専用サーバとして作られており、BINDほど設定が複雑でなく軽量といった特徴があります。そして、ゾーン内の一部レコードのみ独自の結果を返すということも可能です。今回はunboundを使うことにしました。

そしてRoute 53にはAPIがあるので、これを使ってレコード一覧を取得し、プライベートアドレスのAレコードをdnsmasqやunboundの設定書式で出力するRubyスクリプトを書きました。

spookies-jp/gen-internal-records-from-route53 · GitHub

使い方ですが、まず$ bundle install$ gem install route53で依存gemをインストールして下さい。その後、config.yamlに、AWSの認証情報と、処理対象にするドメイン(未指定でRoute 53アカウント上の全てのドメイン)を指定します。address_map.yamlには、外部アドレスと内部アドレスの対応を記述します。その後$ ./gen-internal-records-from-route53.rb -t [dnsmasq|unbound]と実行すると、プライベートアドレスに変換したAレコードが出力されます。ファイルに保存するには、-oオプションで出力先を指定するか、シェルのリダイレクトを使って下さい。このファイルを、各デーモンのメイン設定ファイル内からincludeすれば各デーモンで独自のレコードを返せるようになります。Ruby 1.8.7と1.9.2で動作を確認しています。

ちなみにdnsmasqやunboundは、独自にAレコードを返すことはできますが、CNAMEレコードを返すことはできません。 ((dnsmasqにはcnameオプションがありますが、ターゲットが/etc/hostsかDHCPから取得したもののみという制約があります。 Man page of DNSMASQ)) *1 そこでスクリプト内で、「プライベートアドレスを返すAレコードのホスト名」をターゲットにしているCNAMEレコードは、「プライベートアドレスを返すAレコード」に変換するようにしています。どうしてもCNAMEのままでないと困る場合は、ゾーン全体のレコードをBIND用に出力するようなスクリプトを書く必要があるかと思います。(どうせスクリプトで自動化するのならそれでもよかったのですが)

*1:local-zone: … 参照、ワイルドカード、CNAME/DNAMEサポート、DNSSEC権威サービスといった複雑な権威データを必要とするときには、後述するスタブゾーンの節に詳細な記述があるstub-zoneを設定してください。unbound.conf(5) | 日本Unboundユーザ会