Terraform 身份认证

更新时间:

本文详细介绍阿里云 Terraform Provider 的身份认证方式。

概述

Terraform 身份认证是指对使用到的 Terraform 提供商插件的身份认证,即在通过 Terraform 操作阿里云基础设施前对阿里云 Terraform Provider 的身份认证,只有认证通过后才能与阿里云 API 进行通信并创建和管理阿里云的基础设施资源。

阿里云 Terraform Provider 的身份认证方式有很多中,选择不同的认证方式一方面依赖于 Terraform 安装和运行所在的环境,另一方面取决于您的使用场景。

认证方式

使用说明

适用场景

静态配置

(access_key)

将 AK 以明文的方式定义在配置文件中或者通过参数传入的方式进行设置

开发环境测试验证;

CI/CD 资源管理流程

环境变量

(ALIBABA_CLOUD_ACCESS_KEY)

将 AK 信息保存为环境变量,在没有静态配置的情况下,从环境变量中读取

开发环境测试验证;

独立的运行环境,如 ECS

ECS 服务角色

(ecs_role_name)

从 ECS 实例的 Meta 中获取绑定到这个实例上的 RAM 角色对应的访问凭证

Terraform 运行在 ECS 实例中

角色扮演

(assume_role)

通过角色扮演的方式换取访问凭证

多账号资源管理;

CI/CD 资源管理流程

OIDC 角色扮演(assume_role_with_oidc)

通过 OIDC 角色扮演的方式换取访问凭证

Terraform 运行在阿里云 Kubernetes 集群中

共享的配置文件

(profile)

将如上认证方式通过阿里云 CLI 配置到统一的文件中,通过指定 profile 的名称来获取访问凭证

多账号,多地域资源管理;

开发环境测试验证

多环境资源管理

认证方式

接下来将详细介绍不同认证方式的用法。

静态配置

静态配置是指直接在 Terraform 配置文件的 provider 代码块中定义访问凭证,如下所示:

provider "alicloud" {
  access_key = "<你的 Access Key ID>"
  secret_key = "<你的 Access Key Secret>"
  # 如果是 STS 的访问凭证,此处需要配置 security_token
  # security_token = "<你的 STS Token>"
}

为了安全起见,推荐以变量的方式传入访问凭证,而非直接以明文的方式定义在配置文件中,并且在变量中不定义默认值:

variable "access_key_id" {
  description = "The Access Key ID for operating your infrastructure"
}
variable "access_key_secret" {
  description = "The Access Key Secret for operating your infrastructure"
}
variable "security_token" {
  description = "The Security Token for operating your infrastructure"
}
provider "alicloud" {
  access_key = var.access_key_id
  secret_key = var.access_key_secret
  # 如果是 STS 的访问凭证,此处需要配置 security_token
  # security_token = var.security_token
}

当运行 terraform 命令时可以通过 -var 选项传入具体变量值:

$ terraform plan -var access_key_id="Abc12345" -var access_key_secret="Abc12345" -var security_token="Abc12345"

环境变量配置

基于环境变量身份认证方式跟静态配置的区别是将访问凭证保存在特定的环境变量中,当执行 Terraform 命令时,如果配置模板中没有显示声明访问凭证,那么将尝试从如下的环境变量中获取。环境变量的设置方式如下:

$ export ALICLOUD_ACCESS_KEY="<你的 Access Key ID>"
$ export ALICLOUD_SECRET_KEY="<你的 Access Key Secret>"
# # 如果是 STS 的访问凭证,此处需要配置 security_token
$ export ALICLOUD_SECURITY_TOKEN="<你的访问 Token>"

设置了环境变量之后,provider 块可以不在配置模板中显示声明或者只声明地域信息:

provider "alicloud" {
  region = "cn-hangzhou"
}

当然,region 也是支持通过环境变量 ALICLOUD_REGION进行配置的。如果 region 没有显示声明也没有配置环境变量,cn-beijing 将是其默认配置值。

相比于静态配置,环境变量的配置方式更简单,安全性也好一些。

ECS 服务角色

当在 ECS 实例上运行 Terraform 来管理阿里云资源时,除了如上的静态配置和环境变量配置外,还有一种免配置访问凭证的方式:ECS 服务角色,即阿里云 RAM 角色允许您将一个角色关联到 ECS 实例上,实现在实例内部自动获取并刷新临时访问凭证的能力,无需直接暴露 AccessKey,减少密钥泄露的风险;同时,也可借助 RAM 角色的精细化访问权限控制来避免权限过度分配。详见通过ECS实例RAM角色授权ECS访问其他云服务

基于 ECS 服务角色的身份认证配置方式非常简单:

  1. 准备一台 ECS 实例,该实例要具备公网访问能力;

  2. 创建一个 RAM 角色,并将该角色绑定到 ECS 实例上;

  3. 在 provider 块中定义 ecs_role_name 参数,并将 RAM 角色名赋值给此参数:

provider "alicloud" {
  ecs_role_name = "<绑定到 ECS 实例上的 RAM 角色名称>"
}

当然,也可以通过环境变量 ALICLOUD_ECS_ROLE_NAME来赋值。

基于 ECS 服务角色的认证方式,安全性高,当在 ECS 实例中执行 Terraform 时,我们强烈推荐使用该认证方式。

角色扮演(AssumeRole)

通常访问和管理阿里云资源的主体是 RAM 用户或者云服务(比如 ECS 服务),对应的访问凭证也是 RAM 用户的 AccessKey 或者通过云服务角色换取到的 STS AccessKey,相应的访问权限也是由绑定到 RAM 用户或者云服务角色上的权限策略所决定的。当我们想要将访问权限跟 RAM 用户或者云服务角色分离,或者想跨账号访问云资源时,可以使用角色扮演的身份认证方式,将 RAM 用户或者云服务角色授权(Assume)给某个具体的 RAM 角色,让这个 RAM 角色获取一个临时身份访问凭证,进而完成资源的访问。

基于角色扮演的身份认证是通过调用 AssumeRole - 获取扮演角色的临时身份凭证 来实现的,因此在 Terraform 中配置角色扮演身份认证前,首先需要准备如下内容:

  1. 创建一个 RAM 用户,并为该用户授权系统策略 AliyunSTSAssumeRoleAccess,使其能够访问 AssumeRole API

  2. 为 RAM 用户创建 AccessKey,用于在 Terraform 中调用 AssumeRole API 换取临时身份访问凭证

  3. 创建一个授信实体为“阿里云账号”的 RAM 角色,并为该角色授权想要访问的云服务的权限策略(比如访问 OSS 的全部访问权限 AliyunOSSFullAccess),并记录该 RAM 角色对应的 ARN

  4. 根据自身场景,编写需要访问的云资源的 RAM 自定义权限策略(比如只访问杭州地域的所有 Bucket),用于和授权给 RAM 角色的权限策略取交集,实现精细化的访问控制

在准备好如上内容后,在 provider 块中按照如下的方式进行声明:

provider "alicloud" {
  # 用于调用 AssumeRole 的 RAM 用户的 AccessKey ID
  access_key = "<RAM 用户的 AccessKey ID>"
  # 用于调用 AssumeRole 的 RAM 用户的 AccessKey Secret
  secret_key = "<RAM 用户的 AccessKey Secret>"
  assume_role {
    role_arn           = "<一个 RAM 角色的 ARN>"
    policy             = "<一个 RAM 权限策略的具体内容>"
    session_name       = "<一个自定义的角色会话名称>"
    session_expiration = <临时身份访问凭证的最大有效期>
  }
}

在 Terraform 中配置角色扮演的身份认证时,支持如下参数:

  1. access_key / secret_key

    当扮演 RAM 用户的临时身份时,access_key 和 secret_key 时必填的,并且必须被授予 AssumeRole 的访问权限,否则无法通过 AssumeRole 获取到临时访问凭证。和静态设置一样,该参数也可以通过环境变量的方式来设置。

  2. assume_role

    assume_role 是一个参数块,用来设置获取临时身份的参数,其包含如下参数:

    1. role_arn,必填参数,标识使用哪个角色来扮演身份,其值的格式为:acs:ram::<阿里云账号ID>:role/<RAM 角色名称>,如:acs:ram::151192xxxxxx:role/k8srole

    2. policy,选填参数,指定角色扮演的临时身份被授权了哪些权限。该参数为非必填参数,如果指定该权限策略,则临时身份最终的权限策略取跟扮演的 RAM 角色绑定的权限策略与该权限策略的交集;如果不指定该权限策略,则临时身份最终的权限策略取跟扮演的 RAM 角色绑定的权限策略。

    3. session_name,选填参数,标识角色会话名称。该参数为用户自定义参数。通常设置为调用角色扮演的用户身份,例如:用户名。如果不设置,阿里云 Provider 将默认值设置为 terraform。

    4. session_expiration,选填参数,标识临时身份的有效期,单位是秒,即多少秒之后换取到的临时身份将失效。该参数最小值为 900 秒,最大值为扮演的 RAM 角色的MaxSessionDuration时间,MaxSessionDuration默认值为 3600 秒,可以通过 UpdateRole - 更新角色信息 来修改最大会话时间,能修改的最大值为 43200 秒。

    5. external_Id,选填参数,标识角色外部 ID。该参数为外部提供的用于表示角色的参数信息,主要功能是防止混淆代理人问题。更多信息,请参见使用ExternalId防止混淆代理人问题

如下是一个角色扮演的示例,并允许获取杭州的 OSS Bucket 的列表:

provider "alicloud" {
  # 为了安全起见,此处的 access_key 和 secret_key 已经通过环境变量进行了设置
  region = "cn-hangzhou"
  assume_role {
    role_arn           = "acs:ram::11827xxxxxx:role/tf-assume-role"
    policy             = <<EOF
    {
      "Version": "1",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": "oss:ListBuckets",
          "Resource": "acs:oss:oss-cn-hangzhou:*:*"
        }
      ]
    }
    EOF
    session_name       = "terraform-assume-role-session"
    session_expiration = 1000
  }
}

需要注意的是,运行本示例之前已经为 RAM 角色 tf-assume-role 设置了 AliyunOSSFullAccess 的权限,否则当访问杭州 OSS Bucket 的时候将报无权限的错误。

当然,如果不想通过 policy 参数来设置精细化的访问策略,也可以直接将上述 policy 的权限直接授权给 RAM 角色 tf-assume-role,这样只需要在 assume_role 块中指定 role_arn 即可。

如果 Terraform 的运行环境是 ECS 实例,那么可以结合 ECS 服务角色来消除对 AccessKey 的暴露,此时上述的示例可以改为如下的设置方式:

provider "alicloud" {
  ecs_role_name = "<绑定到 ECS 实例上的 RAM 角色名称>"
  region        = "cn-hangzhou"
  assume_role {
    role_arn           = "acs:ram::11827xxxxxx:role/tf-assume-role"
    policy             = <<EOF
    {
      "Version": "1",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": "oss:ListBuckets",
          "Resource": "acs:oss:oss-cn-hangzhou:*:*"
        }
      ]
    }
    EOF
    session_name       = "terraform-assume-role-session"
    session_expiration = 1000
  }
}

此时,只需要为绑定到 ECS 实例上的 RAM 角色授权系统策略 AliyunSTSAssumeRoleAccess,使其能够访问 AssumeRole API 即可。

OIDC 角色扮演(AssumeRoleWithOIDC)

和角色扮演(AssumeRole)类似,OIDC 角色扮演也是通过获取扮演 RAM角色 的临时身份凭证来实现对阿里云资源的访问的,唯一的不同是,OIDC 角色扮演要扮演的身份由 OIDC 身份提供商颁发的,详见管理OIDC身份提供商

基于 OIDC 角色扮演的身份认证是通过调用 AssumeRoleWithOIDC - OIDC角色SSO时获取扮演角色的临时身份凭证 来实现的,因此在 Terraform 中配置 OIDC 角色扮演身份认证前,首先需要准备如下内容:

  1. 创建一个 OIDC 身份提供商,并从外部 IdP 申请签发 OIDC 令牌(OIDC Token),记录 OIDC 身份提供商的 ARN 和 OIDC Token

  2. 创建一个授信实体为“身份提供商”的 RAM 角色,并为该角色授权想要访问的云服务的权限策略(比如访问 OSS 的全部访问权限 AliyunOSSFullAccess),并记录该 RAM 角色对应的 ARN

  3. 根据自身场景,编写需要访问的云资源的 RAM 自定义权限策略(比如只访问杭州地域的所有 Bucket),用于和授权给 RAM 角色的权限策略取交集,实现精细化的访问控制

在准备好如上内容后,在 provider 块中按照如下的方式进行声明:

provider "alicloud" {
  assume_role_with_oidc {
    oidc_provider_arn  = "<一个 OIDC 身份提供商的 ARN>"
    oidc_token         = "<外部 IDP 申请签发的 OIDC Token>"
    role_arn           = "<一个 RAM 角色的 ARN>"
    policy             = "<一个 RAM 权限策略的具体内容>"
    role_session_name  = "<一个自定义的角色会话名称>"
    session_expiration = <临时身份访问凭证的最大有效期>
  }
}

在 Terraform 中配置 OIDC 角色扮演的身份认证时,所有的参数都放在 assume_role_with_oidc 参数块中,其支持如下参数:

  1. oidc_provider_arn,必填参数,标识OIDC 身份提供商的 ARN,其值的格式为:acs:ram::<阿里云账号ID>:oidc-provider/<RAM Provider 角色名称>,如:acs:ram::151192xxxxxx:oidc-provider/ackrole,您可以通过 RAM 控制台或 API 查看 OIDC 身份提供商的 ARN。该参数支持通过环境变量 ALIBABA_CLOUD_OIDC_PROVIDER_ARN 设置。

  2. oidc_token,选填参数,标识由外部 IdP 签发的 OIDC 令牌(OIDC Token),长度:4~20000 个字符。该参数和 参数 oidc_token_file 两者必须设置一个。

  3. oidc_token_file,选填参数,标识存放由外部 IdP 签发的 OIDC 令牌(OIDC Token)的文件绝对路径。该参数和 参数 oidc_token_file 两者必须设置一个,并且支持通过环境变量 ALIBABA_CLOUD_OIDC_TOKEN_FILE 设置。

  4. role_arn,必填参数,标识使用哪个角色来扮演身份,其值的格式为:acs:ram::<阿里云账号ID>:role/<RAM 角色名称>,如:acs:ram::151192xxxxxx:role/k8srole,支持通过环境变量 ALIBABA_CLOUD_ROLE_ARN 设置。

  5. policy,选填参数,指定角色扮演的临时身份被授权了哪些权限。该参数为非必填参数,如果指定该权限策略,则临时身份最终的权限策略取跟扮演的 RAM 角色绑定的权限策略与该权限策略的交集;如果不指定该权限策略,则临时身份最终的权限策略取跟扮演的 RAM 角色绑定的权限策略。

  6. role_session_name,选填参数,标识角色会话名称。该参数为用户自定义参数。通常设置为调用角色扮演的用户身份,例如:用户名。如果不设置,阿里云 Provider 将默认值设置为 terraform。该参数支持通过环境变量 ALIBABA_CLOUD_ROLE_SESSION_NAME 设置。

  7. session_expiration,选填参数,标识临时身份的有效期,单位是秒,即多少秒之后换取到的临时身份将失效。该参数最小值为 900 秒,最大值为扮演的 RAM 角色的MaxSessionDuration时间,MaxSessionDuration默认值为 3600 秒,可以通过 UpdateRole - 更新角色信息 来修改最大会话时间,能修改的最大值为 43200 秒。

目前阿里云 ACK 已经支持通过开启 RRSA 功能来实现 OIDC Provider 的创建和 OIDC 令牌的签发,详见:通过RRSA配置ServiceAccount的RAM权限实现Pod权限隔离,并且在开启 RRSA 功能后,ack-pod-identity-webhook组件已为 Pod 自动注入了配置 ALIBABA_CLOUD_OIDC_PROVIDER_ARN,ALIBABA_CLOUD_OIDC_TOKEN_FILE,ALIBABA_CLOUD_ROLE_ARN,此时如果在 ACK 内运行 Terraform 命令,上述的获取杭州的 OSS Bucket 的列表的示例可以简化为:

provider "alicloud" {
  region = "cn-hangzhou"
  assume_role_with_oidc {
    policy             = <<EOF
    {
      "Version": "1",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": "oss:ListBuckets",
          "Resource": "acs:oss:oss-cn-hangzhou:*:*"
        }
      ]
    }
    EOF
    session_name       = "terraform-assume-role-session"
    session_expiration = 1000
  }
}

和角色扮演一样,如果不想通过 policy 参数来设置精细化的访问策略,也可以直接将上述 policy 的权限直接授权给 RAM 角色,这样上述的示例可以进一步得到简化:

provider "alicloud" {
  region = "cn-hangzhou"
  assume_role_with_oidc {
    policy             = <<EOF
    {
      "Version": "1",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": "oss:ListBuckets",
          "Resource": "acs:oss:oss-cn-hangzhou:*:*"
        }
      ]
    }
    EOF
    session_name       = "terraform-assume-role-session"
    session_expiration = 1000
  }
}

共享的配置文件(profile)

共享额配置文件 - Profile 是阿里云 CLI 提供的一个通用功能,用于用统一的方式配置访问阿里云资源所需的凭证信息。目前 Profile 对访问凭证的设置支持对静态配置、ECS 服务角色、角色扮演这三种方式,具体的配置方式详见配置凭证

当使用阿里云 CLI 完成多个凭证的配置后,Terraform 可以直接基于 Profile 的名称来配置身份认证方式:

provider "alicloud" {
  region                  = "cn-hangzhou"
  shared_credentials_file = "<Profile 配置文件的绝对路径>"
  profile                 = "<所要使用的 Profile 的名称>"
}

在 Terraform 中通过 Profile 配置身份认证时,支持如下参数:

  1. profile,必填参数,profile 的名称,标识将使用哪个 profile 来进行身份认证

  2. shared_credentials_file,选填参数,标识profile 的存储位置,该文件可以在通过 CLI 设置 profile 的时候通过选项 --config-path 来指定。

配置了 Profile 的后,当涉及到多环境切换或者多个账号切换的时候将非常方便。比如,想要在开发测试环境中分别在北京和杭州创建阿里云资源,此时 provider 的配置方式可以使用如下的方式:

provider "alicloud" {
  alias   = beijing
  region  = "cn-beijing"
  profile = "bj-test"
}

provider "alicloud" {
  alias   = hangzhou
  region  = "cn-hangzhou"
  profile = "hz-test"
}

总结

最后,对如上的身份认证方式做个总结。

  1. 优先级

    通常在配置 provider 的时候,只选择其中的一种身份认证方式,但是当同时设置了多种认证方式后,将按照优先级顺序选择优先级最高的认证方式,具体的优先级如下:静态配置 > 环境变量 > Profile 静态配置 > ECS 服务角色 > Profile ECS 服务角色 > OIDC 角色扮演 > 角色扮演

  2. 安全性

    不同的身份认证方式都有不同的适用场景,当 Terraform 应用到生产环境的时候,安全性是必须要考虑的因素。我们推荐 ECS 服务角色、角色扮演、OIDC 角色扮演三种认证方式,这三种方式使用的是临时身份,一方面无需直接暴露 AccessKey,减少密钥泄露的风险,另一方面,临时身份可以设置过期时间,可以有效地控制暴露范围。

  3. 精细化授权

    在企业生产环境中,通常都会按照应用、团队、项目等维度对资源管理的权限进行严格的控制,此时对权限策略的精细化控制就变得至关重要。角色扮演、OIDC 角色扮演是我们推荐的精细化授权的认证方式,可以按照不同项目或者应用的基础设施代码设置不同的权限策略。