前提となる環境
- オンプレミス環境に既に権威サーバがある(例:
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)を追加することで対応しています。構成図としては以下のようになります。
※BINDなどのよく使われるDNSサーバはフォワーダーとしては設定できますが、権威サーバとして振る舞うように設定ができませんでした。もし設定できる/他のDNSサーバでできるよ、という情報がありましたら是非教えていただきたいです
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 SECTION、ADDITIONAL 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障害も考慮して、
- AZを2つ利用
- AZ単位にCoreDNSサーバを構築
- 事前にENIを作成して固定プライベートIP化しておき、EC2起動時にLambdaで自動アタッチさせる
- AutoScalingGroupをAZ単位に作成してMaxとMinを1台としておき、EC2障害時に自動復旧させる
- CoreDNSのログはCloudWatch Logsで外部に保存
という構成にしています。また、上記環境の構築をCloudFormationで自動化もしており、
- cfn-initでEC2起動時にCloudWatch LogsのエージェントのインストールやCoreDNSのインストール・設定させる
ということもしております。これにより、他のVPCにも横展開が容易にできるようにもしています。
まとめ
AWSハイブリッド構成にて、Route 53 Private Hosted Zoneでサブドメインを管理する方式の一つとして、CoreDNSを活用した方式を紹介しました。同じようにオンプレ-AWSのハイブリッド環境でRoute 53を活用したくて悩んでいらっしゃる方の助けになれば幸いです。
参考
- AWSハイブリッド構成のDNS設計レシピ
- オンプレからAWSのDNSを引く高可用性な構成
- Expose internal route53 DNS over VPN to on-premise ActiveDirectory ← 同じことをやりたいという質問ですが、良い回答は得られていませんでしたので、今回作成したCoreDNS + Amazon DNS pluginを紹介しています。