跳至正文
老丹的足迹 —— 代码写给机器,游记写给自己,感悟写给时间
老丹的足迹 老丹的足迹
老丹的足迹 老丹的足迹
  • 首页
  • 示例页面
  • 首页
  • 示例页面
老丹的足迹 老丹的足迹
老丹的足迹 老丹的足迹
  • 首页
  • 示例页面
  • 首页
  • 示例页面

阿里云前缀列表 API 实战:如何精准更新指定的 CIDR 地址块

一、问题场景

在实际运维中,我们经常会遇到这样一个需求:服务器的公网 IP 地址是动态变化的(如家庭宽带、云服务器的弹性 IP 变更等),每次 IP 变化后都需要手动更新安全组规则,将新的 IP 地址加入白名单。

手动操作不仅繁琐,还容易遗漏,导致服务访问中断。那么,有没有办法让这个过程自动化呢?

答案是:使用阿里云前缀列表 + API 自动更新。

二、什么是前缀列表?

前缀列表(Prefix List)是阿里云提供的一项网络管理功能,可以将一个或多个 CIDR 地址块打包成一个逻辑单元。之后在配置安全组规则或路由表时,可以直接引用这个前缀列表,而不是逐个填写 IP 地址。

核心优势:当前缀列表中的条目发生变化时,所有引用了该列表的规则会自动生效,无需手动修改每一条规则。

三、解决思路

我们的目标是:当服务器 IP 变化时,自动更新前缀列表中对应的 CIDR 地址块。

解决方案分为三步:

  1. 固定标识:为每个需要动态更新的条目设置一个固定的描述(Description),作为唯一标识
  2. 查询定位:调用 API 查询前缀列表,根据描述找到当前的 CIDR 地址块
  3. 精准更新:删除旧的 CIDR,添加新的 CIDR(格式:新IP/32),保留原描述

整个流程可以用下图表示:

获取公网IP → 对比上次IP → 查询当前CIDR → 删除旧CIDR → 添加新CIDR → 保存新IP

四、涉及的 API 接口

前缀列表的管理涉及两个 ECS API:

4.1 DescribePrefixListAttributes

作用:查询前缀列表的详细信息,包括所有条目的 CIDR 和描述。

请求示例:

Action=DescribePrefixListAttributes
RegionId=cn-hangzhou
PrefixListId=pl-xxxxx
Version=2014-05-26

响应结构:

{
  "Entries": {
    "Entry": [
      {
        "Cidr": "192.168.1.0/24",
        "Description": "my-server"
      }
    ]
  }
}

4.2 ModifyPrefixList

作用:修改前缀列表,支持添加或删除条目。

重要说明:该接口没有直接的“修改条目”功能。更新一个条目需要组合使用删除和添加:

操作参数
删除旧 CIDRRemoveEntry.1.Cidr=192.168.1.0/24
添加新 CIDRAddEntry.1.Cidr=10.0.0.1/32
保留描述AddEntry.1.Description=my-server

五、API 调用要点

5.1 公共参数

所有阿里云 API 请求都需要携带以下公共参数:

参数说明
AccessKeyId访问密钥 ID
Format固定 JSON
SignatureMethod固定 HMAC-SHA1
SignatureVersion固定 1.0
SignatureNonce唯一随机数
TimestampUTC 时间戳

5.2 签名算法

签名是调用阿里云 API 的核心步骤,流程如下:

  1. 对所有请求参数按参数名升序排序
  2. 构造规范化查询字符串
  3. 构造待签名字符串:GET&%2F& + URLEncode(规范化字符串)
  4. 使用 AccessKeySecret& 作为密钥,计算 HMAC-SHA1
  5. 将结果 Base64 编码,得到 Signature

5.3 Endpoint 地址

前缀列表属于 ECS 产品,请求地址为:

https://ecs.{RegionId}.aliyuncs.com/

例如杭州地域:https://ecs.cn-hangzhou.aliyuncs.com/

5.4 地域配置

前缀列表是地域级资源,RegionId 必须与创建前缀列表时选择的地域一致。

地域RegionId
华东1(杭州)cn-hangzhou
华东2(上海)cn-shanghai
华北2(北京)cn-beijing
华南1(深圳)cn-shenzhen

六、C++ 实现代码

以下是完整的 C++ 实现,基于阿里云签名算法和 libcurl。

6.1 配置文件 Config.h

#ifndef CONFIG_H
#define CONFIG_H

#include <string>

// 阿里云账号配置
#define ACCESS_KEY_ID       "your-access-key-id"
#define ACCESS_KEY_SECRET   "your-access-key-secret"

// 前缀列表配置
#define PREFIX_LIST_REGION_ID       "cn-hangzhou"
#define PREFIX_LIST_ID              "pl-xxxxxxxxxxxx"
#define PREFIX_LIST_CHECK_INTERVAL  60
#define PREFIX_LIST_RETRY_TIMES     3
#define PREFIX_LIST_RETRY_DELAY_SEC 5

// 路径配置
#define STATE_FILE_DIR      "/tmp/prefix_updater/"
#define LOG_FILE_PATH       "/tmp/prefix_updater/updater.log"

namespace Config {
    inline std::string getAccessKeyId()     { return ACCESS_KEY_ID; }
    inline std::string getAccessKeySecret() { return ACCESS_KEY_SECRET; }
    inline std::string getRegionId()        { return PREFIX_LIST_REGION_ID; }
    inline std::string getPrefixListId()    { return PREFIX_LIST_ID; }
    inline int getCheckInterval()           { return PREFIX_LIST_CHECK_INTERVAL; }
    inline int getRetryTimes()              { return PREFIX_LIST_RETRY_TIMES; }
    inline int getRetryDelaySec()           { return PREFIX_LIST_RETRY_DELAY_SEC; }
    inline std::string getStateFileDir()    { return STATE_FILE_DIR; }
    inline std::string getLogFilePath()     { return LOG_FILE_PATH; }
}

#endif

6.2 核心更新逻辑

bool PrefixListUpdater::updateOnce() {
    // 1. 获取当前公网IP
    std::string currentIP = getPublicIP();
    if (currentIP.empty()) return false;

    // 2. 检查IP是否变化
    if (!isIPChanged(currentIP)) return true;

    // 3. 查询当前CIDR(根据描述)
    std::string oldCidr = getCurrentCidrByDescription();
    if (oldCidr.empty()) return false;

    // 4. 更新:删除旧CIDR,添加新CIDR
    std::string newCidr = currentIP + "/32";
    bool success = updateCidrBlock(oldCidr, newCidr);

    if (success) {
        saveLastIP(currentIP);
    }

    return success;
}

6.3 查询当前 CIDR

std::string PrefixListUpdater::getCurrentCidrByDescription() {
    std::map<std::string, std::string> params;
    params["Action"] = "DescribePrefixListAttributes";
    params["RegionId"] = m_config.regionId;
    params["PrefixListId"] = m_config.prefixListId;

    std::string resp = sendRequest(params);

    // 解析JSON,根据Description找到对应的Cidr
    return findCidrByDescription(resp, m_config.description);
}

6.4 更新 CIDR 地址块

bool PrefixListUpdater::updateCidrBlock(const std::string& oldCidr, 
                                         const std::string& newCidr) {
    std::map<std::string, std::string> params;
    params["Action"] = "ModifyPrefixList";
    params["RegionId"] = m_config.regionId;
    params["PrefixListId"] = m_config.prefixListId;

    // 删除旧的
    params["RemoveEntry.1.Cidr"] = oldCidr;

    // 添加新的(保留原描述)
    params["AddEntry.1.Cidr"] = newCidr;
    params["AddEntry.1.Description"] = m_config.description;

    std::string resp = sendRequest(params);

    return resp.find("\"RequestId\"") != std::string::npos;
}

七、使用示例

7.1 编译

chmod +x build.sh
./build.sh

7.2 运行

# 单次更新(测试用)
./prefix_updater my-server-ip -once

# 持续监控(每60秒检查一次)
./prefix_updater my-server-ip

# 指定检查间隔(300秒)
./prefix_updater my-server-ip -i 300

7.3 命令行参数

参数说明
<description>必需,要更新的条目描述
-once只运行一次,不进入守护模式
-h显示帮助信息

八、注意事项

限制项说明
CIDR 格式单个 IP 必须写为 x.x.x.x/32
条目唯一性同一前缀列表中 CIDR 不能重复
容量限制单个前缀列表最多 200 个条目
描述长度2-32 个字符
原子操作同一请求中不能同时删除和添加同一个 CIDR
地域匹配RegionId 必须与前缀列表创建地域一致

九、总结

通过阿里云前缀列表 API,我们可以实现 CIDR 地址块的精准自动更新。核心要点如下:

  1. 固定描述作为标识:为每个条目设置不变的 Description,用于查询定位
  2. 先查后改:先调用 DescribePrefixListAttributes 获取当前 CIDR,再执行更新
  3. 删加组合:通过 RemoveEntry + AddEntry 实现原子性的条目更新
  4. IP 格式规范:动态 IP 必须转换为 /32 格式

这套方案已在实际生产环境中验证,可稳定运行,有效解决了动态 IP 场景下的网络访问控制问题。

作者

老丹

关注我
其他文章
上一个

阿里云DNS API调用详解:从零实现一个DDNS客户端

下一个

阿里云前缀列表权限设置完全指南

关于博主

    老丹是一名C/C++后台开发工程师,信奉“无抽象不设计,无性能不生产”。

  • 技术栈:Modern C++、Linux环境编程、多线程/并发、网络编程等。
  • 信条:能用constexpr解决的问题绝不拖到运行时,能靠RAII避免的泄漏绝不写析构。
  • 正在填坑:从解封装到渲染的C++全链路实现,正在驯服FFmpeg与H.264/H.265。
  • 输出原则:这里的每一段代码都经过-Wall -Wextra -Werror -O2的洗礼。

搜索

近期文章

  • 阿里云前缀列表权限设置完全指南 2026年6月9日
  • 阿里云前缀列表 API 实战:如何精准更新指定的 CIDR 地址块 2026年6月9日
  • 阿里云DNS API调用详解:从零实现一个DDNS客户端 2026年6月9日
  • curl库完全指南:从命令行到libcurl开发 2026年6月8日
  • FastCGI 协议详解:从 CGI 的瓶颈到高性能 Web 应用 2026年6月7日

文章分类

  • C/C++开发 (9)
  • Linux服务配置 (3)
  • 计算机理论 (4)
联系我们:📍 地址:中国·广东省深圳市   |   ✉️ 邮箱:support@tanglinux.com   |   💬 QQ:870866607
版权所有:老丹的足迹粤ICP备2026061170号-1       公安备案图标 粤公网安备44030002013274号