Featured image of post 动态域名解析--Cloudflare

动态域名解析--Cloudflare

为Cloudflare域名配置动态域名解析

之前就写过关于动态域名解析的文章,使用的是阿里云的API。最近将域名转移到了Cloudflare上,顺便记录一下如何使用Cloudflare的API进行动态域名解析。

缘起

之前的文章里提到过,由于工作原因需要经常使用ssh登录家里的和办公室的个人电脑,但是这些电脑基本都没有固定的IP地址,所以当IP地址发生变动之后,往往不能及时连接到电脑上。就考虑使用域名代替IP地址来访问这些电脑,并且购买了一两个便宜的域名。很长一段时间都在用阿里云的域名解析服务。之前关于在阿里云上进行动态域名解析的文章可以参考“个人网站的建立过程(一):购买个人域名并配置动态域名解析”

后来我把域名转移到了Cloudflare上,主要是因为Cloudflare的CDN服务和DNS解析速度都比阿里云快。现在记录一下如何使用Cloudflare的API进行动态域名解析。

前提

  1. 拥有自己的域名

    • 购买的域名可以在Cloudflare上进行解析。
    • 如果没有自己的域名,可以在Namecheap上购买一个便宜的域名。
  2. 拥有Cloudflare账号

    • 如果没有Cloudflare账号,可以在Cloudflare上注册一个账号。
    • 注册完成后,登录Cloudflare账号,添加自己的域名,并将域名的DNS服务器指向Cloudflare提供的DNS服务器。

域名解析到服务器IP地址

  • 购买的域名需要解析到服务器的IP地址才能直接通过域名访问服务器。
  • 如果服务器具有固定IP,只需要在Cloudflare上将域名和服务器绑定即可。
  • 如果服务器没有固定IP,则需要将域名动态解析到服务器的IP地址。

有固定IP——直接绑定

  1. 获取IP地址

    • 可以前往IPv6测试网站查看网络是否支持IPv6: IPv6测试结果

    • 如果是类Unix系统,可以通过如下命令获取IPv4地址:

      1
      
      curl ipinfo.io/ip
      

      通过如下命令获取IPv6地址:

      1
      
      curl ipv6.icanhazip.com
      
  2. 将域名与IP地址绑定

    • 登录Cloudflare账号,点击左上角的菜单,在菜单里找到DNS选项: Cloudflare DNS
    • 点击Add Record按钮,添加一条A记录(IPv4)或AAAA记录(IPv6),将域名解析到服务器的IP地址: Cloudflare DNS
    • 填入需要解析的域名和对应的IP地址(IP地址正确与否不重要,如果错误的话,待会儿运行本脚本后会把它改正;但域名一定要是你在之前“有固定IP——直接绑定”第2步中创建的域名)。
    • 如果你有多个域名需要解析,可以仿照上面的例子自行添加域名,IPv4和IPv6都支持。再次强调,本文件中需要解析的域名需要是之前在“有固定IP——直接绑定”第2步中在Cloudflare上已经创建的域名。
  3. 测试是否绑定成功 绑定完成后可能需要10分钟的时间等待Cloudflare DNS服务器完成更新(一般提交之后立刻就会更新)。然后可以查看是否绑定成功。

    • 建议在另一个网络环境通过“ping”命令测试
      1
      
      ping surface.jinli.cyou
      
      如果可以在公网ping通,则绑定成功。
  4. 完成! 绑定成功之后就可以通过域名来访问服务器了,例如如果需要使用ssh远程登录服务器,就可以直接ssh域名:

    也可以用服务器搭建NAS(Network Attached Storage)服务等,然后通过域名访问。

无固定IP——绑定之后再动态解析

如果电脑或服务器没有固定IP地址,则需要先按照上面的步骤在Cloudflare上绑定当前的IP地址。然后监控本机IP地址,一旦发生改变,就通过Cloudflare提供的API上传到Cloudflare DNS服务器,修改原来绑定的IP地址。

  1. DDNS脚本

    • 我使用的DDNS(Dynamic Domain Name System)脚本是一个让ChatGPT生成的bash脚本:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    
    #!/bin/bash
    
    # Config
    API_TOKEN="your_cloudflare_api_token"
    ZONE_ID="your_cloudflare_zone_id"
    RECORDS=("host1.example.com:A" "host1.example.com:AAAA" "host2.example.com:A" "host2.example.com:AAAA")  # Add as needed
    
    # Get current IPv4 and IPv6 addresses
    IPV4=$(curl -s https://ipv4.icanhazip.com)
    IPV6=$(ip -6 addr show scope global | grep inet6 | awk '{print $2}' | cut -d/ -f1 | head -n 1)
    
    if [ -z "$IPV4" ]; then
      echo "No IPv4 address found."
    else
      echo "Detected IPv4: $IPV4"
    fi
    
    if [ -z "$IPV6" ]; then
      echo "No IPv6 address found."
    else
      echo "Detected IPv6: $IPV6"
    fi
    
    # Function to update DNS record
    update_record() {
      local name="$1"
      local type="$2"
      local ip="$3"
    
      echo "Processing $type record for $name"
    
      # Get record info
      RECORD=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?type=$type&name=$name" \
        -H "Authorization: Bearer $API_TOKEN" \
        -H "Content-Type: application/json")
    
      RECORD_ID=$(echo "$RECORD" | jq -r '.result[0].id')
      CURRENT_IP=$(echo "$RECORD" | jq -r '.result[0].content')
    
      if [ "$CURRENT_IP" == "$ip" ]; then
        echo "  - $type is already up to date: $ip"
        return
      fi
    
      # Update the DNS record
      UPDATE=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$RECORD_ID" \
        -H "Authorization: Bearer $API_TOKEN" \
        -H "Content-Type: application/json" \
        --data "{\"type\":\"$type\",\"name\":\"$name\",\"content\":\"$ip\",\"ttl\":120,\"proxied\":false}")
    
      SUCCESS=$(echo "$UPDATE" | jq -r '.success')
    
      if [ "$SUCCESS" == "true" ]; then
        echo "  - Updated $type record to $ip"
      else
        echo "  - Failed to update $type record: $UPDATE"
      fi
    }
    
    # Loop through all record names and update the specified type (A or AAAA)
    for record in "${RECORDS[@]}"; do
      # Split the record into name and type
      IFS=':' read -r name type <<< "$record"
    
      if [ "$type" == "A" ] && [ -n "$IPV4" ]; then
        update_record "$name" "A" "$IPV4"
      elif [ "$type" == "AAAA" ] && [ -n "$IPV6" ]; then
        update_record "$name" "AAAA" "$IPV6"
      else
        echo "  - Skipping $type record for $name due to missing IP address."
      fi
    done
    
    • 这个脚本要求用户指定想要解析的域名和解析类型(A或AAAA),格式为domain_name:record_type,例如host1.example.com:A。如果有多个域名需要解析,可以在脚本中添加多个记录,格式为host1.example.com:Ahost2.example.com:AAAA等。
    • 该脚本会自动检测当前的IPv4和IPv6地址,并将其更新到Cloudflare DNS服务器上。脚本中使用了jq命令来解析JSON格式的数据,因此需要安装jq命令:
      1
      
      sudo apt install jq
      
    • 该脚本使用了Cloudflare的API进行动态域名解析,因此需要在Cloudflare上生成API Token和Zone ID。下面简单介绍一下如何在Cloudflare上获取API Token和Zone ID。将Zone_ID填入上面脚本中的ZONE_ID变量中。
  2. 获取API Token和Zone ID

    • 登录Cloudflare账号,点击你的域名,在右侧有一列菜单。菜单靠下的部分有Zone ID和Account ID。在这两个ID下面就是“Get your API token"的链接,点击它。 Cloudflare API Token
    • 在User API Token的页面,选择“Create Token”。这里需要选择创建API Token的类型,这里选择“Edit Zone DNS”类型就行,即这个Token只能修改DNS记录。
    • 然后会让你设置Token的权限,这里根据需要设置就行: Cloudflare API Token
    • 设置完成后,点击“Continue to summary”,然后点击“Create Token”按钮。创建完成后,会显示Token的值,页面也会提示你可以在目标电脑上运行一个curl命令来测试Token是否有效。测试有效之后把API Token复制下来填入上面脚本中的API_TOKEN变量中。

    注意: 这个API Token和上面的Zone_ID都是非常重要的,千万不要公开它们。如果泄露了API Token,应该立即登录Cloudflare账号删除该Token。Zone_ID和API Token是你在Cloudflare上进行操作的凭证,泄露后可能会导致你的域名被恶意修改。

  3. 测试运行DDNS脚本

    在修改完上面的脚本之后,保存为ddns.sh文件。然后

    1. 确保安装了curljq命令:

      1
      
      sudo apt install curl jq
      
    2. 赋予脚本可执行权限:

      1
      
      chmod +x ddns.sh
      
    3. 运行脚本:

      1
      
      ./ddns.sh
      
    4. 如果脚本运行成功,会输出类似下面的结果:

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      
      Detected IPv4: 111.111.111.111
      Detected IPv6: 12aa3:4567:89ab:cdef
      Processing A record for host1.example.com
        - Updated A record to 111.111.111.111
      Processing AAAA record for host1.example.com
        - Updated AAAA record to 12aa3:4567:89ab:cdef
      Processing A record for host2.example.com
        - Updated A record to 111.111.111.111
      Processing AAAA record for host2.example.com
        - Updated AAAA record to 12aa3:4567:89ab:cdef
      
  4. 设置定时任务

    • 如果电脑/服务器无固定IP,那么IP每隔一段时间就会改变,我们需要在IP改变之后尽快更新Cloudflare DNS服务器上的IP记录。
    • 人工监控IP变化不太方便,我们可以每隔一段时间(例如每小时)让系统自动运行一次DDNS脚本,这样我们就可以做到在IP改变之后一小时内更新。
    • 类Unix系统中的定时任务可以使用系统程序crontab实现,你可以在这里看到一些设置crontab的方法。
  5. Linux 在Linux系统中,设置crontab定时任务非常简单。我们可以在命令行输入以下命令: shell crontab -e 此命令会打开一个包含crontab定时任务的配置文件,在文件末尾加入一行 shell 42 * * * * cd ~/.config/ddns && ./ddns.sh 即可。上述命令表示每小时的第42分钟运行ddns.sh脚本。你可以根据需要修改时间。

  • 之后可以使用如下命令查看crontab定时任务是否在运行:
    1
    
    crontab -l
    
  1. macOS 在macOS上由于其系统安全性的考虑,在使用crontab命令前,还需要经过一些特殊的设置。
1. 为`cron`命令添加访问全盘文件的权限:
    - 打开“系统偏好设置”->“安全性与隐私”->“隐私”选项卡,点击左下角的锁图标解锁设置。
    - 在左侧列表中选择“完全磁盘访问”,然后点击右侧的加号按钮,添加`/usr/sbin/cron`程序。注意,在点击加号按钮后会弹出一个Finder窗口,让你选择要添加的程序。但`cron`程序默认是隐藏的,在Finder窗口中无法直接找到。你可以在Finder中按下`Command + Shift + G`组合键,输入`/usr/sbin/`,然后选择`cron`程序添加即可。
    - 关闭“系统偏好设置”窗口,确保权限已打开。
2. 这个脚本需要用到的`curl`、`jq`、`yq`和`ifconfig`这些命令,但是`cron`运行脚本的时候并不会加载用户的环境变量,所以需要在脚本中指定这些命令的完整路径。可以通过如下命令查看这些命令的完整路径:
    ```shell
    which curl jq yq ifconfig
    ```
    然后将这些命令的完整路径填入脚本中,例如:
    ```bash
    export PATH="$PATH:/usr/local/bin:/usr/bin:/opt/local/bin:/sbin"
    ```
3. 为此,我专门写了一个脚本作为调用`ddns.sh`的中介脚本,命名为`cron_ddns.sh`,内容如下:
    ```bash
    #!/bin/bash

    echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting cron job for Cloudflare DDNS update..."

    export PATH="$PATH:/usr/local/bin:/usr/bin:/opt/local/bin:/sbin"
    echo "PATH is $PATH"

    cd /Users/lijin/.config/ddns
    echo "Current directory is $(pwd)"

    echo "Running script cloudflare.sh"
    /bin/bash ddns.sh
    ```
4. 赋予脚本可执行权限:
    ```shell
    chmod +x cron_ddns.sh
    ```
5. 然后在`crontab`中添加定时任务:
    ```shell
    crontab -e
    ```
    在文件末尾添加一行:
    ```shell
    42 * * * * /Users/lijin/.config/ddns/cron_ddns.sh >> /tmp/ddns.log 2>&1
    ```
comments powered by Disqus