适用于临时MFA凭证的AWS CLI Automation

Multi-Factor Authentication (MFA), is the panacea to the security dichotomy where password authentication is a single point of failure.

多重身份验证(MFA)是安全二分法的灵丹妙药,其中密码身份验证是单点故障。

So, where could it go wrong with awscli? Let’s find out !

那么, awscli哪里出问题了? 让我们找出答案!

Recently, I started using Amazon Web Services (AWS)’s products more frequently. This was mostly related to a product handover from another team, where many aspects of the infrastructure are on AWS.

最近,我开始更频繁地使用Amazon Web Services(AWS)的产品。 这主要与另一个团队的产品移交有关,该团队的基础架构的许多方面都在AWS上。

When using AWS, I started onboarding on its MFA related workflows. This was because, as part of the security policies at where I worked, MFA/2FA has became more of a central focus of our operations. Calling AWS’s APIs, or more accurately, using awscli, was no exception to this rule; all accounts had to have MFA enabled.

使用AWS时,我开始加入与MFA相关的工作流程。 这是因为,作为我工作过的安全策略的一部分,MFA / 2FA成为了我们运营的重点。 使用awscli调用或更准确地说是AWS的API也不例外。 所有帐户都必须启用MFA。

问题” (The “Problem”)

Image for post
here for its code repository 此处

Since MFA had already been enabled as a requirement for accessing our AWS resources, various operations of awscli are also affected. For example, if I were to access or delete an EC2 instance via awscli, I would have to first retrieve some temporary credentials. This process requires the serial code of my MFA token device as well as the temporary generated six digit MFA code. The serial code can be found in the web console, while the six digit MFA code is generated from your device, and must be inputted before it expires.

由于已经启用MFA作为访问我们的AWS资源的要求,因此awscli各种操作也会受到影响。 例如,如果要通过awscli访问或删除EC2实例,则必须首先检索一些临时凭证。 此过程需要我的MFA令牌设备的序列号以及临时生成的六位数MFA代码。 序列号可以在Web控制台中找到,而六位数的MFA代码是从您的设备生成的,并且必须在到期之前输入。

When authenticated using aws sts get-session-token, you would be issued a set of temporary credentials that you can use, as seen below.

使用aws sts get-session-token身份验证时,将向您提供一组可以使用的临时凭证,如下所示。

Image for post

Unlike the aws configure command, there is no option given to immediately save it to my machine. Hence, there would be some manual copy and paste action required to using these credentials, causing the process for generating this set of credentials and using them, an overall cumbersome process.

aws configure 命令不同,没有提供立即将其保存到我的计算机的选项。 因此,使用这些凭据需要进行一些手动复制和粘贴操作,从而导致生成这组凭据并使用它们的过程非常麻烦。

Let’s look at the two ways that the documentation suggests, in generating and using these credentials, and how we can make it better.

让我们看看在生成和使用这些凭证时文档建议的两种方式,以及如何使其变得更好。

文档方法1-将临时凭证与环境变量一起使用 (Documentation Method 1 — Using temporary credentials with environment variables)

The first method details the use of environment variables, in order to use them with awscli. This is seen as the following:

第一种方法详细介绍了环境变量的使用,以便将它们与awscli一起使用。 如下所示:

export AWS_ACCESS_KEY_ID="access-key-id"
export AWS_SECRET_ACCESS_KEY="secret-access-key"
export AWS_SESSION_TOKEN="session-token"

The major gripes I have with this method, are these two things:

我对这种方法的主要掌握是以下两点:

  1. We have to manually retrieve the serial code from the web console every time we need to use it.

    每次需要使用时,我们都必须从Web控制台手动检索序列号。
  2. We have to manually copy and paste from the credential output to environmental variables

    我们必须手动将凭据输出中的内容复制并粘贴到环境变量中
  3. We have to manually “unset” the environment variables once it expires. The only way to know if it has expired without saving the expiry date, is to receive an error indicating the expiry when running other AWS commands with the temporary credentials.

    过期后,我们必须手动“重置”环境变量。 在不保存到期日期的情况下唯一知道其到期的方法是在使用临时凭证运行其他AWS命令时收到指示到期的错误。

From the above, you can see that the process becomes cumbersome at the start and the end of the lifecycle for each set of temporary credentials.

从上面可以看到,对于每组临时凭证,该过程在生命周期的开始和结束时都很麻烦。

文档方法2-将临时凭证与命名配置文件一起使用 (Documentation Method 2 — Using temporary credentials with named profiles)

The second method, is to save the credentials as a different named profile. Named profiles are essentially different credential scopes, where you can choose one of them to use for any awscli command.

第二种方法是将凭据保存为其他命名的profile 。 命名配置文件本质上是不同的凭证范围,您可以在其中选择其中一个供任何awscli命令使用。

To save the credentials, you can use various awscli commands. When I first started to use awscli with MFA, I used aws configure , which was something I was already familiar with. This is seen as follows:

要保存凭据,可以使用各种awscli命令。 当我第一次开始在MFA中使用awscli时,我使用了aws configure ,这是我已经熟悉的东西。 如下所示:

Image for post

If you compare the above prompts of aws configure on what values can be set to the temporary credentials issued earlier, you may realise that it is missing a “session token” parameter. The immediate solution to this is to edit the credential file, .aws/credentials, and add the property in directly:

如果比较上面的aws configure提示aws configure ,可以将哪些值设置为较早发布的临时凭据,则可能会意识到它缺少“会话令牌”参数。 对此的直接解决方案是编辑凭据文件.aws/credentials ,然后将属性直接添加到:

[default]
aws_access_key_id = "user-access-key-id"
aws_secret_access_key = "user-secret-access-key"


[mfa-user]
aws_access_key_id = "temp-access-key-id"
aws_secret_access_key = "temp-secret-access-key"
aws_session_token = "temp-session-token"

Some time later, I found out another way to do this, which was by using aws configure set aws_session_token “temp-session-token” . This allows us to bypass the step for editing the credentials file directly, localising all the steps to the CLI, making it slightly easier.

一段时间后,我发现了另一种方法,即使用aws configure set aws_session_token “temp-session-token” 。 这使我们可以绕过直接编辑凭据文件的步骤,将所有步骤都本地化到CLI,从而使其变得稍微容易一些。

Regardless, I felt that this method missed the mark for me as well, for the following reasons:

无论如何,我认为此方法也对我不利,原因如下:

  1. We still have to manually retrieve the serial code from the web console every time we need to use it.

    每次需要使用序列号时,我们仍然必须从Web控制台手动检索序列号。
  2. We still have to perform the manual step of copy and pasting of the temporary credentials output.

    我们仍然必须执行复制和粘贴临时凭证输出的手动步骤。
  3. There is also the lack of knowledge of when the credentials would expire. Only when some command ran from awscli returns with the error, can we know that the entire process of retrieving the credentials has to be done again.

    还缺少有关凭证何时到期的知识。 仅当从awscli运行某些命令并返回错误时,我们才能知道必须重新完成整个检索凭证的过程。

  4. There is an additional complexity to running any awscli commands, where you have to indicate the named profile. For example, to describe EC2 instances with the default profile, you can use the following command — aws ec2 describe-instances . However, to use the named profile, you have to append each command with an additional profile flag — aws ec2 describe-instances --profile mfa-user . This adds another manual step in this entire MFA process. Writing to the default profile is an option to avoid adding this flag to every command, but creates another issue where your regular user credentials has to be saved in another profile instead.

    运行任何awscli命令会带来额外的复杂性,您必须在其中指示命名的配置文件。 例如,要使用默认配置文件描述EC2实例,可以使用以下命令— aws ec2 describe-instances 。 但是,要使用命名的配置文件,您必须在每个命令后附加一个附加的配置文件标志— aws ec2 describe-instances --profile mfa-user 。 这在整个MFA流程中增加了另一个手动步骤。 写入默认配置文件是一种避免将此标志添加到每个命令的选项,但是会产生另一个问题,必须将常规用户凭据保存在另一个配置文件中。

DevOps方式-“脚本化”我的出路 (The DevOps way — “Scripting” my way out)

Since I felt that the above-mentioned methods were too cumbersome with too many manual steps, I sought to find a better way to solve the problem. Note that these were the objectives I wanted to achieve with this effort:

由于我认为上述方法过于繁琐且需要太多手动步骤,因此我寻求找到一种更好的方法来解决该问题。 请注意,这些是我希望通过此努力实现的目标:

  1. Automate the entire process — the user should only be concerned with entering the MFA device serial code, and MFA six digit token code.

    自动化整个过程-用户只应考虑输入MFA设备序列号和MFA六位数令牌代码。
  2. Re-prompt the user to key in a new MFA six digit token code if validation of the code fails (non-number input, more or less than 6 digits, code expired etc.). We can also do the same for the MFA device serial code.

    如果验证失败(非数字输入,多于或少于6位数字,代码过期等),则重新提示用户键入新的MFA六位数令牌代码。 我们也可以对MFA设备序列号执行相同的操作。
  3. This process should be on a per code repository basis. However, the scripts can be adapted to work on the system level as well.

    此过程应基于每个代码存储库。 但是,这些脚本也可以适用于系统级别。
  4. (Optional) Abstract awscli away from the developers. If developers need to perform some action like shell-ing into their virtual machines, provide a script or some application that allows them to do so, without the context of the service provider or underlying tool. Removing the context is advantageous, as it allows us to maintain a common interface for all tools, and also allows for a more concise documentation on how to use them (reducing context-switching and outbound tool documentation references). In a multi-cloud setting, this causes less disruptions for developers, especially since infrastructure of the systems should not affect their day to day work.

    (可选)远离开发人员的抽象awscli 。 如果开发人员需要在虚拟机中执行一些操作,例如外壳化,请提供脚本或一些允许他们执行此操作的应用程序,而无需服务提供商或基础工具的上下文。 删除上下文是有利的,因为它使我们可以为所有工具维护一个通用接口,并且还允许提供有关如何使用它们的更简洁的文档 (减少上下文切换和出站工具文档参考) 。 在多云环境中,这对开发人员造成的干扰较小,尤其是因为系统的基础结构不应影响他们的日常工作。

With the above objectives in mind, let’s get started!

考虑到上述目标,让我们开始吧!

序列号-自动化的第一件事 (Serial code — the first thing to automate)

The first thing to automate, would be the process to “register” the serial code of the MFA device. This will form part of my MFA process, where this will be a one-time step and used for every subsequent generation of temporary credentials, as opposed to having to load the AWS web console and retrieve this number every time.

自动化的第一件事是“注册” MFA设备序列号的过程。 这将构成我的MFA流程的一部分,这将是一次性步骤,并且用于以后的临时凭证生成,而不必每次都加载AWS Web控制台并检索此编号。

The following script was created for performing this “registration”:

创建了以下脚本来执行此“注册”:

#!/bin/sh


# Default filename values - change this or add as environment values, depending on your own needs
MFA_SERIAL_FILE=".mfaserial"


# For variable value of $TMP_DIR, you can either set this as a path that is .gitignored in this project, for saving this developer's context for this project, or a absolute path from root as a system configuration.  wrap this script with some other environment variable injected for this value
mkdir -p $TMP_DIR


# A loop to continue prompting the user for the device serial code until a non-empty string is returned
echo "Tip - You can get your ARN for MFA device here: https://console.aws.amazon.com/iam/home#/security_credentials"
while true; do
    read -p "Please input the ARN (e.g. \"arn:aws:iam::12345678:mfa/username\"): " mfa
    case $mfa in
        "") echo "Please input a valid value.";;
        * ) echo $mfa > $TMP_DIR/$MFA_SERIAL_FILE; break;;
    esac
done

When running from the terminal, the prompt will look like this:

从终端运行时,提示符将如下所示:

Image for post

If the developer enters a wrong input, such as an empty string, the prompt is repeated:

如果开发人员输入了错误的输入(例如空字符串),则会重复提示:

Image for post

Inputting a valid string will complete the process:

输入有效的字符串将完成此过程:

Image for post

下一步,获取MFA,并在后台将其缓存直到过期 (Next step, getting the MFA — and caching it until it has expired, under the hood)

After we have handled the configuration process for setting the serial code, the next step would be to automate the process for the entry of the six-digit token code. The temporary credentials will be returned in the following JSON format:

在我们完成了用于设置序列码的配置过程之后,下一步将是使输入六位数令牌代码的过程自动化。 临时凭证将以以下JSON格式返回:

{  "Credentials": {    "AccessKeyId": "REDACTED",    "SecretAccessKey": "REDACTED",    "SessionToken": "REDACTED",    "Expiration": "2020-01-23T19:27:32Z"  }}

There are two ways that we could go around implementing this automation. One way would be to extract all properties except for expiration, and apply them as environment variables for the next awscli command that requires the credentials.

我们可以通过两种方式来实现这种自动化。 一种方法是提取除到期日外的所有属性,并将它们作为环境变量应用到下一个需要凭据的awscli命令。

The second way, which I implemented, was to cache the temporary credentials in a file (and .gitignored if saved in the project’s context) which includes the expiration timestamp. By doing this, I was able to script in the logic to check for this file every time I need the temporary credentials, and prompt only when it has expired.

我实现的第二种方法是将临时证书缓存在一个文件中(如果保存在项目上下文中,则将忽略.git,该文件包括到期时间戳)。 这样,我便可以编写逻辑脚本,以在每次需要临时凭据时检查该文件,并仅在该文件过期时进行提示。

#!/bin/bash


# Default filename values - change this or add as environment values, depending on your own needs
MFA_SERIAL_FILE=".mfaserial"
AWS_TOKEN_FILE=".awstoken"


# Validate that the configuration has been done before
# If not, prompt the user to run that first
if [ ! -e $TMP_DIR/$MFA_SERIAL_FILE ]; then
  echo "Configuration is missing, please run the one-time configuration step first"
  exit 0;
fi


# Retrieve the serial code 
_MFA_SERIAL=`cat $TMP_DIR/$MFA_SERIAL_FILE`


# Function for prompting for MFA token code
promptForMFA(){
  while true; do
      read -p "Please input your 6 digit MFA token: " token
      case $token in
          [0-9][0-9][0-9][0-9][0-9][0-9] ) _MFA_TOKEN=$token; break;;
          * ) echo "Please enter a valid 6 digit pin." ;;
      esac
  done


  # Run the awscli command
  _authenticationOutput=`aws sts get-session-token --serial-number ${_MFA_SERIAL} --token-code ${_MFA_TOKEN}`
  
  # Save authentication to some file
  echo $_authenticationOutput > $TMP_DIR/$AWS_TOKEN_FILE;
}


# If token is present, retrieve it from file
# Else invoke the prompt for mfa function
if [ -e $TMP_DIR/$AWS_TOKEN_FILE ]; then
  _authenticationOutput=`cat $TMP_DIR/$AWS_TOKEN_FILE`
  _authExpiration=`echo $_authenticationOutput | jq -r '.Credentials.Expiration'`
  _nowTime=`date -u +'%Y-%m-%dT%H:%M:%SZ'`
  
  # Retrieving is not sufficient, since we are not sure if this token has expired
  # Check for the expiration value against the current time
  # If expired, invoke the prompt for mfa function
  if [ "$_authExpiration" \< "$_nowTime" ]; then
    echo "Your last token has expired"
    promptForMFA
  fi
else
  promptForMFA
fi


# "Return" the values to the calling script.
# There are a few ways to "return", for example writing to file
# Here, we assume that this script is "sourced" - see more on "sourcing" here: https://bash.cyberciti.biz/guide/Source_command
_AWS_ACCESS_KEY_ID=`echo ${_authenticationOutput} | jq -r '.Credentials.AccessKeyId'`
_AWS_SECRET_ACCESS_KEY=`echo ${_authenticationOutput} | jq -r '.Credentials.SecretAccessKey'`
_AWS_SESSION_TOKEN=`echo ${_authenticationOutput} | jq -r '.Credentials.SessionToken'`

Using the above, I “sourced” the script from all of the other scripts that symbolises the different functionalities that a developer can perform. One such example was to be able to SSH to a virtual machine:

使用以上内容,我从其他所有脚本中“获取”了脚本,这些脚本表示开发人员可以执行的不同功能。 一个这样的示例是能够SSH到虚拟机:

Image for post

If the credentials are not saved in the system, we will prompt the user to to key in the MFA token code. However, if the user has already done the above, and the temporary credentials have not yet expired, the following will transpire instead:

如果凭据未保存在系统中,我们将提示用户键入MFA令牌代码。 但是,如果用户已经执行了上述操作,并且临时凭据尚未过期,则将改为以下内容:

Image for post

摘要 (Summary)

By doing the above, I was able to simplify a process with multiple manual steps, to a process with a one time configuration step, and a periodic MFA step that occurs only when there are no valid credentials. This helped to cut down minutes of figuring out the context of each command, following by copy+pasting, for each developer using these scripts.

通过执行上述操作,我能够将具有多个手动步骤的过程简化为具有一次性配置步骤的过程,以及仅在没有有效凭据时才发生的周期性MFA步骤。 对于使用这些脚本的每个开发人员,这有助于减少确定每个命令的上下文的时间,其次是复制+粘贴。

Ciao!

再见!

Sidenote: you might have also realised that I’m using npm-scripts, for serving the interface to different functionalities for the developers. All this integration did was to provide a common interface to invoke the actual shell script. You could do the same, or use a makefile, to create a common interface for all to use.

旁注:您可能还已经意识到,我正在使用npm-scripts ,为开发人员提供不同功能的接口。 所有这些集成所做的只是提供一个通用接口来调用实际的shell脚本。 您可以执行相同的操作,或使用makefile来创建供所有人使用的通用接口。

翻译自: https://levelup.gitconnected.com/aws-cli-automation-for-temporary-mfa-credentials-31853b1a8692

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值