AWS IAM 계정을 보안 모범 사례 기반으로 다루는 방법
AWS 리소스를 관리할 때는 보통 보안 상의 이유로 루트 계정으로 관리하지 않고, 따로 IAM
계정을 생성하고 해당 계정이 사용할 리소스에 대한 여러가지 접근 권한 정책을 부여하여 사용하게 된다.
예를 들면, IAM 계정에 모든 EC2에 대해 읽기 권한만 주고 싶다면 아래의 정책을 생성하여 할당한다.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ec2:Describe*",
"Resource": "*"
}
]
}
그리고 일반적으로, 좀 더 수준 높은 보안을 위해 IAM 계정에 MFA(Multi Factor Authentication)
를 설정하는 것이 권장된다.
이처럼 하나의 AWS 계정 안에서 IAM 계정을 어떻게 하면 효율적으로, 그리고 보안적인 측면에서 문제가 없도록 설정하는 방법을 공유하고자 한다.
루트 계정의 보안
루트 계정은 root
즉, 최고 권한을 가진 계정이다. 우리가 AWS에서 처음 계정을 등록하면 루트 계정을 생성한 것이다.
최고 권한을 가진 만큼 사용하는 것을 지양하는게 중요하고, 그럼에도 사용하게 된다면 보안적인 측면을 고려해야 한다.
MFA 활성화
첫번째는 MFA를 활성화 하는 것이다. AWS에 루트 계정으로 로그인 하고 오른쪽 위에 있는 계정 설정 메뉴에서 Security Crendentials 을 클릭하여 들어가서 적용하면 된다.
그리고 Assign MFA 버튼을 클릭하여 다음 단계로 이동한다.
MFA로 등록할 Device 이름을 정하고 Authenticator app 을 클릭하고 다음 단계로 이동한다.
MFA를 등록할 서드 파티 앱을 설치하고 (일반적으로
Google Authenticator
를 사용) QR코드나 시크릿키를 이용하여 등록하고 나오는 코드 6자리 숫자를 연속으로 두 개 입력하고 등록한다.이렇게 루트 계정에 MFA를 등록하면, AWS 콘솔에 로그인할 때 아이디, 비밀번호 입력 이후에 MFA 코드를 입력하여 로그인하게 된다.
루트 계정의 무분별한 사용 지양
말 그대로, 루트 계정을 무분별하게 사용하지 않는 것이다. 최고 권한을 가진 계정이기 때문이다. 그래서 필요한 권한을 적절하게 가진 IAM 계정을 만들어 사용하는 것이다.
액세스 키 발급 및 사용 지양
액세스 키라고 하는
AWS API
를 호출할 때 사용하는 키를 루트 계정에서는 발급하지 않는게 권장된다. 필요에 의해서 발급했다면 필요한 작업을 마친 이후에는 즉시 삭제해야 한다.루트 액세스 키가 탈취된다면 탈취한 사람은 내 계정에 AWS 내의 모든 리소스를 자기 마음대로 프로비저닝 할 수 있게 되니 조심해서 사용을 해야 한다.
IAM 계정 할당 (with IAM 계정 그룹)
IAM 계정을 할당할 때는 콘솔 액세스
와 프로그래밍 방식 액세스
를 권한을 할당할 수 있다. 기본적으로, 프로그래밍 방식 액세스가 할당이 되고 콘솔 액세스의 경우에는 선택사항이다.
콘솔 액세스를 허용하게 되면 우리가 루트 계정을 로그인 하듯이 IAM 계정의 아이디, 비밀번호로 AWS 콘솔에 접근하게 된다. 단, 어떤 루트 계정의 IAM 계정인지 식별하기 위해 루트 계정의 계정 ID
를 필요로 한다.
비밀번호 정책
IAM > 계정 설정
메뉴로 들어가게 되면 암호 정책을 사용할 수 있다.기본 값으로 설정되어 있는데 편집 에 들어가서 패스워드 정책, 만료일, 재사용 제한 등의 다양한 설정이 가능하다.
최소한 8자리 이상, 영대소문자, 특수문자에 대한 정책 정도는 설정해두는 것이 좋다.
MFA 활성화
IAM > 사용자 > 보안 자격 증명
메뉴에서 각각의 IAM 사용자에 대해 MFA를 활성화할 수 있다. 앞서 설명한 루트 계정에 MFA를 설정하는 것과 똑같다.정책(Policy)를 활용하여 최소 권한 부여
IAM 사용자는 AWS 리소스에 대한 권한을 부여 받게 되는데, 이는 정책(Policy)을 통해서 받게 된다. 정책을 부여할 땐 리소스에 대한 권한을 최소한으로 부여해서 사용해야 한다.
정책을 생성할 수 있는 방법은 AWS 콘솔에서 제공하는
시각적 편집기
을 통해 생성하거나,JSON
형태로 된 정책 내용을 넣어주는 방법, 두가지를 제공한다.AWS 리소스에 어떤 권한을 부여해야 할지 모르겠다면,
시각적 편집기
를 이용하면 된다.A라는 엔지니어에게
example
이라는 S3 버킷안에 있는 모든 객체(Object)의 읽기 권한을 부여해준다고 가정했을 때아래의 정책과 같이 권한을 부여해줄 수 있다.
IAM > 정책 > 정책 생성
메뉴로 들어가 시각적 편집기를 클릭한다.서비스
에서 S3를 검색하여 클릭한다.작업
에서 액세스 레벨 > 읽기 > GetObject를 클릭한다.GetObject는 S3 버킷 안에 있는 객체를 읽을 수 있는 권한이다.
리소스
에서 ARN 추가를 클릭한다.여기서 최소한의 권한 부여를 위해 특정 버킷을 지정하고, 모든 오브젝트에 접근하도록 설정한다.
좀 더 최소한의 권한을 지정하려면
Object name
에 특정 오브젝트를 지정하는 것이 맞겠지만, 이렇게 되면 관리가 너무 힘들어 지므로, 특정 버킷을 지정하는 정도로만 설정한다.추가로, 요청 조건에 MFA와 소스 IP를 지정할 수 있다. 이것을 지정하면 해당 정책을 가진 IAM 사용자의 경우에는 MFA 인증을 제공해야 되고, 지정된 소스 IP에서 요청해야만 리소스 접근이 가능하다.
위에서 시각적 편집기로 설정한 정책은 아래의 json 내용으로 간단하게 정리할 수 있다.
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::example/*" } ] }
IAM 그룹 활용
만약 100개의 IAM 사용자가 있다고 가정했을 때, 만들어 놓은 정책을 100개에다가 일일이 할당하기란 사실 쉽지 않다.
그럴 때
IAM 그룹
을 이용하여 같은 권한을 부여할 IAM 사용자를 그룹핑하고, 쉽게 정책 할당이 가능하다.IAM > 사용자 그룹 > 그룹 생성
메뉴로 들어간다.사용자를 그룹에 넣어준다.
위에서 만들어준 정책을 그룹에 할당한다.
IAM 역할 사용
그렇다면, AWS 리소스에서 다른 리소스로 접근할 때는 어떻게 사용하는 것이 좋을까?
예를 들어, AWS EC2에서 S3 버킷에 객체를 업로드해야 하는 애플리케이션 이 있을 수 있다. 이 경우에는 권한을 두 가지 방법으로 할당할 수 있다.
- IAM 계정의 액세스 키 사용
- IAM 역할 사용
액세스 키를 사용하려면 EC2 인스턴스에
AWS-CLI
를 설치하고, 액세스 키를 등록해주어야 한다. 이렇게 액세스 키를 등록하여 사용하는 프로그래밍 방식의 경우에는 키가 노출될 수 있고, 키가 많아지게 되면 관리 하기도 굉장히 어려워 진다.가령, 액세스 키가 탈취되었다고 생각했을 때 액세스 키를 새로 재발급하고 EC2에 다시 액세스 키를 등록해주어야 한다. 이렇게 관리적 측면, 보안적 측면에서도 아쉬운 상황이 발생하게 된다.
그렇기 때문에
IAM 역할
을 사용하는 것이 권장된다. 역할을 사용하게 되면, 키를 관리하지 않아도 되기 때문이다.역할도 IAM 사용자와 마찬가지로 역할을 생성하고 정책을 부여하여 사용하게 되는데 한가지 다른 점은
신뢰할 수 있는 엔터티
를 설정해야 한다는 점이다.쉽게 설명하면, 해당 역할이 어디서 어떤 곳에서 실행이 가능할지 권한을 주는 것이다.
IAM > 역할 > 역할 생성
메뉴로 들어가서 신뢰할 수 있는 엔터티를 설정한다.EC2 인스턴스에서 IAM 역할을 사용하려면
AWS 서비스
를 선택하고사용 사례
에서 EC2를 선택하면 된다.다음으로 넘어가서 역할에 정책을 지정해주고 역할을 생성하면 된다.
그리고 EC2 인스턴스에 IAM 역할을 할당해주면 된다.
이렇게 되면
example
이라고 하는 EC2 인스턴스가example
버킷에 객체를 업로드할 수 있는 권한을 가지게 된 것이다.
IAM 사용자에게 MFA 설정을 강제하는 방법
IAM 사용자의 MFA 활성화
경우에는 기본적으로 각 사용자에게 따로 강제하고 있지는 않다. 때문에, 보안적 측면에서 사용자마다 MFA를 강제하고 자신이 직접 MFA를 제어할 수 있도록 아래의 정책을 설정해야 한다. 단, 다른 IAM 사용자의 MFA를 제어할 수는 없게 한다.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowListActions",
"Effect": "Allow",
"Action": [
"iam:ListUsers",
"iam:ListVirtualMFADevices"
],
"Resource": "*"
},
{
"Sid": "AllowManageOwnPasswords",
"Effect": "Allow",
"Action": [
"iam:ChangePassword",
"iam:GetUser"
],
"Resource": "arn:aws:iam::*:user/${aws:username}"
},
{
"Sid": "AllowIndividualUserToManageTheirOwnMFA",
"Effect": "Allow",
"Action": [
"iam:CreateVirtualMFADevice",
"iam:DeleteVirtualMFADevice",
"iam:ListMFADevices",
"iam:EnableMFADevice",
"iam:ResyncMFADevice"
],
"Resource": [
"arn:aws:iam::*:mfa/${aws:username}",
"arn:aws:iam::*:user/${aws:username}"
]
},
{
"Sid": "AllowIndividualUserToDeactivateOnlyTheirOwnMFAOnlyWhenUsingMFA",
"Effect": "Allow",
"Action": [
"iam:DeactivateMFADevice"
],
"Resource": [
"arn:aws:iam::*:mfa/${aws:username}",
"arn:aws:iam::*:user/${aws:username}"
],
"Condition": {
"Bool": {
"aws:MultiFactorAuthPresent": "true"
}
}
},
{
"Sid": "BlockMostAccessUnlessSignedInWithMFA",
"Effect": "Deny",
"NotAction": [
"iam:CreateVirtualMFADevice",
"iam:EnableMFADevice",
"iam:ListMFADevices",
"iam:ListUsers",
"iam:ChangePassword",
"iam:GetUser",
"iam:ListVirtualMFADevices",
"iam:ResyncMFADevice"
],
"Resource": "*",
"Condition": {
"BoolIfExists": {
"aws:MultiFactorAuthPresent": "false"
}
}
}
]
}
Sid
별로 설명하자면,
- AllowListActions: IAM 사용자의 리스트를 조회할 수 있는 권한
- AllowManageOwnPasswords: 자기 자신의 IAM 사용자 정보와 패스워드 변경 권한
- AllowIndividualUserToManageTheirOwnMFA: 자기 자신의 MFA 디바이스 등록 및 활성화, 재설정 권한
- AllowIndividualUserToDeactivateOnlyTheirOwnMFAOnlyWhenUsingMFA: 자기 자신의 MFA를 비활성화 할 수 있는 권한
- BlockMostAccessUnlessSignedInWithMFA:
NotAction
에 포함된 리소스를 제외하고 모든 리소스에 대한 차단
요약하자면, 기본적으로 모든 리소스에 대한 권한을 MFA를 제공하지 않으면 차단하고 자기 자신의 IAM 계정에 대한 비밀번호 변경, MFA 제어 권한을 부여한 IAM 정책이다.
해당 정책을 부여한 사용자로 로그인을 한 뒤에 MFA 설정을 하지 않고 AWS 콘솔에 접속하면 정책 설정대로 아래와 같이 모든 리소스에 대한 접근 권한이 차단된다.
하지만, MFA 디바이스를 등록하는 부분은 액세스가 가능하다.
MFA 강제 설정 이후에 프로그래밍 방식 접근
AWS 콘솔은 로그인 시에 MFA를 요구하기 때문에, MFA 인증 코드를 입력하여 로그인하여 다른 리소스에 접근할 수 있는 권한을 획득하면 된다.
하지만, 일반적인 프로그래밍 방식의 경우에는 일반적으로 Access Key
와 Secret Key
만 등록을 하여 사용하게 되는데 이 경우는 MFA 인증을 요구하지 않기 때문에 접근이 불가능하다.
그래서 임시 자격 증명을 획득하고, Session Token
을 이용하여 접근할 수 있는 권한을 할당받아야 한다.
임시 자격 증명을 획득하기 위해 IAM Access Key
를 먼저 등록하기 위해 IAM > 사용자 > 보안 자격 증명 > 액세스 키
메뉴로 가서 액세스 키를 생성한다.
액세스 키와 비밀 액세스 키를 이용하여 아래의 명령어로 로컬 PC의 AWS CLI에 키를 등록한다.
> aws configure
AWS Access Key ID [None]:AKIAXTXDUDJKZX6JFHQX
AWS Secret Access Key [None]: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
Default region name [None]:ap-northeast-2
Default output format [None]:json
액세스 키를 등록했으면, 아래의 명령어를 이용해 등록된 MFA 디바이스의 ARN을 넣고 임시 자격 증명을 받는다.
> aws sts get-session-token \
--serial-number arn:aws:iam::12345678910:mfa/southouse \
--token-code 482412
{
"Credentials": {
"AccessKeyId": "AKIAXTXDUDJKZX6JFHQX",
"SecretAccessKey": "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",
"SessionToken": "IQoJb3JpZ2luX2VjECQaDmFwLW5vcnRoZWFzdC0yIkgwRgIhAOvG6xSPAf7DImMXZdVn4y53oPxNcc0ohvRcrnwadWZVAiEA4FTWE/RCkWenDG6qu+so4Iq5/KVNOuzNWGPJYGcxnh0q+AEInf//////////ARADGgwwMTA0NDc4MDE4NjIiDAeVwiNP28hDFGljgirMAdrG/qtdQVFW4XYdw/lAM2izpIugJYgk8pVXTLzG3VyPdvYgpzhsoGAFySXSKXi27Ttwi360HxR3hq09VPH5JbcZ4v+tbpyFIb2H4djW9YgOxza4QGE0UzpH/7Wg6K1hRME1P5+YN9dQJeNYKRVlfy5bWu9VZGwp6HhutWlisUYERr1guh00+GZBN8sIIMLHUf1xfmQGGus4WSQv1WA8D4mg+U3rPId4WrbRXs3ePwYPCVgFTtnQNQtBrmL8iiib1Sf/VHN958nCmBp+3jDKn+KeBjqXARdXkS77amZHaaQYfHEH0fn0joWJNN5NP5nisrBnciTuccqO0kY+NlLrtgSDreLTqvunqgzytYFjsjGViFXtp3GQzGH6SR8kfGSpJXRlnZkNvm7AVG5N5UijeqtKQ66IRy5hCCoPChWba1MdJpP56NbAl2X2G+55ZBRX+B5bpGoA3zkOxbsy3zhrPsYGmNZvDCFd3/A0G00=",
"Expiration": "2023-01-31T15:49:30+00:00"
}
}
SessionToken
을 ~/.aws/credentials
파일에 넣어준다.
~/.aws/credentials
[default]
aws_access_key_id = AKIAXTXDUDJKZX6JFHQX
aws_secret_access_key = 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
aws_session_token = IQoJb3JpZ2luX2VjECQaDmFwLW5vcnRoZWFzdC0yIkgwRgIhAOvG6xSPAf7DImMXZdVn4y53oPxNcc0ohvRcrnwadWZVAiEA4FTWE/RCkWenDG6qu+so4Iq5/KVNOuzNWGPJYGcxnh0q+AEInf//////////ARADGgwwMTA0NDc4MDE4NjIiDAeVwiNP28hDFGljgirMAdrG/qtdQVFW4XYdw/lAM2izpIugJYgk8pVXTLzG3VyPdvYgpzhsoGAFySXSKXi27Ttwi360HxR3hq09VPH5JbcZ4v+tbpyFIb2H4djW9YgOxza4QGE0UzpH/7Wg6K1hRME1P5+YN9dQJeNYKRVlfy5bWu9VZGwp6HhutWlisUYERr1guh00+GZBN8sIIMLHUf1xfmQGGus4WSQv1WA8D4mg+U3rPId4WrbRXs3ePwYPCVgFTtnQNQtBrmL8iiib1Sf/VHN958nCmBp+3jDKn+KeBjqXARdXkS77amZHaaQYfHEH0fn0joWJNN5NP5nisrBnciTuccqO0kY+NlLrtgSDreLTqvunqgzytYFjsjGViFXtp3GQzGH6SR8kfGSpJXRlnZkNvm7AVG5N5UijeqtKQ66IRy5hCCoPChWba1MdJpP56NbAl2X2G+55ZBRX+B5bpGoA3zkOxbsy3zhrPsYGmNZvDCFd3/A0G00=
이렇게 임시 자격 증명을 획득하고, 리소스에 접근이 가능하게 된다.
임시 자격 증명 자동화
기본적으로 임시 자격 증명은 3시간이 만료 시간으로 지정되어 있다. 때문에, 3시간마다 이 작업을 수행해줘야 한다는 이야기인데 너무 번거로운 작업이다. 그래서 스크립트를 만들어서 관리하도록 한다.
일단, AWS CLI의 profile 중 default
말고, 자기 자신의 profile을 새로 지정하기 위해 ~/.aws/credentials
파일과 ~/.aws/config
파일을 수정한다.
~/.aws/config
[southouseAccount]
mfa_serial = arn:aws:iam::12345678910:mfa/southouse
output = json
[profile southouse]
user_arn = arn:aws:iam::12345678910:user/southouse
source_profile = southouseAccount
region = ap-northeast-2
output = json
~/.aws/credentials
[southouseAccount]
aws_access_key_id = AKIAXTXDUDJKZX6JFHQX
aws_secret_access_key = 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
그리고 아래의 python
스크립트 파일을 저장한다.
aws-configure.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)
마지막으로 파라미터를 profile 사용자 이름인 southouse
를 전달하여 스크립트를 실행한다.
> aws-configure.py southouse
Updating southouse profile
OTP from device: 519793
이렇게 MFA 코드를 입력하고, ~/.aws/credentials
파일을 확인하면 임시 액세스 토큰이 자동으로 등록 되어 있다.
~/.aws/credentials
[southouseAccount]
aws_access_key_id = AKIAXTXDUDJKZX6JFHQX
aws_secret_access_key = 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
[southouse]
aws_access_key_id = AKIAXTXDUDJKZX6JFHQX
aws_secret_access_key = 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
aws_session_token = FwoGZXIvYXdzEDIaDFFvIIXoLCpLpE4XoCKGAQp01nEdz+WUJO9RNNyRiimnNJmYrpmjKQs+dvZKc9FjCmw+lx2c73wKQPfBdGqBodRP35IdmclPPFQ2Ji5w/vLcd6X+gppVrHm8tl/1fj8+P7M7hpiU3BBtc0t+4cnFigJiNr/6Z/qpuOG6CJxF7UodTqccPvAtYLlAmgTIqRHPUR1Qa/46KLTH4Z4GMiizbbQhRDDguMkOsByrt4SJEbrd4l6WMjVBMcM169MwKDq100JAm9F1
profile을 새롭게 등록했기 때문에, AWS CLI 명령을 실행할 때는
--profile southouse
를 파라미터로 넘겨주어야 한다.
> aws ec2 describe-availability-zones --all-availability-zones --profile southouse