Terraform无法在启用MFA时扮演角色。

19
我很难以让Terraform扮演一个需要MFA的另一个账户中的IAM角色。以下是我的设置:
AWS配置
[default]
region = us-west-2
output = json

[profile GEHC-000]
region = us-west-2
output = json

....

[profile GEHC-056]
source_profile = GEHC-000
role_arn = arn:aws:iam::~069:role/hc/hc-master
mfa_serial = arn:aws:iam::~183:mfa/username
external_id = ~069

AWS凭证
[default]
aws_access_key_id = xxx
aws_secret_access_key = xxx


[GEHC-000]
aws_access_key_id = same as above
aws_secret_access_key = same as above

IAM用户分配的策略:
STS策略
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AssumeRole",
            "Effect": "Allow",
            "Action": [
                "sts:AssumeRole"
            ],
            "Resource": [
                "arn:aws:iam::*:role/hc/hc-master"
            ]
        }
    ]
}

用户政策
{
    "Statement": [
        {
            "Action": [
                "iam:*AccessKey*",
                "iam:*MFA*",
                "iam:*SigningCertificate*",
                "iam:UpdateLoginProfile*",
                "iam:RemoveUserFromGroup*"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:iam::~183:mfa/${aws:username}",
                "arn:aws:iam::~183:mfa/*/${aws:username}",
                "arn:aws:iam::~183:mfa/*/*/${aws:username}",
                "arn:aws:iam::~183:mfa/*/*/*${aws:username}",
                "arn:aws:iam::~183:user/${aws:username}",
                "arn:aws:iam::~183:user/*/${aws:username}",
                "arn:aws:iam::~183:user/*/*/${aws:username}",
                "arn:aws:iam::~183:user/*/*/*${aws:username}"
            ],
            "Sid": "Write"
        },
        {
            "Action": [
                "iam:*Get*",
                "iam:*List*"
            ],
            "Effect": "Allow",
            "Resource": [
                "*"
            ],
            "Sid": "Read"
        },
        {
            "Action": [
                "iam:CreateUser*",
                "iam:UpdateUser*",
                "iam:AddUserToGroup"
            ],
            "Effect": "Allow",
            "Resource": [
                "*"
            ],
            "Sid": "CreateUser"
        }
    ],
    "Version": "2012-10-17"
}

强制使用多因素身份验证策略
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "BlockAnyAccessOtherThanAboveUnlessSignedInWithMFA",
            "Effect": "Deny",
            "NotAction": "iam:*",
            "Resource": "*",
            "Condition": {
                "BoolIfExists": {
                    "aws:MultiFactorAuthPresent": "false"
                }
            }
        }
    ]
}

main.tf

provider "aws" {
  profile                 = "GEHC-056"
  shared_credentials_file = "${pathexpand("~/.aws/config")}"
  region                  = "${var.region}"
}

data "aws_iam_policy_document" "test" {
  statement {
    sid    = "TestAssumeRole"
    effect = "Allow"

    actions = [
      "sts:AssumeRole",
    ]

    principals = {
      type = "AWS"

      identifiers = [
        "arn:aws:iam::~183:role/hc-devops",
      ]
    }

    sid    = "BuUserTrustDocument"
    effect = "Allow"

    principals = {
      type = "Federated"

      identifiers = [
        "arn:aws:iam::~875:saml-provider/ge-saml-for-aws",
      ]
    }

    condition {
      test     = "StringEquals"
      variable = "SAML:aud"
      values   = ["https://signin.aws.amazon.com/saml"]
    }
  }
}

resource "aws_iam_role" "test_role" {
  name               = "test_role"
  path               = "/"
  assume_role_policy = "${data.aws_iam_policy_document.test.json}"
}

获取来电者身份。
bash-4.4$ aws --profile GEHC-056 sts get-caller-identity
Enter MFA code for arn:aws:iam::772660252183:mfa/503072343:
{
  "UserId": "AROAIWCCLC2BGRPQMJC7U:botocore-session-1537474244",
  "Account": "730993910069",
  "Arn": "arn:aws:sts::730993910069:assumed-role/hc-master/botocore-session-1537474244"
}

"而且出现了错误:"
bash-4.4$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.


Error: Error refreshing state: 1 error(s) occurred:

* provider.aws: Error creating AWS session: AssumeRoleTokenProviderNotSetError: assume role with MFA enabled, but AssumeRoleTokenProvider session option not set.
4个回答

13

Terraform目前不支持提示输入MFA令牌,因为它旨在尽可能以较少的交互方式运行,并且显然需要对提供程序结构进行重大改进才能支持此交互式提供程序配置。有关此问题的更多讨论,请参见此问题

正如在那个问题中提到的,最好的方法是在运行Terraform之前使用某种脚本/工具来假定角色。

我个人使用AWS-Vault并编写了一个小的shim shell脚本,我从中创建符号链接terraform(还有其他一些我希望使用AWS-Vault获取凭据的东西,例如aws),它检测到被调用的内容,使用which-a 找到“真实”的二进制文件,然后使用AWS-Vault的exec使用指定的凭据运行目标命令。

我的脚本看起来像这样:

#!/bin/bash

set -eo pipefail

# Provides a shim to override target executables so that it is executed through aws-vault
# See https://github.com/99designs/aws-vault/blob/ae56f73f630601fc36f0d68c9df19ac53e987369/USAGE.md#overriding-the-aws-cli-to-use-aws-vault for more information about using it for the AWS CLI.

# Work out what we're shimming and then find the non shim version so we can execute that.
# which -a returns a sorted list of the order of binaries that are on the PATH so we want the second one.
INVOKED=$(basename $0)
TARGET=$(which -a ${INVOKED} | tail -n +2 | head -n 1)

if [ -z ${AWS_VAULT} ]; then
    AWS_PROFILE="${AWS_DEFAULT_PROFILE:-read-only}"
    (>&2 echo "Using temporary credentials from ${AWS_PROFILE} profile...")

    exec aws-vault exec "${AWS_PROFILE}" --assume-role-ttl=60m -- "${TARGET}" "$@"
else
    # If AWS_VAULT is already set then we want to just use the existing session instead of nesting them
    exec "${TARGET}" "$@"
fi
它将使用您~/.aws/config文件中与您设置的AWS_DEFAULT_PROFILE环境变量匹配的配置文件,如果没有设置,则默认使用只读配置文件。这确保 AWS-Vault 假定了 IAM 角色,获取凭证并将其设置为目标进程的环境变量。就 Terraform 而言,这意味着它通过环境变量获得了凭证,这样就可以正常工作了。

1
你能更新一下 shim 步骤吗?我无法使用 ${TARGET} 变量获取 TF。 - ehime
1
你是在路径中把东西链接到目标二进制文件之前的shim吗?这样运行 which terraform 应该返回指向shim的符号链接。which -a 应该同时显示shim符号链接和真正的 terraform 二进制文件。 - ydaetskcoR

4
另一种方法是使用 credential_process 来使用本地脚本生成凭证并将令牌缓存到新配置文件(我们称之为tf_temp)中。
该脚本将会:
  • 检查配置文件tf_temp的令牌是否仍然有效

  • 如果令牌有效,则从现有配置中提取令牌,使用aws configure get xxx --profile tf_temp

  • 如果令牌无效,则提示用户输入MFA令牌

  • 使用aws assume-role --token-code xxxx ... --profile your_profile生成会话令牌

  • 使用aws configure set xxx --profile tf_temp设置临时配置文件令牌tf_temp

您将拥有:

~/.aws/credentials

[prod]
aws_secret_access_key = redacted
aws_access_key_id = redacted

[tf_temp]

[tf]
credential_process = sh -c 'mfa.sh arn:aws:iam::{account_id}:role/{role} arn:aws:iam::{account_id}:mfa/{mfa_entry} prod 2> $(tty)'

mfa.sh

gist

将此脚本移动到/bin/mfa.sh/usr/local/bin/mfa.sh中:

#!/bin/sh
set -e

role=$1
mfa_arn=$2
profile=$3
temp_profile=tf_temp

if [ -z $role ]; then echo "no role specified"; exit 1; fi
if [ -z $mfa_arn ]; then echo "no mfa arn specified"; exit 1; fi
if [ -z $profile ]; then echo "no profile specified"; exit 1; fi

resp=$(aws sts get-caller-identity --profile $temp_profile | jq '.UserId')

if [ ! -z $resp ]; then
    echo '{
        "Version": 1,
        "AccessKeyId": "'"$(aws configure get aws_access_key_id --profile $temp_profile)"'",
        "SecretAccessKey": "'"$(aws configure get aws_secret_access_key --profile $temp_profile)"'",
        "SessionToken": "'"$(aws configure get aws_session_token --profile $temp_profile)"'",
        "Expiration": "'"$(aws configure get expiration --profile $temp_profile)"'"
    }'
    exit 0
fi
read -p "Enter MFA token: " mfa_token

if [ -z $mfa_token ]; then echo "MFA token can't be empty"; exit 1; fi

data=$(aws sts assume-role --role-arn $role \
                    --profile $profile \
                    --role-session-name "$(tr -dc A-Za-z0-9 </dev/urandom | head -c 20)" \
                    --serial-number $mfa_arn \
                    --token-code $mfa_token | jq '.Credentials')

aws_access_key_id=$(echo $data | jq -r '.AccessKeyId')
aws_secret_access_key=$(echo $data | jq -r '.SecretAccessKey')
aws_session_token=$(echo $data | jq -r '.SessionToken')
expiration=$(echo $data | jq -r '.Expiration')

aws configure set aws_access_key_id $aws_access_key_id --profile $temp_profile
aws configure set aws_secret_access_key $aws_secret_access_key --profile $temp_profile
aws configure set aws_session_token $aws_session_token --profile $temp_profile
aws configure set expiration $expiration --profile $temp_profile

echo '{
  "Version": 1,
  "AccessKeyId": "'"$aws_access_key_id"'",
  "SecretAccessKey": "'"$aws_secret_access_key"'",
  "SessionToken": "'"$aws_session_token"'",
  "Expiration": "'"$expiration"'"
}'

在提供程序设置中使用tf配置文件。第一次使用时,您将被提示输入MFA令牌:

# terraform apply
Enter MFA token: 428313

这个解决方案适用于terraform和/或terragrunt,表现良好。

3

我曾使用一种非常简单但可能有些不太正规的解决方法来解决这个问题:

首先,让TF从环境变量中选择凭据。然后:

AWS凭据文件:

[access]
aws_access_key_id = ...
aws_secret_access_key = ...
region = ap-southeast-2
output = json

[target]
role_arn = arn:aws:iam::<target nnn>:role/admin
source_profile = access
mfa_serial = arn:aws:iam::<access nnn>:mfa/my-user

在控制台中

CREDENTIAL=$(aws --profile target sts assume-role \
  --role-arn arn:aws:iam::<target nnn>:role/admin --role-session-name TFsession \
  --output text \
  --query "Credentials.[AccessKeyId,SecretAccessKey,SessionToken,Expiration]")

<enter MFA>

#echo "CREDENTIAL: ${CREDENTIAL}"
export AWS_ACCESS_KEY_ID=$(echo ${CREDENTIAL} | cut -d ' ' -f 1)
export AWS_SECRET_ACCESS_KEY=$(echo ${CREDENTIAL} | cut -d ' ' -f 2)
export AWS_SESSION_TOKEN=$(echo ${CREDENTIAL} | cut -d ' ' -f 3)

terraform plan

更新:更好的解决方案是使用https://github.com/remind101/assume-role来实现相同的结果。


1
更好的做法是使用AWS Vault。它可以做到 https://github.com/remind101/assume-role 所能做到的,但它还可以让你将凭证从明文的 ~/.aws/credentials 文件中移动到加密保险库中。 - Ritchie

2

我个人使用aws-vault,当启用IAM MFA时,它与terraform非常配合使用。

  1. 安装aws-vault:https://github.com/99designs/aws-vault.git
  2. 将您的AWS凭据添加(存储)到aws-vault中:aws-vault add profilename
  3. 更新您的~/.aws/config文件,添加“mfa_serial”和“role_arn”信息。
[profile <profilename>]
region = <region>
mfa_serial = arn:aws:iam::<AWSAccountA>:mfa/<username>
role_arn = arn:aws:iam::<AWSAccountB>:role/<rolename>

使用以下命令来运行terraform命令:
$aws-vault exec <profilename> -- terraform apply
$<input your mfa code>

完成。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接