Lambda 함수를 사용한 AWS IAM 액세스 키 교체
Architecutre Diagram

Lambda 함수를 사용한 AWS IAM 액세스 키 교체

이 글은 영어에서 자동으로 기계 번역되었으며 부정확한 내용이 포함될 수 있습니다. 자세히 보기
원본 보기

AWS 보안 모범 사례에 따라 AWS 계정 보안을 개선하기 위해 IAM 액세스 키를 정기적으로 교체해야 합니다.

이 기사에서는 우리가 어떻게 하는지 알려 드리겠습니다.자동화하다Lambda 함수를 사용하여 액세스 키 수명에 따라 IAM 액세스 키를 교체하는 프로세스입니다.

Use this link to rotate the keys manually using AWS document https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html#Using_RotateAccessKey

AWS IAM (ID 및 액세스 관리) 모든 AWS에 걸쳐 세분화된 찾기 액세스 제어를 제공합니다.

AWS 액세스 키IAM 사용자에 대한 장기 자격 증명입니다. 액세스 키를 사용하여 AWS CLI 또는 AWS API에 대한 프로그래밍 방식 요청에 서명할 수 있습니다 (직접 또는 AWS SDK를 사용하여).

AWS 람다서버를 프로비저닝하거나 관리하지 않고도 거의 모든 유형의 애플리케이션 또는 백엔드 서비스에 대한 코드를 실행할 수 있는 서버리스 이벤트 기반 컴퓨팅 서비스입니다. 200개 이상의 AWS 서비스 및 Software as a Service에서 Lambda를 트리거할 수 있습니다 (SaaS) 응용 프로그램을 사용하고 사용한 만큼만 비용을 지불합니다.

아키텍처 다이어그램

No alt text provided for this image

AWS Lambda 함수를 사용한 IAM 액세스 키 교체 및 AWS SES를 사용한 알림 전송

다이어그램은 다음 워크플로를 보여줍니다.

1- IAM 사용자는 IAM 액세스 키를 사용하여 프로그래밍 방식으로 로그인합니다.

2- CloudWatch 이벤트는 24시간마다 Lambda 함수를 시작합니다.

3- Lambda 함수는 각 AWS 계정 ID에 대해 Lambda 함수를 시작하고 추가 처리를 위해 메타데이터를 전달합니다. 모든 사용자가 키 연령에 액세스하는지 확인하고 계정 소유자에게 이메일을 시작합니다

4- 제 경우에는 AWS SES를 사용하여 이메일을 보내기 위해 세 가지 조건을 설정했습니다.

Lambda 함수의 Python 스크립트:

파이썬으로 액세스 키 회전 스크립트를 작성했습니다. 이 Python 스크립트는 먼저 AWS IAM에서 각 사용자 정보/메타데이터를 가져옵니다. 아시다시피 AWS IAM 사용자는 사람일 수도 있고 기계일 수도 있으므로 각 사용자에게 다음 정보를 적절하게 태그했습니다.

  • 이름
  • 사용자 유형 (직원 또는 기계)
  • 메일 주소 (ABC@xyz.com)

No alt text provided for this image
No alt text provided for this image

Lambda 함수는 IAM 사용자 태그에서 위의 정보를 가져오고 UserType이 "종업원"이고 키 사용 기간이 70일, 80일 또는 90일인 경우 AWS SES를 사용하여 알림 이메일을 보내고 액세스 키를 비활성화합니다. UserType이 "기계" 그리고 키 나이 70일, 80일 또는 90일이면 관련 팀에 알림 이메일을 보내드립니다 (IT, 운영 등과 같습니다.).

경고 보내기 및 액세스 키 비활성화 조건:

첫 번째 조건은 사용자의 IAM 액세스 키 사용 기간이 70일인 경우 경고 이메일을 보냅니다."오늘은 액세스 키의 70일째입니다. 90일이 되기 전에 액세스 키를 교체하십시오.".

두 번째 조건: 사용자의 IAM 액세스 키 사용 기간이 80일인 경우 경고 이메일을 보냅니다."오늘은 액세스 키의 80일째입니다. 90일이 되기 전에 액세스 키를 교체하십시오.".

세 번째 조건: 사용자의 IAM 액세스 키 사용 기간이 90일인 경우 경고 이메일을 보냅니다."오늘은 액세스 키의 90일째이며 키가 비활성화되었습니다. 새 키를 생성하거나 IT 지원 팀에 문의하십시오.".

Lambda 함수 코드:

내 github 저장소에서 적절한 형식의 코드 가져 오십시오 : https://github.com/engr-usman/blogs.git

import boto3, os, time, datetime, sys, jso
from datetime import date
from botocore.exceptions import ClientError
iam = boto3.client('iam')
def lambda_handler(event, context):
email_70_list = []
email_80_list = []
email_90_list = []
# print("All IAM user emails that have AccessKeys 70 days or older")
unique_user_list = (iam.list_users()['Users'])
for userlist in unique_user_list:
userKeys = iam.list_access_keys(UserName=userlist['UserName'])
for keyValue in userKeys['AccessKeyMetadata']:
UserAccessKeyID = keyValue['AccessKeyId']
IAMUserName = keyValue['UserName']
#print(f"IAMUserName IAM Users:{len(IAMUserName)}: {IAMUserName}")
if keyValue['Status'] == 'Active':
currentdate = date.today()
active_days = currentdate - keyValue['CreateDate'].date()
#print ("The active days details are: ", active_days)
#print ("datetime details are: ", datetime.timedelta(days=15))
# if Access key age is greater then or equal to 70 days, send warning
if active_days == datetime.timedelta(days=int(os.environ['days_70'])):
userTags = iam.list_user_tags(UserName=keyValue['UserName'])
email_tag = list(filter(lambda tag: tag['Key'] == 'email', userTags['Tags']))
if(len(email_tag) == 1):
email = email_tag[0]['Value']
email_70_list.append(email)
print("This User: ", IAMUserName, ", with the email: ", email, ", is having access key age is 70 days")
email_unique = list(set(email_70_list))
print("Email list: ", email_unique)
RECIPIENTS = email_unique
SENDER = os.environ['sender_email']
AWS_REGION = os.environ['region']
SUBJECT_70 = os.environ['SUBJECT_70']
BODY_TEXT_70 = os.environ['BODY_TEXT_70']
BODY_HTML_70 = os.environ['BODY_HTML_70']
CHARSET = "UTF-8"
client = boto3.client('ses',region_name=AWS_REGION)
try:
response = client.send_email(
Destination={
'ToAddresses': RECIPIENTS,
},
Message={
'Body': {
'Html': {
'Charset': CHARSET,
'Data': BODY_HTML_70,
},
'Text': {
'Charset': CHARSET,
'Data': BODY_TEXT_70,
},
},
'Subject': {
'Charset': CHARSET,
'Data': SUBJECT_70,
},
},
Source=SENDER,
)
except ClientError as e:
print(e.response['Error']['Message'])
else:
print("Email sent! Message ID:"),
print(response['MessageId'])
# if Access Key Age is greater then 80 days, send email alert
if active_days == datetime.timedelta(days=int(os.environ['days_80'])):
userTags = iam.list_user_tags(UserName=keyValue['UserName'])
email_tag = list(filter(lambda tag: tag['Key'] == 'email', userTags['Tags']))
if(len(email_tag) == 1):
email = email_tag[0]['Value']
email_80_list.append(email)
print("The User: ", IAMUserName, ", with the email: ", email, ", is having access key age is 80 days")
email_unique = list(set(email_80_list))
print("Email list: ", email_unique)
RECIPIENTS = email_unique
SENDER = os.environ['sender_email']
print("Sender: ", SENDER)
AWS_REGION = os.environ['region']
SUBJECT_80 = os.environ['SUBJECT_80']
BODY_TEXT_80 = os.environ['BODY_TEXT_80']
BODY_HTML_80 = os.environ['BODY_HTML_80']
CHARSET = "UTF-8"
client = boto3.client('ses',region_name=AWS_REGION)
try:
response = client.send_email(
Destination={
'ToAddresses': RECIPIENTS,
},
Message={
'Body': {
'Html': {
'Charset': CHARSET,
'Data': BODY_HTML_80,
},
'Text': {
'Charset': CHARSET,
'Data': BODY_TEXT_80,
},
},
'Subject': {
'Charset': CHARSET,
'Data': SUBJECT_80,
},
},
Source=SENDER,
)
except ClientError as e:
print(e.response['Error']['Message'])
else:
print("Email sent! Message ID:"),
print(response['MessageId'])
# if Access Key Age is greater then 90 days, send email alert and inactive access keys
if active_days >= datetime.timedelta(days=int(os.environ['days_90'])):
userTags = iam.list_user_tags(UserName=keyValue['UserName'])
email_tag = list(filter(lambda tag: tag['Key'] == 'email', userTags['Tags']))
user1_tag = list(filter(lambda tag: tag['Key'] == 'UserType', userTags['Tags']))
if(len(email_tag) == 1):
email = email_tag[0]['Value']
email_90_list.append(email)
print("The User: ", IAMUserName, ", with the email: ", email, ", is having access key age is greater then 90 days")
if(len(user1_tag) == 1):
user1tag = user1_tag[0]['Value']
if user1tag == "Employee":
iam.update_access_key(AccessKeyId=UserAccessKeyID,Status='Inactive',UserName=IAMUserName)
print("Status has been updated to Inactive")
email_unique = list(set(email_90_list))
print("Email list: ", email_unique)
RECIPIENTS = email_unique
SENDER = os.environ['sender_email']
print("Sender: ", SENDER)
AWS_REGION = os.environ['region']
SUBJECT_90 = os.environ['SUBJECT_90']
BODY_TEXT_90 = os.environ['BODY_TEXT_90']
BODY_HTML_90 = os.environ['BODY_HTML_90']
CHARSET = "UTF-8"
client = boto3.client('ses',region_name=AWS_REGION)
try:
response = client.send_email(
Destination={
'ToAddresses': RECIPIENTS,
},
Message={
'Body': {
'Html': {
'Charset': CHARSET,
'Data': BODY_HTML_90,
},
'Text': {
'Charset': CHARSET,
'Data': BODY_TEXT_90,
},
},
'Subject': {
'Charset': CHARSET,
'Data': SUBJECT_90,
},
},
Source=SENDER,
)
except ClientError as e:
print(e.response['Error']['Message'])
else:
print("Email sent! Message ID:"),
print(response['MessageId'])n        

참고 : 들여 쓰기는 위의 코드에서 올바르지 않습니다.( 그리고 나는 단지 아이디어를 위한 많은 코드 개선이 있다는 것을 알고 있습니다.

CloudWatch 이벤트 규칙 활성화:

이제 CloudWatch 이벤트 규칙을 생성하기만 하면 됩니다 (이제 AWS EventBridge) 특정 시간에 이 Lambda 함수를 트리거합니다. 제 경우에는 오전 9시에 이 cron 작업을 활성화했습니다.

No alt text provided for this image

AWS EventBridge 스케줄러 및 대상

결론

이는 누구도 계정 리소스에 액세스할 수 없도록 IAM 액세스 키를 교체하는 AWS 보안 모범 사례입니다. Lambda 함수는 이러한 종류의 작업을 수행하고 CloudWatch와 같은 다른 서비스에서 트리거하는 데 가장 적합한 도구입니다.

이 기사를 재미있게 읽으셨기를 바라며, 질문이 있으시면 언제든지 저에게 연락해 주십시오.

Usman Ahmad what are the roles you provided your lambda function ?

Hi Usman Ahmad I am trying to fetch the details of both sender and region details here. bot its not happening. pls let me know whats this sender refers to? how i should try in my code. as you said in the post i have created the mail in ses and mentioned the VAULE in the IAM user tags. can you help me what i am missing inorder to get to get the region and sender details

  • 이 이미지의 대체 텍스트 설명이 없음

Hi Usman! Can you publish script in the correct format (at least screenshot), please? I'm not Python specialist for now, maybe in the future. Thank you.

댓글을 보거나 남기려면 로그인

함께 조회된 페이지