AWS Parameter Store로 SpringBoot 프로퍼티 값 암호화해서 관리하기
적용 배경
DB password 등을 관리하는 application.yml 파일을 Github에 그대로 올리게 된다면 보안 상 문제가 될 수 있다.
이러한 문제를 해결하고 application.yml 값을 안전하게 다루기 위해 사람들은 다양한 방식을 적용한다.
현재 왓캠퍼스 프로젝트는 빠른 배포를 위해서 단순히 application-prod.yml를 Git Actions Secrets을 통해 관리하고 있었다.
그리고 CD 과정에서 artifact 파일을 생성하기 전, resources 패키지에 해당 파일을 저장해주는 방식을 사용했다.
하지만 그렇게 관리했을 때 특정 프로퍼티 값은 위와 같이 읽을 수 없는 문제가 발생했다.
application-prod.yml 을 통해 다른 로컬에서 작업하려면 직접 해당 파일을 전달해야 한다는 문제도 존재했다.
아무튼 이러한 설정으로 인해 구축해놓은 CI/CD 배포 자동 파이프라인이 정상적으로 동작하지 않았고
이로 인해 로컬에서 bootJar를 통해 artifact 파일을 만들어 FTP 프로토콜을 통해 EC2에 파일을 전달 후 수동 배포하는 상황이었다.
과거에는 이러한 문제를 해결하기 위해 과거에는 AWS Cloud에 private 값을 저장해서 사용할 수 있는 AWS SecretsManager를 사용했었다.
하지만 SecretsManager는 유료 서비스이기 때문에 무료로 이용할 수 있는 AWS Parameter Store를 사용하여 문제에 해결책을 제시고자 하였다.
설정 난이도또한 Secrets Manager보다 쉽다고 느껴서 사용해보면 좋을 것 같다고 느꼈다.
Secrets Manager를 사용하는 방법도 궁금하다면 여기서 확인할 수 있다.
적용 방법
1. AWS Parameter Store에 프로퍼티 값 추가
AWS ParameterStore 접속
파라미터 생성 시에 가장 중요한 것 중 하나는 이름이다. 이름은 계층 구조로 표현할 수 있다.
수십 또는 수백 개의 매개변수를 Flat 목록으로 관리하는 것은 전체 값을 다룰 때 시간이 많이 걸릴 수도 있고
매개변수 값이 많아짐에 따라 식별도 어려울 수 있다.
결국 실수로 잘못된 매개변수를 사용하거나 동일한 구성 데이터를 여러 개 만드는 일도 생길 수 있다.
때문에 ParameterStore는 파라미터를 계층으로 관리하여 매개변수를 구성할 수 있도록 만들었다.
/{APP_NAME}/secrets_{PROFILE}/{KEY_NAME}
최대 15개의 레벨 계층을 만들 수 있으며 본인은 위와 같은 형태로 계층을 나눠보았다.
- APP_NAME : 서비스하는 애플리케이션 이름
- PROFILE : 사용할 환경 (ex. prod, dev, test)
- KEY_NAME : 파라미터 값의 구분자
더 자세한 내용은 공식문서에서 확인할 수 있다.
해당 값을 입력했다면 '유형', '값'을 설정하면 끝이다.
'유형'은 3가지 값이 있는데 보안이 필요한 값이라면 보안 문자열을 사용하는 것이 안전하다.
그리고 자세히 보면 조금 생소한 용어들이 있다.
KMS (AWS Key Management Service)
데이터 암호화에 사용되는 암호화 키를 쉽게 생성하고 제어할 수 있는 AWS 서비스이다.
데이터를 보호할 때는 암호화 키를 보호해야한다.
키를 암호화하는 경우 암호화 키도 보호해야 하고, 데이터 보호 계층에서 가장 높은 레벨에 있는 암호화 키(Root Key)를 보호해야 한다.
여기서 AWS KMS가 사용된다.
위와 같은 암호화 구조에서 KMS는 Root Key를 보호한다.
KMS는 AWS 내에서 완전히 생성, 관리, 사용 및 삭제된다.
자세한 내용은 공식문서에서 확인할 수 있다.
SSM (AWS Systems Manager)
AWS에서 제공하는 리소스 및 애플리케이션 관리를 자동화하고 운영 작업을 간소화하기 위한 서비스 중 하나로 Parameter Store 및 다양한 기능을 제공한다.
2. AWS IAM 역할 생성
다음으론 AWS에서 역할을 생성해야한다.
역할은 AWS 서비스나 애플리케이션이 특정 리소스에 접근할 수 있는 권한을 부여하는 IAM 엔티티이다.
지금부터 SSM에 접근 가능한 IAM 역할을 생성하여 서비스의 EC2에 적용할 것이다.
[필수 참고 내용]
현재 개발중인 왓캠퍼스의 CI/CD는 Git Actions에서 동작하고 있으며,
특히 artifact 파일을 EC2에서 실행시키는 CD는 Self Hosted Runner를 통해 EC2에서 직접 동작하고 있다.
때문에 본인은 AWS에 IAM 역할을 적용해주는 것만으로 설정을 마칠 수 있지만, 나와 같은 환경이 아니라면 CD 스크립트에 추가적인 설정이 필요할 수 있다.
Self Hosted Runner에 대한 내용은 여기서 확인할 수 있다.
IAM 역할 생성
IAM 역할 생성 클릭 후 AWS 서비스 엔터티 유형 선택
사용 사례는 EC2를 선택해주면 된다.
SSM(AWS Systems Manager)에 대한 ReadOnlyAccess 권한 정책을 선택
만약 더 많은 권한이 필요하다면 AmazonSSMFullAccess 권한 정책을 선택하면 된다.
이름은 식별이 가능할 정도로만 잘 정해주면 된다.
EC2 역할 설정
다음으로 EC2에 방금 생성한 역할을 적용한다.
적용할 EC2를 선택 - 작업 - 보안 - IAM 역할 수정
방금 생성한 역할을 적용하면 끝!
(Optional) 로컬 환경 세팅
해당 ParameterStore 설정 값을 로컬에서도 이용하려면 IAM을 사용해야한다.
SSM 관련 권한을 가진 IAM 사용자를 생성하고, 로컬에 AWS Configuration 작업을 통해 IAM의 권한을 등록해주어야 한다.
AWS IAM 사용자 생성
이번엔 역할이 아닌 사용자를 생성한다.
이름은 식별이 간편할 정도로만 정해주면 된다.
역할과 똑같이 용도에 맞게 AmazonSSMReadOnlyAccess 또는 AmazonSSMFullAccess 중 하나를 선택하면 된다.
정해졌다면 사용자 생성!
이제 해당 사용자의 보안 자격 증명을 클릭한다.
그리고 csv 파일인 엑세스 키를 발급받으면 된다.
AWS Configuration
이제 흭득한 IAM 사용자 엑세스 키를 활용해서 로컬 환경에 configuration 을 진행할 수 있다.
관련된 내용은 이전에 작성해둔 포스팅에서 확인할 수 있다.
3. SpringBoot 환경 설정
마지막으로 SpringBoot 환경 설정을 통해서 Parameter Store에 등록해놓은 파라미터 값을 사용하기만 하면 된다.
해당 내용은 코프링 프로젝트이기 때문에 build.gradle.kts 로 설정을 수행한다.
Spring Cloud 의존성 추가
dependencies {
implementation(platform("io.awspring.cloud:spring-cloud-aws-dependencies:3.2.0"))
implementation("io.awspring.cloud:spring-cloud-aws-starter-parameter-store")
}
build.gradle.kts 에 'Spring Cloud AWS BOM'과 'Spring Cloud AWS'에서 제공하는 Parameter Store의 의존성을 추가한다.
Spring Cloud AWS BOM 의존성은 그냥 사용해서는 안되고 버전과 함께 사용해야한다.
implementation(platform())은 'BOM(Bill of Meterials)'를 나타내며 BOM은 '자재 명세서' 라는 뜻을 가진다.
BOM에 관련 버전을 명시해줌으로써 spring-cloud-aws에 포함된 의존성을 사용할 때 어떤 버전에 호환되는 의존성을 가져올 지 정의할 수 있다.
이를 통해 Parameter Store 의존성의 버전이 자동으로 맞춰질 수 있는 것이다.
Could not find org.springframework.cloud:spring-cloud-starter-aws-parameter-store-config: Required by: project
만약 해당 설정을 해주지 않는다면 위와 같은 오류가 발생한다.
자신의 스프링부트 버전에 맞는 Spring Cloud AWS의 버전은 Github 문서에서 확인할 수 있다.
내가 사용하는 Spring Boot 버전은 3.3.x 버전이다.
그렇다면 내 프로젝트에 호환되는 Spring Cloud AWS 버전은 3.2.x 버전이며 때문에 3.2.0 버전을 선택해주었다.
application.yml 파일 설정
spring:
config:
activate:
on-profile: prod
import: aws-parameterstore:/whatcampus/secrets_prod/
이젠 application.yml 파일에서 Parameter Store의 환경변수를 끌어다가 사용하기만 하면 된다.
나는 위와 같이 application-prod.yml 에 spring:config:import 를 통해서 파라미터 값을 가져왔다.
'aws-parameterstore:{파라미터_계층}' 을 입력해주면 된다.
나는 굉장히 가볍게 설정했지만 좀 더 자세한 설정이 알고 싶다면 공식 문서를 확인하자
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: ${DB_URL}
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
위 설정이 끝났다면 이제 파라미터 계층에 등록해준 파라미터의 이름을 입력해줌으로써 값을 끌어다 쓸 수 있게 된다.
4. 정상 동작 확인
이 설정을 통해서 로컬에서도 정상적으로 실행됨을 확인할 수 있었고
build되고 artifact 파일이 실행되어 배포가 수행된 것을 확인할 수 있었다.
결론
오늘은 private한 값을 AWS의 서비스 중 하나인 ParameterStore를 통해 무료로 암호화하여 관리하는 방법에 대해서 알아보았다.
최대한 잘 정리해보려고 노력했는데 이 내용이 많은 이들에게 도움이 되었으면 좋겠다.
아래 글들도 참고하기에 충분히 좋은 것 같으니 읽어보면 좋을 것 같다.
- https://gong-story.tistory.com/45
- https://treecode.tistory.com/122
- https://jojoldu.tistory.com/509