AWS
dns
route53
CoreDNS

AWSハイブリッド構成にてRoute 53 Private Hosted Zoneでサブドメインを管理する

前提となる環境

  • オンプレミス環境に既に権威サーバがある(例: example.org)
  • AWS Direct ConnectやVPNでオンプレミス環境とAmazon VPCを接続している

やりたいこと

  • Route 53 Private Hosted Zoneでサブドメインを管理したい(例: sub.example.org)
  • サブドメインをオンプレミス側からでも解決できるようにしたい

課題

  • Route 53 Private Hosted Zoneがサブドメインの委任に対応していない
  • Amazon DNSがVPC専用のため、オンプレミスからのリクエストに直接応答できない

モチベーション

クラスメソッドさんがまとめられたAWSハイブリッド構成のDNS設計レシピ設計レシピ5 : オンプレミスとAWSの権威サーバーを併用する では、上記課題があるためにRoute 53は使わずに、DNSキャッシュサーバと権威サーバをVPC内に構築するという設計パターンが紹介されています。

しかし、APIで色々とコントロールできるRoute 53はやっぱり魅力的なわけです。例えば弊社ではAWS上にKubernetesを構築して開発環境用の各種サーバを運用しているのですが、そうするとDNSレコードの登録をKubernetesへのデプロイと連動させて自動化したい、というモチベーションがありました。

また、バックアップ運用など、耐障害を考慮したDNSサーバを自前で構築・運用したくないというのもモチベーションのひとつです。Route 53のSLAは100%であり魅力的です。

というわけで本記事では、VPC内に権威サーバとして振る舞いつつバックエンドにRoute 53 Private Hosted Zoneを使用するDNSサーバを配置し、上記課題を解決する方式について説明したいと思います。

実現方式

前述のとおり、

  • 権威サーバをVPC内に構築する
  • その権威サーバでは直接サブドメイン管理をせずに、Amazon DNSにリクエストを委譲してRoute 53 Private Hosted Zoneにて名前解決を行う

ということを実現すればよさそうです。今回実現にあたり、CoreDNS に自作プラグイン(Amazon DNS plugin)を追加することで対応しています。構成図としては以下のようになります。

image.png

※BINDなどのよく使われるDNSサーバはフォワーダーとしては設定できますが、権威サーバとして振る舞うように設定ができませんでした。もし設定できる/他のDNSサーバでできるよ、という情報がありましたら是非教えていただきたいです :bow:

CoreDNSとは

CoreDNSについて簡単に説明しておきます。

CoreDNSはGolangで実装されたDNSサーバで、KubernetesのデフォルトDNSとしてkube-dnsの代わりとなることが予定されています

Currently, CoreDNS is Alpha in Kubernetes 1.9. We have a roadmap which will make CoreDNS Beta in version 1.10 and eventually be the default DNS, replacing kube-dns.

CoreDNSの特徴として、プラグインでDNSのリクエスト・レスポンス処理の拡張が簡単に行えるという点があります。

今回作成したAmazon DNS pluginでは、リクエストを受け付けるとVPC内のAmazon DNSに対してそのままリクエストをフォワードし、レスポンスを返す際に権威サーバの情報を付加して返す、ということをやっています。

CoreDNSそのものについては、QiitaにもCoreDNS入門記事がありますのでそちらを参照してみてください。

CoreDNS(+Amazon DNS plugin)の設定

CoreDNSの設定はとてもシンプルです。ゾーンごとに適用したいプラグインの設定を並べる形となります。今回作成したAmazon DNS pluginの設定としては、

  • soa: SOAレコード設定
  • ns: NSレコード設定(複数設定可)
  • nsa: NSレコードで設定したFQDNを解決するためのAレコード設定(複数設定可)

を行うようにしています。例えば、サブドメインとしてsub.example.orgを管理する設定としては、以下のような設定になります。

. {
    amazondns sub.example.org {
        soa "sub.example.org 3600 IN SOA ns.sub.example.org hostmaster.sub.example.org (2018030619 3600 900 1209600 900)"
        ns "sub.example.org 3600 IN NS ns.sub.example.org"
        nsa "ns.sub.example.org 3600 IN A 192.168.0.10"
    }
}

また、ネームサーバが複数台構成としたい場合(例えば、AWSなのでマルチAZ構成でAZ単位にネームサーバを配置したい場合)があるかと思いますが、その場合は下記のように複数設定ができるようにしています。

. {
    amazondns sub.example.org {
        soa "sub.example.org 3600 IN SOA ns1.sub.example.org hostmaster.sub.example.org (2018030619 3600 900 1209600 900)"
        ns "sub.example.org 3600 IN NS ns1.sub.example.org"
        ns "sub.example.org 3600 IN NS ns2.sub.example.org"
        nsa "ns1.sub.example.org 3600 IN A 192.168.0.10"
        nsa "ns2.sub.example.org 3600 IN A 192.168.0.130"
    }
}

動作確認

事前設定

上記CoreDNSの設定でCoreDNSをVPC内で起動しつつ、AWSの環境を

  • Route 53 Private Hosted Zoneをsub.example.orgというFQDNで作成し、VPCにアタッチ
  • 上記ゾーンにtest.sub.example.orgのAレコードを登録
  • ELBを作成し、上記ゾーンにlb.sub.example.orgでELBのCNAMEレコードを登録

のように設定しておきます。

名前解決

CoreDNSを起動しているサーバにて、digコマンドを使って動作を確認してみます。権威サーバとしての挙動を確認したいので、+norecurseをつけています。

test.sub.example.orgのAレコードが返るだけでなく、AUTHORITY SECTIONADDITIONAL SECTIONが合わせて返ってきているところがポイントです。

> dig @localhost test.sub.example.org +norecurse

; <<>> DiG 9.11.1 <<>> @localhost test.sub.example.org +norecurse
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 28681
;; flags: qr aa ra; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 3

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 23246de45b4a3601 (echoed)
;; QUESTION SECTION:
;test.sub.example.org.        IN  A

;; ANSWER SECTION:
test.sub.example.org.   3600  IN  A  10.0.0.10

;; AUTHORITY SECTION:
sub.example.org.        3600  IN  NS  ns1.sub.example.org.
sub.example.org.        3600  IN  NS  ns2.sub.example.org.

;; ADDITIONAL SECTION:
ns1.sub.example.org.    3600  IN  A   192.168.0.10
ns2.sub.example.org.    3600  IN  A   192.168.0.130

;; Query time: 12 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Tue Feb 20 15:11:55 JST 2018
;; MSG SIZE  rcvd: 146

NSレコードも確認してみます。こちらはCoreDNSの設定ファイルに記述したNSレコードの設定が静的に返ってきます。

> dig @localhost sub.example.org ns

; <<>> DiG 9.11.1 <<>> @localhost sub.example.org ns +norecurse
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 2719
;; flags: qr aa ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 3

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: c1c3332966dba8fd (echoed)
;; QUESTION SECTION:
;sub.example.org.            IN  NS

;; ANSWER SECTION:
sub.example.org.       3600  IN  NS  ns1.sub.example.org.
sub.example.org.       3600  IN  NS  ns2.sub.example.org.

;; ADDITIONAL SECTION:
ns1.sub.example.org.   3600  IN  A   192.168.0.10
ns2.sub.example.org.   3600  IN  A   192.168.0.130

;; Query time: 1 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Tue Feb 20 15:08:27 JST 2018
;; MSG SIZE  rcvd: 125

最後に、作成しておいたELBのCNAMEレコードを解決してみます。通常、CNAMEレコードが返ってくるところなのですが、Amazon DNS pluginの内部処理でAレコードに敢えて変換して返しています(Route 53のAliasのような挙動になります)。こうするとこで、問い合わせ元がAmazon DNSに到達できない場所、つまりVPC外でも名前解決できるようにしています。

> dig @localhost lb.sub.example.org

; <<>> DiG 9.11.1 <<>> @localhost test.sub.example.org +norecurse
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 63630
;; flags: qr aa ra; QUERY: 1, ANSWER: 2, AUTHORITY: 2, ADDITIONAL: 3

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 89a840e16b4d3fc7 (echoed)
;; QUESTION SECTION:
;lb.sub.example.org.         IN  A

;; ANSWER SECTION:
lb.sub.example.org.    54    IN  A   10.0.0.16
lb.sub.example.org.    54    IN  A   10.0.0.132

;; AUTHORITY SECTION:
sub.example.org.       3600  IN  NS  ns1.sub.example.org.
sub.example.org.       3600  IN  NS  ns2.sub.example.org.

;; ADDITIONAL SECTION:
ns1.sub.example.org.   3600  IN  A   192.168.0.10
ns2.sub.example.org.   3600  IN  A   192.168.0.130

;; Query time: 11 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Tue Feb 27 11:20:08 JST 2018
;; MSG SIZE  rcvd: 174

上位の委任元へのフォワード設定

CoreDNSの設定に下記のように上位ドメインに対するフォワード設定を追記しておきます。これにはCoreDNSに付属のproxyプラグインを利用しています。こうすることで、VPC内からこのDNSサーバに対して上位ドメインの名前解決がリクエストされた場合は、オンプレミス側のDNSサーバに問い合わせがフォワードされ名前解決が可能になります。

example.org {
    proxy . /etc/resolv.conf
}

/etc/resolv.conf にはオンプレミス側のネームサーバを設定しておきます。

注意点として、VPC内ではデフォルトではAmazon DNSがDNSサーバとして設定されるため、VPC内のDNSクライアント側では明示的にCoreDNSのサーバをDNSサーバとして設定するか、VPCのデフォルトのDNSサーバを設定変更する必要があります。

上位の委任元のDNSサーバの設定

上位のドメイン(例: example.org)を管理しているDNSサーバにはサブドメイン(例: sub.example.org)への委任を示すグルーレコードの登録をお忘れなく。この登録が完了すれば、オンプレ側のネットワークからサブドメイン配下のFQDNの名前解決ができるようになります。

その他考慮点

せっかくRoute 53を利用しても、CoreDNSサーバの障害対策ができていなければSLA100%の意味がありません。今運用している環境では、AZ障害も考慮して、

という構成にしています。また、上記環境の構築をCloudFormationで自動化もしており、

  • cfn-initでEC2起動時にCloudWatch LogsのエージェントのインストールやCoreDNSのインストール・設定させる

ということもしております。これにより、他のVPCにも横展開が容易にできるようにもしています。

まとめ

AWSハイブリッド構成にて、Route 53 Private Hosted Zoneでサブドメインを管理する方式の一つとして、CoreDNSを活用した方式を紹介しました。同じようにオンプレ-AWSのハイブリッド環境でRoute 53を活用したくて悩んでいらっしゃる方の助けになれば幸いです。

参考