Alert for Old Access Keys in AWS - python

I didn't get an answer to my original question, but I've tweaked my code and I'm leaving this here in case anyone is trying to figure this out in the future. I'm new to Python, but this worked. This is a Python script I created for a Lambda function that checks the users in your AWS account and publishes a notification to an SNS topic. I've scheduled a CloudWatch rule with a cron expression to run it every day.
import boto3, json, time, datetime, sys, re
iam_client = boto3.client('iam')
sns_client = boto3.client('sns')
users = iam_client.list_users()
user_list = []
for key in users['Users']:
user_list = key['UserName']
accesskeys = iam_client.list_access_keys(UserName=key['UserName'])
for items in user_list.split('\n'):
for key in accesskeys['AccessKeyMetadata']:
accesskeydate = accesskeys['AccessKeyMetadata'][0]['CreateDate']
accesskeydate = accesskeydate.strftime("%Y-%m-%d %H:%M:%S")
currentdate = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime())
accesskeyd = time.mktime(datetime.datetime.strptime(accesskeydate, "%Y-%m-%d %H:%M:%S").timetuple())
currentd = time.mktime(datetime.datetime.strptime(currentdate, "%Y-%m-%d %H:%M:%S").timetuple())
active_days = (currentd - accesskeyd)/60/60/24
message = (key['UserName'],int(round(active_days))),
message = re.sub(r'[^a-zA-Z0-9 ]', "", str(message))
message = re.sub(r' ', ' is ', str(message))
if active_days >= 90:
sns_client.publish(
TopicArn='arn:aws:sns:us-west-2:xxxxxxxxxxxxx:topic-name',
Subject='User with Old Access Key Detected',
Message="The access key for " + str(message) + " days old. This user access key should be replaced ASAP.",
)

Related

Trying to return multiple values in output

I'm trying to get running hours on an EC2 tagged instance with (Tag-periods - active) in an sns email notification, however I'm new to Python. Nevertheless, even though I have more than 5 instances with these tag values, I only returned one of them. I tried hundreds more times, but I still couldn't attain an acceptable result.
import json
import boto3
from datetime import datetime
from datetime import timedelta
region ='ap-south-1'
ec2 = boto3.resource('ec2',region)
client = boto3.client('ec2',region)
def lambda_handler(event, context):
ec2 = boto3.resource('ec2')
sns = boto3.client('sns')
instances = ec2.instances.filter(Filters=[{'Name':'tag:Period', 'Values':['active']}])
active = ''
for instance in instances:
instanceStartTime = instance.launch_time.strftime("%Y-%m-%d %H:%M:%S")
currentTime = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
date_time_obj1 = datetime.strptime(instanceStartTime, '%Y-%m-%d %H:%M:%S')
date_time_obj2 = datetime.strptime(currentTime, '%Y-%m-%d %H:%M:%S')
timeDiff = (date_time_obj2 - date_time_obj1).total_seconds() / 3600.0
active += '\n Instance Id : ' + instance.id
active += '\n Instance Start Time : ' + instanceStartTime
active += '\n current Time : ' + currentTime
active += '\n Running Time : ' + str(timeDiff) + '\n\n'
for x in active:
if str(timeDiff)>='08.0':
response = sns.publish(
TopicArn='arn:aws:sns:ap-south-1:123456789:SNS-Notification',
Message='Current Active Instances ' + active,
)
return response
output:
enter image description here
In this block:
for x in active:
if str(timeDiff)>='08.0':
response = sns.publish(
TopicArn='arn:aws:sns:ap-south-1:123456789:SNS-Notification',
Message='Current Active Instances ' + active,
)
return response
You're returning after the first iteration that executes the if statement: in Python a function exits from execution after returning.
Try this instead:
for x in active:
if str(timeDiff)>='08.0':
sns.publish(
TopicArn='arn:aws:sns:ap-south-1:123456789:SNS-Notification',
Message='Current Active Instances ' + active,
)
(Also: why are you iterating over active, which is a string? And what's that time comparison as a string?)

Lambda - Converting output in forloop to a dic to allow for single email rather than multiple

I am struggling to convert this captured output as a dictionary so i can distribute the list of users who have a password over 90 days old:
import boto3
import datetime
from dateutil.tz import tzutc
sts_client = boto3.client('sts')
assumed_role_object=sts_client.assume_role(
RoleArn="arn:aws:iam::9999999999:role/role",
RoleSessionName="AssumedRoleSession2"
)
credentials=assumed_role_object['Credentials']
resource=boto3.resource(
'iam',
aws_access_key_id=credentials['AccessKeyId'],
aws_secret_access_key=credentials['SecretAccessKey'],
aws_session_token=credentials['SessionToken'],
)
sns = boto3.client('sns')
import json
import boto3
def lambda_handler(context,event):
today = datetime.datetime.now()
for user in resource.users.all():
if user.password_last_used is not None:
delta = (today - user.password_last_used.replace(tzinfo=None)).days
if delta >= 90:
print(','.join(('Username: ',str([user.user_name]), str(delta))))
sns_message = ','.join(('Username: ',str([user.user_name]), str(delta)))
response = sns.publish(
TopicArn='arn:aws:sns:eu-west-2:1111111111:topic',
Message= sns_message,
Subject='Users who have failed to log in over 90 Days',
)
My current attempt is causing 189 emails containing a single user name and password age, i want an email contenting a list of users and their password ages
Is there anyone kind enough to help me?
Thanks
Nick
Your sns.publish is inside your for loop. Create your list of users inside the loop then call the publish command.
I may get some of the syntax wrong as i can't test, but hopefully you'll the point.
user_list = []
for user in resource.users.all():
if user.password_last_used is not None:
delta = (today - user.password_last_used.replace(tzinfo=None)).days
if delta >= 90:
print(','.join(('Username: ',str([user.user_name]), str(delta))))
user_list.append(','.join(('Username: ',str([user.user_name]), str(delta))))
sns_message = user_list # you may need to check formatting of this prior to sending to sns???? it's a list, not a string
response = sns.publish(
TopicArn='arn:aws:sns:eu-west-2:1111111111:topic',
Message= sns_message,
Subject='Users who have failed to log in over 90 Days',
)

AWS Lambda limiting Python output

I have a lambda which i want to run every morning showing the users with AccessKeys older than 90 days. What i am noticing is that the return of users is limited
import boto3, json, time, datetime, sys, pprint
sns = boto3.client('sns')
usernames = []
mylist = []
sts_client = boto3.client('sts')
assumed_role_object=sts_client.assume_role(
RoleArn="arn:aws:iam::11111111:role/lambda_role",
RoleSessionName="AssumedRoleSession4"
)
credentials=assumed_role_object['Credentials']
client=boto3.client(
'iam',
aws_access_key_id=credentials['AccessKeyId'],
aws_secret_access_key=credentials['SecretAccessKey'],
aws_session_token=credentials['SessionToken'],
)
def lambda_handler(event, context):
users = client.list_users()
for key in users['Users']:
a = str(key['UserName'])
usernames.append(a)
for username in usernames:
try:
res = client.list_access_keys(UserName=username)
accesskeydate = res['AccessKeyMetadata'][0]['CreateDate']
accesskeydate = accesskeydate.strftime("%Y-%m-%d %H:%M:%S")
currentdate = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime())
accesskeyd = time.mktime(datetime.datetime.strptime(accesskeydate, "%Y-%m-%d %H:%M:%S").timetuple())
currentd = time.mktime(datetime.datetime.strptime(currentdate, "%Y-%m-%d %H:%M:%S").timetuple())
active_days = (currentd - accesskeyd)/60/60/24 ### We get the data in seconds. converting it to days
if 90 < active_days:
a = str(username)
c = int(int(round(active_days)))
mylist.append(a)
mylist.append(c)
except:
f = str('')
print(mylist)
finallist = ''.join(str(mylist))
finallist = finallist
sns_message = (finallist)
response = sns.publish(
TopicArn='arn:aws:sns:eu-west-2:111111:sns',
Message= sns_message,
Subject='Access Keys which need rotating',
)
How do i ensure all the users are captured in the python query and then emailed out via SNS?
Thanks
If you read the IAM list_users() documentation, you will see that results can be truncated and you need to paginate. The response you get back will include IsTruncated=True in which case you need to re-issue the list_users() call, supplying Marker, which you retrieve from the previous response.
Alternatively, and probably preferably, you can use a built-in paginator.

How can I add the authentication step in my script?

I have a script below which I would like to schedule to run it everyday through SQL Server and store the calendar events to database but the problem is I would need to have my outlook open in order to run this script, how can I add my credential in the script so I don't have to open outlook and have the script run without keeping my outlook opened? Thank you
import win32com.client, datetime
from datetime import date
from dateutil.parser import *
import calendar
import pandas as pd
import pyodbc
Outlook = win32com.client.Dispatch("Outlook.Application")
ns = Outlook.GetNamespace("MAPI")
Item = Outlook.CreateItem ( 1 )
Recip = Item.Recipients.Add ( 'Corporate Master Calendar' )
appts = ns.GetSharedDefaultFolder(Recip,9).Items
appts.Sort("[Start]")
appts.IncludeRecurrences = "False"
eoy=date(date.today().year, 12, 31)
eoy=eoy.strftime("%m/%d/%Y")
begin = date.today()
begin = begin.strftime("%m/%d/%Y")
appts = appts.Restrict("[Start] >= '" +begin+ "' AND [Start] <= '" +eoy+ "'")
apptDict = {}
item = 0
for indx, a in enumerate(appts):
organizer = str(a.Organizer)
start = a.Start
end = a.End
subject = str(a.Subject)
location = str(a.Location)
categories = str(a.Categories)
body=str(a.Body)
itemid=str(a.GlobalAppointmentId)
lmt=str(a.LastModificationTime)
apptDict[item] = {"GlobalAppointmentId": itemid, "Organizer": organizer, "Subject": subject, "Location": location, "Start": start, "End": end, "Body": body,"Categories": categories, "LMT":lmt}
item = item + 1
You can't - with MFA, even storing the credentials in the keyring (which only deals with basic credentials) won't do anything.

Send an email only if # isn't in csv

I have two CSV files, one with IOC hits and a second that is a watchfile. The watchfile adds an # to the file along with the IOC domain and last seen date. I'm trying to send one email when an IOC hit for that day, but I can't seem to get my loop right. Currently it emails every time, even though the # is present in the watchfile.csv. I've printed the values for val and emailed and they show up in the correct format, but it still emails every time the script is ran.
finIOChit.csv
last: 2017-01-17 query: rabbitons.pw,
watchfile.csv
last: 2017-01-17 query: # rabbitons.pw,
import smtplib
import csv
import os
import re
from datetime import *
today = date.today()
today = datetime.combine(today, datetime.min.time())
# Setup email alerting
sender = 'server#company.com'
receivers = ['user#company.com']
patn = re.compile('20\d{2}-\d{2}-\d{2}')
watchfile = open('watchfile.csv', 'r+w')
alreadyemailed = re.compile('#')
with open('finalIOChit.csv') as finalhit:
for hit in finalhit:
for line in watchfile:
emailed = alreadyemailed.findall(line)
for match in patn.findall(hit):
val = datetime.strptime(match, '%Y-%m-%d')
if val == today and emailed != '#':
hit = re.sub('query: ','query: # ',hit)
watchfile.write(hit)
message = """From:server <server#comapny.com>
To: user <user#company.com>
Subject: Passive DNS hit
"""
subject = ' ' + str(hit)
messagefull = message + subject
try:
smtpObj = smtplib.SMTP('emailserver')
smtpObj.sendmail(sender, receivers, messagefull)
except SMTPException:
print "Error: unable to send email"

Categories