#!/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