CloudFrontで代替ドメイン名を付け替えるときの注意点〜事前にCNAMEレコードの更新が必要なケース

CloudFrontでCNAMEsを付け替えようとしたところ、重複していないのにCNAMEAlreadyExistsエラーが現れました。ドメイン名が設定するディストリビューション以外の*.cloudfornt.netを指しているとエラーとなるようです。
2020.12.11

はじめに

清水です。先日、Amazon CloudFrontでディストリビューションに設定している代替ドメイン名(Alternate Domain Names (CNAMEs))を別のディストリビューションに付け替える作業を行いました。この作業自体、例えば代替ドメイン名はすべてのCloudFrontディストリビューションで重複が許されないことなどから、特にダウンタイムなく付け替え作業を行う場合はいくつかポイントとなる点があります。 *1 しかしダウンタイムは許容できること、また同じAWSアカウント内の付け替えであること *2 などの理由からそこまでハマらずに作業できるかなと思っていました。具体的には以下の手順です。

  1. 前提条件として、ディストリビューションAに代替ドメイン名www.example.comが設定されている。またRoute 53でwww.example.comのCNAMEレコードとしてディストリビューションAのDomain Name(d1234.cloudfront.net)が設定されている
  2. CloudFrontディストリビューションの設定時、代替ドメイン名の重複が不可であることから、まずディストリビューションAの代替ドメイン名設定www.example.comを削除する
  3. ディストリビューションAの設定変更が反映されたら、続いてディストリビューションBに代替ドメイン名www.example.comを設定する
  4. 最後にRoute 53でwww.example.comのCNAMEレコードをディストリビューションAのDomain Name(d1234.cloudfront.net)からディストリビューションBのDomain Name(d5678.clodufront.net)に変更

実際に上記の手順を行ってみたところ、3.のディストリビューションBに代替ドメイン名を設定することができず、CNAMEAlreadyExistsエラーが発生しました。ディストリビューションAから設定は削除しているのにな、と思い、少し時間をおいてみるも状況は変わらず。ドキュメントなど確認したところ、4.のDNSレコードを予めディストリビューションBのDomain Nameに設定しておく必要があることがわかりました。

さらに確認してみたところ、DNSレコードは登録していない状況であれば設定は可能、またA ALIASレコードでもこの現象はおきません。DNSレコードがディストリビューションAのままだった場合にのみ起きた現象でした。つまり、ディストリビューションへ代替ドメイン名を設定しようとしているときは、そのドメイン名の名前解決結果で他のCloudFrontディストリビューションのDomain nameがCNAMEレコードとして返らない状況が必要であると推測しています。

代替ドメイン名付け替え時のエラーと対策を再現してみた

ということで、注意点やエラー対策などは「はじめに」に述べたとおりとなりますが、具体的に実際の手順の再現を行い確認してみたいと思います。

前提条件となる環境

まずCloudFrontのオリジンとしては、EC2上にAmazon Linuxを準備、Apache + PHPを稼働させておきます。以下のPHPコードを準備し、リクエストヘッダの内容をレスポンスするようにしておきます。(ディストリビューションによって設定を変えて、こちらのレスポンス内容でも確認できるようにしました。)

<?php

foreach (getallheaders() as $name => $value) {
    echo "$name: $value\n";
}

?>

続いてディストリビューションAを作成します。Behavior SettingsはUse legacy cache settingsのデフォルト設定としました。Alternate Domain Names(CNAMEs)にmove-cname.example.comを指定、SSL CertificateではACMに設定した証明書を指定します。また確認用にComment欄にDistribution-Aを設定しました。

ディストリビューションAを作成、状態がDeployedになったらRoute 53でCNAMEレコードを設定します。

レコード設定後、digコマンドで確認すると以下のようにディストリビューションAのレコードが登録されている状態です。

% dig move-cname.example.net +short
dvxrl4sXXXXXX.cloudfront.net.
99.84.130.33
99.84.130.23
99.84.130.67
99.84.130.43

実際にcurlコマンドでリクエストを投げて確認してみると、以下の具合です。

% curl https://move-cname.example.net/
Host: origin-server.example.net
User-Agent: Amazon CloudFront
X-Amz-Cf-Id: w_HJMopPpdBjqxYe9AI1WH9lRvKD6NAWxlf7gBGdtgZ3odSZLeeaZw==
Connection: Keep-Alive
X-Forwarded-For: XX.XX.XX.XX
Via: 2.0 e547c32d3950bb9fc00d08713c96bea4.cloudfront.net (CloudFront)

代替ドメイン名設定で重複させるとエラーが出現することの確認

続いて、同じAWSアカウント上にディストリビューションBを作成します。オリジンはディストリビューションAと同じもの、Behavior SettingsはUse legacy cache settingsでデフォルト設定からCache Based on Selected Request HeadersをAllとしました。Alternate Domain Names(CNAMEs)とSSL Certificateの項目は現段階では設定しません。確認用にComment欄にDistribution-Bを設定します。

さて、まずはディストリビューションAに「move-cname.example.com」の代替ドメイン名の設定が残った状態で、ディストリビューションBにも代替ドメイン名「move-cname.example.com」を設定しようとしてみます。が、これはエラーとなります。CloudFrontの代替ドメイン名は重複ができないためですね。

ディストリビューションAから代替ドメイン名設定の削除

それでは、と、まずはディストリビューションAから「move-cname.example.com」の代替ドメイン名の設定を削除します。

なお、ここで[Yes, Edit]をしてディストリビューション詳細画面に遷移すると以下のようなWarning!が現れます。

ディストリビューションAから「move-cname.example.com」の代替ドメイン名の設定が削除できました。

ディストリビューションBへの代替ドメイン名設定の試行とエラーの出現

これならCloudFrontの代替ドメイン名の重複状態にはなりませんよね。設定できるはず、と考え設定してみようとしますが、やはりエラーが発生してしまします。内容は先ほどと同じくCNAMEAlreadyExistsエラーです。

エラー回避方法の確認としてのDNSレコード更新

Amazon CloudFront 開発者ガイドの「代替ドメイン名 (CNAME) を追加してカスタム URL を使用する」のページを確認してみましょう。

今回の代替ドメイン名の状況は、「代替ドメイン名を使用する上での制限」の「既にディストリビューションを指している代替ドメイン名」の項目に状況が類似しているかと思いました。対策としては、事前にDNSレコードを更新する必要があるとのことです。ということなので、ディストリビューションBに代替ドメインを設定する前に、Route 53側でCNAMEレコードをディストリビューションBのDomain Nameに更新します。

digコマンドで確認すると、以下のようにディストリビューションBのレコードとなってる状況に更新されました。

% dig move-cname.example.net +short
dl45va6XXXXXX.cloudfront.net.
13.32.52.228
13.32.52.183
13.32.52.18
13.32.52.176

この状態で、再度ディストリビューションBに代替ドメイン名を設定しています。今度は設定ができました!

StatusがDeployedになるのをまって、curlコマンドで代替ドメイン名に設定したドメインmove-cname.example.netにリクエストを投げてみます。レスポンスには以下のようにCloudFront-*なヘッダが増えているので、ディストリビューションBが設定されていることがわかります。

% curl https://move-cname.example.net/
Host: move-cname.example.net
User-Agent: curl/7.64.1
X-Amz-Cf-Id: cc6FwHjN8PMa_QunyRdkG1-I0vkBoKeVJzvnRI5Zr6iZyhKoOvMIlw==
Connection: Keep-Alive
Via: 2.0 53784e962439e344b6be04336e793994.cloudfront.net (CloudFront)
X-Forwarded-For: XX.XX.XX.XX
Accept: */*
CloudFront-Is-Mobile-Viewer: false
CloudFront-Is-Tablet-Viewer: false
CloudFront-Is-SmartTV-Viewer: false
CloudFront-Is-Desktop-Viewer: true
CloudFront-Viewer-Country: JP
CloudFront-Forwarded-Proto: https

DNSレコードが存在しないケースでの確認

DNSレコードの更新の際に、上記の例ではRoute 53のCNAMEレコードをディストリビューションBのDomain Nameに更新を行いました。これとは別にRoute 53のCNAMEレコード(ディストリビューションAのDomain Nameに設定されている)を削除する、というパターンも確認してみました。この状態でdigコマンドでmove-cname.example.netを確認すると、レコードが存在しない状況であることがわかります。

% dig move-cname.example.net

; <<>> DiG 9.10.6 <<>> move-cname.example.net
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 53072
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;move-cname.example.net.                IN      A

;; AUTHORITY SECTION:
example.net.            897     IN      SOA     ns-XXXX.awsdns-XX.co.uk. awsdns-hostmaster.amazon.com. 1 7200 900 1209600 86400

;; Query time: 15 msec
;; SERVER: 192.168.XXX.XXX#53(192.168.XXX.XXX)
;; WHEN: Fri Dec 11 19:49:58 JST 2020
;; MSG SIZE  rcvd: 138

この状態で、ディストリビューションBに代替ドメイン名を設定すると、CNAMEAlreadyExistsエラーは発生せず、こちらも設定することができました。

以上から、CloudFrontディストリビューションで代替ドメイン名Alternate Domain Names (CNAMEs)を設定する際には、その代替ドメイン名のDNSレコードをチェックし、設定しようとしているディストリビューションのDomain Name以外の値がCNAMEで設定されていた場合はCNAMEAlreadyExistsエラーが発生する、というような挙動なのかなと推測しています。

A ALIASレコードを使っている場合の確認

上記事象については、いずれもDNSレコードの登録をRoute 53でCNAMEレコードを使って行っていました。それではA ALIASレコードで行った場合はどうなるのかも気になったので確認してみました。まずはディストリビューションAにmove-aalias.example.netの代替ドメイン名を設定します。

そしてRoute 53でA ALIASレコードとしてディストリビューションAを登録します。

この状態から、ディストリビーションAの代替ドメイン名の設定を削除。そしてDNSレコードの設定はそのままに、ディストリビューションBに代替ドメイン名move-aalias.example.netを設定します。CNAMEレコードを使っていたときはここでCNAMEAlreadyExistsエラーが発生していましたが、A ALIASレコードではCNAMEAlreadyExistsエラーは発生せず、そのまま設定できる、という挙動になりました。

これは、A ALIASレコードでは名前解決を行っても、直接IPアドレスが返るのでディストリビューションのDomain Nameがレコードからはわからない、ということが起因しているのではないか、と予想しています。

% dig move-aalias.example.net +short
99.84.130.23
99.84.130.43
99.84.130.33
99.84.130.67

まとめ

CloudFrontで代替ドメイン名を付け合えるときに遭遇したCNAMEAlreadyExistsエラーとその対策についてまとめてみました。今回の、「CloudFrontディストリビューション内では代替ドメイン名が重複していないのに、ドメイン名の名前解決結果が別のディストリビューションのDomain NameになっているとCNAMEAlreadyExistsエラーが出てしまう」、という挙動、たしか以前はエラーが出ずに代替ドメイン名が付け替えられたと思っていたのですが。もしかしたら代替ドメイン名を設定する際に証明書が必要になった、などのタイミングでアップデートされたのか、もしくはA ALIASレコードのときの挙動と勘違いしてしまっていたのか。いずれにせよ、現状では本エントリで扱ったように、CNAMEレコードで別のディストリビューションのDomain Nameを示している時はCNAMEAlreadyExistsエラーが発生します。Warning!としても示されたとおり、CloudFrontディストリビューション側の代替ドメイン名の削除を行ったタイミングで、Route 53などDNS側のレコードも変更・削除するよう意識すると良いかと思いました。

脚注

  1. ダウンタイムなく代替ドメイン名を付け替える場合の方法はこちらのブログエントリなどをご参照ください。 → CloudFrontのCNAMEAlreadyExistsエラーを回避しつつCNAMEを付け替える | Developers.IO
  2. 見知らぬ第3者によってすでに代替ドメインが設定されてしまっているような場合、TXTレコードを使ってドメイン所有者である証拠をAWSに示す必要があります。このようなケースはこちらの公式FAQの内容が参考になるかと思います。 → CloudFront エラー「CNAMEAlreadyExists」の解決