반응형
0. 들어가며
해당 글은 본인이 노션으로 작성해 공유한 AWS 보안 점검 및 설정 가이드의 내용을 티스토리에 옮긴 것입니다.
1. 관련 법령
2. 개요
- AWS 리소스 핸들링을 위해 IAM 계정에 Access key(Access key ID/Secret access key)를 발급한 경우 주기적으로 변경 필요
3. 취약점 판단 기준
- Access key 변경 주기를 지정하지 않고, 주기적으로 Access key를 변경하지 않은 경우 취약
- Access key 변경 주기를 지정하고, 주기적으로 Access key를 변경한 경우 취약하지 않음
4. 취약점 확인 방법 - (1) 관리 콘솔에서 확인
- 관리 콘솔에서 [IAM] 검색 → [사용자] 메뉴 클릭
- *[활성 키 수명(Active key age)]*이 내부 정책에서 정의한 Access key 변경 주기를 초과하는지 여부 확인
4. 취약점 확인 방법 - (2) AWS CLI에서 확인
- IAM 권한을 보유한 계정의 Access key를 활용해 AWS CLI에서 Command(1) 실행
aws iam get-credential-report --query 'Content' --output text | base64 -d | cut -d, -f1,9,10,11,14,15,16
- access_key_active, access_key_last_lotated 컬럼 값을 확인하여 주기적 변경 여부 확인
5. 취약점 조치 방법
- [알림] 다양한 방법이 있을 수 있으나 본 항목에서는 Secret Manager와 Lambda를 활용한 방법을 안내
- 관리 콘솔에서 [Secret Manager] 검색 → [새 보안 암호 저장] 클릭
- Access key를 저장할 Secret Manager 생성(키/값에는 임시 값 입력)
- [자동 교체 구성] 비활성화 설정 후 [다음] 클릭
- 관리 콘솔에서 [IAM] 검색 → [정책] 메뉴 → [정책 생성] 클릭
- JSON(1)을 활용하여 JSON 정책 생성
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "SecretsManagerReadWritePermission",
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue",
"secretsmanager:DescribeSecret",
"secretsmanager:PutSecretValue",
"secretsmanager:UpdateSecretVersionStage",
"secretsmanager:ListSecretVersionIds"
],
"Resource": [
"SECRETS MANAGER의 ARN"
]
},
{
"Sid": "KMSWritePermission",
"Effect": "Allow",
"Action": [
"kms:Decrypt",
"kms:Encrypt"
],
"Resource": [
"aws/secretsmanager의 ARN"
]
},
{
"Sid": "SecretsManagerKMSListingPermission",
"Effect": "Allow",
"Action": [
"kms:ListKeys",
"secretsmanager:ListSecrets"
],
"Resource": "*"
}
]
}
- IAM 서비스에서 [역할] 메뉴 → [역할 만들기] 클릭
- 사용 사례 설정
- JSON(1)을 활용해 만든 정책과 [AWSLambdaBasicExecutionRole] 정책 할당
- 관리 콘솔에서 [Lambda] 검색 → [함수 생성] 클릭
- 함수 생성 설정
- Lambda(1)을 활용해 함수 작성 → [Deploy] 클릭
import boto3
import json
import logging
import os
import time
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def lambda_handler(event, context):
arn = event['SecretId']
token = event['ClientRequestToken']
step = event['Step']
# Setup the client
secretsmanager_client = boto3.client('secretsmanager')
# Make sure the version is staged correctly
metadata = secretsmanager_client.describe_secret(SecretId=arn)
logging.info(repr(metadata))
versions = metadata['VersionIdsToStages']
if token not in versions:
logger.error("Secret version %s has no stage for rotation of secret %s." % (token, arn))
raise ValueError("Secret version %s has no stage for rotation of secret %s." % (token, arn))
if "AWSCURRENT" in versions[token]:
logger.info("Secret version %s already set as AWSCURRENT for secret %s." % (token, arn))
return
elif "AWSPENDING" not in versions[token]:
logger.error("Secret version %s not set as AWSPENDING for rotation of secret %s." % (token, arn))
raise ValueError("Secret version %s not set as AWSPENDING for rotation of secret %s." % (token, arn))
if step == "createSecret":
logging.debug("createSecret %s" % arn)
logging.info("for IAM user access keys secret creation is handled by IAM ")
elif step == "setSecret":
logging.debug("setSecret %s" % arn)
current_dict = get_secret_dict(secretsmanager_client, arn, "AWSCURRENT", required_fields=['username'])
username = current_dict['username']
master_dict = get_secret_dict(secretsmanager_client, current_dict['masterarn'], "AWSCURRENT")
master_iam_client = boto3.client('iam', aws_access_key_id=master_dict['accesskey'], aws_secret_access_key=master_dict['secretkey'])
# load any pre-existing access keys. sorted by created descending. if the count is 2+ remove the oldest key
existing_access_keys = sorted(master_iam_client.list_access_keys(UserName=username)['AccessKeyMetadata'], key=lambda x: x['CreateDate'])
if len(existing_access_keys) >= 2:
logger.info("at least 2 access keys already exist. deleting the oldest version: %s" % existing_access_keys[0]['AccessKeyId'])
master_iam_client.delete_access_key(UserName=username, AccessKeyId=existing_access_keys[0]['AccessKeyId'])
# request new access key and gather the response
new_access_key = master_iam_client.create_access_key(UserName=username)
current_dict['accesskey'] = new_access_key['AccessKey']['AccessKeyId']
current_dict['secretkey'] = new_access_key['AccessKey']['SecretAccessKey']
logging.info('applying new secret value to AWSPENDING')
# save the new access key to the pending secret
secretsmanager_client.put_secret_value(SecretId=arn, ClientRequestToken=token, SecretString=json.dumps(current_dict), VersionStages=['AWSPENDING'])
elif step == "testSecret":
logging.debug("testSecret %s" % arn)
# load the pending secret for testing
pending_dict = get_secret_dict(secretsmanager_client, arn, "AWSPENDING", required_fields=['username'], token = token)
# attempt to call an iam service using the credentials
test_client = boto3.client('iam', aws_access_key_id=pending_dict['accesskey'], aws_secret_access_key=pending_dict['secretkey'])
try:
test_client.get_account_authorization_details()
except test_client.exceptions.ClientError as e:
# the test fails if and only if Authentication fails. Authorization failures are acceptable.
if e.response['Error']['Code'] == 'AuthFailure':
logging.error("Pending IAM secret %s in rotation %s failed the test to authenticate. exception: %s" % (arn, pending_dict['username'], repr(e)))
raise ValueError("Pending IAM secret %s in rotation %s failed the test to authenticate. exception: %s" % (arn, pending_dict['username'], repr(e)))
elif step == "finishSecret":
logging.debug("finishSecret %s" % arn)
# finalize the rotation process by marking the secret version passed in as the AWSCURRENT secret.
metadata = secretsmanager_client.describe_secret(SecretId=arn)
current_version = None
for version in metadata["VersionIdsToStages"]:
if "AWSCURRENT" in metadata["VersionIdsToStages"][version]:
if version == token:
# The correct version is already marked as current, return
logger.info("finishSecret: Version %s already marked as AWSCURRENT for %s" % (version, arn))
return
current_version = version
break
# finalize by staging the secret version current
secretsmanager_client.update_secret_version_stage(SecretId=arn, VersionStage="AWSCURRENT", MoveToVersionId=token, RemoveFromVersionId=current_version)
logger.info("finishSecret: Successfully set AWSCURRENT stage to version %s for secret %s." % (token, arn))
else:
raise ValueError("Invalid step parameter")
def get_secret_dict(secretsmanager_client, arn, stage, required_fields=[], token=None):
# Only do VersionId validation against the stage if a token is passed in
if token:
secret = secretsmanager_client.get_secret_value(SecretId=arn, VersionId=token, VersionStage=stage)
else:
secret = secretsmanager_client.get_secret_value(SecretId=arn, VersionStage=stage)
plaintext = secret['SecretString']
secret_dict = json.loads(plaintext)
# Run validations against the secret
for field in required_fields:
if field not in secret_dict:
raise KeyError("%s key is missing from secret JSON" % field)
# Parse and return the secret JSON string
return secret_dict
- JSON(2)를 활용해 IAM 계정이 자신의 패스워드와 Access Key를 변경하도록 하는 정책 생성
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iam:ListUsers",
"iam:GetAccountPasswordPolicy"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"iam:*AccessKey*",
"iam:ChangePassword",
"iam:GetUser",
"iam:*ServiceSpecificCredential*",
"iam:*SigningCertificate*"
],
"Resource": [
"arn:aws:iam::*:user/${aws:username}"
]
}
]
}
- Access Key를 자동으로 교체할 IAM 계정에 JSON(2)를 활용해 만든 정책 할당
- 이후의 작업을 위해 임시로 [AWSLambda_FullAccess] 정책 추가 할당 필요
- [AWSLambda_FullAccess] 정책을 할당한 IAM 계정의 AWS CLI 접속
- Command(2) 명령어 실행
aws lambda add-permission --function-name [lambda의 ARN] --principal secretsmanager.amazonaws.com --action lambda:InvokeFunction --statement-id SecretsManagerAccess
- 관리 콘솔에서 [Secret Manager] 검색 → 생성한 보안 암호 클릭
- [보안 암호값 검색] 클릭 → [편집] 클릭
- 보안 암호 값 편집
- accesskey : Access key를 변경하고자 하는 IAM 계정의 Access Key 값
- secretkey : Access key를 변경하고자 하는 IAM 계정의 Secret Access Key 값
- username : Access key를 변경하고자 하는 IAM 계정명
- masterarn : 해당 Secret Manager의 ARN
- [교체 편집] 클릭 → 교체 구성 설정
- [저장] 시 Access Key는 자동으로 교체되며, 변경 주기 이전에 교체를 원할 경우 [보안 암호 즉시 교체] 클릭
- Secret Manager에서 Access Key 변경 확인
- CloudWatch Log groups에서 Access Key 변경 확인
6. 참고
반응형
'AWS' 카테고리의 다른 글
AWS IAM 계정 비밀번호 복잡도 및 변경 주기 정책 설정 (0) | 2023.03.15 |
---|---|
AWS IAM 계정에 권한 직접 할당 여부 확인 (0) | 2023.03.15 |
AWS Access key 이용 현황 관리 (0) | 2023.03.15 |
AWS root 계정 사용 여부 확인 (0) | 2023.03.15 |
AWS root 계정의 Access key ID / Secret access key 삭제 (0) | 2023.03.15 |