Configuring credentials for programmatic access in AWS SDK

AWS provides various ways to set credentials when we work with the SDK. If we want to write code that interacts with a service like S3, we'll need to provide the SDK with a valid access key and secret access key. In this post, I'll write about some ways of how these credentials can be configured in the SDK.

If we want to access the services of AWS (e.g. S3 or DynamoDB), we’ll need to have programmatic access credentials.

These credentials are then shared with the Command Line Interface (CLI) and the Software Development Kit (SDK).

We can get the credentials after logging in to our account. Here’s a really good description about how to get an access key and secret access key for your account if you are not familiar with it.

1. Access key and secret access key

Credentials for programmatic access consist of the access key and secret access key.

The access key is a public key, and it’s OK to share it with others. It has a similar format to AKIAHIMSHT5KMDW9UIN3D.

The secret access key - as the name implies - should be kept secret, and may not be shared in any circumstances. The secret key is longer than the access key, and contains more random characters.

The access key and secret access key together make up the credentials and the user can access the services through the SDK or CLI given they have the necessary permissions.

2. Avoid hardcoding

The easiest way to provide credentials for AWS services is to hardcode them in the application, something like this:

const AWS = require('aws-sdk')

const s3 = new AWS.S3({
  endpoint: 'ap-southeast-2',
  credentials: {
    accessKeyId: 'ACCESS KEY AS A STRING',
    secretAccessKey: 'SECRET ACCESS KEY AS A STRING'
  }
})

// do something with s3...

This method might be OK if one plays with AWS on their local computer, but it’s definitely a no-go when it comes to production. It’s a security risk, and in case of the account getting compromised, the attacker will have the secret key, and they can do whatever they want with our account.

Never include credentials in the application code!

3. Using shared credentials

When we create the credentials in the console (or the administrator gives them to us), it’s a good idea to save them in a shared credentials file on the local machine.

3.1. Creating the shared file

If the CLI is installed, we can save the credentials by entering the following command in the terminal:

aws configure --profile PROFILE_NAME

PROFILE_NAME can be the name of the account, or anything that is easy to remember. When the above command is run, we’ll be prompted to enter the access key, secret access key, region and type (usually json). Once it’s done, the credentials under the named profile will be saved to the ~/.aws/credentials file.

3.2. Using the profile

By default, SDK will look for the AWS_PROFILE environment variable. If it’s not set, the default profile from the shared credentials file will be used. If we are not happy with the default profile, or the service needs to relate to another account, we can set the value of the environment variable:

AWS_PROFILE=my-account node NAME_OF_THE_FILE_WHERE_THE_APP_STARTS

Another option is to set the profile in the application code:

const credentials = new AWS.SharedIniFileCredentials({
  profile: 'my-account'
})

AWS.config.credentials = credentials

Here we set the credentials at the global level, before any services have been initiated, and by default, the services will use these credentials.

4. Setting credentials in production

It’s all nice, but what if we want to run our application in production?

We probably don’t want to create a shared credentials file on the production server, and still, our application will need the access key and secret access key to make calls to AWS services on our behalf (in our S3 example, it can be an upload to S3).

4.1. Using environment variables

One option is to create a user just for this purpose, and give them the least permissions possible.

For example, we can create an s3-user, and request programmatic access for this user, so we’ll can download the credentials of the user from the console.

By default, the SDK looks for the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables. It’s possible to save the s3-user’s access key and secret access key to a safe place, like Secrets Manager, and assign the environment variables these values.

If we want to simulate this situation on localhost, we can start the local Node server like this:

AWS_ACCESS_KEY_ID=s3_user_access_key AWS_SECRET_ACCESS_KEY=s3_user_secret_access_key node NAME_OF_THE_APP_FILE

Of course, if we have multiple services, and not only S3, the user should be named something else, because it doesn’t look very good if s3-user uploads some items to DynamoDB. We also need to add the relevant permissions to the user accordingly.

Note that although this is a working solution, it’s not the recommended way of providing credentials in the application.

4.2. Loading credentials from a json file

This solution is similar to the shared-credentials-file type but it’s adjusted to production environment.

We can create a credentials.json file with the access key and secret access key:

{
  "accessKeyId": "access_key_here",
  "secretAccessKey": "secret_access_key_here",
  "region": "ap-southeast-2"
}

The json can then be referred to at the global level using the loadFromPath method:

// ...
AWS.config.loadFromPath('./credentials.json)
// ... more code here

This solution also works, but it still uses the credentials of a user we created, even if that user has the least possible permissions.

4.3. Creating roles

Finally, we have arrived at the recommended solution.

The best practice is to create an IAM role, attach IAM policies to it, and assign the role (together with the policies) to the AWS service which contains our application code, like EC2 or ECS.

In this case, the role will provide the service with temporary credentials to access another services (like S3) on our behalf.

The best in this method is that we don’t have to worry about creating and managing user credentials, AWS does all of that for us.

5. Conclusion

When AWS services are used through the CLI or SDK (called programmatic access), credentials are needed: the access key and the secret access key.

These credentials can be provided in various ways to the SDK. On localhost, it’s OK to configure user’s credentials, if they have the same permissions as the application in production.

The best practice for production is to create an IAM role though, and attach policies to it, and this role will provide the necessary credentials when they are needed.

Thanks for reading, and see you next time.