Cloudflare를 사용한 동적 DNS 호스트 이름
Cloudflare DNS 호스트(A) 레코드를 서버의 새 IP 주소로 자동 업데이트하는 Python 스크립트.
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 주소가 변경되지 않은 경우에도 레코드를 맹목적으로 업데이트합니다. 원하는 경우 해당 값을 저장하고 변경된 경우에만 레코드를 업데이트하여 더 효율적으로 만들 수 있습니다.
이 스크립트는 작업을 예약할 경우 결과를 검토할 수 있도록 로그 파일에도 기록합니다.
Tagged with:
DNS networking