AWS Amplify Announced IAM Role Support For SSR Applications, Finally

Cover Image for AWS Amplify Announced IAM Role Support For SSR Applications, Finally
Visit pacebits.com for my recent projects

Introduction

AWS announced the support for IAM roles for server-side rendered (SSR) applications, which is called "IAM Compute Role". (See What's New).

This is an important feature because in earlier days, to allow my website to access AWS services, I had to store the credentials as plain text in one form or another, which is very insecure. Once this feature was announced, I immediately updated my website to use this IAM Compute Role instead of the previous credentials. In this blog post, I will walk through the background and explain how it worked before and after this feature was announced.

Background

First, let me explain my use case. My website is built with NextJS and hosted on AWS Amplify. It has a page for collecting visitors' feedback, which publishes a message to an AWS SNS queue when someone presses the button on my website, and my other services subscribe to that queue and process the messages accordingly. The part of "publishing message to SNS queue" is rendered on the server-side and requires AWS credentials for communicating with SNS APIs.

Your use cases might be different; for example, your application may need to get a file from S3 buckets or access DynamoDB, but the gist will be the same - your SSR application will need AWS credentials. The challenge is how to safely store and access such credentials.

How it was implemented before this feature

Before AWS announced the new feature of IAM Compute Role, Amplify had the concept of "Service Role", which is only used by Amplify when deploying the application. But there was no way to specify an IAM role for applications running on Amplify to assume. As a result, I had to resort to a very insecure and hacky method to store credentials for accessing AWS services.

Since this insecure method is obsolete now, I will not go too deep into it. On a high level, I had to save the credentials as Environment Variables settings, which Amplify can access at build time. Meanwhile, there is a script in amplify.yml to write those environment variables into a .env.production file, which will be consumed by NextJS as environment variables at runtime. The credentials consisted of the access key pair from an IAM user, and both the access key ID and secret were stored as plain text in the Environment Variables setting of Amplify. This meant that if someone compromised the Amplify settings, they could also gain access to other AWS services in my account.

Many folks have been complaining about this issue for a few years; more discussion can be found in this Github issue.

How it is implemented today

With the new feature of Amplify Compute Role, the implementation becomes much more straightforward and secure.

Step 1: Create IAM Role

The first step is to create an IAM role with the proper permission policy and trust policy. Let's start with the trust policy. Since Amplify will need to assume this role, the trust policy should allow that. Here is how it should look:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "amplify.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Regarding the permission policy of this role, it will depend on your needs. For example, if your application should be able to access S3 buckets, you should grant permission for that. If you used the hacky workaround of using an IAM user before, you can pretty much copy the permissions of the IAM user to this new IAM role. In my use case, where the application will publish messages to an SNS queue, the permission policy looks like this:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "sns:Publish",
      "Resource": "arn:aws:sns:us-east-1:{account_id}:{queue_name}"
    }
  ]
}

Step 2: Configure Compute Role in Amplify

Now, go to your application in Amplify, then open the page of "App setting" - "IAM roles"

Configure Compute Role in Amplify

This page used to have only the "Service role" part, which is the IAM role that Amplify will assume at build time. Now, there is a section to configure the "Compute role", which is the IAM role that the server-side rendered (SSR) part of your application will assume at runtime to access AWS services. If this is the first time you come to this page, the "Default role" part will show the text "No default role set", and you can click the "Edit" button on the right side to specify the IAM role you created in Step 1. As you can see on this page, you can also specify different IAM roles for the different branches of your application, which will help you better differentiate the beta or production permissions.

Step 3: Re-deploy the application

After you configure the Compute Role for your Amplify application, it's very likely you don't need to make any changes in your application to make it work, especially if you were using the hacky workaround. You can trigger a new deployment of your Amplify application by pushing up either a commit of cleaning up some previous stuff or just an empty commit.

Voila, it should work now!

At this point, you can consider removing those hacky workarounds, like removing the script in amplify.yml about writing credentials into the NextJS environment variable file, deleting the build-time environment variables of the credentials from Amplify settings, and deleting the IAM user.

Conclusion

The introduction of the IAM Compute Role feature signals that AWS Amplify values the security concerns raised by its customers. By allowing the SSR application to assume IAM roles, we don't have to store sensitive credentials in any insecure ways. This feature also simplifies the development and deployment process. I hope this blog post is useful for those who share the same concerns earlier. Enjoy coding!

References