Kyler Middleton is the Cloud IAM Advocate at IAM Pulse. She's part of our community team, and loves to connect with cloud operations and developers (read: that's you!). If you'd like to meet up with the community team and discuss your solutions, hacks, or how you'd like to help us elevate the practice of IAM, please reach out!
- STS Primer 1: Theory - First video in this series
- GitHub Repo with all Commands and Terraform Code - You can and should run the entire demo yourself
- Kyler's Twitter
- IAM Pulse Twitter
- IAM A Developer, What Do I Really Need to Know About IAM - An article from our inimitable CEO, Ivan Dwyer, on what parts of IAM are critical to know as a developer (hint: STS is in there!)
- Best Practice IAM Policies, Including STS Policies - Our gold mine of best practice IAM and STS trust policies
- AWS STS API Reference - AWS API reference for STS
I hope you’re all doing great. It is finally warming up here after a very long winter with a newborn, and I am beyond thrilled to go outside without 3 layers of coats!
During our last video we talked about what AWS STS is and why we’d do it - namely that it lets us assume a different IAM role, potentially in a different AWS account. Now that we have the foundation down, we’re going to go do it! I find that real-world experience helps me the best, so let’s get some real world experience together.
First let's look at our AWS console and the ec2 instance and the roles and policies that are in place today. I'll also publish all of the terraform used to deploy this ec2 instance and all these roles so you can build this in your own accounts and play as well!
Let's jump into the EC2 panel. Here's an ec2 instance I've created and assigned an IAM role. You can see the IAM role there in the center there. Let's click on that IAM role to view its properties.
Okay, here's the IAM role. This role exists as a separate resource from the ec2 instance. Let's scroll down a little and expand the permissions policy to see what we permit this host to do.
Okay, we can see in that json policy that we permit only using the sts:AssumeRole action on a single resource, and nothing else. This IAM role is pretty safe - it can't natively do anything, even to itself.
Let's check out the role we are allowed to attempt to assume. Let's copy the name and then go to Roles on the left and then search for that name. Let's scroll down a little and expand the permissions policy.
Oh, wow! This role has deity permissions, meaning it can do any action against any resource in the account. This is really broad! It's not a best practice - you should be limiting even your CI/CD builders to some limit, but it's a common pattern for, say, a terraform builder, which is exactly what we're going to demo here.
Let's scroll up a little and check the Trust Relationships tab for this deity assumed role. This trust relationship is STS assume related, and says who is trusted to assume the role. Since this is a deity do-anything role, we have this trust policy really limited - it can only be assumed from a single IAM role - the one we have hard assigned to the ec2 instance we created.
But that's enough theory, huh? Let's get to the terminal and do some cool stuff. This terminal is SSH'd into the ec2 instance we were just looking at.
Then let's install terraform. First we're going to do everything using the AWS CLI, and then I'll show you how easy Terraform makes this for us. We'll wget the most recent terraform, unzip it, and drop it into our PATH.
Then we want to set a few variables we'll use in our commands. These are mostly so you can run these commands in any region world-wide and it'll work, how about that?
Next, let's look at our current identity. The AWS STS get-caller-identity command shows us that we currently are using the permissions of the "Assigned" role. That's the first role we looked at, and doesn't have any special permissions.
Let's test that theory, I have an AWS command to describe all the ec2 instances in this region. Let's run it.
And you can see it gives us an "Unauthorized" error. We don't have any rights, so it makes perfect sense that when we tried to do something, we were blocked.
But we have access to a Deity role that can do anything. Let's look at how we'd utilize it. Let's clear the screen and check out the assume command. There's a lot here, but let's check out each section. First, our command. We say use the AWS command line and the STS assume-role command. We tell it the exact ARN of the Deity role we want to assume, and give it a role session name. This session name is beyond the scope of what we're doing today, but it'd let you assume several different roles at once and do different things. Totally okay if that makes no sense.
Then let's check out the JSON-packed response. We have a credentials block with a secret access key, a token, and an access key ID. This response (rather than a permission denied error), means the STS service is granting us the rights to assume the target role, but it doesn't do it for us - we need to export the right variables into our terminal.
We could copy and paste each of the values we got into the right variables, but there is a wonderful uber-command for this. You can type this if you're very brave, but I'll include it in the github repo for easy copy and paste.
This command runs that same command we just talked about, but traps the JSON response and parses it with JQ to export all the right values in the right place. This immediately elevates our terminal commands to use the new role.
Let's check me on that - again, let's run the get-caller-identity command to see what role we're using for our commands. And we can see that we are now operating as the Deity role, the one with unfettered *:* permissions to this account.
Let's run the same command that we ran earlier that got us an error message. And we see that it worked, yay! The response isn't important to us, rather it's that we were able to do something with our new role that was blocked by our assigned role.
To revert to our assigned role, instead of the assumed Deity role, we can curl the ec2 metadata to get the creds for our implicit IAM user. Here's what that looks like - You'll see the same access key, secret access key, and token, just like before.
Let's run another uber command to do the same thing - query that node, then map the response using JQ to the local terminal variables.
And let's check our STS identity again, and we can see we're back to the assigned role.
That was a lot of commands, right? You can certainly use these commands to do assuming, but most tools make it easier. Let's run some terraform and see how easy they make it.
Here's a terraform configuration in visual studio code. Check out the AWS provider block on line 13, and specifically the assume_role command on line 15. You're now an expert at what that does - it's assuming a role as part of any resource built with this provider. Afterwards, it releases the role and the host goes back to the implicit role.
But don't listen to me, we can test this. We have a data call to find out "current identity" which is a whole bunch of stuff - region, account number, but also the IAM user doing all of this.
And we have an output that will paste the ARN of the IAM role running this command.
Let's switch over to our terminal and check our terraform command. I already have this terraform file copied to this directory, so let's run it with auto-approval.
There are no changes because there are no resources in our terraform config, but it does output the IAM user who ran the terraform - and it's our assumed deity role.
Pretty cool, huh? Terraform does all this for you.
Phew, that was a journey, wasn't it? And that's it! Now you understand IAM roles, trust policies, how to use the AWS STS command to assume and release roles, and how to use Terraform to do the same thing. And all in under 10 minutes! That's pretty cool.
Thanks all, until next time!