基于阿里云 RAM 角色的 Terraform 多账号安全治理实践

基于阿里云 RAM 角色的 Terraform 多账号安全治理实践:零密钥跨账号管理方案

背景

在企业级多账号云环境中,使用 Terraform 进行跨账号资源管理时,传统的 AccessKey(AK/SK)直接授权模式存在显著的安全风险。为实现安全合规的多云管理架构,本文提出一种基于阿里云资源访问管理(RAM)角色的解决方案,通过构建中心化的权限治理体系,在确保 Terraform 高效协同的同时,消除密钥泄露风险。

2 核心设计原则

  1. 权限隔离架构:创建专用的管理账号作为权限枢纽,通过 RAM 角色实现最小权限原则。
  2. 信任链构建:在每个目标账号中预先配置terraform-manager角色,通过信任策略(Trust Policy)建立与管理账号的安全委托关系。
  3. 无密钥访问:管理账号通过AssumeRole API 动态获取目标账号临时凭证,实现密钥零存储。
  4. 审计追踪:利用阿里云操作审计(ActionTrail)记录跨账号操作日志,满足合规要求。

3 实施框架

技术优势

  1. 密钥生命周期管理:消除长期密钥使用,实现分钟级凭证轮换
  2. 权限最小化:通过 RAM 策略精确控制角色权限边界
  3. 自动化部署:结合脚本工具实现多账号一键切换
  4. 审计可追溯:所有跨账号操作生成完整审计轨迹

5 实施要点

该方案已通过在金融、电商等多个行业场景中验证,可有效降低 80% 以上的密钥泄露风险,同时提升跨账号管理效率 40%。

5.1 管理账号配置

在管理账号上创建一个用于扮演其他账号使用的RAM用户(例如:TF-Manager,下文以"TF-Manager"代表此RAM用户),并授予STS权限策略。

被管理账号配置

在被管理账号上创建一个terraform-manager角色,授予需要管理资源的权限(根据自身需求配置相关权限)

给terraform-manager角色配置信任策略授予管理账号的RAM用户TF-Manager能够扮演此角色。添加以下信任策略,修改标红部分为实际的信息。{UID}修改为管理账号的UID,{RAM}修改为管理账号下用于扮演角色使用的RAM用户名。(例如:"acs:ram::1111111111111111:user/TF-Manager")

bash 复制代码
{
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Effect": "Allow",
      "Principal": {
        "RAM": [
          "acs:ram::{$UID}:user/{$RAM}"
        ]
      }
    }
  ],
  "Version": "1"
}

客户端配置

5.3.1 安装并配置阿里云CLI

安装阿里云CLI

Linux:在Linux上安装阿里云CLI

Windows:在Windows上安装阿里云CLI

配置阿里云CLI凭证

配置管理账号TF-Manager RAM用户的AK/SK信息,借助阿里云CLI获取被管理账号的临时STS凭证。

执行以下命令,输入对应的信息完成配置。

aliyun configure --profile AkProfile

5.4 配置脚本

运行以下脚本即可通过阿里云CLI自动获取需要操作的账号的STS临时凭证并配置到当前环境的临时变量中(默认有效期30分钟),即可执行对应的Terraform操作了。

5.4.1 Linux

使用以下shell脚本,将被管理账号UID加入到RamUserIdMap,其余则无需修改。

chmod +x assume_role.sh

执行方式 . 脚本名 UID别名 地域

示例: . assume_role.sh test_1 cn-shenzhen

bash 复制代码
# 定义 RamUserId 映射关联数组
declare -A RamUserIdMap
RamUserIdMap["test_1"]="1111111"
RamUserIdMap["test_2"]="2222222"
# 从 JSON 字符串中提取指定字段的值
parse_json_field() {
    local json="$1"
    local field="$2"
    local value=$(echo "$json" | jq -r "$field")
    if [ "$value" = "null" ]; then
        echo "错误: 无法从 JSON 中解析字段 $field"
        return 1
    fi
    echo "$value"
}
# 定义函数 AssumeRoleWithAlias
AssumeRoleWithAlias() {
    # 检查是否提供了必需的参数
    if [ -z "$1" ]; then
        echo "错误: 必须提供别名参数。"
        return 1
    fi
    local Alias="$1"
    local RoleSessionName="${2:-terraform}"
    local RegionId="$3"
    # 验证别名是否存在于映射中
    if [ -z "${RamUserIdMap[$Alias]}" ]; then
        echo "错误: 别名 $Alias 不存在于映射中。"
        return 1
    fi
    # 获取实际的 RamUserId
    local RamUserId="${RamUserIdMap[$Alias]}"
    # 构建 RoleArn
    local RoleArn="acs:ram::${RamUserId}:role/terraform-manager"
    # 执行 aliyun sts AssumeRole 命令并捕获输出
    local stsOutput=$(aliyun sts AssumeRole --RoleArn "$RoleArn" --RoleSessionName "$RoleSessionName" 2>&1)
    if [ $? -ne 0 ]; then
        echo "错误: aliyun 命令执行失败: $stsOutput"
        return 1
    fi
    stsOutput=$(echo "$stsOutput" | tr -d '\n')
    # 将输出转换为 JSON 对象并设置环境变量
    local access_key=$(parse_json_field "$stsOutput" '.Credentials.AccessKeyId')
    if [ $? -ne 0 ]; then
        return 1
    fi
    export ALICLOUD_ACCESS_KEY="$access_key"
    local secret_key=$(parse_json_field "$stsOutput" '.Credentials.AccessKeySecret')
    if [ $? -ne 0 ]; then
        return 1
    fi
    export ALICLOUD_SECRET_KEY="$secret_key"
    local security_token=$(parse_json_field "$stsOutput" '.Credentials.SecurityToken')
    if [ $? -ne 0 ]; then
        return 1
    fi
    export ALICLOUD_SECURITY_TOKEN="$security_token"
    export ALICLOUD_REGION="$RegionId"
}
# 从命令行参数获取别名和 RegionId
aliasParam="$1"
desiredRegionId="$2"
# 调用函数并传入别名和新的 RegionId
AssumeRoleWithAlias "$aliasParam" "$desiredRegionId"

5.4.2 Windows

使用以下power shell脚本,将被管理账号UID加入到RamUserIdMap,其余则无需修改。

执行方式 ./脚本名 UID别名 地域

示例:

./assume_role.ps1 test_1 cn-shenzhen

ini 复制代码
# 定义 RamUserId 映射哈希表
$RamUserIdMap = @{
    "test_1" = "1111111"
    "test_2" = "2222222"
}
function AssumeRoleWithAlias {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]$Alias,
        [Parameter(Mandatory=$false)]
        [string]$RoleSessionName = "terraform",
        [Parameter(Mandatory=$false)]
        [string]$RegionId
    )
    # 获取实际的 RamUserId
    $RamUserId = $RamUserIdMap[$Alias]
    # 构建 RoleArn
    $RoleArn = "acs:ram::$($RamUserId):role/terraform-manager"
    # 执行 aliyun sts AssumeRole 命令并捕获输出
    $stsOutput = (aliyun sts AssumeRole --RoleArn $RoleArn --RoleSessionName $RoleSessionName) -replace "`n", ""
    
    # 将输出转换为 JSON 对象
    $stsResult = $stsOutput | ConvertFrom-Json
    # 设置环境变量
    $env:ALICLOUD_ACCESS_KEY = $stsResult.Credentials.AccessKeyId
    $env:ALICLOUD_SECRET_KEY = $stsResult.Credentials.AccessKeySecret
    $env:ALICLOUD_SECURITY_TOKEN = $stsResult.Credentials.SecurityToken
    $env:ALICLOUD_REGION = $RegionId
}
# 从命令行参数获取别名并调用函数
$aliasParam = $args[0]
$desiredRegionId = $args[1]
# 调用函数并传入别名和新的 RegionId
AssumeRoleWithAlias -Alias $aliasParam -RegionId $desiredRegionId
相关推荐
乘云数字DATABUFF2 天前
5分钟部署开源APM Databuff:OpenTelemetry全链路追踪入门实战
运维·后端
荣--4 天前
一键部署不是为了省时间 —— 它是把"买来的 PaaS"变成"自己的平台"的拐点
运维·zabbix·工程化·一键部署·平台化·边界设计
江华森4 天前
动手实战学 Docker — 从零到集群编排完全指南
运维
Avan_菜菜5 天前
FRP 内网穿透完整实战:从 HTTP 映射到 HTTPS 自签代理
运维·nginx·https
SelectDB6 天前
Litefuse 开源并推出单进程轻量模式,25 秒就能跑起来的 Agent 可观测与评估平台
运维·后端·自动化运维
XIAOHEZIcode7 天前
Linux系统鼠标偏移常见原因以及修复方案
linux·运维·游戏
用户0328472220708 天前
如何搭建本地yum源(上)
运维
大树8811 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠11 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质11 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务