The open blogging platform. Say no to algorithms and paywalls.

Automated AWS IAM Access Keys Rotation

In this article, I will explain a serverless Lambda solution designed to automatically rotate the access keys for IAM users based on the access key age. When access keys are nearing expiration, the solution will automatically create a new access key and send a notification to the subscribed user through SNS. Subsequently, the old access keys will be disabled and deleted once they have expired. The automation is scheduled to run on a daily basis, ensuring that access keys are regularly updated.

IAM access key rotation is an important security practice that can help mitigate the risk of unauthorized access to AWS resources.

Over time, access keys can become compromised due to various reasons such as accidental exposure, sharing, or malicious activity. If a key is compromised, an attacker could gain access to AWS resources, potentially causing data breaches or other security incidents.

By rotating access keys, we can reduce the time during which a compromised key can be used. Rotating access keys on a regular basis can also help identify and revoke access for unused keys and reduce the risk of potential human errors or mistakes.

Additionally, some compliance standards and regulations, such as PCI DSS, require access key rotation as a security best practice.

Architecture Diagram

Automation Workflow-:

  • EventBridge(CloudWatch Event): Set up an Amazon Eventbridge that will trigger the Lambda function to run every day at a specific interval. I have used a schedule expression that will trigger every day at 12:00pm UTC “cron(0 12 * _ ? _)”.
  • Lambda function: Create a Lambda function using Python that will rotate access keys for IAM users. The function uses the AWS SDK to interact with the IAM APIs to generate new access keys. It will also disable/delete the old access keys after a specified interval.
  • SNS(Simple Notification Service): Amazon SNS is used to send notifications for each event. After the Lambda function generates new access keys, it will send email notifications to the user or group with new access key credentials. It will also notify the user on the inactivation and deletion of the old access key.
  • Configure IAM permissions: In order for the Lambda function to have access to rotate IAM access keys, it needs permission to list, create, update, and delete IAM access keys. Create an IAM role with these permissions and attach it to the Lambda function. Also, add the required permissions for the Lambda function to publish messages to SNS “sns: publish” action.

Lambda Implementation (Python 3.9):

import boto3
import os
from datetime import datetime, timezone, timedelta
from botocore.exceptions import ClientError

#Creating a new Access Key
def  create_new_key(iam, user_name):
new_key = iam.create_access_key(UserName=user_name)['AccessKey']
print(f"New access key is created for user {user_name}: {new_key['AccessKeyId']}")
return new_key['AccessKeyId'], new_key['SecretAccessKey']

#Deactivating the old Access Key
def  deactivate_key(iam, user_name, key_id):
iam.update_access_key(UserName=user_name, AccessKeyId=key_id, Status='Inactive')
print(f"Deactivated access key {key_id} for user {user_name}")

#Deleting the Old Access Key
def  delete_key(iam, user_name, key_id):
iam.delete_access_key(UserName=user_name, AccessKeyId=key_id)
print(f"Deleted access key {key_id} for user {user_name}")

def  lambda_handler(event, context):
#Initializing the IAM client
iam = boto3.client('iam')
# Initializing SNS client
sns = boto3.client('sns')
topic_arn = os.environ['sns_topic_arn']
try:
#Getting a list of all IAM users
response = iam.list_users()
except ClientError as e:
print(f"Failed to list IAM users: {e}")
return{
"statusCode":500,
"body":"Failed to list IAM users"
}

#For each IAM user, check if any access keys are older than the specified time
for user in response['Users']:
user_name = user['UserName']
try:
access_keys = iam.list_access_keys(UserName=user_name)['AccessKeyMetadata']
except ClientError as e:
print(f"Failed to list access key for user {user_name}: {e}")
continue

for access_key in access_keys:
key_id = access_key['AccessKeyId']
status = access_key['Status']
create_date = access_key['CreateDate']
age = (datetime.now(timezone.utc)-create_date).days
last_used = iam.get_access_key_last_used(AccessKeyId=key_id).get('AccessKeyLastUsed').get('LastUsedDate')

try:
if age == timedelta(days=int(os.environ['env1_create_key'])).days:
new_key_id, new_secret = create_new_key(iam, user_name)
sns.publish(TopicArn=topic_arn,
Message=f"""A new access key has been created for user {user_name}:
New Access Key Id= {new_key_id}
New Secret Key= {new_secret}
Please update the application and tool with a new access key. The old key will be inactive soon.""",
Subject="New Access Key Created")

if age >= timedelta(days=int(os.environ['env2_disable_key'])).days and status =='Active':
if last_used is  None:
deactivate_key(iam, user_name, key_id)
sns.publish(TopicArn=topic_arn,
Message=f"The access key {key_id} has been Inactivated for user {user_name}.",
Subject="Access Key Inactive")
elif((datetime.now(timezone.utc) - last_used).days >= int(os.environ['last_used_threshold'])):
deactivate_key(iam, user_name, key_id)
sns.publish(TopicArn=topic_arn,
Message=f"The access key {key_id} has been deactivated for user {user_name}.",
Subject="Access Key Inactive")
elif((datetime.now(timezone.utc) - last_used).days < int(os.environ['last_used_threshold'])):
sns.publish(TopicArn=topic_arn,
Message=f"""The access key {key_id} for user {user_name} is approaching expiration date and it will be deactivated soon.
Please start using new access key generated.""",
Subject="Inactive Access Key Alert")


if age >= timedelta(days=int(os.environ['env3_delete_key'])).days and status =='Inactive':
delete_key(iam, user_name, key_id)
sns.publish(TopicArn=topic_arn,
Message=f"The access key {key_id} has been deleted for user {user_name}.",
Subject="Access Key Deleted")
except ClientError as e:
print(f"Failed to rotate access key for user {user_name}: {e}")
continue


return {
"statusCode": 200,
"body": "Key Rotation is Successfully Completed!!"
}

NOTE: The Lambda function is configurable, allowing you to modify parameters based on your requirements. These parameters, such as the creation, disabling, deletion days, and last usage threshold are set as environment variables within the Lambda function. Additionally, the SNS topic ARN is included as a configurable parameter.

Environment Variables

To streamline the process, I’m creating a new access key on the 165th day, disabling access keys after 180 days, and deleting them after 185 days. These timeframes can be adjusted to suit your specific needs.

Flow Chart

Below are the steps involved in key rotation:

  1. The lambda function will retrieve the list of all IAM users.
  2. For each IAM user, it will check if any access keys are older than 165 days. If a key exceeds this threshold, the function will generate a new access key for the user and send an email notification containing the new key credentials.

SNS Notification on New Key Creation

  1. For disabling access keys, the Lambda function will verify if the old key’s age is equal to or greater than 180 days and if the key has been unused for more than 10 days or has never been used(Last used=None). If these conditions are met, the function will deactivate the old key; otherwise, it will continue sending alerts.

SNS Notification on Old Key Inactivation

  1. The function will also perform clean-up by deleting all access keys that are equal to or greater than 185 days old and have an inactive status. In addition, it will notify the respective user of the key deletion.

SNS Notification on Old Key Deletion

Thanks for Reading!

References:




Continue Learning