Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Doesn't ask MFA token code when using assume_role with MFA required #2420

Open
jsi-p opened this Issue Nov 23, 2017 · 13 comments

Comments

Projects
None yet
10 participants
@jsi-p
Copy link

jsi-p commented Nov 23, 2017

When using multiple AWS accounts it's good practice to only allow access via AssumeRole from a master account. This can be done with or without requiring MFA. Terraform supports assume_role with s3 state file and aws provider configurations, but doesn't seem to ask the MFA token code when one is required. This prevents using AssumeRole for credentials when MFA is required.

AWS documentation describing MFA with cross account AssumeRole: http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_mfa_configure-api-require.html#MFAProtectedAPI-cross-account-delegation

Terraform Version

$ terraform --version
Terraform v0.11.0

Affected Resource(s)

Both of these support assume_role, so they should also support asking for MFA token code:

  • S3 backend configuration
  • AWS provider configuration

Terraform Configuration Files

terraform {
  backend "s3" {
    bucket = "terraform-state-bucket"
    key = "tf-state"
    region = "eu-west-1"
    role_arn = "arn:aws:iam::916005212345:role/OrganizationAccountAccessRole"
  }
}
provider "aws" {
  region = "eu-west-1"
  assume_role {
    role_arn = "arn:aws:iam::916005212345:role/OrganizationAccountAccessRole"
    session_name = "terraform-session"
  }
}

Actual Behavior (with DEBUG)

$ TF_LOG=debug terraform init
2017/11/23 13:36:12 [INFO] Terraform version: 0.11.0  
2017/11/23 13:36:12 [INFO] Go runtime version: go1.9.2
2017/11/23 13:36:12 [INFO] CLI args: []string{"/usr/local/Cellar/terraform/0.11.0/bin/terraform", "init"}
2017/11/23 13:36:12 [DEBUG] Attempting to open CLI config file: /Users/***/.terraformrc
2017/11/23 13:36:12 [DEBUG] File doesn't exist, but doesn't need to. Ignoring.
2017/11/23 13:36:12 [INFO] CLI command args: []string{"init"}
2017/11/23 13:36:12 [DEBUG] command: loading backend config file: /Users/***

Initializing the backend...
2017/11/23 13:36:12 [WARN] command: backend config change! saved: 16375281725947963338, new: 2383462577283113429
Backend configuration changed!

Terraform has detected that the configuration specified for the backend
has changed. Terraform will now reconfigure for this backend. If you didn't
intend to reconfigure your backend please undo any changes to the "backend"
section in your Terraform configuration.


2017/11/23 13:36:12 [INFO] Building AWS region structure
2017/11/23 13:36:12 [INFO] Building AWS auth structure
2017/11/23 13:36:12 [INFO] Setting AWS metadata API timeout to 100ms
2017/11/23 13:36:13 [INFO] Ignoring AWS metadata API endpoint at default location as it doesn't return any instance-id
2017/11/23 13:36:13 [INFO] Attempting to AssumeRole arn:aws:iam::***:role/OrganizationAccountAccessRole (SessionName: "", ExternalId: "", Policy: "")
2017/11/23 13:36:13 [INFO] AWS Auth provider used: "SharedCredentialsProvider"
2017/11/23 13:36:13 [DEBUG] plugin: waiting for all plugin processes to complete...
Error initializing new backend: 
Error configuring the backend "s3": The role "arn:aws:iam::***:role/OrganizationAccountAccessRole" cannot be assumed.

  There are a number of possible causes of this - the most common are:
    * The credentials used in order to assume the role are invalid
    * The credentials do not have appropriate permission to assume the role
    * The role ARN is not valid

Please update the configuration in your Terraform files to fix this error
then run this command again.
@jsi-p

This comment has been minimized.

Copy link
Author

jsi-p commented Nov 23, 2017

This comment describes the same issue (while they workaround by not using MFA, which we do not want to do):

hashicorp/terraform#11270 (comment)

@jsi-p

This comment has been minimized.

Copy link
Author

jsi-p commented Nov 23, 2017

I would also like to see this work when simply using AWS_PROFILE env with the target profile configured to use a cross account role_arn and mfa_serial. In fact this would be the preferred way to use terraform for us.

$HOME/.aws/credentials:

[default]
aws_access_key_id = ***
aws_secret_access_key = ***

[subaccount]
role_arn = arn:aws:iam::***:role/OrganizationAccountAccessRole
source_profile = default
mfa_serial = arn:aws:iam::***:mfa/xxx@yyy.com

Terraform invocation:

AWS_PROFILE=subaccount terraform init
@tamsky

This comment has been minimized.

Copy link
Contributor

tamsky commented Nov 29, 2017

FYI use of shared [profile]s via ~/.aws/config only landed a few days ago in:

#1608

Comment from that PR:

This won't support assuming roles where an MFA device has been specified for the profile (in ~/.aws/config)

There was another implementation of #1608 -- it appears to include some (possibly limited) MFA support, but was closed in favor of 1608:

https://github.com/terraform-providers/terraform-provider-aws/pull/1590/files#diff-0460e055ea98f6ed9713f2265a1b1d49R266

Getting the go-sdk to prompt (once, and only once) for an MFA code would appear to involve a bit more work, but not much, by my reading of the comments.

@tamsky

This comment has been minimized.

Copy link
Contributor

tamsky commented Nov 29, 2017

aws/aws-sdk-go#1088 adds the StdinTokenProvider which we'd use.

@tamsky

This comment has been minimized.

Copy link
Contributor

tamsky commented Nov 29, 2017

Also, caching of the temporary sts credentials in a portable cross-process manner might be an open issue: aws/aws-sdk-go#1329

@tamsky

This comment has been minimized.

Copy link
Contributor

tamsky commented Nov 29, 2017

awless's implementation of 15min temporary credential caching:
wallix/awless#109 (comment)
in
wallix/awless@5a73223

@danlsgiga

This comment has been minimized.

Copy link

danlsgiga commented Dec 7, 2017

Just had the same issue... Have a profile with a role to be assumed and MFA enabled but it seems MFA is not supported at this moment.
This would be a great addition to our setup.

We don't really need it to be prompted (probably should not even be considered)... working just like Packer works would be sufficient and that is having a mfa_code option where we can pass using -var or env vars.

https://www.packer.io/docs/builders/amazon-ebs.html#mfa_code

@mikemoate

This comment has been minimized.

Copy link
Contributor

mikemoate commented Dec 8, 2017

@tamsky I tried to add the MFA functionality into #1608 (we ideally wanted to be using MFA) but you are correct that the lack of an STS credentials cache essentially makes this unusable for the end user (repeated prompts for MFA token, and as tokens can only be used once, you have to wait until a new token is generated each time).

Since the inability to assume roles defined in the profile file was hurting us more, I elected to pursue that fix for now (still hasn't quite been released, see hashicorp/terraform#16661), though I might return to the MFA issue when I next have some spare capacity (if someone else hasn't stepped in to solve it).

(the original PR hashicorp/terraform#11734 that I resurrected to form #1608, had some discussion on MFA which led to me experimenting and finding the limitations)

@apparentlymart

This comment has been minimized.

Copy link
Contributor

apparentlymart commented Dec 18, 2017

Hi all!

In general, Terraform doesn't currently support this sort of dynamic prompting for providers, and indeed the existing prompting features in Terraform are there primarily to help new users get started and we expect users to switch pretty early to using Terraform as a non-interactive, scripted tool, ideally running in a centrally-administered environment to avoid the need to sprawl various secrets across multiple user workstations.

Some users have reported success using a wrapper/helper script that calls sts:GetSessionToken to obtain temporary credentials in return for an MFA token, and then passes those temporary credentials to Terraform via environment variables, thus allowing that wrapper program to use whatever interactive means is appropriate (CLI prompt, web UI, etc) to collect that MFA token. This is, indeed, the methodology used by Terraform Enterprise for its AWS MFA support.

Managing authentication use-cases in wrapper scripts allows for other more advanced use-cases too, such as SAML authentication.

At this time we do not have plans to support interactive authentication to providers since it would require some significant changes to the provider model. We will probably look at this again in future in the context of some other breaking change to the provider model so that we can bundle multiple changes together, though as noted above we generally expect Terraform to be used non-interactively in some sort of automation once users pass the experimental phase, so any features we build in this area will need to also solve for a non-interactive workflow for that common case.

@phil-hachey

This comment has been minimized.

Copy link

phil-hachey commented Jan 22, 2018

For my purposes, I needed this feature for AWS org resource management across multiple accounts, so I can easily bootstrap new accounts. Another use case is for DevOps to manage resources across accounts originating from a "security" account (as described here). In all scenarios, MFA should be enabled for cross account access (internal requirement).

I ended up getting this to work by requesting temporary credentials using MFA and saving them to ~/.aws/credentials. With these credentials, the "MFA required" condition has been met and you're free to use the assume_role block of the aws provider in Terraform:

provider "aws" {
  alias  = "dev_account"
  region = "${var.aws_region}"

  assume_role {
    role_arn = "${var.dev_account_target_role_arn}"
  }
}

I created a simple python cli to generate the creds:
https://github.com/phil-hachey/aws-mfa-tool

@scalp42

This comment has been minimized.

Copy link
Contributor

scalp42 commented Aug 8, 2018

Has anyone tried working around the issue by using something like https://github.com/remind101/assume-role ?

@kmaris

This comment has been minimized.

Copy link

kmaris commented Aug 8, 2018

@scalp42 assume-role works. I also used a smaller and simpler script to do about the same: https://gitlab.com/kmaris/wtf (The name is terrible, I know).

@andreas-venturini

This comment has been minimized.

Copy link

andreas-venturini commented Aug 8, 2018

I ended up getting this to work by requesting temporary credentials using MFA and saving them to ~/.aws/credentials

I suggest aws-vault for this purpose as it is much more convenient. aws-vault stores your long-term access credentials encrypted and will deal with exposing temporary AWS access credentials to your shell or application so you don't have to. As a matter of fact, you can delete your credentials file and manage AWS API access entirely w/ aws-vault and named profiles defined in ~/.aws/config.

I'll show an example that uses cross account IAM role delegation for executing terraform commands.
OrganizationAccountAccessRole is the IAM role that authorized IAM users from an AWS master account can assume. The trust policy of the IAM role enforces MFA (set aws:MultiFactorAuthPresent boolean flag as described here)

The terraform AWS provider config:

provider "aws" {
  profile = "${var.aws_vault_profile}"
  region  = "${var.aws_region}"
  version = "~> 1.30.0"

  assume_role {
    role_arn = "arn:aws:iam::${var.aws_app_account_id}:role/OrganizationAccountAccessRole"
  }
}

Here is the corresponding ~/.aws/config file. It defines a terraform profile which uses MFA (the [profile ...] block is automatically created by aws-vault when adding a new profile - you just have to fill in the arn of the MFA device).

[default]
aws_region=eu-central-1

[profile terraform]
mfa_serial=arn:aws:iam::123456789012:mfa/Andreas

Then simply use aws-vault exec to wrap the terraform command - or set up an alias like so (note to change the backend depending on your system environment - this example uses the secret service API w/ keyring which works well for me on Ubuntu):

# always use aws-vault to wrap terraform command
alias terraform='aws-vault exec terraform --backend=secret-service -- terraform'

When you run terraform you will be automatically prompted for the access token:

➜  terraform-setup (master) ✗ terraform workspace list                                                          
Enter token for arn:aws:iam::123456789012:mfa/Andreas: 

Of course you can also use multiple profiles w/ aws-vault. There are more advanced configuration examples in the aws-vault docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.