DDNS: CloudFlare

July 1, 2019

    DDNS

    홈 서버의 공인 IP 주소에 도메인을 연결해두면 외부에서 홈 서버의 SSH, FTP, VPN 등에 접속할 때 숫자로 된 IP 주소 대신에 도메인을 입력할 수 있어서 편리하다. 다만 가정에 서비스되는 인터넷 회선은 대부분 유동 IP 주소 서비스이기 때문에 공인 IP 주소가 변경될 가능성이 있다. 그래서 IP 주소가 변경되면 변경된 IP 주소로 도메인을 다시 연결해주는 DDNS (Dynamic DNS) 서비스가 필요하다.

    CloudFlare DNS

    만일 자신의 도메인이 연결된 DNS가 DDNS 기능을 제공한다면 그것을 이용하면 되고, DDNS 기능이 없다면 DDNS를 적용할 수 있는 별도의 DNS 서비스에 도메인을 연결해서 이용할 수 있다. 여기서는 CloudFlare의 DNS 서비스를 선택했는데, CloudFlare가 제공하는 API를 이용하면 별도의 프로그램을 설치하지 않아도 간단한 셸 스크립트만으로 DDNS 기능을 적용할 수 있다. CloudFlare는 DNS 뿐만 아니라 CDN, SSL, DDoS 방어 등의 기능을 제공하는데, 무료 상품을 이용하더라도 DNS 기능을 사용하기에 충분하다.

    DNS 레코드 입력

    CloudFlare에 가입하고 자신의 도메인을 추가한 다음, DNS 레코드 입력 단계에서 자신이 기존에 사용하던 DNS 레코드를 빠짐없이 입력한다. 이때 루트 도메인 및 서브도메인과 연결할 홈 서버의 IP 주소를 기록하는 A 레코드 항목은 IP 주소 값을 0.0.0.0 으로 입력해두면 이후 DDNS 설정을 완료했을 때 IP 주소가 제대로 업데이트되는지 확인하기 쉽다.

    설정 항목 중에 아래와 같은 구름 모양의 버튼이 있는데, CloudFlare의 CDN등의 다른 기능은 이용하지 않고 DNS만 이용하려면 이것을 클릭해서 회색으로 설정한다.

    CloudFlare에서 DNS 레코드 입력을 마쳤으면 자신의 도메인을 등록한 사업자에 접속해서 네임 서버를 CloudFlare의 네임 서버로 변경한다. 이후 네임 서버 정보가 업데이트되도록 기다린 다음에 CloudFlare의 Overview 페이지에서 Re-check now 버튼을 눌러 자신의 도메인이 정상적으로 연결되었는지 확인한다.

    Global API Key 확인

    CloudFlare 웹사이트 우측 상단의 개인정보 항목을 클릭하면 My Profile 페이지가 나오는데, 이 페이지 하단의 API Keys 항목에서 Global API Key 값을 확인하고 별도로 기록해둔다.

    셸 스크립트 작성

    이제 우분투 서버의 IP 주소를 CloudFlare의 DNS 레코드에 전달할 셸 스크립트를 작성한다. CloudFlare는 DNS 레코드를 외부에서 제어할 수 있도록 API를 제공하기 때문에 간단한 스크립트를 작성하는 것만으로 DDNS를 적용할 수 있다.

    curl -V

    CloudFlare의 API는 curl 명령을 이용해서 작동하므로 먼저 위 명령으로 (V는 대문자) 우분투 서버에 curl이 설치되어 있는지 확인한다. curl 7.58.0 등의 버전이 출력되지 않고 curl이 설치되어 있지 않다는 메시지가 출력된다면 sudo apt update && sudo apt install curl 명령으로 curl을 설치한다.

    sudo nano /usr/local/etc/ddns-cloudflare.sh

    nano 편집기로 적당한 위치에 적당한 이름으로 셸 스크립트 파일을 하나 생성한다.

    그다음, CloudFlare의 API 문서를 참고해서 DNS 레코드를 업데이트하는 스크립트를 작성한다. 현재 내가 작성해서 사용하고 있는 스크립트는 아래와 같으며, 빨간색으로 표시한 CloudFlare 정보만 자신의 것으로 바꿔서 그대로 사용해도 된다.

    #!/bin/bash
    
    ##### CloudFlare A Recoard Updater by varins.com
    ##### Separate the contents of A_Record with commas (,)
    ##### To force updating, run with -f
    
    Login_Email=id@example.com
    Global_API_Key=hadbkv85sf68hgueaf89o9svgoidbki73o92
    Domain=example.com
    A_Record=example.com,sub.example.com
    
    ##### v1.0.3 Published on 30 June 2019
    
    [ ! -f /var/tmp/ip.txt ] && touch /var/tmp/ip.txt
    IP=$(curl -s "https://ipv4.icanhazip.com/")
    PIP=$(cat /var/tmp/ip.txt)
    echo -e "CloudFlare A Recoard Updater v1.0.3"
    echo -e "Current IP: $IP"
    echo -e "Previous IP: $PIP"
    
    if [ "$IP" == "$PIP" ] && [[ $1 != "-f" ]]; then
      echo "No need to update"; exit 0
    elif [[ $1 == "-f" ]]; then
      echo "Force updating A recoard......"
    elif [ "$IP" != "$PIP" ]; then
      echo "Updating A recoard......"
    fi
    
    echo $IP > /var/tmp/ip.txt
    
    V4="https://api.cloudflare.com/client/v4/zones"
    H1="-HX-Auth-Email:$Login_Email"
    H2="-HX-Auth-Key:$Global_API_Key"
    H3="-HContent-Type:application/json"
    ZN=$(curl -s -X GET "$V4?name=$Domain" $H1 $H2 $H3 \
        | grep -Po '(?<="id":")[^"]*' | head -1)
    
    string=$A_Record
    IFS=',' AARY=(${string})
    ATOTAL=${#AARY[*]}
    
    function aid() {
      for arec in "${AARY[@]}"
      do
        (curl -s -X GET "$V4/$ZN/dns_records?name=$arec" $H1 $H2 $H3 \
        | grep -Po '(?<="id":")[^"]*' | head -1)
      done
    }
    
    IFS=$'\n' AIDARY=($(aid))
    
    for ((i=0; i<$ATOTAL; i++))
    do
      (curl -s -X PUT "$V4/$ZN/dns_records/${AIDARY[$i]}" $H1 $H2 $H3 \
      --data "{\"type\":\"A\",\"name\":\"${AARY[$i]}\",\"content\":\"$IP\"}" \
      | grep -Po '(?<="name":")[^"]*|(?<="content":")[^"]*|(?<=}},)[^}]*' \
      | xargs)
    done
    

    Login_Email   CloudFlare에 로그인할 때 ID로 사용하는 이메일 주소를 입력한다.
    Global_API_Key   앞서 확인한 CloudFlare의 Global API Key를 입력한다.
    Domain   CloudFlare에 등록한 도메인을 입력한다. 1개만 적용이 가능하다.
    A_Record   Domain 항목에 입력한 도메인의 루트 도메인 및 서브도메인을 모두 기입한다. 각각의 구분은 쉼표 ,로 한다. 이 도메인들은 모두 CloudFlare의 DNS 레코드에 먼저 생성되어 있어야 한다.

    이 스크립트는 Domain 항목에 1개의 도메인만 입력할 수 있으므로, 만일 CloudFlare에 여러 개의 도메인을 등록했다면 이 스크립트 파일을 여러 개 만들어서 사용하면 된다.

    편집을 마쳤으면 Ctrl키와 x키를 동시에 눌러서 nano 편집기를 빠져나오면서 저장한다.

    sudo chmod u+x /usr/local/etc/ddns-cloudflare.sh

    작성한 스크립트에 실행 권한을 부여한다.

    DDNS 업데이트

    sudo /usr/local/etc/ddns-cloudflare.sh -f

    -f 옵션으로 이 스크립트를 실행해서 CloudFlare의 DNS를 최초로 업데이트 한다. -f 옵션을 붙이면 우분투 서버의 IP 주소 변경 여부에 상관 없이 현재의 IP 주소로 CloudFlare의 DNS를 업데이트 하고, -f 옵션 없이 실행하면 IP 주소가 변경된 경우에만 CloudFlare의 DNS를 업데이트한다.

    업데이트에 성공 했으면 example.com 123.123.123.123 success:true,errors:[],messages:[] 형태의 메시지가 출력되고, CloudFlare의 DNS 설정 페이지를 살펴보면 IP 주소가 업데이트된 것을 확인할 수 있다.

    그다음, 이 스크립트를 우분투 서버의 작업 스케줄러에 등록해서 주기적으로 IP 주소를 확인하고 업데이트하도록 설정한다.

    sudo crontab -e

    우분투 서버의 작업 스케줄러 crontab을 편집 모드로 실행한다. 만일 어떤 편집기를 사용하겠냐고 물어오면 nano 편집기를 뜻하는 숫자를 입력한다.

    1 * * * * /usr/local/etc/ddns-cloudflare.sh

    이 내용은 매 시 1분에 (1시간에 1번) ddns-cloudflare.sh 파일을 실행하는 스케줄이다. 1 * * * * 부분은 왼쪽부터 분, 시, 일, 월, 요일을 의미한다. 5분 간격으로 실행하는 스케줄은 */5 * * * * /usr/local/etc/ddns-cloudflare.sh 이다. 원하는 스케줄을 crontab의 끝부분에 기록하고, Ctrl키와 x키를 동시에 눌러 nano 편집기를 빠져나오면서 저장한다. sudo crontab -l 명령으로 crontab의 내용을 확인할 수 있다.

     

    본 글의 저작권은 작성자 Varins에게 있습니다.
    Varins의 사전 서면 동의 없이는 본 글의 전부 또는 일부를 무단으로 전재, 게시, 배포하는 것을 금지합니다.
    

Leave a comment

댓글은 관리자의 승인 이후에 게시됩니다.