Never Assume Anything — Always STS:AssumeRole Everything!
In this article I’m going to show you how to implement a secure cloud authentication framework using modern best-practices and tools in the AWS ecosystem.
The following authentication principles & patterns should be able to be applied to any cloud ecosystem which supports the underlying constructs… users, roles, keys & tokens.
The tool I’ve published and the examples given here are for AWS, but could be adapted to other clouds.
Don’t leave your keys lying around
Organisations’ secret cloud access keys are left lying around inside build systems, developer’s workstations, text files, Slack messages, and more often than you might think - public repositories.
We need a seamless solution for legitimate users while making life harder for bad actors.
IAM not impressed.
The best way to provide access to an AWS cloud account is to deploy a user, generate access keys and bind high-powered, god-like policies to it… right? right?
WRONG!
This is bad in the following ways:
Long lived access keys are a hacker’s dream. The second law of thermodynamics — entropy increasing — pretty much guarantees that our precious secret keys floating around in environment variables, build systems, log files and accidentally committed into git repos — are going to eventually leak out into the wild… it’s just a matter of time.
This model does not scale to multiple account / org setup. It falters with many users in a single account.
It does not work for cross-org access. Since the owner of the user is responsible for the user — while the owner of the role is responsible for the role. These responsibilities should be decoupled.
Access keys should not carry significant power. The possessor of the keys should also need to know at least the name of the role and the account which they are intending to assume, this makes lost keys useless without this additional information.
IAM keys are inherently session-less. They work until revoked. Assuming a role is temporary and session-based. The keys eventually expire — making them useless if leaked after expiry. This also forces users and services to continually re-authenticate their sessions.
Show me the solution.
If you’re new to the STS:AssumeRole pattern, conceptually it’s as simple as a user making a call to the Session Token Service to request a new temporary authentication context, which carries all the permissions attached to the role.
We need to set-up our users and roles:
IAM Users should have no implicit rights attached to them outside of the ability to mange themselves, (e.g MFA enrolment, Key Rotation, etc)
IAM Roles have an attached Trust Policy which defines what entities can assume it. E.g:
{
"Version": "2012-10-17",
"Statement": {
"Effect": "Allow",
"Principal": {
"AWS": "123456789012"
},
"Action": "sts:AssumeRole"
}
}
Now an IAM user can run the following to temporarily ASSUME the role using this native aws-cli command:
aws assume-role --role-arn arn:aws:iam::123456789012:role/MyRoleName --role-session-name MyRoleSessionID_1
That command returns three new values in a JSON structure:
.AccessKeyId
.SecretAccessKey
.SessionToken
Introducing AssumeRole.sh
These values are only useful if we tell our AWS client authentication libraries to actually use them. This can be done in a few ways — I’ve written a tool which makes this process a breeze, and can be integrated into existing user and automated tool workflows. It’s available as a free open-source tool:
https://github.com/danktec/AssumeRole
source AssumeRole.sh --role arn:aws:iam::[aws-account-id]:role/OrganizationAccountAccessRole
The AssumeRole.sh script is a pure bash implementation with no major external dependencies. It “just works”. Pure bash means it will continue to be reliable into the future. External dependencies are a nightmare to manage - our design goals here are simplicity, reliability, security, audit-ability.
I’ve designed AssumeRole.sh to be “bash sourced” since it’s a canonical way to inject environment variables into the current shell context.
AssumeRole.sh is a simple yet effective wrapper for the aws sts assume-role command, with some useful helper functions and features to improve security and usability forthe AssumeRole pattern.
The script generates these AWS authentication environment variables for the user:
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_SESSION_TOKEN=
With these bash environment variables set, we can now access any AWS service for which the role has been configured. After the session expiry time, these credentials will be expired and not usable.
Context Switching & PoLP
It’s essential for engineers to test access and adhere to the Principle of Least Privilege. To effectively do this, we need a way to quickly switch our security context between different roles:
AssumeRole.sh makes this a breeze:
source AssumeRole.sh --role arn:aws:iam::12345678910:role/RoleOne
That returns access keys for RoleOne
source AssumeRole.sh --role arn:aws:iam::12345678910:role/RoleTwo
That purges access keys for RoleOne and replaces them with keys for RoleTwo
This allows the users to quickly switch authentication contexts on the fly based on the job they are doing.
The user’s keys don’t need to carry any privileges beyond the ability to assume the role(s) they need. While the roles only carry the minimum privileges required for their defined purpose.
Conclusion
AWS IAM can be a real minefield of complexity and pitfalls. Most AWS users start out with a single account and a few users, before long this entry-level style of authentication flow needs to be upgraded to something which scales and is more robust.
AWS Access Keys are literally your keys to the kingdom. Don’t leave them lying around for the bad guys to find.
The cloud is many things — inherently high-security is probably not one of them. That means it’s on US to implement secure patterns and best-practices which improve our posture without impacting engineer’s velocity.
I hope you enjoyed this read. Please like the post & leave some feedback if you found it useful.
Further reading
https://technology.riotgames.com/news/key-conjurer-our-policy-least-privilege
This article was originally cross-posted on Medium in November 2024. https://medium.com/@1dank/never-assume-anything-always-sts-assume-everything-bfcbd8759d39