commit b5269d28261a9d88ad230787659ea7098e8bb5d4 Author: wzj <244142824@qq.com> Date: Sun Apr 13 10:07:12 2025 +0800 first commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..21813a7 --- /dev/null +++ b/README.md @@ -0,0 +1,130 @@ +```markdown +# 阿里云安全组动态IP更新工具(CentOS 7.9 适用) + +![CentOS 7.9](https://img.shields.io/badge/CentOS-7.9-blue) +![阿里云CLI](https://img.shields.io/badge/Aliyun-CLI-orange) + +用于动态更新阿里云安全组规则的自动化脚本,特别适配 CentOS 7.9 系统环境。 + +## 核心功能 + +- ✅ 自动检测公网IP变动 +- ✅ 批量更新多安全组规则 +- ✅ 智能迁移旧IP规则到新IP +- ✅ 严格验证协议和端口规则 +- ✅ 自动记录IP变更历史 + +## 系统要求 + +- CentOS 7.9 操作系统 +- 已配置阿里云API访问权限 +- 基础工具:`curl`、`jq` + +## 安装指南 + +### 1. 安装基础依赖 +```bash +sudo yum install -y curl jq +``` + +### 2. 安装阿里云CLI(二进制方式) +![下载及教程地址](https://help.aliyun.com/zh/cli/install-cli-on-linux?spm=a2c4g.11186623.help-menu-29991.d_2_2.3745606fJSX2wt) +```bash +# 解压安装包 +tar -zxvf aliyun-cli-linux-latest-amd64.tgz -C /usr/local/bin/ + +# 设置执行权限 +chmod +x /usr/local/bin/aliyun + +# 验证安装 +aliyun --version +``` + +### 3. 配置阿里云CLI +```bash +aliyun configure +``` +按照提示输入: +- AccessKey ID +- AccessKey Secret +- 默认地域ID(如 `cn-beijing`) +- 输出格式选择 `json` + +## 脚本配置 + +编辑 `alisg.sh` 修改以下参数: + +```bash +# 必填配置 +REGION="cn-beijing" # 您的阿里云地域 +SECURITY_GROUP_IDS=("sg-2zebj4i9uydml05xwejg") # 安全组ID数组 +LOG_FILE="/var/log/ip-update.log" # 推荐使用绝对路径 + +# 可选调试参数 +# _LAST_IP="142.171.168.162" # 手动指定旧IP +# _CURRENT_IP="115.171.170.151" # 手动指定新IP +``` + +## 使用方法 + +### 首次运行(测试模式) +```bash +chmod +x alisg.sh +./alisg.sh +``` + +### 生产环境建议 +```bash +# 1. 创建专用目录 +sudo mkdir -p /opt/aliyun_sg +sudo cp alisg.sh /opt/aliyun_sg/ +sudo chown root:root /opt/aliyun_sg/alisg.sh + +# 2. 设置定时任务(每小时检查) +echo "0 * * * * root /opt/aliyun_sg/alisg.sh >> /var/log/ip-update.log 2>&1" | sudo tee /etc/cron.d/aliyun_sg_update +``` + +## 日志监控 + +建议定期检查日志文件: +```bash +tail -f /var/log/ip-update.log +``` + +典型成功日志示例: +``` +[2023-11-15 14:00:01] 检测到IP变更 +旧IP:142.171.168.162 +新IP:115.171.170.151 +安全组 sg-2zebj4i9uydml05xwejg 处理完成: +- 已移除 TCP/22/22 规则 +- 已添加 TCP/22/22 规则(新IP) +IP日志已更新 +``` + +## 安全建议 + +1. 限制日志文件权限: +```bash +sudo chmod 600 /var/log/ip-update.log +``` + +2. 使用最小权限的AccessKey + +3. 定期备份安全组规则: +```bash +aliyun ecs DescribeSecurityGroupAttribute --RegionId $REGION --SecurityGroupId $SG_ID > sg_backup.json +``` + +## 故障排除 + +| 错误现象 | 解决方案 | +|---------|----------| +| `aliyun: command not found` | 检查PATH或使用完整路径 `/usr/local/bin/aliyun` | +| 权限拒绝错误 | 使用 `sudo` 或检查AccessKey权限 | +| JSON解析错误 | 安装正确版本的 `jq` 工具 | + +## 许可证 + +MIT License - 自由使用和修改 +``` diff --git a/alisg.sh b/alisg.sh new file mode 100644 index 0000000..a19f3ae --- /dev/null +++ b/alisg.sh @@ -0,0 +1,162 @@ +#!/bin/bash + +# 定义一些常量 +REGION="cn-beijing" # 设置区域 +SECURITY_GROUP_IDS=("sg-2zebj4i9uydml05xwejg") # 设置你的安全组ID列表使用空格分隔,例如("sg-2zebj4i9uydml05xwejg" "sg-3jd9dkjs99ddjfksssdqe") +LOG_FILE="ip-beijing.log" # 存储当前外网IP的日志文件 + +# 允许用户在脚本上方指定 last_ip 和 current_ip,如果为空则使用脚本自动获取(last_ip从LOG_FILE获取,current_ip从公网出口获取) +_LAST_IP="" +_CURRENT_IP="" + +# 获取当前外网IP +get_current_ip() { + if [[ -z "$_CURRENT_IP" ]]; then + _CURRENT_IP=$(curl -s ipinfo.io | grep -oP "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}") + fi + echo "$_CURRENT_IP" +} + +# 获取上次保存的外网IP +get_last_ip() { + if [[ -z "$_LAST_IP" ]]; then + if [[ -f "$LOG_FILE" ]]; then + _LAST_IP=$(cat "$LOG_FILE") + else + echo "没有找到历史IP,无法比较, 请把需要修改的历史IP加入变量“_LAST_IP”或者写入${LOG_FILE}" >&2 + return 1 + fi + fi + echo "$_LAST_IP" +} + +# 更新日志文件,保存当前外网IP +update_ip_log() { + echo "$1" > "$LOG_FILE" +} + +# 获取当前安全组的所有IP规则 +get_security_group_rules() { + aliyun ecs DescribeSecurityGroupAttribute \ + --RegionId "$REGION" \ + --SecurityGroupId "$1" \ + --NicType "internet" \ + --Direction "ingress" | jq -r '.Permissions.Permission[] | {IpProtocol, SourceCidrIp, PortRange, Description}' +} + +# 验证IpProtocol值是否合法 +is_valid_ip_protocol() { + local protocol=$1 + case "$protocol" in + "TCP" | "UDP" | "ICMP" | "GRE" | "All") return 0 ;; + *) return 1 ;; + esac +} + +# 验证PortRange格式是否合法 +is_valid_port_range() { + local port_range=$1 + if [[ "$port_range" =~ ^[0-9]+/[0-9]+$ ]]; then + local start_port=$(echo "$port_range" | cut -d'/' -f1) + local end_port=$(echo "$port_range" | cut -d'/' -f2) + if [[ "$start_port" -le 65535 && "$end_port" -le 65535 ]]; then + return 0 + fi + fi + return 1 +} + +# 移除指定的IP规则 +remove_ip_rule() { + aliyun ecs RevokeSecurityGroup \ + --RegionId "$REGION" \ + --SecurityGroupId "$1" \ + --SourceCidrIp "$2" \ + --IpProtocol "$3" \ + --NicType "internet" \ + --PortRange "$4" +} + +# 添加新的IP规则 +add_ip_rule() { + aliyun ecs AuthorizeSecurityGroup \ + --RegionId "$REGION" \ + --SecurityGroupId "$1" \ + --SourceCidrIp "$2" \ + --IpProtocol "$3" \ + --NicType "internet" \ + --PortRange "$4" \ + --Description "$5" +} + +# 主程序 + +# 如果用户没有指定 last_ip 或 current_ip,自动获取 +if [[ -z "$last_ip" ]]; then + last_ip=$(get_last_ip) +fi +if [[ -z "$current_ip" ]]; then + current_ip=$(get_current_ip) +fi + +# 如果IP发生变化 +if [[ "$current_ip" != "$last_ip" ]]; then + echo "修改后IP:$current_ip" + echo "修改前IP:$last_ip" + [ -z $last_ip ] && exit + # 遍历所有安全组ID并更新规则 + for sg_id in "${SECURITY_GROUP_IDS[@]}"; do + # 获取当前安全组的规则 + rules=$(get_security_group_rules "$sg_id") + + # 查找并移除旧的IP规则 + if [[ -z "$rules" ]]; then + echo "没有找到任何安全组规则,无需处理" + exit 0 + fi + + found_rules=false + for rule in $(echo "$rules" | jq -r '.SourceCidrIp + "," + .IpProtocol + "," + .PortRange + "," + .Description' | grep $last_ip); do + # 获取规则中的SourceCidrIp, IpProtocol 和 PortRange + source_ip=$(echo "$rule" | awk -F',' '{print $1}') + ip_protocol=$(echo "$rule" | awk -F',' '{print $2}') + port_range=$(echo "$rule" | awk -F',' '{print $3}') + description=$(echo "$rule" | awk -F',' '{print $4}') + # 如果该规则的 IP 是旧 IP,则移除它 + if [[ "$source_ip" == "$last_ip/32" || "$source_ip" == "$last_ip" ]]; then + # 验证IpProtocol和PortRange的合法性 + if is_valid_ip_protocol "$ip_protocol" && is_valid_port_range "$port_range"; then + echo "移除旧规则:$source_ip $ip_protocol $port_range $description" + remove_ip_rule "$sg_id" "$source_ip" "$ip_protocol" "$port_range" | jq '.RequestId' + else + echo "无效的规则:$source_ip $ip_protocol $port_range" + fi + fi + done + + if [[ "$found_rules" == false ]]; then + echo "没有找到匹配 $last_ip 的旧规则,无需操作" + exit 0 + fi + + # 添加新的IP规则 + for rule in $(echo "$rules" | jq -r '.SourceCidrIp + "," + .IpProtocol + "," + .PortRange + "," + .Description' | grep $last_ip); do + ip_protocol=$(echo "$rule" | awk -F',' '{print $2}') + port_range=$(echo "$rule" | awk -F',' '{print $3}') + description=$(echo "$rule" | awk -F',' '{print $4}') + # 验证IpProtocol和PortRange的合法性 + if is_valid_ip_protocol "$ip_protocol" && is_valid_port_range "$port_range"; then + echo "添加新的规则:$current_ip $current_ip/32 $ip_protocol $port_range $description" + add_ip_rule "$sg_id" "$current_ip/32" "$ip_protocol" "$port_range" "$description" | jq '.RequestId' + else + echo "无效的规则:$current_ip $ip_protocol $port_range" + fi + done + done + + # 更新日志文件,保存当前IP + update_ip_log "$current_ip" +else + echo "IP没有变化,脚本不会进行任何操作" +fi +