이번 포스팅은 AWS SDK 사용 시 Credentials 정보를 통해 AWS Resource를 접근하기 위한 방법에 대해서 작성한다.
일반적으로 Local 환경에서 개발을 진행할 때 AWS Resource를 접근하려면 IAM User를 생성하여 해당 User에 Role을 부여하여 원하는 resource를 접근하는 방법을 많이 사용했었다. 이러한 경우 프로젝트의 설정이나 어딘가에서 access-key, secret-key를 기입하여 해당 정보를 통해서 자격증명을 해야만 관련 resource를 사용이 가능하다.
대부분 이러한 방식으로 Local 환경에서 개발을 할 경우는 문제가 없이 진행이 가능하다. 하지만 Dev, Prod 환경에서도 마찬가지로 IAM User를 생성하고 해당 User의 credentail 정보를 통해서 접근하는 것은 보안상 좋지 않은 방식이다. 일단 프로젝트 코드상에서 이를 선언에서 사용하는 것은 보안에 매우 취약하다고 볼 수 있다. 그렇기 때문에 Vault 등 외부 Property관리 툴을 사용하여 이런 민감한 정보들을 보호하기도 한다. 하지만 작은 프로젝트에서 Vault까지 구축해서 관리하기에는 너무 부담이 될 수도 있다. Credential 정보 하나 때문에 Vault를 구축하는 것도 맞는 방법이지만 투 머 치한 과정일수 있다. 그렇다고 해서 Jenkins나 Docker image를 만들어서 관련 환경변수에 값을 넘겨주는 방식도 어느 정도는 관련 민감정보를 숨길 수 있지만 이 또한 어딘가에서 가로챈다면 문제가 발생할 것이다.
이러한 부분들을 Instance Profile이라는 방식을 사용하면 간단히 해결할수 있다. Instance Profile이 어떤 것인지 찾아보았지만 처음에는 개념적으로 이해가 잘되지 않았다. 리서치를 하다 보니 EC2 Instance에 Instance Profile이라는 정보가 있는 것을 확인했다.
EC2 (또는 다른 Resource)에 직접 Role을 부여해서 내부에서 SDK 사용시 Credential을 IAM Access정보로 하는 것이 아니라 이 Profile에 등록된 Role에 대해서 접근을 하는 방식으로 이해하였다. 해당 방법은 DevOps 분들에게 처음 들었지만 이렇게 관리되는 것이 효과적이라고 생각이 들었다. 그 이유는 요즘 Terraform이든 CloudFormation이든 Iac(Infra as Code)와 같은 인프라를 코드로 형상관리하고 인프라의 변경사항을 관리할 수 있는 도구들이 대세가 되었다. 그렇기 때문에 DevOps에서 사용하는 서비스, 팀, 환경(Dev, Prod 등)에 따라서 매번 IAM User를 만들고 Role을 부여해 주는 방식은 너무나도 복잡할 것으로 예상된다. 이러한 번거로움을 줄이는데 효과적인 방법이라고 생각이 된다.
그럼 이 방법에 대해서는 어느정도 이해가 되었는데 이것을 그럼 SDK에서 어떻게 Load 하는지가 또 미궁에 빠졌다. 대부분 구글링을 통해서 Credential를 사용하는 방법을 찾아보니 다음과 같았다.
- application.properties( 또는 application.yml)에 accesskey, secretKey를 등록하여 @Value annotation을 통해 값을 Load
- Aws**Client에서 Credentail에서 해당 자격증명을 통해 AWSStaticCredentialsProvider로 자격증명
그렇기 때문에 Local과 Dev, Prod환경에 대한 SDK Credentail정보를 Spring Boot의 profiles.active에 따라서 설정을 다르게 Load 하는 방식을 사용하려고 하였다. 그 이유는 로컬에서는 EC2 같은 리소스가 없기 때문에 Instance Profile 자격증명에 대해서 사용이 불가하기 때문이다. 그렇기 때문에 Local에서는 별도로 개발을 위한 IAM User가 필요할 것이다. 그렇기 때문에 이를 코드상에서 구현하려면 구분이 필요했다. 그러던 중에 aws document에서 찾은 내용을 확인하였다.
내용중에 이 부분만 참고해서 보면 될 것 같다.
자격증명은 이 순서대로 확인을 하고있다. 그렇기 때문에 설정에 의해 자동적으로 해당 환경을 참조하여 자격증명을 확인할 수 있다.
그럼 SpringBoot에서 어떻게 설정해야하는지 확인해 보자.
AWSCredentialsProvider의 Interface의 구현체를 찾다 보니 아래와 같은 구현체를 찾을 수 있었다.
DefaultAWSCredentialsProviderChain
내부를 살펴보면 다음과 같이 되어있다.
순서(?)가 설명과는 좀 다르게 되어있는 것 같지만 대체적으로 다음과 같은 순서로 Credential의 환경을 체크하고 있었다. Instance Profile 방식은 보이지 않지만 이 부분은 EC2 ContainerCredentialsProviderWrapper에 정의가 되어있는 것을 확인할 수 있었다.
AWS Docs에서 나온 대로 ECS Conatiner 자격증명을 선행으로 Check 하고 이후 설정이 없는 경우 마지막에 InstanceProfileCredentialsProvider를 통해서 체크하는 코드를 확인할 수 있었다. 그렇기 때문에 프로젝트에 반영할 때는 이 DefaultAWSCredentialsProviderChain을 사용하여 코드를 작성하면 된다.
하지만 여기서 주의해야 할 점이 있다. 해당 구현체를 사용하더라도 환경에 따라서 Load 되는 자격증명이 다르기 때문에 원하는 자격증명을 사용하기 위해서는 중복된 설정이 존재하면 안 된다.
- 중복설정 예시
- InstanceProfile 방식을 사용하려고 했지만 설정 또는 환경 변수에 accesskey를 등록한 경우
- ~/. aws/config 또는 ~/. aws/credentials에 관련 정보가 있는 경우 등
- InstanceProfile 방식을 사용하려고 했지만 설정 또는 환경 변수에 accesskey를 등록한 경우
이러한 경우 환경변수 등에 값이 있다고 판단하기 때문에 InstanceProfile에 대한 자격증명을 사용할 수가 없다. 추가적으로 주의해야 할 점은 만약 ProfileCredentailsProvider를 방식을 사용하려고 이 DefaultAWSCredentialsProviderChain을 설정하였다면 Aws profile의 default profile을 읽어 오기 때문에 다수의 Profile을 설정했을 경우에는 원하는 Profile을 로딩할 수 없다. ( 여기서 Profile은 AWS Client를 통해 설정한 Profile 정보이다.)
정리를 해보면 다음과 같다 Local 환경과 Dev, Prod 환경에서 모두 사용될 코드를 작성해 보면 다음과 같이 진행하면 될 것 같다.
- DefaultAWSCredentialsProviderChain을 코드상에 사용
- Local
- 개발을 위해 application.properies에 accessKey, SecretKey 추가
- 또는 AWS Cli를 통해 profile을 설정하고 credential 정보를 입력(코드상에 없도록)
- application.properties는 local, dev, prod환경에 따라 설정을 분리
- 개발을 위해 application.properies에 accessKey, SecretKey 추가
- Dev, Prod
- application.properties에 별도로 accessKey, SecretKey 등록 X
- DevOps(혹은 본인이 관리한다면 직접)에 EC2에 InstanceProfile에 Role 부여 요청
- Local
이와 같은 방식으로 사용하면 Local, Dev, Prod환경에 따라 코드 분리 없이 사용이 가능하다.
여기서 추가적인 내용으로 InstanceProfile 방식은 EC2에 적용되는 방식이다. 만약에 ECS를 사용하고 있다면 Container기 때문에 TaskRole에 적용을 해야 한다. ECS는 EC2 방식도 있지만 Fargate 방식도 있기 때문에 인스턴스가 없을 수도 있다. 그래서 ECS를 사용한다면 TaskRole을 통해 Role 권한 부여를 진행하면 된다.
점점 더 알아야 할 내용들이 많아지고 있어서 힘이 든다...ㅎㅎ
댓글