CloudFlareを利用したIPアドレス(IPv6/IPv4)のDNSレコード自動更新

:::note info
2023/06/06 ついでにIPv4も対応させコードを大幅に変更しました。
2023/06/05 レコード更新タイミングをDNS逆引きと一致しない時だけにしました。
2023/06/04 初版
:::

# 初めに
WEBサーバーIPv6移行についてCloudFlareを利用した
DNSレコード自動更新方法について（※会員登録方法などは割愛します。）
既にMyDNSなどでDDNSを運用されてる方は競合するので停止するか見直して下さい。

以下記事参考にしてます。先駆者様に感謝
https://zenn.dev/ten_takano/articles/20230706-cloudflare-ddns
https://qiita.com/napspans/items/455103748f3a0dd7ee18


# CloudFlareにて空のDNSレコードを登録
WEBサイトメニューから自サイトを登録して設定に進みます。
![cf001.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/802449/d3521757-9888-7c9b-0a21-12188b5f41b3.jpeg)

DNSレコードからレコードの追加を押下
![cf012.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/802449/735c071f-5a64-ff8e-268c-35b2117a1705.jpeg)

タイプAAAA（IPv6）、名前（自ドメイン）、IPv6アドレスに::と入力
プロキシを外して空のレコードを登録します。（このレコードが後で更新されます。）
![cf013.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/802449/86b1a4fd-27b0-84ac-da0c-84220cd5f699.jpeg)

空のレコードが追加されたか確認します。
![cf015.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/802449/c0032759-be93-d6d6-2615-5d773a378ea8.jpeg)


概要ページに戻り下にスクロールしていくと APIトークンを取得 があるので押下
![cf002.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/802449/36cf8ef9-3ae6-ddec-95a7-a082b90d146b.jpeg)

ユーザーAPIトークンからトークンを作成するを押下
![cf005.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/802449/9d56045d-7f41-8422-af07-cf93ecb320ec.jpeg)

ゾーンDNSのテンプレートを使用します。
![cf006.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/802449/476c6bc4-0ceb-4d67-6fa1-b445627ef722.jpeg)

アクセス許可で＋さらに追加するを押下し、ゾーン・ゾーン・読み取りで追加し
ゾーンリソースで自ドメインを指定する
![cf007.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/802449/723c3613-d7a8-b2dc-6901-41389888f364.jpeg)

概要に進むで次へ
![cf008.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/802449/509c0784-bfe2-5d9b-b09e-2988b86552dc.jpeg)

APIトークンがDNS編集、ゾーン読み取りになってるのを確認して作成を押下する
![cf009.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/802449/0df0a7ff-c9dc-6d20-09da-06930ec9d9e4.jpeg)

APIトークンが表示されます。次に作成するシェルスクリプトで指定しますので控えて下さい。
※閉じると二度と表示されません
![cf010.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/802449/9f7933a1-2754-04c3-90b2-25e2644871c1.jpeg)

# シェルスクリプト作成
例としてCloudFlare_ip.shで作成します。
```sh: shell (Linux)
vi CloudFlare_ip.sh
```
以下設定を自環境に置き換えて下さい
DOMAIN="自ドメイン"
EMAIL="アカウント登録のメールアドレス"
NIC="グローバルIP取得しているNIC名"
API_TOKEN="先ほど生成したトークン"

動作概要
IPとcurlコマンドでIPv6とIPv4のアドレスを取得し
nslookupでドメインを逆引き取得しそれぞれを比較
何れか一致しなかった時だけ、
APIトークンからゾーンID＆DNSレコードIDを取得しDNSレコードを更新します。
IPv6はAAAAレコード且つ自ドメイン＝nameが一致するレコード
IPv4はAレコード且つ自ドメイン＝nameとwww.自ドメイン＝name一致のレコード
```sh: CloudFlare_ip.sh（Linux）
#!/bin/bash

# 設定
DOMAIN="niuind.com"
EMAIL="ここにメールアドレス"
NIC="enp1s0"
API_TOKEN="ここにAPIトークン"

# 設定終わり


# IPv6アドレスをNICから取得
NICIP6=`ip -f inet6 -o addr show ${NIC}|grep global|cut -d\  -f 7 | cut -d/ -f 1`
# IPv4アドレスをcurlで取得
NICIP4=`curl -s -4 ifconfig.me`

# nslookupで逆引き(IPv6)
DNSIP6=`nslookup -query=aaaa ${DOMAIN} | grep 'Address:' | awk 'NR==2{print $2}'`
# nslookupで逆引き(IPv4)
DNSIP4=`nslookup -query=a ${DOMAIN} | grep 'Address:' | awk 'NR==2{print $2}'`


Set_CFDNSRecord(){
        # 1DOMAIN 2EMAIL 3API_TOKEN 4NICIP 5DNSIP 6TYPE 7WWW

        NAME="$7$1"

        # ZONE_ID 取得
        ZONE_ID=`curl -s "\n" --request GET \
                --url https://api.cloudflare.com/client/v4/zones?name=$1 \
                --header 'Content-Type: application/json' \
                --header "X-Auth-Email: $2" \
                --header "Authorization: Bearer $3" | jq -r .result[].id`
        #echo "ZONE_ID:" $ZONE_ID

        # AAAA or A 且つNAMEがDOMAINと一致のRECORD_ID 取得
        RECORD_ID=`curl -s "\n" --request GET \
                --url https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records \
                --header 'Content-Type: application/json' \
                --header "X-Auth-Email: $2" \
                --header "Authorization: Bearer $3" | jq -r '.result[] | select(.type=="'"$6"'" and .name=="'"$NAME"'") | .id'`
        #echo "RECORD_ID("$6"):" $RECORD_ID

        DATE=`date '+%Y-%m-%d %H:%M:%S'`

        # CloudFlare レコード更新
        FRAG=`curl -s --request PUT \
          --url https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$RECORD_ID \
          --header 'Content-Type: application/json' \
          --header "X-Auth-Email: $2" \
         --header "Authorization: Bearer $3" \
         --data '{
           "content": "'"$4"'",
           "name": "'"$NAME"'",
           "proxied": false,
           "type": "'"$6"'",
           "comment":"'"renew at ${DATE}"'"
         }' | jq -r '.success'`

        # successなら更新完了とみなす
        if [ $FRAG = true ]; then
                echo "CloudFlare 更新完了しました($6 $NAME)"
                echo "OLD:$5"
                echo "NEW:$4"
        else
                echo "更新失敗しました。"
                echo "NICIP:$4"
                echo "DNSIP:$5"
        fi
}

# IPv6 or IPv4 どちらか一致しなければレコード更新する
if [ ! "$NICIP6" = "$DNSIP6" ]; then
        Set_CFDNSRecord $DOMAIN $EMAIL $API_TOKEN $NICIP6 $DNSIP6 "AAAA"
fi
if [ ! "$NICIP4" = "$DNSIP4" ]; then
        Set_CFDNSRecord $DOMAIN $EMAIL $API_TOKEN $NICIP4 $DNSIP4 "A"
        Set_CFDNSRecord $DOMAIN $EMAIL $API_TOKEN $NICIP4 $DNSIP4 "A" "www."
fi
if [ "$NICIP6" = "$DNSIP6" ] && [ "$NICIP4" = "$DNSIP4" ]; then
        echo "IP一致の為更新ありません。"
        echo "NICIP6: $NICIP6"
        echo "DNSIP6: $DNSIP6"
        echo "NICIP4: $NICIP4"
        echo "DNSIP4: $DNSIP4"
fi
```

忘れずに実行権限を付与して実行します。
```shell: shell (Linux)
chmod +x CloudFlare_ip.sh
./CloudFlare_ip.sh
```


更新完了したらCloudFlareのDNSレコードから確認して下さい。
![cf011.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/802449/066e9151-e384-290a-46ca-b2b3846e3046.jpeg)

定期実行例としてsystemdでユニット化してタイマーで回します。
```shell: shell (Linux)
vi /etc/systemd/system/CloudFlare_ip.service
```
```shell: CloudFlare_ip.service (Linux)
[Unit]
Description=CloudFlare IP renew dns record

[Service]
Type=simple
ExecStart=/usr/bin/CloudFlare_ip.sh

[Install]
WantedBy=multi-user.target                               
```
作成したCloudFlare_ip.shを移動しておきます。
```shell: shell (Linux)
mv CloudFlare_ip.sh /usr/bin/CloudFlare_ip.sh
```
続いてtimerを作成します。
```shell: shell (Linux)
vi /etc/systemd/system/CloudFlare_ip.timer
```

とりあえず15分定期実行にしてみます。
※IP一致判定をするだけでIP不一致でなければCloudFlareでレコード更新しません
```shell: CloudFlare_ip.timer (Linux)
[Unit]
Description=CloudFlare IP renew dns record

[Timer]
Persistent=true
OnCalendar=*-*-* *:00,15,30,45

[Install]
WantedBy=timers.target
```
サービスの自動起動を有効にしスタートする
```shell: shell (Linux)
systemctl enable CloudFlare_ip.timer
systemctl start CloudFlare_ip.timer
```

タイマー有効にしたら確認。以下の様に表示されていればOK
```shell: shell (Linux)
systemctl list-timers
NEXT                        LEFT          LAST                        PASSED       UNIT                         ACTIVATES
Wed 2024-06-05 11:45:00 JST 2min 16s left Wed 2024-06-05 11:42:41 JST 1s ago       CloudFlare_ip.timer        CloudFlare_ip.service
```

ログとしてはこんな感じに出ます。
```shell: shell (Linux)
systemctl status CloudFlare_ip.service --lines=20
6月 06 17:00:11 localhost.localdomain CloudFlare_ip.sh[11038]: CloudFlare 更新完了しました(AAAA niuind.com)
6月 06 17:00:11 localhost.localdomain CloudFlare_ip.sh[11038]: OLD:(DNSIP)
6月 06 17:00:11 localhost.localdomain CloudFlare_ip.sh[11038]: NEW:(NICIP)
6月 06 17:00:13 localhost.localdomain CloudFlare_ip.sh[11038]: CloudFlare 更新完了しました(A niuind.com)
6月 06 17:00:13 localhost.localdomain CloudFlare_ip.sh[11038]: OLD:(DNSIP)
6月 06 17:00:13 localhost.localdomain CloudFlare_ip.sh[11038]: NEW:(NICIP)
6月 06 17:00:14 localhost.localdomain CloudFlare_ip.sh[11038]: CloudFlare 更新完了しました(A www.niuind.com)
6月 06 17:00:14 localhost.localdomain CloudFlare_ip.sh[11038]: OLD:(DNSIP)
6月 06 17:00:14 localhost.localdomain CloudFlare_ip.sh[11038]: NEW:(NICIP)
6月 06 17:45:06 localhost.localdomain CloudFlare_ip.sh[11238]: IP一致の為更新ありません。
6月 06 17:45:06 localhost.localdomain CloudFlare_ip.sh[11238]: NICIP6: (NICIP)
6月 06 17:45:06 localhost.localdomain CloudFlare_ip.sh[11238]: DNSIP6: (DNSIP)
6月 06 17:45:06 localhost.localdomain CloudFlare_ip.sh[11238]: NICIP4: (NICIP)
6月 06 17:45:06 localhost.localdomain CloudFlare_ip.sh[11238]: DNSIP4: (DNSIP)
```

# その他注意
トリガーがnslookupの逆引きと一致しなければ、なので
DNSレコードが更新された後も他のネームサーバーのキャッシュ残がヒットして
再度更新するなど処理が重複する可能性があります。
（問題になる様な負荷では無いですが）
DNSレコードのProxyを設定すると当然CloudFlareのProxyアドレスが返ってくるので
ホストのIPアドレスと一致する事はほぼなくなり毎回DNSレコードを更新する様になります。


以上。お疲れ様でした。