aws mfa 认证
I am currently working on improving the security of cloud operations for one of my clients and wanted to share an interesting solution I developed to help provide programmatic access to AWS from local developer environments using Federated Identities only.
我目前正在为一个客户改善云操作的安全性,并希望分享我开发的一个有趣的解决方案,以帮助仅使用联邦身份从本地开发人员环境提供对AWS的编程访问。
挑战 (The Challenge)
Accessing AWS resources from outside VPC usually requires some sort of AWS credentials. There are two forms of access credentials — long term and short term keys. To create long term credentials, you use IAM users. Each member on the team gets their own IAM user that they can then use to generate these long term credentials.
从VPC外部访问AWS资源通常需要某种AWS凭证。 访问凭证有两种形式-长期密钥和短期密钥。 要创建长期凭证,请使用IAM用户。 团队中的每个成员都有自己的IAM用户,然后可以使用他们来生成这些长期凭证。
However, my goal was to completely migrate our team to using purely Federated Identities for all forms of AWS access. This approach comes with several great benefits described in detail in this chapter of IAM service documentation. One additional challenge we were facing was that our corporate login portal was protected by an MFA layer.
但是,我的目标是将我们的团队完全迁移到对所有形式的AWS访问都使用纯粹的联合身份。 此方法具有IAM服务文档这一章中详细描述的一些巨大好处。 我们面临的另一个挑战是我们的公司登录门户受到MFA层的保护。
Note: If you don’t have a corporate portal for federated user authentication, please take a look at this AWS blog post to see how you can set it up.
注意:如果您没有用于联合用户身份验证的公司门户,请 查看此AWS博客文章, 以了解如何进行设置。
解决方案 (The Solution)
The solution that I came up with consists of multiple parts.
我想出的解决方案包括多个部分。
- First, we’re going to use a browser automation framework to load up and log in to the portal. We’re going to be using Selenium and that’s what’s going to help us solve the MFA challenge I mentioned above. 首先,我们将使用浏览器自动化框架来加载并登录到门户。 我们将使用Selenium,这将帮助我们解决上面提到的MFA挑战。
We will then use a MITM proxy to capture network exchange data and a few lines of code to extract a SAML assertion from it. In my experience Selenium API was not helpful with this at all and using MITM proxy was the only way to capture the SAML AJAX exchange.
然后,我们将使用MITM代理捕获网络交换数据,并使用几行代码从中提取SAML断言。 以我的经验,Selenium API根本没有帮助,使用MITM代理是捕获SAML AJAX交换的唯一方法。
Lastly, we will use the STS class from AWS SDK to authenticate as a Federated Identity of our choice. Authentication step will return a set of temporary access credentials that we will use to set up access to CodeCommit with
git *
command-line util, and access to AWS resources from a Node.js application.最后,我们将使用AWS开发工具包中的STS类将身份验证为我们选择的联合身份。 身份验证步骤将返回一组临时访问凭证,我们将使用它们来设置对使用
git *
命令行util的CodeCommit的访问,以及从Node.js应用程序对AWS资源的访问。
1.自动化企业门户登录 (1. Automating Corporate Portal Sign-In)
The implementation of this step heavily depends on how your corporate sign-in portal is implemented. You will need to use the Selenium web driver to programmatically interact with the sign-in page. On top of that, you will also likely need to implement some sort of command-line prompt to collect ADFS credentials from the user, notify them of the current sign-in process status, whether they should grab and enter an OTP code or take action on whatever other MFA mechanism you might be using.
此步骤的实现很大程度上取决于公司登录门户的实现方式。 您将需要使用Selenium Web驱动程序以编程方式与登录页面进行交互。 最重要的是,您可能还需要实施某种命令行提示符,以从用户那里收集ADFS凭据,将当前登录过程的状态通知给用户,无论是应获取并输入OTP代码还是采取措施。您可能正在使用的任何其他MFA机制。
2.设置代理,提取SAML声明 (2. Setting Up a Proxy, Extracting a SAML Assertion)
We will be using browsermob-proxy for this step since it supports HTTPS (full MITM). It will run as a separate process in the operating system. Setting up this proxy was a little frustrating to me in the beginning and you might feel the same way, so let me explain how it works:
我们将在此步骤中使用browsermob-proxy ,因为它支持HTTPS(完整的MITM)。 它将在操作系统中作为单独的进程运行。 刚开始时,设置此代理会使我有些沮丧,您可能会有相同的感觉,所以让我解释一下它是如何工作的:
You spawn a browsermob-proxy daemon in your system. Separately from your Selenium app. You select a host and port to run it on, however that daemon itself is not a proxy yet.
您在系统中生成了browsermob-proxy守护程序。 与您的Selenium应用分开。 您选择了一个主机和端口来运行它,但是该守护程序本身还不是代理 。
To create a proxy, you send an HTTP POST request to the daemon to the
/proxy
route with query parameters as settings. That creates a new proxy on that daemon and makes it available at a specified host and port. Two settings you might end up finding useful areport
andhttpProxy
. First allows you to specify which port to create a proxy on, and second allows you to specify an upstream proxy, which is useful if your corporate sign-in portal is hidden behind a corporate proxy.要创建代理,您需要使用查询参数作为设置向
/proxy
路由向守护程序发送HTTP POST请求。 这将在该守护程序上创建一个新的代理,并使其在指定的主机和端口上可用。 您可能最终发现有用的两个设置是port
和httpProxy
。 首先,您可以指定要在哪个端口上创建代理,其次,您可以指定上游代理,如果您的公司登录门户隐藏在公司代理的后面,则这很有用。
The following example will create a browsermob daemon on localhost port 9900 and a MITM proxy on localhost port 27960. It will also instruct your new proxy to send all incoming requests through proxy.corp.company.com
upstream proxy effectively creating a proxy chain.
下面的示例将在localhost端口9900上创建一个browsermob守护程序,并在localhost端口27960上创建一个MITM代理。它还将指示您的新代理通过proxy.corp.company.com
上游代理发送所有传入请求, proxy.corp.company.com
有效地创建了代理链。
# start a browsermob daemon
# to install it, follow instructions on it's GitHub homepage
# https://github.com/lightbody/browsermob-proxy/
/usr/bin/browsermob-proxy -port 8080# create a MITM proxy
curl -X POST localhost:9900/proxy?port=27960&httpProxy=proxy.corp.company.com
After proxy is instantiated you will need to instruct the browser instance to use it. If you’re using Chrome pass the following switch when launching it:
实例化代理后,您将需要指示浏览器实例使用它。 如果您使用的是Chrome,请在启动时通过以下开关:
--proxy-server localhost:27960
With Selenium that can be done by using ChromeOptions class. You can read more about configuring proxy settings in Chrome over CLI here.
使用Selenium可以通过使用ChromeOptions类来完成。 您可以在此处阅读有关在Chrome over CLI中配置代理设置的更多信息。
Now to extract a SAML assertion we will need to do a few things. First, we will need to start recording network activity before the console sign-in event. Second, after the sign-in is successfully completed, we will need to fetch the recorded activity back from the proxy server:
现在要提取SAML断言,我们需要做一些事情。 首先,我们需要在控制台登录事件之前开始记录网络活动。 第二,成功完成登录后,我们需要从代理服务器取回记录的活动:
# initiate recording by sending a PUT request to browsermob daemon;
# captureContent param will enable recording of POST data;
# number between /proxy and /har is port where MITM proxy is runningcurl -X PUT localhost:9900/proxy/27960/har?captureContent=true# user signs-in to AWS console through corporate sign-in portal...
# ...success!# run the same exact command to fetch the data recorded so far
curl -X PUT localhost:9900/proxy/27960/har?captureContent=true
This command will return a pretty hefty JSON response in HAR format which you will need to programmatically iterate through, looking for a POST request to https://signin.aws.amazon.com/saml. The request will contain a field named SAMLResponse
and data within that field is what we need.
该命令将以HAR格式返回一个相当庞大的JSON响应,您需要以编程方式对其进行迭代,以查找对https://signin.aws.amazon.com/saml的POST请求。 该请求将包含一个名为SAMLResponse
的字段,该字段中的数据就是我们所需要的。
At this point, you may also choose to shut down the proxy to prevent potential memory leaks. That can be done by sending a DELETE request to the daemon:
此时,您还可以选择关闭代理,以防止潜在的内存泄漏。 可以通过向守护程序发送DELETE请求来完成:
curl -X DELETE localhost:9900/proxy/27960
3.提取和使用临时访问凭证 (3. Fetching and Using Temporary Access Credentials)
Now that we have the SAML assertion it’s time to turn it into a pair of temporary access credentials. As I already mentioned we’re going to be using STS class from AWS SDK (read more on assumeRoleWithSAML
here).
现在我们有了SAML断言,是时候将其变成一对临时访问凭据了。 正如我已经提到,我们要使用从AWS SDK STS类 (阅读更多关于assumeRoleWithSAML
这里 )。
// javascript codeconst params = {
SAMLAssertion: "AssertionYouGotFromHarDump",
PrincipalArn: "arn:of:saml:provider:for:federated/identity",
RoleArn: "arn:of:federated:identity:that:fetched/samlAssertion"
}
sts.assumeRoleWithSAML(params, () => ...);
Function call responds with STS credentials that can be used in our local environment applications now.
函数调用以STS凭据响应,该凭据现在可以在我们的本地环境应用程序中使用。
// successful response
{
...
Credentials: {
AccessKeyId: "AAAAAABBBBBBBCC...",
Expiration: <Date Representation>,
SecretAccessKey: "sEcReTaCcEsSkEy...",
SessionToken: "sEsSiOnToKeN..."
}
...
},
To use them, first, you need to make sure that you don’t specify any AWS access keys in your OS, shell or application environment variables, including injection of environment variables into your application from .env
files.
要使用它们,首先,需要确保在OS,shell或应用程序环境变量中未指定任何AWS访问密钥,包括从.env
文件向应用程序注入环境变量。
Once verified you can plug in your newly obtained credentials over to ~/.aws/credentials
file by either modifying it programmatically or by using AWS CLI:
验证后,您可以通过编程方式修改它或使用AWS CLI将新获得的凭据插入~/.aws/credentials
文件中:
# set credentials on the default AWS CLI profileaws configure set aws_access_key_id AAAAAAAAAA... --profile default
aws configure set aws_secret_access_key bQbGAb... --profile default
aws configure set aws_session_token aaABAaabAA... --profile default
This will provide access to AWS for all applications or shell scripts that use AWS CLI or AWS SDK because both tools will read from configuration stored in the ~/.aws
directory. In fact, there is a whole fallback system that these tools follow which is why I asked you to make sure you don’t specify any environment variables first!
这将为使用AWS CLI或AWS开发工具包的所有应用程序或Shell脚本提供对AWS的访问,因为这两种工具都将从存储在~/.aws
目录中的配置读取。 实际上,这些工具遵循一个完整的后备系统 ,这就是为什么我要您确保不首先指定任何环境变量的原因!
Note: You might prefer to go an extra step and assume a different role with more restrictive permissions depending on the application that you’re trying to provide programmatic access for. In that case, you will need to run another STS call using credentials that you have already obtained:
注意:根据您要为其提供程序化访问的应用程序,您可能更愿意采取其他措施,并担任其他角色,并具有更多限制性的权限。 在这种情况下,您将需要使用已经获得的凭据来运行另一个STS调用:
const params = {
AccessKeyId: "...keyYouObtained",
SecretAccessKey: "...keyYouObtained",
SessionToken: "...keyYouObtained",
RoleArn: "arn:of:role:that:you:want/toUse",
RoleSessionName: "user.email@company.com"
}
sts.assumeRole(params, ...);
Keep in mind though that with this approach you’re going to have to work through an additional challenge of trying to figure out how to insert credentials specifically into applications that are going to be using them, as we won’t be able to simply edit ~/aws
configuration with a single pair of credentials anymore.
请记住,尽管采用这种方法,您将不得不克服另一个挑战,即试图弄清楚如何将凭证专门插入将要使用它们的应用程序中,因为我们将无法简单地进行编辑~/aws
配置不再带有一对凭据。
配置Git以使用临时证书 (Configuring Git To Use Temporary Credentials)
So far we were able to grab a set of temporary credentials and use them in applications running on our local environments. However, if you’re using CodeCommit you might want to also update Git configuration to work over temporary STS credentials. This will allow you to avoid having to use SSH or HTTPS credentials which are essentially just another form of long term credentials, that we’re trying to get rid of.
到目前为止,我们已经能够获取一组临时凭据并将其用于在本地环境中运行的应用程序中。 但是,如果您使用的是CodeCommit,则可能还需要更新Git配置以使用临时STS凭据。 这将使您避免使用SSH或HTTPS凭证,而这实际上只是我们要摆脱的另一种长期凭证。
To switch to temporary credentials, disable any current Git credential managers in use, then run following git commands:
要切换到临时凭证,请禁用任何当前正在使用的Git凭证管理器 ,然后运行以下git命令:
git config --global credential.helper \
'!aws codecommit credential-helper $@'
git config --global credential.UseHttpPath true
Note: If you’re running these git commands on Windows, omit ‘\’ with a new line, and use double-quotes instead of single-quotes to wrap [!aws … $@] part.
注意:如果您在Windows上运行这些git命令,请用新行省略'\',并使用双引号而不是单引号来包装[!aws…$ @]部分。
After that make sure to update origin URLs in every local Git repository pulled from CodeCommit to HTTPS format if you have been using SSH format. Also, make sure to do the same inpackage.json
files across all projects that use CodeCommit packages as NPM dependencies.
之后,如果您一直在使用SSH格式,请确保将每个从CodeCommit提取到HTTPS格式的本地Git存储库中的原始URL更新。 另外,请确保在所有使用CodeCommit软件包作为NPM依赖项的项目中,在package.json
文件中执行相同的操作。
If these package.json
updates, however, break your build or deployment pipelines in the cloud, check out the article I wrote about using temporary credentials in CI/CD pipelines in AWS cloud!
但是,如果这些package.json
更新破坏了您在云中的构建或部署管道,请查看我写的有关在AWS云中在CI / CD管道中使用临时凭证的文章 !
结论 (Conclusion)
Depending on your use case you might end up automating this process even further to better improve the developer experience. I decided to not dive into that much detail since this article is getting pretty long at this point :), but I can absolutely imagine packaging up this Selenium application along with browsermob daemon and all of its dependencies in a Docker image and even integrating it as a micro-service into existing containerized applications.
根据您的用例,您可能最终会进一步自动化该过程,以更好地改善开发人员的体验。 我决定不去讨论那么多细节,因为本文到现在为止还很长:),但是我绝对可以想象将这个Selenium应用程序与browsermob守护程序及其所有依赖项一起打包在Docker映像中,甚至将其集成为将微服务引入现有的容器化应用程序。
Switching to short term credentials improves the overall security of cloud operations of your team. An additional benefit in using purely Federated Identities to provide AWS access is that access is revoked as soon as ADFS credentials bound to the identity cease to exist. So if someone leaves the company you don’t leave yourself a chance to forget to clean up your IAM users list and accidentally leave a hole in your infrastructure security!
切换到短期凭证可提高团队的云运营的整体安全性。 使用纯联邦身份提供AWS访问的另一个好处是,一旦绑定到该身份的ADFS凭据不复存在,访问将被撤销。 因此,如果有人离开了公司,您将不会失去忘记清理IAM用户列表的机会,并且会意外地在基础架构安全方面留下漏洞!
Thank you for reading this article! If you would like to see more content like this in the future please leave a like and share this article. Till next time!
感谢您阅读本文! 如果您以后希望看到更多类似内容,请点赞并分享此文章。 直到下一次!
aws mfa 认证