Route 53で動的DNSの設定（DDNS）

前回は、いろいろな構成で自分自身のインターネット上から見れるIPをどのようにとるか解説しました。
https://qiita.com/takeofuture/items/293bab2628429498642c

ここでは、どのようにDDNSを運用するかの忘備録になります。
DDNSは基本は普通の静的DNS + 自動IPレコード更新機能だという理解です。
今回はRoute　53で実験しました
（自動更新する仕組みがないDNSサービスではできない仕組みであるとは思います）

- １).　
まず静的DNSをZONEに作る。（ZONEは既にあるもととします。ここではAレコードのDDNSを実施することを想定してます）
  まず現在の自分のIPをチェック。前回の記事でも書いたようここで大事なのは必ずグローバルアドレスである必要があります。オフィスなどでは普通は各端末や社内サーバーにグローバルアドレスでなくいわゆるプライベートアドレスというものです。ただしこのプライベートアドレスはインターネットからは到達できないためDDNSでは使用することはできません。
https://api.ipify.org/?format=json
https://whatismyipaddress.com/ja/index
のようなサイトからまず**現在**の自分のグローバルアドレスを確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3955606/8d6b0828-0474-753f-030c-a994664935e0.png)

- 2). 
Route 53のゾーンにAレコードを設定（ゾーン自他の作成方法は割愛いたします）
今回はZONEで定義したドメインにddns_test というAレコードを設定しました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3955606/715c7142-26b3-4b4c-793f-e30dd6435fca.png)


- 3).
次にアクセス権限を持つIAMを作成します。ここは一般的なIAMの設定とポリシーの設定などで、文書は割愛してあまり説明文書なしでスクショだけぱぱっと貼り付けます。m(__)m

*アクセスポリシーの定義(ZONE IDはRoute 53の対象ZONEより取ってきてください)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3955606/0b4adef3-590e-4ac7-6a01-e13aeddb1d31.png)
```
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "route53:ChangeResourceRecordSets",
        "route53:GetChange",
        "route53:ListResourceRecordSets"
      ],
      "Resource": "arn:aws:route53:::hostedzone/<ZONE_ID>"
    }
  ]
}

```
ZONE IDはRoute 53の対象ZONEより
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3955606/7594f0cb-ccd7-9449-eddd-a2eddd40bade.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3955606/4a1cd863-70de-5060-3d95-981354738983.png)

*ユーザ設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3955606/c99c3277-6f67-79a6-e38b-99e0b5ff3b49.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3955606/91246a78-9389-0cf5-ca81-90bc1523f8b4.png)

上で設定したポリシーを割り当てます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3955606/60b5e1e5-78c6-9e1b-dd30-4b5ac10a2ddc.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3955606/9c3f8875-7732-9a14-cc70-525dccf659ff.png)

次にユーザーのアクセスキーを作成します。大事な秘蔵情報なので管理には注意。
（間違ってもQIITAの公開記事にはいれないようにしないと💦）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3955606/a29507c2-7712-d4d6-ef6b-38ae47c6d35b.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3955606/85a7f128-4d67-fb30-6658-aa7dd4b0637e.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3955606/2228911d-af85-6193-b165-2851a1fd8c36.png)

２つのキーを保存しておきます（あとでアクセスに使います）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3955606/2e620fbc-046e-d9fd-56c9-19e47d4cb515.png)

AWSのRoute53のようなAWS CLIコマンドでもできるようです
https://github.com/little-forest/aws-r53

私はPYTHONで管理したかったので、PYTHONで実施してます
いきなり説明なしにコードを貼り付けます。
前回の記事の説明とうり社内サーバーなどがあるとして、その社内サーバーがグローバルIPで割り当てられていても、プライベートで割り当てられていても、そのサーバーに対してインターネット上にあるユーザーや端末がまずどこに接続してリクエストを送ればいいかを把握するものになります。
（ **注意：** ロードバランスで複数のIPを使っている場合や、アウトバウンドとインバウンドで使うIPが異なるような設計のネットワークでは正常に動かない可能性もあります）
```
import boto3
import requests
from config import AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, ZONE_ID, DOMAIN_NAME

def update_route53_record(zone_id, domain_name, ip_address, ttl=300):
    """
    Route 53のAレコードをUPSERTする関数
    Args:
        zone_id (str): ホストゾーンのID
        domain_name (str): 更新するドメイン名（例: example.jp）
        ip_address (str): 設定するIPアドレス
        ttl (int): TTL（デフォルト: 300秒）
    Returns:
        dict: Route 53のレスポンス
    """
    client = boto3.client(
        'route53',
        aws_access_key_id=AWS_ACCESS_KEY_ID,
        aws_secret_access_key=AWS_SECRET_ACCESS_KEY
    )
    change_batch = {
        'Changes': [
            {
                'Action': 'UPSERT',
                'ResourceRecordSet': {
                    'Name': domain_name,
                    'Type': 'A',
                    'TTL': ttl,
                    'ResourceRecords': [{'Value': ip_address}],
                }
            }
        ]
    }
    response = client.change_resource_record_sets(
        HostedZoneId=zone_id,
        ChangeBatch=change_batch
    )
    return response

def get_global_ip():
    """
    グローバルIPアドレスを取得する。
    Returns:
        str: グローバルIPアドレス
    """
    try:
        response = requests.get("https://api.ipify.org?format=json")
        response.raise_for_status()
        return response.json().get("ip")
    except requests.RequestException as e:
        print(f"エラーが発生しました: {e}")
        return None
        
# 使用例
if __name__ == "__main__":
    # 必要なパラメータを設定
    ZONE_ID = ZONE_ID  # ホストゾーンID
    DOMAIN_NAME = DOMAIN_NAME   # 更新するドメイン名
    IP_ADDRESS = get_global_ip()  # 更新するIPアドレス
    TTL = 60
    try:
        print(f"A: IP[{IP_ADDRESS}] => FQDN:[{DOMAIN_NAME}]で更新。")
        response = update_route53_record(ZONE_ID, DOMAIN_NAME, IP_ADDRESS, TTL)
        print("Aレコードを更新しました:")
        print(response)
    except Exception as e:
        print(f"エラーが発生しました: {e}")
```

早速、テストしました。
異なるグローバルIPが設定してある最初に初期設定したWIFIネットワークとは違うWIFIに接続してコード実行しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3955606/08a01e28-8dfc-3fcc-ffdd-232d25bcdb74.png)

Route 53の該当ZONEの対象Aレコードをチェックして新しいIPで更新されていることが確認できました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3955606/cf8e98a3-1afd-f296-d634-af6e9bfdc5fe.png)