Cloudflareを使用したダイナミックDNSホスト名

サーバーの新しいIPアドレスでCloudflareDNSホスト(A)レコードを自動的に更新するPythonスクリプト。

C05348A3-9AB8-42C9-A6E0-81DB3AC59FEB
           

CloudflareをDNSプロバイダーとして使用している場合は、Pythonスクリプトを使用して、VMが新しいIPアドレスを検出するたびにVMのDNSホストレコードを更新できます。スクリプトはサーバー自体で実行する必要があります。これは、 https://api.ipify.org/を呼び出して、ホストの外部IPアドレスを取得するためです。

適切な認証でCloudflareAPIトークンを取得する

このスクリプトを実行する前に、Cloudflareからいくつかの詳細を取得する必要があります。アカウントのドメインの[概要]セクションの[API]で、ゾーンIDを見つけ、その情報をコピーして、スクリプトで<zone_id>を置き換えることができるはずです。

次に、[APIトークンを取得]をクリックします。

そのページの[APIトークン]で、[トークンの作成]をクリックし、[ゾーンDNSの編集]テンプレートを使用します。

次の権限が必要です。

  • リソース:ゾーン、権限:ゾーン、読み取り
  • リソース:ゾーン、権限:DNS、編集

[ゾーンリソース]で、[特定のゾーンを含める]を選択し、ドメイン名を選択します。

Cloudflareは使用できるAPIトークンを提供します。スクリプト内の<cloudflare_token>の値をそのトークンに置き換える必要があります。

また、 <your.dns.name>の値を、ホストに持たせるDNSホスト名(Aレコード)に置き換える必要があります。

DNSレコードのIDを取得します

次に、その特定のDNSエントリのCloudflareIDである<record_id>の適切な値を取得する必要があります。これを行うには、Cloudflareで任意のIPアドレスを使用してそのホスト(A)レコードを手動で作成し、次のスクリプトを使用してそのレコードのIDを照会する必要があります。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
 
import requests
 
token = '<cloudflare_token>'
DNSname = '<your.dns.name>'
zone_id = '<zone_id>'
 
url = f'https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records?type=A&name={DNSname}&page=1&per_page=20&order=type&direction=desc&match=all'
 
headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
    'Authorization': f'Bearer {token}'
}
 
try:
    r = requests.request(method='GET', url=url, headers=headers, timeout=25)
except Exception as x:
    print('Connection failed :( %s' % x.__class__.__name__)
else:
    print(r.status_code)
    if r.status_code == 200:
        response = r.content
        print(response)
 
    else:
        print('Error: %s' % r.status_code)
        print(r.headers)
        print(r.text)

このスクリプトは、次のようなものを出力します(大幅に編集されています)。返されたフィールド「id」の値に注意し、「<record_id>」を以下のスクリプトのそのフィールドの値に置き換えます。これを行う必要があるのは1回だけで、レコードを削除しない限りIDは変更されません。

python3 get_record_id.py 
200
b'{"result":[{"id":"<record_id>","zone_id":"<zone_id>","zone_name":"dns.name","name":"your.dns.name","type":"A","content":"172.0.0.1","proxiable":true,"proxied":false,"ttl":120,"locked":false,"meta":{"auto_added":false,"managed_by_apps":false,"managed_by_argo_tunnel":false,"source":"primary"},"created_on":"2021-07-31T21:13:57.939216Z","modified_on":"2022-03-07T00:37:04.153213Z"}],"success":true,"errors":[],"messages":[],"result_info":{"page":1,"per_page":20,"count":1,"total_count":1,"total_pages":1}}'

DNSホスト(A)レコードを現在のIPで更新します

これは、そのDNS名のIPアドレスを必要な回数だけ設定するために使用できるスクリプトです。スクリプトは最初にhttps://api.ipify.org/を呼び出してホストのパブリックIPを取得し、次にそのIPアドレスでDNSレコードを更新することに注意してください。一言で言えばダイナミックDNS!

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
 
""""
Update the value of an existing A record in Cloudflare based on the host's current public IP address.
"""
 
 
# I M P O R T S ###############################################################
 
import urllib
import json
import sys
import os
import time
from logging.handlers import RotatingFileHandler
import traceback
import datetime
import requests
import logging
 
__version__ = "1.0.1"
 
 
# G L O B A L S ###############################################################
 
zone_id = '<zone_id>'
token = '<cloudflare_token>'
DNSname = '<your.dns.name>'
record_id = '<record_id>'
 
logger = logging.getLogger()
logger.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
ch.setFormatter(formatter)
logger.addHandler(ch)
try:
    logFile = os.path.realpath(__file__).split('.')[-2] + ".log"
    fh = RotatingFileHandler(logFile, maxBytes=(1048576 * 50), backupCount=7)
    fh.setLevel(logging.DEBUG)
    fh.setFormatter(formatter)
    logger.addHandler(fh)
    logger.info("Log file:    %s" % logFile)
except Exception as e:
    logger.warning("Unable to log to file: %s - %s" % (logFile, e))
logger.info("-" * 80)
logger.info("Version:  %s" % (__version__))
 
 
# F U N C T I O N S ###########################################################
 
 
def main():
 
    url = f'https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records/{record_id}'
 
    t0 = time.time()
    try:
        r = requests.request(method='GET', url='https://api.ipify.org/', timeout=25)
    except Exception as x:
        logger.error('Connection failed :( %s' % x.__class__.__name__)
    else:
        if r.status_code == 200:
            response = r.content
            newIP = str(response, 'utf-8').strip()
            logger.info(newIP)
        else:
            logger.error('Error: %s' % r.status_code)
            logger.debug(r.headers)
            logger.debug(r.text)
            logger.debug(sys.exc_info()[:2])
    finally:
        t1 = time.time()
        logger.info('Connection took %.4fs' % (t1 - t0))
 
 
    headers = {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
        'Authorization': f'Bearer {token}'
    }
    data = {
        "type": "A",
        "name": DNSname,
        "content": newIP,
        "ttl": 120,
        "proxied": False
    }
 
    logger.info("Updating IP address")
    t0 = time.time()
    try:
        r = requests.request(method='PUT', url=url, data=json.dumps(data), headers=headers, timeout=25)
    except Exception as x:
        logger.error('Connection failed :( %s' % x.__class__.__name__)
    else:
        logger.info(r.status_code)
        if r.status_code == 200:
            response = r.content
            logger.info(response)
        else:
            logger.error('Error: %s' % r.status_code)
            logger.error(r.headers)
            logger.error(r.text)
            logger.error(sys.exc_info()[:2])
    finally:
        t1 = time.time()
        logger.info('Connection took %.4fs' % (t1 - t0))
 
    sys.exit(0)
 
 
if __name__ == "__main__":
    main()
 
# E N D   O F   F I L E #######################################################

このスクリプトは、IPアドレスが変更されていない場合でも、レコードを盲目的に更新することに注意してください。必要に応じて、その値を保存し、変更された場合にのみレコードを更新することで、より効率的にすることができます。

このスクリプトはログファイルにも書き込むため、タスクをスケジュールした場合に結果を確認できます。

投稿コメント 0

Tagged with:
DNS networking