Using AWS CLI or Terraform with Credentials from Google Workspace SSO
In my current lab environment, I AWS to provide services that would otherwise be difficult for myself to handle (while trying to follow best practices), such as key management and replicated file storage. I use Google Workspace in order to provide a single sign-on experience across applications, whether they support only LDAPS or more modern Auth N&Z flows like OIDC and SAML.
With such a combination of services, I have my lab configured such that I can log into the Google Workspace user dashboard and assume a preconfigured role for a particular account from a selection page. I do this through setting up a SAML trust relationship with each AWS IAM role that I wish for users of my lab organization to be able to access. Once the role is assumed, the user is able to access the AWS console with the permissions of the role.
Through this method, I can focus on scoping permissions to roles instead of to users. This subtle difference can help prevent permission creep whereby a user may over time become overprivileged. Applying permissions directly to users can create an issue with not knowing what each individual can access. This method of granting authorization prevents that mishap by enabling organizations to easily see the permissions to which a user has been assigned via the roles they can assume.
The Issue
This setup has worked great, but there is one gaping problem that has not been seriously addressed by AWS for years (and it’ll be easy to guess why that is the case).
That problem is enabling API access (via the CLI or Terraform) through credentials assumed after authenticating to the Google Workspace IdP.
Multiple Sources of Identity: An Anti-Pattern as a Workaround
How do people and organizations work around this?
One anti-pattern that I have seen is long-term utilization of IAM users for engineers or people who otherwise need access to AWS. In doing so, multiple sources of identity are created in their system; at least one for access to SSO via their IdP, and one for AWS via their IAM user. By doing this, they can create api keys that can be used in the configuration of the AWS CLI. While the ease in doing so can be alluring, and even if the organization tends toward the usage of roles via policies applied directly, having multiple sources of identity always becomes problematic in auditing user permissions and granting/removing access.
Possibilities?
The AWS API supports assuming a role via a SAML response. As previously mentioned, I have integrated Google Workspace with AWS by setting up trust relationships in roles that I wish users to be able to assume. This should, in theory, be easy to do.
However, we run into an issue because Google Workspace does not provide a supported solution to getting a SAML response programmatically. Unsupported workarounds do exist such as aws-google-auth. Because they are not officially supported by either Google or Amazon, I steered clear of it.
Solution
As a result, I solved the problem by utilizing AWS SSO with Google Workspace as an external IdP. It’s not a perfect solution (and I’ll detail the issues below), but it was the best available.
You might ask: what is AWS SSO?
AWS Single Sign-On (AWS SSO) is a cloud service that allows you to grant your users access to AWS resources, such as Amazon EC2 instances, across multiple AWS accounts. By default, AWS SSO now provides a directory that you can use to create users, organize them in groups, and set permissions across those groups. You can also grant the users that you create in AWS SSO permissions to applications such Salesforce, Box, and Office 365. AWS SSO and its directory are available at no additional cost to you.
Essentially, the service can act as a SAML IdP and provides minimal support for OIDC (integrated only with AWS API and tooling). We can configure it to use an external identity provider, but a user must exist in AWS SSO with a username matching the Name ID attribute passed (*sigh*). AWS SSO also doesn’t support SCIM (essentially user replication from the external IdP) with Google Workspace (to nobody’s surprise). This unfortunately means that every user we want to be able to use AWS programmatically will need a user provisioned for them in AWS SSO.
Note that AWS SSO accounts will only work if there is a user with a matching email address as the username in Google Workspace. Essentially, to use AWS, a user will have to authenticate to Google, which will then sign a message that will be relayed to AWS by the user proving that they are in control of the user with the matching email address. This shouldn’t be a cumbersome task as the number of users that will probably need such access should be limited; changes to the system should be relegated almost entirely to CI/CD tooling. However, this is still a very slippery slope to having multiple sources of identity and so is not an ideal solution. Attribute based access control (ABAC) is supported in AWS SSO, but permission sets (IAM policies) cannot be applied to users on it alone, and so utilizing AWS SSO groups (another source of identity) is still required, unfortunately.
To make matters a little more annoying, AWS doesn’t provide an adequate public API for the SSO service. AWS SSO groups and users cannot be codified, making the utilization of identity-as-code near impossible and so making auditing more difficult. As a result, the Terraform resources are limited, and doesn’t support the codification of users. So much of what we’ll do here will be manual.
Bill of Materials
In order to build the solution, we’ll need to set up several things:
- AWS SSO
- Create user(s) with username matching their email in Google Workspace
- Create an SSO group to which they are assigned
- Assign a permission set to the group allowing members to assume specific roles to which they should be authorized.
- Google Workspace
- Create a SAML application configured to allow AWS SSO as a client
Steps
First, go to the AWS SSO service page.
Next to Identity source, click the Change button and select the option for the External identity provider. Take note of the different URLs listed as they’ll be required for the SAML application created next.
In a new tab, go to your Google Workspace admin dashboard and start creating a new custom SAML application.
After choosing a name, download the IdP metadata file.
On the next page, apply the URLs previously retrieved from AWS earlier.
AWS URL | Google Workspace URL |
---|---|
AWS SSO ACS URL | ACS URL |
AWS SSO issuer URL | Entity ID |
AWS SSO Sign-in URL | Start URL |
Set the Name ID options as follows:
After creating the SAML application in Google Workspace, it may be disabled by default for all users.
You’ll need to enable it if that is the case.
Back in AWS SSO, upload the metadata file downloaded from Google Workspace to configure it to use the SAML application just created.
Next, we’ll need to create a group to manage the permissions for our users. I created a group called Command-Line-Users as follows.
For each user, you’ll have to create a new AWS SSO user.
Add each user to the group(s) required:
Next, go to the AWS SSO > AWS Accounts page. Select an account to which you wish to grant access. As odd as it may sound, click the Assign users button. Now, select the group you just created.
Create a new permission set (IAM policy) on the next page.
The permissions policy will be an IAM policy that will grant the ability to assume role(s). It’s advisable to not allow users to directly assume a managed policy as they have had the history of being over-privileged (see Scott Piper). Additionally, the permissions assigned to such policies can shift under your feet as they are modified by AWS. Instead, create your own role that you have the ability modify through code. Even if it starts as only a proxy for the managed policy, if you need to change permissions across accounts or resources in the future, you will only need to change them in specific roles.
Ensure that the role to which you’re granting access has a trust policy allowing both your IdP and your AWS account root the ability to access
After creating the permission set, go back to AWS SSO and assign it to the group.
In a terminal window, run the command aws configure sso
. You should be presented with a prompt with a URL and a temporary code. Navigating to the URL should force you to authenticate to your IdP.
Following that, you will be redirected to an AWS page so that you can verify the request with the code given.
After verifying, you should receive a message like:
Now, you can use the AWS CLI with your SSO profile!
To get credentials you can use with Terraform, you can assume a role to get a temporary keypair, and then export those credentials as environmental variables.
$ aws sts assume-role --role-arn <role arn> --role-session-name <pick your own>
{
"Credentials": {
"AccessKeyId": "xxx",
"SecretAccessKey": "xxx",
"SessionToken": "xxx",
"Expiration": "xxx"
},
"AssumedRoleUser": {
"AssumedRoleId": "xxx",
"Arn": "xxx"
}
}
With the access keypair, you can then export environmental variables as follows:
export AWS_ACCESS_KEY_ID="xxx"
export AWS_SECRET_ACCESS_KEY="xxx"
export AWS_SESSION_TOKEN="xxx"
When this keypair or your SSO session expires, you may start to receive error messages.
To fix this, you’ll need to log in once again over the command line as follows:
With all this in place, you should now be able to utilize your CLI, scripts, or Terraform that depend on AWS credentials!
If you’re interested in learning how to use AWS, Cloudflare, Google Workspace and more with modern DevOps practices in order to create a hybrid cloud/on-prem malware lab, check out my book on Cyber Range Essentials!