본문 바로가기

AWS/Secrets Manager

Secrets Manager를 사용하여 주기적으로 Access Key 교체

안녕하세요 ^^

오늘은 지난번 포스팅인 '자신의 패스워드 및 Access Key 교체' 포스팅의 조금 업그레이드 버전인,

지정한 날짜마다 주기적으로 Access Key를 사용자에 자동으로 생성해주고,

애플리케이션단에서 변경된 Access Key로 교체하는 로직을 구현하도록 권고하고,

이를 기반으로 오래된 Access Key가 사용되지 않도록 하는 Secrets Manager를 소개하도록 하겠습니다.
(AccessKey 뿐만 아니라, DB 자격증명 또한 지원합니다.)

 

그전에, 이 녀석이 어떠한 이점을 가져다주는지, 왜 써야 하는지 간략하게 말씀드리겠습니다.


많은 애플리케이션 관리자분들께서, AWS에서 제공하는 SDK를 사용하시기 위해 애플리케이션 소스코드에

Access Key의 정보를 하드코딩 하시는 경우가 굉장히 많습니다.

 

이렇게 하드 코딩된 Key가 만약 외부로 유출된다면, 정말 끔찍한 보안사고를 초래할 수 있습니다.

또한, 보안관리측면에서 너무 오래된 Key를 교체하고자 할 때, 모든 소스에서 일일이 이전 Key 값을 발라내고,

새로운 Key 값으로 교체해야 되기때문에 이 과정에서 휴먼에러가 발생할 수 있고, 나아가 서비스 장애로 번질 수 있습니다. (Ex. 예전 Key 값의 일부를 수정하지 못한 경우)

 

 

혹은, 환경변수를 사용하여 소스코드에 입력하였다고 하더라도, 오래된 Key를 교체하고자 할 때,

수동으로 새로운 Key를 발급받고, 새로운 Key로 환경변수를 교체해주고의 작업은 최첨단 수동 프로세스지요.

제가 늘 강조드리는 점..

 

1. IT는 구글링과 구걸링

2. 최첨단 수동 프로세스를 자동화 (= IT인은 게을러야 한다.)

 

위와 같기에.. 이러한 최첨단 수동 프로세스를 자동화해주는 것이 바로 Secrets Manager입니다.

그럼 바로 실제 설정을 시작해보겠습니다.


먼저, Secrets Manager에서 새로운 Secret을 생성하겠습니다.

 

AWS Web Console - Secrets Manager - Store a new secret

 

1. 우리는 AccessKey를 등록할 것이기 때문에 Other type of secrets를 선택합니다.

2. 일반 텍스트(Plaintext)가 아닌 비밀 키/값(Secret key/value) 유형을 선택합니다.

3. Key와 Value는 이후에 변경할 것이기 때문에 우선은 임의의 값으로 입력하여 생성합니다.

4. 이러한 Secret을 암호화할 KMS키를 AWS에서 제공하고 있는 aws/secretsmanager를 사용합니다.
(다른 KMS Key를 사용하셔도 무방하지만, 그렇게 된다면 이후 진행할 내용에서 선택하신 KMS Key를 사용하도록 설정 일부분을 수정해주셔야 합니다!!)

 

Step 1

 

Next로 넘어가서..

 

1. 만들려는 Secret의 이름과 설명, 태그를 입력해줍니다.

2. Resource Permissions는 생성되는 Secret을 사용할 수 있는 사용자를 지정할 수 있지만, 넘기겠습니다.

 

Step 2

 

Next로 넘어가서..

 

1. 지금 당장은 Secret을 회전하는 데 사용할 Lambda 함수도 없기 때문에 우선은 Disable automatic rotation을 선택하여 자동 회전을 비활성화한 상태로 생성하겠습니다.

Next를 눌러 작성한 내용을 확인하시고, 생성해주시면 됩니다.

 

Step 3

 

이렇게 Secret까지는 생성했습니다. 이제 우리가 해줄 일은 이 Secret이 자동으로 Access Key를 교체할 수 있도록,

Lambda 함수를 연결해주고, SecretsManager가 연결된 Lambda 함수를 사용할 수 있도록 허용해주어야 합니다.


다음 단계로 Lambda 함수를 생성해야 하는데요.

Lambda 함수를 생성하기 전에, Lambda 함수에게 연결해줄 역할과 정책을 먼저 생성하겠습니다.

 

 

AWS Web Console - IAM - Policies - Create policy - JSON

 

1. 아래 JSON을 복사/붙여넣기 합니다.

2. 아래 JSON에서 SECRETSMANAGER_ARN_of_SECRET, KMS_KEY_ARN_of_aws/secretsmanager 를자신의 환경에 맞게  변경합니다.

3. Policy의 이름과 설명을 적절하게 입력하여 생성해줍니다.

 

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "SecretsManagerReadWritePermission",
            "Effect": "Allow",
            "Action": [
                "secretsmanager:GetSecretValue",
                "secretsmanager:DescribeSecret",
                "secretsmanager:PutSecretValue",
                "secretsmanager:UpdateSecretVersionStage",
                "secretsmanager:ListSecretVersionIds"
            ],
            "Resource": [
                "SECRETSMANAGER_ARN_of_SECRET"
            ]
        },
        {
            "Sid": "KMSWritePermission",
            "Effect": "Allow",
            "Action": [
                "kms:Decrypt",
                "kms:Encrypt"
            ],
            "Resource": [
                "KMS_KEY_ARN_of_aws/secretsmanager"
            ]
        },
        {
            "Sid": "SecretsManagerKMSListingPermission",
            "Effect": "Allow",
            "Action": [
                "kms:ListKeys",
                "secretsmanager:ListSecrets"
            ],
            "Resource": "*"
        }
    ]
}

다음은 생성한 Policy와 Lambda가 실행되는 로그를 CloudWatch LogGroup에 쓸 수 있도록 역할에 연결하겠습니다.

 

AWS Web Console - IAM - Roles - Create role

 

1. 이 역할을 Lambda가 사용할 수 있도록 Lambda를 신뢰관계(Trust relationship)로 등록합니다.

 

Step 1

 

2. 위에서 생성한 Custom policy와 AWS 관리형 정책인 AWSLambdaBasicExecutionRole을 연결해줍니다.
3. 이후 적절한 태그와 Role 이름, 설명을 작성하여 Role을 생성합니다.

 

Step 2

 


다음은 Lambda 함수를 생성하겠습니다.

 

AWS Web Console - Lambda - Function - Create function

 

1. 코드를 직접 작성할 거기 때문에 Author from scratch

2. 적절한 함수의 이름을 입력합니다.

3. Runtime은 Python으로 생성하겠습니다.

4. Role은 Use an existing role을 선택하여 위에서 생성한 Role을 연결합니다.

5. 함수를 생성합니다.

 

Lambda 함수 생성

 

다음은 생성된 함수에 아래 코드를 붙여 넣습니다.

 

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

 

이후, 생성된 함수에서 스크롤을 조금 내려 아래 Timeout이 3초로 된 것을 5초 정도로 수정해주겠습니다.

 

함수 실행시간 수정


자!! 이제 SecretManager에서 Secret Key/Value 부분을 수정하고, Automatic rotation 부분을 활성화하면서

생성한 Lambda 함수를 연결해주면 되는데요!

 

그전에, 테스트 또한 함께 진행해야 하기 때문에 IAM User를 생성하고(혹은 생성된 IAM User에 부착해도 됩니다.),
자신의 패스워드/AccessKey를 변경할 수 있는 정책을 생성하여 부착하시면 됩니다.

저는, 새로운 User를 생성하고 테스트를 위해 AmazonS3ReadOnlyAccess 정책을 함께 부착했습니다.

 

 

위 과정은 포스팅이 길어지는 관계로 생략하고, 해당 IAM User에 새로운 AccessKey를 발급해주겠습니다.

아래 IAM User의 세부항목의 Security credentials에서 Create access key를 눌러 AccessKey를 발급해주시면 됩니다.

이후, AccessKey, Secret Access Key의 값은 SecretsManager에 등록해야 하기 때문에 기록해둡니다.

 

(기존에 사용하시던 AccessKey가 있더라도 새로 발급하시는 걸 권고드립니다. SecretsManager는 Automatic rotation이 활성화되면 그 즉시 등록된 Secret을 한번 회전시키고 이후 설정된 값을 기반으로 주기적으로 회전시킵니다.

사용 중인 Key가 즉시 1회 교체되기 때문에, 새로운 Key를 생성하는 것을 권고드립니다.)

 

 

Access Key 생성


자!! 아래의 사전 준비는 다 끝났습니다.

 

1. SecretsManager에서 Secret 생성

2-1. Lambda가 사용할 Role에 부착될 Policy

2-2. Lambda가 사용할 Role

2-3. SecretsManager의 Automatic rotation을 위한 Lambda 함수

3-1. IAM User에게 자신의 패스워드/AccessKey를 교체할 수 있는 Policy를 부착.

3-2. IAM User에게 AccessKey를 발급해주고 기록.

 

 

이제 아까 생성해두었던 Secret으로 돌아가서 Secret Key/Value를 수정해주겠습니다.


AWS Web Console - SecretsManager - Secrets

 

생성했던 Secret의 상세정보의 Secret value를 Retrieve secret value를 눌러 폴딩을 해제해주시고,

아래의 Key/Value로 작성해줍니다.

 

1. accesskey : 위에서 생성한 Access Key 값

2. secretkey : 위에서 생성한 Secret Access Key 값

3. username : 해당 Access Key를 갖고 있는 IAM User의 이름

4. masterarn : 해당 Secret의 ARN

 

Step 1

 

그리고 Automatic rotation을 활성하고, 생성한 Lambda를 선택하여 Save를 누르겠습니다.

Step 2

 

그럼..!! 쨔라쟌!! 축하드립니다. 에러에 당첨되셨습니다.

 

대충 SecretsManager가 Lambda 함수를 호출할 수 없다는 내용

 

SecretsManager가 우리가 생성한 Lambda 함수를 호출할 수 없기 때문에 발생하는 에러인데요.

그럼 SecretsManager가 Lambda 함수를 호출할 수 있도록 설정해주어야겠군요??

근데, Secret에는 Role도 없고.. 어디서 설정하는지 한참 헤매었습니다..(저만 그런 거겠죠..?)

Lambda의 Resource-based permissions에서 수정을 해주어야 합니다.

 

근데, 이 부분이 되게 웃긴 게.. 생성하다 보면 Source ARN이 Required 필드인데요..

AWS CLI를 통해 명령을 날리면 생략할 수 있습니다..

저희는 생략해야 합니다. Source ARN에 Secret의 ARN도 넣어보고 별 짓 다해봤는데 안돼요..

(혹시 방법이나 왜 그런지 아시는 분이 계시다면 꼭 좀 고견 부탁드리겠습니다.)

 

AWS CLI로 생성한 Resource based policy를 콘솔에서 수정하고자 할 때 필수입력 항목으로 나옵니다.


여하튼.. 그래서 저희는 콘솔에서는 작업을 해줄 수가 없고.. AWS CLI를 통해 명령어 한 줄 날리겠습니다.

(위에서 생성한 IAM User에 잠깐 LambdaFullAccess Policy를 부착하세요.)

 

아래 명령에서 --function-name의 값을 Lambda 함수의 ARN으로 교체하시면 됩니다.

 

CMD

C:\Users\jang_> aws lambda add-permission --function-name arn:aws:lambda:ap-northeast-2:AWS_ACCOUNT_ID:function:SecretManager-Rotation-AccessKey --principal secretsmanager.amazonaws.com --action lambda:InvokeFunction --statement-id SecretsManagerAccess

 

요렇게 커맨드 날려서 아래 사진의 정보 보시면 실제로 Resource-based policy가 등록되어 있습니다.

 

Resource-based policy

 

자.. 여기까지 되었으니, 다시 SecretsManager로 돌아가서 Automatic rotation을 활성시켜 보겠습니다.


Automatic rotation 활성화!

그러면 기다리고 기다리던..!! rotation 작업이 예약되었다는 메시지를 확인 가능합니다!

 

아싸 성공! 그렇지만, Lambda가 실제로 잘 돌아갔는지는..?! 로그 확인!!

 

위 메시지는 Rotation 작업이 성공적으로 예약되었다는 것을 알려주는 메시지입니다.

실제로 Rotation을 수행하는 Lambda에서 오류가 발생했는지의 여부는 CloudWatch LogGroup에서 확인합시다!!


AWS Web Console - CloudWatch - Log groups - Lambda 함수 명의 Log group 선택 - 최신 로그 스트림 선택

 

Error가 없습니다!

 

그럼.. 실제로 IAM User의 AccessKey가 교체되었는지는 Secret의 세부 정보에서 Secret value 값을 확인해보시면 됩니다.

주의하실 점은, Lambda에서 해당 유저에게 활성되어 있는 AccessKey가 2개 이상이면, 가장 오래된 Key를 제거합니다.

또한, IAM User의 세부항목에서 보시면 아시겠지만, 기존 Key를 자동으로 삭제해주지는 않고 신규 Key를 하나 생성해줍니다. Secret에 등록된 Secret value는 신규 Access Key의 값을 갖게 됩니다.

 

변경된 Secret value!!


자.. 이제 마지막으로 애플리케이션 관리자 측에서 최신으로 변경된 AccessKey를 조회해야 하는데요..!

 

 

SecretsManager에 AWS가 제공하고 있는 코드를 사용하시면 됩니다.

아래 샘플 코드는 Secret key/value를 조회하여, JSON 형식으로 변환해줍니다.

변환된 JSON 형식에서 필요한 정보만 빼서 오래된 Key를 신규 Key로 교체하도록 애플리케이션에서 로직을 짜주면 됩니다.

이후, 교체가 완료된 이후에는 예전 Key가 삭제까지 되도록 하거나 Key 삭제는 관리자가 직접 하도록 하셔도 됩니다.

 

AWS에서 제공하는 샘플 코드

 

애플리케이션 쪽에서 위 코드를 가지고 Secret에서 신규 Key를 가져오려면 당연히 권한이 필요하겠지요..??

아래 Json으로 Policy를 생성하고 IAM User에 할당해주시면 됩니다.

아래에서  SECRETSMANAGER_ARN_of_SECRET, KMS_KEY_ARN_of_aws/secretsmanager 을 자기 자신의 것으로 바꿔주면 됩니다.

 

 

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "SecretsManagerPermission",
            "Effect": "Allow",
            "Action": [
                "secretsmanager:GetSecretValue"
            ],
            "Resource": [
                "SECRETSMANAGER_ARN_of_SECRET"
            ]
        },
        {
            "Sid": "KMSPermission",
            "Effect": "Allow",
            "Action": [
                "kms:Decrypt",
                "kms:Encrypt"
            ],
            "Resource": [
                "KMS_KEY_ARN_of_aws/secretsmanager"
            ]
        },
        {
            "Sid": "ListingPermission",
            "Effect": "Allow",
            "Action": [
                "kms:ListKeys",
                "kms:ListAliases"
            ],
            "Resource": "*"
        }
    ]
}

자.. 그러면 테스트를 해보겠습니다..!!

코드는 샘플 코드에서 약간 수정했습니다.

 

# Use this code snippet in your app.
# If you need more information about configurations or implementing the sample code, visit the AWS docs:   
# https://aws.amazon.com/developers/getting-started/python/

import boto3
import base64
from botocore.exceptions import ClientError
import jmespath
import json


def get_secret():

    secret_name = "Create_by_JunHyeong_APIKey-Secret"
    region_name = "ap-northeast-2"

    # Create a Secrets Manager client
    session = boto3.Session(region_name=region_name,profile_name='Secret-Test')
    sm = session.client(
        service_name='secretsmanager',
        region_name=region_name
    )

    # In this sample we only handle the specific exceptions for the 'GetSecretValue' API.
    # See https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html
    # We rethrow the exception by default.

    try:
        get_secret_value_response = sm.get_secret_value(
            SecretId=secret_name
        )
    except ClientError as e:
        if e.response['Error']['Code'] == 'DecryptionFailureException':
            # Secrets Manager can't decrypt the protected secret text using the provided KMS key.
            # Deal with the exception here, and/or rethrow at your discretion.
            raise e
        elif e.response['Error']['Code'] == 'InternalServiceErrorException':
            # An error occurred on the server side.
            # Deal with the exception here, and/or rethrow at your discretion.
            raise e
        elif e.response['Error']['Code'] == 'InvalidParameterException':
            # You provided an invalid value for a parameter.
            # Deal with the exception here, and/or rethrow at your discretion.
            raise e
        elif e.response['Error']['Code'] == 'InvalidRequestException':
            # You provided a parameter value that is not valid for the current state of the resource.
            # Deal with the exception here, and/or rethrow at your discretion.
            raise e
        elif e.response['Error']['Code'] == 'ResourceNotFoundException':
            # We can't find the resource that you asked for.
            # Deal with the exception here, and/or rethrow at your discretion.
            raise e
    else:
        # Decrypts secret using the associated KMS CMK.
        # Depending on whether the secret is a string or binary, one of these fields will be populated.
        if 'SecretString' in get_secret_value_response:
            secret = get_secret_value_response['SecretString']
            try:
                secret_json = json.loads(secret)
            except json.decoder.JSONDecodeError:
                exit("AWS was not happy with that one")
            #accesskey = jmespath.search("accesskey", secret)
            #secretkey = jmespath.search("secretkey", secret)
        else:
            secret = base64.b64decode(get_secret_value_response['SecretBinary'])
            
    return secret

def main(secret):
    print(secret)

if __name__ == "__main__":
    secret = get_secret()
    main(secret)
    

 

위 코드를 실행했을 때 나오는 값입니다.

 

CMD

PS C:\Users\jang_\Desktop\AWS-Python> python .\get_secret.py

{"accesskey":"AKIAWJGUM5H4WJ5E2RG5","secretkey":"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","username":"SecretsManager-Test-User","masterarn":"arn:aws:secretsmanager:ap-northeast-2:432089655801:secret:Create_by_JunHyeong_APIKey-Secret-x26tB9"}
PS C:\Users\jang_\Desktop\AWS-Python>

 

아래처럼 등록된 Profile로 S3 명령어를 실행했을 때 정상적으로 조회가 됩니다.

또한, Secret에 등록된 Value와 Profile에 입력한 Value가 동일한 것 확인할 수 있습니다.

 

이후, Automatic rotation을 활성화하여 Key를 교체 완료했고, Secret value가 변경된 것도 확인할 수 있습니다.

 

Secret을 회전시켜 새로운 Access Key 생성

 

IAM User의 상세정보에는 회전하기 전 Key와 신규 Key 모두가 등록되어 있습니다.

 

IAM User의 상세정보

 

그럼 위 Python 코드를 다시 실행시켜서 확인해보겠습니다.

 

PS C:\Users\jang_\Desktop\AWS-Python> python .\get_secret.py

{"accesskey": "AKIAWJGUM5H4YYQ4AUWW", "secretkey": "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", "username": "SecretsManager-Test-User", "masterarn": "arn:aws:secretsmanager:ap-northeast-2:432089655801:secret:Create_by_JunHyeong_APIKey-Secret-x26tB9"}

 

조회된 accesskey 값이 위에 신규 생성된, Last used 가 N/A인 Access Key ID와 동일합니다.

그럼 기존 녀석을 지우고 aws s3 ls 명령어를 사용해볼까요??

당연한 얘기지만 Invalid Access Key Id입니다.

 

기존 Access Key를 삭제 후 aws s3 ls 명령어 사용


이렇게 변경되는 Access Key를 애플리케이션 단에서 조회하고 신규 Access Key로 교체하도록 로직 수정을 권고하고, 오래된 Access Key는 삭제하도록 하면 됩니다.

 

중요한 점은 Secret을 조회하는 API당 비용이 부과되기 때문에, 애플리케이션 단에서는 늘 Secret을 조회하는 것이 아닌,

교체 시기에 맞게 cron을 구성하여 변경된 Secret의 Value를 확인하고, 기존 Key와 값이 다르다면 변경하도록 하거나,

혹은 Secrets Manager의 Rotation 과정에서 발생되는 API를 기반으로 애플리케이션 관리자에게 Mail로 Noti를 보내는 방법 등.. 여러 방법이 있습니다.


포스팅이 매우 매우 길어져서 어렵게 느껴지실 수 있을 텐데요..!

 

최종적으로 정리 한번 하겠습니다.

 

1. SecretsManager에서 Secret 생성

2-1. Lambda가 사용할 Role에 부착될 Policy를 생성

2-2. Lambda가 사용할 Role을 생성

2-3. SecretsManager의 Automatic rotation을 위한 Lambda 함수를 생성

2-4. SecretsManager가 Lambda 함수를 사용할 수 있도록 Lambda에서 Resource-based-policy 생성.

3-1. IAM User에게 자신의 패스워드/AccessKey를 교체할 수 있는 Policy를 부착.

3-2. IAM User에게 AccessKey를 발급해주고 기록.

4. Secrets Manager에서 생성한 Secret의 Secret key/value를 변경. (accesskey, secretkey, username, masterarn)

5. AWS CLI를 통하여 SecretsManager가 Lambda 함수를 호출할 수 있도록 명령어 한 줄.

6. Secret을 회전시켜 실제로 Secret value가 변경되는지(Rotation이 성공하는지) 확인.

7. End User 입장에서 최신 버전의 AccessKey 값을 불러올 수 있도록 Permission 작성 및 부착

8. 샘플 코드를 사용하여 실제로 Secret의 key/value를 불러오기.

 

 

위 단계로 하나하나 해보시면서 체크해보시길 바랍니다!!

 

 

제가 설명하는 능력이 부족해서.. 이해가 안 되시는 부분은 댓글 남겨주시면 최대한 아는 만큼 답변드리겠습니다!!

 

 

감사합니다..!!