Cloudflare를 사용한 동적 DNS 호스트 이름

Cloudflare DNS 호스트(A) 레코드를 서버의 새 IP 주소로 자동 업데이트하는 Python 스크립트.

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

Cloudflare를 DNS 공급자로 사용하는 경우 VM에 새 IP 주소가 표시될 때마다 Python 스크립트를 사용하여 VM의 DNS 호스트 레코드를 업데이트할 수 있습니다. 스크립트는 호스트의 외부 IP 주소를 얻기 위해 https://api.ipify.org/ 를 호출하기 때문에 서버 자체에서 실행되어야 합니다.

적절한 승인으로 Cloudflare API 토큰 얻기

이 스크립트를 실행하려면 Cloudflare에서 몇 가지 세부 정보를 얻어야 합니다. 계정에서 도메인의 "개요" 섹션 아래 "API"에서 영역 ID를 찾고 해당 정보를 복사하고 스크립트에서 <zone_id> 를 대체할 수 있어야 합니다.

그런 다음 "API 토큰 가져오기"를 클릭합니다.

해당 페이지의 "API 토큰"에서 "토큰 생성"을 클릭하고 "영역 DNS 편집" 템플릿을 사용합니다.

다음 권한이 필요합니다.

  • 리소스: 영역, 권한: 영역, 읽기
  • 리소스: 영역, 권한: DNS, 편집

"영역 리소스"에서 "특정 영역 포함"을 선택하고 도메인 이름을 선택합니다.

그런 다음 Cloudflare는 사용할 수 있는 API 토큰을 제공합니다. 스크립트의 <cloudflare_token> 값을 해당 토큰으로 바꿔야 합니다.

또한 <your.dns.name> 값을 호스트가 가질 DNS 호스트 이름(A 레코드)으로 바꿔야 합니다.

DNS 레코드의 ID 얻기

그런 다음 해당 특정 DNS 항목의 Cloudflare ID인 <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>"를 해당 필드 값으로 바꿉니다. 이 작업은 한 번만 수행하면 되며 레코드를 삭제하지 않는 한 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}}'

현재 IP로 DNS 호스트(A) 레코드 업데이트

이제 필요한 만큼 해당 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