본문 바로가기

AWS/IAM

[AWS] CLI에서 MFA 임시세션토큰 자동화

안녕하세요 ^^

MFA(Multi Factor Authentication)는 말 그대로 2차인증으로써, 아이디와 패스워드만으로 인증을 하는 것이 아닌,

2차인증을 거쳐 계정의 보안성과 침해사고의 위험을 한층 더 낮춰주는 아주 훌륭한 녀석입니다.

특히나, 막중한 권한을 갖고있는 계정일수록 보안에 더더욱 신경을 써야할텐데요.

다만, 이 보안이라는 것은 철저해질수록 운영적인 불편함이 늘어나는 단점이 존재하는데요.

 

오늘은 이전 글 [AWS/IAM] - [AWS] MFA 강제적용 정책 의 영향을 받고 있는 IAM 사용자가

AWS CLI를 사용하여 작업을 할 때에, 발생되는 불편사항을 Python Code를 이용해서

AWS Profile에 해당 MFA 임시세션토큰이 자동으로 등록되도록 하여, 운영측면에서 조금이나마 편리하게!!!

사용하실 수 있도록 글을 써봅니다..!

 


2021-06-01 추가 수정글...

 

        {
            "Sid": "DenyAllExceptListedIfNoMFA",
            "Effect": "Deny",
            "NotAction": [
                "iam:CreateVirtualMFADevice",
                "iam:EnableMFADevice",
                "iam:GetUser",
                "iam:ListMFADevices",
                "iam:ListVirtualMFADevices",
                "iam:ResyncMFADevice",
                "iam:DeleteVirtualMFADevice",
                "iam:ChangePassword",
                "iam:GetAccountPasswordPolicy",
                "sts:GetSessionToken"
            ],
            "Resource": "*",
            "Condition": {
                "BoolIfExists": {
                    "aws:MultiFactorAuthPresent": "false"
                }
            }

 

MFA 강제적용정책의 Json을 보시면 아시겠지만, 마지막 구문에서 Deny와 NotAction, Condition문으로

이루어져 있는데요. 차례대로 해석을 해보면..

1. Deny 하겠다.

2. 명시 되어있지 않은(NotAction) API들을

3. MFA 인증을 거치지 않았다면.

입니다.

 

즉, MFA인증을 거치지 않으면, 위에 명시된 API를 제외하고는 모두 Deny하는 것이지요.

그렇지만, 보통 CLI를 통해 작업을 하실 때에는 임시세션토큰이 아닌, Access Key, Secrets Key 쌍으로 이루어진

장기자격증명을 사용하게됩니다.

 

장기자격증명에는 해당 MultiFactorAuthPresent 키가 존재하지 않습니다!

그렇기 때문에, GetSessionToken API를 호출해서 임시자격증명을 발급받아야 하는데요.

이 과정이 복잡하기에.. 아래의 3가지 방법 중 한가지를 쓰시면 될 것 같습니다.

 

1. 아래의 Python 코드를 사용한다.

2. Console 사용자와 CLI 사용자를 각각 따로 생성한다.

3. 사용자의 MFA 활성화가 확인 되었다면, MFAForcePolicy를 떼준다.


TEST 환경은 CentOS7 에서 진행되었으며, AWS CLI가 설치되어있다는 가정하에 진행하겠습니다.

 

 

1. AWS CLI Configure

  • aws configure 명령어를 이용해서 Access keySecret access key, Region, 출력 형식을 지정해 줍니다.
  • AWS CLI는 이 정보를 credentials 파일에서 default라는 프로파일(설정 모음)에 저장합니다. 기본적으로
    이 프로파일의 정보는 사용할 프로파일을 명시적으로 지정하지 않는
    AWS CLI 명령이 실행될 때 사용됩니다.
  • credentials 파일과 config 파일이 저장되는 경로는 기본적으로 $HOME/.aws/ 입니다.
  • 아래 내용은 샘플입니다.
[root@localhost ~]# aws configure

AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE
AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Default region name [None]: ap-northeast-2
Default output format [None]: json

 

2. IAM User의 MFA 생성

  • AWS Web Console에서 My Sercurity Credentials에서 MFA 생성을 진행합니다.

test라는 IAM User의 MFA생성 과정

 

  • Multi-factor authentication(MFA) Section에서 Assign MFA device 선택하여 MFA 할당.
  • 예제에서는 Virtual MFA Device를 선택하여 Google MFA Apps로 설정했습니다.

MFA Device 생성

 

  • MFA Device Code를 생성하여 생성되는 코드를 1차, 2차 모두 입력.

MFA Device 동기화 과정

 

 

  • 생성된 MFA Device의 ARN과 IAM User의 ARN을 기록합니다.

MFA Device의 생성 정보와 ARN 정보

 

3. IAM User의 Profile 설정

  • 다시 AWS CLI 환경으로 돌아와서, 해당 test 사용자의 MFA ARN을 ~/.aws/config 에 등록합니다.
[root@localhost ~]# vi ~/.aws/config

[testAccount]
mfa_arn = arn:aws:iam::123456789012:mfa/test
output = json

 

  • 또한, 해당 test 사용자의 AccessKey, SecretAccessKey 정보를 ~/.aws/credentials 파일에 등록합니다.
[root@localhost ~]# vi ~/.aws/credentials

[testAccount]
aws_access_key_id = AKIAWJGUM5H4ROSNQVKJ
aws_secret_access_key = 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

 

  • 이 후, 위에 기록한 AccessKey, SecretAccessKey 정보를 해당 계정에 Mapping하기 위해 ~/.aws/config
    해당 내용을 추가합니다.
[root@localhost ~]# vi ~/.aws/config

[profile test]
user_arn = arn:aws:iam::123456789012:user/test
source_profile = testAccount
region = ap-northeast-2

 

  • ~/.aws/config 의 완성된 내용은 아래와 같습니다.
cat ~/.aws/config

[testAccount]
mfa_arn = arn:aws:iam::123456789012:mfa/test
output = json

[profile test]
user_arn = arn:aws:iam::123456789012:user/test
source_profile = testAccount
region = ap-northeast-2

 

  • MFA 인증을 통해 발급된 임시 Access-key Secret-Access-Key, Session-Token
    ~/.aws/credentials 파일에 등록해주어야 합니다.
  • 해당 임시 세션 토큰은 root 계정일 경우 1시간, IAM 사용자 일 경우 12시간 동안 유효 합니다.
[root@localhost ~]# aws sts get-session-token --serial-number arn:aws:iam::123456789012:mfa/test --token-code MFA-NUMBER –-profile testAccount

{
	"Credentials": {
		"AccessKeyID": "ASIAWJGUM5H47E4ALGBL",
		"SecretAccessKey": "G7/6S/ptMKPGsuJAIC80z5agASDKWSadlqSW",
		"SessionToken": "FwoGZSIRvFKLKWMD/////////AfdaojaefdaslfglajdfakjfadseFLNELKXJZVLAsljfgapkadasasdasdgdsFGJDFOIAEJFVIKEfsgkdne45dla",
		"Expiration': "2020-11-16T15:22:48+09:00"
		}
}

 

보시는 것처럼 해당 정보를 1시간 혹은, 12시간마다 ~/.aws/credentials에 등록해주기는 너무나 번거롭습니다..ㅡ3ㅡ

해서..!! 이 번거로운 작업을 Python 코드로 자동화 하겠습니다!!

 

  • 우선 Python을 설치합니다. 이미 Python이 설치 된 사용자는 해당 과정을 건너띄셔도 무방합니다.
[root@localhost ~]# yum install python3

 

  • 이 후, 해당 코드를 작성 후, 소유권을 700으로 하여 실행권한을 줍니다.
[root@localhost ~]# vi ./aws_mfa.py
#!/usr/bin/python3
import sys
import configparser
import json
import os
from os.path import expanduser

if(len(sys.argv) <= 1 ):
    exit("Need named profile")


home = expanduser("~")
requestedProfile = sys.argv[1]
awsConfig = configparser.ConfigParser()
awsCred   = configparser.ConfigParser()

awsConfig.read("%s/.aws/config" % home)
awsCred.read('%s/.aws/credentials' % home)

try:
    mfaARN = awsConfig[awsConfig["profile " + requestedProfile]['source_profile']]['mfa_serial']
except KeyError:
    try:
        mfaARN = awsConfig['default']['mfa_serial']
    except KeyError:
        exit("Need MFA serial in config file")

profiles = set( awsCred.sections())
configprofiles = set( awsConfig.sections())

if( requestedProfile in profiles and "profile " + requestedProfile in configprofiles):
    print("Updating %s profile" % requestedProfile)
else:
    if( "profile " + requestedProfile in configprofiles):
        print("Creating %s credentials profile" % requestedProfile)
        awsCred.add_section(requestedProfile)
    else:
        exit("No such profile \"%s\" in config" % requestedProfile )

try:
    OneTimeNumber = int(input("OTP from device: "))
except ValueError:
    exit("OTP must be a number")


response = os.popen("aws --profile %s sts get-session-token --serial-number  %s --token-code %s" % ( awsConfig["profile " + requestedProfile]['source_profile'],
                                                                                                 mfaARN,
                                                                                                 str(OneTimeNumber).zfill(6))).read()

try:
    myjson = json.loads(response)
except json.decoder.JSONDecodeError:
    exit("AWS was not happy with that one")

awsCred[requestedProfile]['aws_access_key_id']     = myjson['Credentials']['AccessKeyId']
awsCred[requestedProfile]['aws_secret_access_key'] = myjson['Credentials']['SecretAccessKey']
awsCred[requestedProfile]['aws_session_token']     = myjson['Credentials']['SessionToken']

with open('%s/.aws/credentials' % home, 'w') as awsCredfile:
    awsCred.write(awsCredfile)

 

[root@localhost ~]# chmod 700 ./aws_mfa.py

 

 

  • 이제 해당 Python Code를 실행하면서 임시세션토큰을 발급 할 사용자만 적어주면 됩니다!! (예제에서는 test)
[root@localhost ~]# ./aws_mfa.py test

 

  • ~/.aws/credentials 내용을 살펴보면 해당 임시세션토큰이 자동으로 등록되어 있습니다.
[root@localhost ~]# cat ~/.aws/credentials

[testAccount]
aws_access_key_id = AKIAWJGUM5H4ROSNQVKJ
aws_secret_access_key = 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

[test]
aws_access_key_id = ASIAWJGUM5H47E4ALGBL
aws_secret_access_key = "G7/6S/ptMKPGsuJAIC80z5agASDKWSadlqSW
aws_session_token = FwoGZSIRvFKLKWMD/////////AfdaojaefdaslfglajdfakjfadseFLNELKXJZVLAsljfga

 

 

이렇게 Python Code를 이용하여 AWS CLI 환경에서 임시세션토큰을 자동으로 등록하는 방법을 알아봤습니다.

물론, 방법은 너무나 많습니다. 이 방법만이 정답은 아니고, 환경변수를 등록하여 해결하는 방법도 있고,

많은 방법들이 존재합니다.

 

 

IT는 구글링과 구걸링이라는 말이 있듯이, 하나의 질문으로 구글링과 구걸링을 해보시면 무수히 많은

솔루션을 얻으실 수 있습니다. 제가 기재해드린 방법 또한 하나의 솔루션일 뿐 정답은 아니라는 점 기억해주시고,

더 좋은 방법 더 심플한 방법이 있다면 제가 구걸하겠습니다 ^^ 많은 IT 선배님들의 조언과

질문 부탁드립니다!!^^