Changing access keys (which consist of an access key ID and a secret access key) on a regular schedule is a well-known security best practice because it shortens the period an access key is active and therefore reduces the business impact if they are compromised. Having an established process that is run regularly also ensures the operational steps around key rotation are verified, so changing a key is never a scary step.
Identifying applications and services with old keys become imperative in those case. We are going to use Lambda Functions
to periodically check our account for old keys and notify our Security Operations Team using SNS
You can also follow this article in Youtube
We will need the following pre-requisites to successfully complete this activity,
- Few
IAM Users
with one or two Access Key created in the AWS Region where the solution is deployed - Security Operations SNS Topic ARN - If you need assistance, follow this article
- IAM Role - i.e
Lambda Service Role
- withIAMReadOnlyAccess
permissionsAmazonSNSFullAccess
permissions- You may use an
Inline
policy with more restrictive permissions
The image above shows the execution order, that should not be confused with the numbering of steps given here
The below script is written in Python 2.7
. Remember to choose the same in AWS Lambda Functions.
globalVars['key_age']
- Set thekey_age
to any value you desire, by default it is set to 90 daysglobalVars['SecOpsTopicArn']
- Update the ARN of your SNS Topic
Change the global variables at the top of the script to suit your needs.
# List IAM users having 90 days older Access keys
import datetime, boto3, os, json
from botocore.exceptions import ClientError
# Set the global variables
globalVars = {}
globalVars['Owner'] = "Miztiik"
globalVars['Environment'] = "Test"
globalVars['REGION_NAME'] = "ap-south-1"
globalVars['tagName'] = "Valaxy-Serverless-IAM-Key-Sentry"
globalVars['key_age'] = "90"
globalVars['SecOpsTopicArn'] = ""
def get_usr_old_keys( keyAge ):
client = boto3.client('iam',region_name = globalVars['REGION_NAME'])
snsClient = boto3.client('sns',region_name = globalVars['REGION_NAME'])
usersList=client.list_users()
timeLimit=datetime.datetime.now() - datetime.timedelta( days = int(keyAge) )
usrsWithOldKeys = {'Users':[],'Description':'List of users with Key Age greater than (>=) {} days'.format(keyAge),'KeyAgeCutOff':keyAge}
# Iterate through list of users and compare with `key_age` to flag old key owners
for k in usersList['Users']:
accessKeys=client.list_access_keys(UserName=k['UserName'])
# Iterate for all users
for key in accessKeys['AccessKeyMetadata']:
if key['CreateDate'].date() <= timeLimit.date():
usrsWithOldKeys['Users'].append({ 'UserName': k['UserName'], 'KeyAgeInDays': (datetime.date.today() - key['CreateDate'].date()).days })
# If no users found with older keys, add message in response
if not usrsWithOldKeys['Users']:
usrsWithOldKeys['OldKeyCount'] = 'Found 0 Keys that are older than {} days'.format(keyAge)
else:
usrsWithOldKeys['OldKeyCount'] = 'Found {0} Keys that are older than {1} days'.format(len(usrsWithOldKeys['Users']), keyAge)
try:
snsClient.get_topic_attributes( TopicArn= globalVars['SecOpsTopicArn'] )
snsClient.publish(TopicArn = globalVars['SecOpsTopicArn'], Message = json.dumps(usrsWithOldKeys, indent=4) )
usrsWithOldKeys['SecOpsEmailed']="Yes"
except ClientError as e:
usrsWithOldKeys['SecOpsEmailed']="No - SecOpsTopicArn is Incorrect"
return usrsWithOldKeys
def lambda_handler(event, context):
# Set the default cutoff if env variable is not set
globalVars['key_age'] = int(os.getenv('key_age',90))
globalVars['SecOpsTopicArn']=str(os.getenv('SecOpsTopicArn'))
return get_usr_old_keys( globalVars['key_age'] )
After pasting the code, Scroll down to create a environment variable Key,
- Key as
key_age_cutoff_in_days
and Value as90
- Key
SecOpsTopicArn
and Value asYOUR-SNS-TOPIC-ARN
Save
the lambda function
We are going to use Cloudwatch Scheduled Events to take backup everyday.
rate(1 minute)
or
rate(5 minutes)
or
rate(1 day)
# The below example creates a rule that is triggered every day at 12:00pm UTC.
cron(0 12 * * ? *)
If you want to learn more about the above Scheduled expressions, Ref: CloudWatch - Schedule Expressions for Rules
If you already have IAM Users
with Access Keys, then set the key_age
to appropirate values. If not, Create multiple IAM Users and create Access Key
for each of them and set the key_age
to 1
.
After you have identified old keys, You should follow these steps to rotate the keys
- Create a second access key in addition to the one in use.
- Update all your applications to use the new access key and validate that the applications are working.
- Change the state of the previous access key to inactive.
- Validate that your applications are still working as expected.
- Delete the inactive access key.
We have demonstrated how you can automatically identify users with old Access/Secret Keys.