An AWS account just for getting into other AWS accounts

In You should have lots of AWS accounts, I made a case for isolating environments and even services in their own accounts to reap all sorts of security, reliability, and compliance benefits. With the right tools in hand (Substrate, for one), operating lots of accounts can be just as efficient and far safer than trying to keep all the plates spinning in one account without crashing into each other.

Accounts that host your various environments and services are only part of the story, though. Odd though it may sound, it takes lots of AWS accounts to have lots of AWS accounts. Your first account is the one you use to configure AWS Organizations, which consolidates billing and gives you access to the APIs for opening and closing additional accounts — that’s your management account. Most folks suggest opening a second account to store audit logs from CloudTrail et al. You might choose to open a third account to host VPCs to share into your service accounts. Just like that, you’re up to three accounts before you even begin to host any compute or storage.

And the supporting cast of accounts needs one more player — your administrative access account. (That’s not an official name, by the way. Folks also call it an admin(-istrative or -istrator) account, access account, bastion account, or jump account. I’m sure there are more nicknames, too.) The purpose of this account is to help you and your coworkers access all the rest of your accounts. That’s it! This is the AWS account that makes having lots of AWS accounts efficient and safe. It’s the most important account in your organization.

💡
If you want a tool that can mange all your accounts for you, making creation of accounts, and roles easy, and also bootstrapping your Terraform infrastructure, take a look at Substrate, or contact us for a demo.

What’s in your administrative access account

Your administrative access account should contain the bare minimum AWS resources required to authenticate and authorize access to your AWS organization and to facilitate cross-account actions.

Authentication and authorization can be achieved in a staggering variety of ways, the most common of which are covered below. Each strategy requires a different combination of AWS IAM resources. Thankfully, managing IAM resources is a little easier because they are global resources.

If you wish to provide access via SSH or a (hopefully modern) VPN, however, you’ll be managing regional resources. Even if you provide your customer-facing services from a single region, consider provisioning resources in your administrative access account in at least two regions so you can get into AWS in a normal(ish) way even in case of a regional outage.

What’s not in your administrative access account

Your administrative access account is not where you should host your customer-facing services. It should not store customer data. Production data and services should be hosted in their own account and possibly in lots of accounts.

Your administrative access account should also not host your development environment. If your development environment is to be a faithful facsimile of production, then it should also be hosted in its own account(s).

Don’t store logs and metrics in your administrative access account, either. Doing so would require your service accounts to be able to get into your administrative access account. Any misconfiguration here could allow an attacker that compromises one service account to move from there to your administrative access account which, almost by definition, can access any other account. This could turn a manageable compromise into a catastrophic breach.

How you get into your administrative access account

“Getting into” any account really means getting an AWS access key for some principal — an IAM user or role — in that account. So “getting into” your administrative access account specifically means trading some credentials for an AWS access key ID, secret access key, and possibly session token. What kind of credentials? You have choices.

IAM users

If you and your coworkers each have an IAM user then you’ll be able to trade a username, password, and second factor (you’re using MFA, right?) for access to the AWS Console.

IAM users can have access keys, too, so that they can use the AWS APIs. These access keys don’t have any built-in expiration, though, so it’s critical that you require MFA for all APIs and arrange for folks to use the sts:GetSessionToken API to provide their second factor.

AWS IAM Identity Center

AWS IAM Identity Center (Successor to AWS Single Sign-On), by its full and maximally painful name, improves upon IAM users in two ways. Firstly, it provides a more streamlined way to manage usernames, passwords, and second factors which reduces the risk of misconfiguration. Secondly, it allows you to skip the long-lived access keys and command-line MFA by directly issuing time-bound access keys, which you can get from a web browser or the AWS CLI.

An external identity provider

There are also a few ways to integrate with an external identity provider like Active Directory, Google, or Okta that speaks OAuth OIDC or SAML. One of those ways is to configure IAM Identity Center to rely on your external identity provider (though at the time of this writing it doesn’t support Google). However you choose to integrate, an external identity provider is, in my opinion, the best option because that same identity provider can be used for everything else your company uses, too, from email and HR tools to GitHub and additional cloud service providers. (IAM Identity Center can almost do this, too, but the lack of OAuth OIDC support prevents me from fully endorsing it for this use-case.)

How you move from there to your other accounts

Now that you have an access key in your administrative access account, you’ll naturally want to manage the rest of your accounts, which means getting access keys in those accounts, too.

sts:AssumeRole

The most basic primitive for moving between accounts is the sts:AssumeRole API. It’s available in all the AWS SDKs. It’s available in the AWS CLI as aws sts assume-role. It’s everywhere. This API allows you to trade one access key for another which could have a different role and could even be in another account.

The principal making the sts:AssumeRole request must be allowed to use the sts:AssumeRole API in one of the policies attached to it (or, if the principal is an IAM user, attached to one of the user’s IAM groups). One of those policies should include a statement like this:

{
  "Action": "sts:AssumeRole",
  "Effect": "Allow",
  "Resource": "*"
}

Much more importantly, the role being assumed, in its assume-role/trust policy (it goes by both names), must explicitly name the principals that are allowed to assume it. This is how you express that e.g. only certain people or teams are allowed to access certain accounts. The assume-role/trust policy should include a statement like this:

{    
  "Action": "sts:AssumeRole",  
  "Effect": "Allow",    
  "Principal": {        
    "AWS": [            
      "arn:aws:iam::012345678901:role/Administrator",            
      "arn:aws:iam::012345678901:role/Developer"        
    ]    
  }
}

sts:AssumeRole also has two cousins, sts:AssumeRoleWithSAML and sts:AssumeRoleWithWebIdentity (for OAuth OIDC), which support the aforementioned integrations with external identity providers. They operate in much the same way, trading SAML assertions and OAuth OIDC tokens, respectively, for access keys.

Higher-level tools

Most of the time, though, you don’t interact directly with the sts:AssumeRole API. Instead, you use higher-level tools like the AWS SDKs, Substrate, Granted, etc. which natively understand how to assume roles. Most of the time, you provide an IAM role ARN as part of your configuration and the tools arrange to have the appropriate access key available before using any AWS APIs.

Service-specific cross-account access

Some AWS services, most notably S3 and KMS, natively support cross-account access, which can be useful as a performance optimization (saving lots of sts:AssumeRole API requests), a billing optimization (the requesting account pays for KMS requests, even if it’s a cross-account request), or just a simplification (using an S3 bucket policy to avoid the need for an additional IAM role and policy).

The key to these native cross-account access patterns is a resource policy — e.g. an S3 bucket policy or KMS key policy — that names principals and allows them access to one or more actions. If this sounds familiar, great, because an IAM role’s assume-role/trust policy is a resource policy, too.

A note about IAM Identity Center

Authenticating with IAM Identity Center doesn’t actually get you an access key in any particular account. It’s sort of an AWS foyer. You only get an access key after you choose which account you want to access, at which point you trade an OAuth client ID, client secret, and access token for an access key ID, secret access key, and session token.

Alternatives to an administrative access account

There are a couple of notable ways to avoid having an administrative access account, both seriously flawed. This account is not one to skip when designing your AWS organization.

Use the management account

Your AWS organization’s management account is just sitting there doing next-to nothing. It might seem harmless to overload this account to also serve as your administrative access account. It’d make listing all your accounts and dissecting your AWS bill a bit simpler, to boot.

Don’t do this! Any principal in your management account, by default, is able to assume the OrganizationAccountAccessRole in each and every one of the accounts created using the organizations:CreateAccount API. And this role, having the managed AdministratorAccess policy attached to it, is all powerful. It’s simply not a good security posture to use your management account as your administrative access account.

Another note here about IAM Identity Center: It’s safer to host IAM Identity Center in your management account because authenticating is decoupled from assuming a role. I still recommend against this, though, because it will be both easier to accidentally allow unauthorized users to assume the OrganizationAccountAccessRole in all your accounts and harder to spot because CloudTrail will have a lot more events for your management account than strictly necessary.

IAM users everywhere

IAM users are pretty tedious, even when you only have one account. They’re an additional entitlement to manage during onboarding and offboarding and an additional password and second factor for every employee to manage. They’re risky, too, because they allow folks to get access keys that last forever. But there’s nothing technically stopping you from using IAM users everywhere, so let’s play that out.

Having IAM users in lots of accounts multiplies the tedium by the number of accounts you have. It also multiplies the onboarding and offboarding tedium and risk. (What if you forget to offboard a disgruntled former employee from a production account?) Managing IAM users everywhere disincentivizes creating more accounts, too, causing you to miss out on the isolation they can provide.

An administrative access account saves you from the ever-growing tedium and risk of trying to manage IAM users in lots of accounts. If you have lots of accounts and don’t have one, get one now to massively improve your security posture.

💡
If you want a tool that can mange all your accounts for you, making creation of accounts, and roles easy, and also bootstrapping your Terraform infrastructure, take a look at Substrate, or contact us for a demo.