AWS Secrets Manager를 사용하여 환경변수를 저장하여 배포 환경(AWS ECS)에서 사용하기

728x90

 

서론

Local에서 개발할 때 Secret을 관리하려고 .env 파일을 사용하곤 한다. 해당 파일을 .gitignore에서 지정해두면 github의 공개 저장소에 올라가지 않기 때문이다. 하지만 개발을 완료한 서비스를 배포할 때는 .env 파일이 배포 서버에 존재하지 않기 때문에 해당 서비스가 정상적으로 작동하지 않는다. 

물론 .env 파일을 함께 배포 서버(AWS EC2 등)에 업로드할 수도 있겠으나 그다지 좋은 방법은 아니다. 누군가가 EC2 인스턴스의 접근할 수 있게 되었다면 .env 내용이 탈취되어 보안 상의 문제가 발생하기 때문이다.

보안 상의 문제를 방지하기 위해 secret을 관리하는 방법에는 여러가지가 있다. 대표적으로 AWS Secrets manager를 사용하는 방법과 Docker Secrets을 사용하는 방법이 있는데 이번 글에서는 AWS Secrets Manager를 사용하는 방법에 대해 알아볼 것이다. 

Spring, AWS ECS, Docker

구현 예시 코드로 Java와 Spring을 사용한다. 배포 환경은 AWS의 ECS를 사용하여 배포한다고 가정한다. ECS 배포를 위해 해당 코드는 docker를 통해 image build하여 docker image 저장소에 업로드 되어있다고 가정한다. 

Spring Java Code


보통 secret을 사용하는 경우는 아래와 같이 특정 외부 서비스를 사용하기 위해 configuration 설정할 때 사용할 것이다. 예시로 Redis cloud에 연결하기 위해 configuration을 설정한다고 가정한다.

RedisConfig.class

@Configuration
public class RedisConfig {
    @Value("${redis.host}")
    public String host;
    @Value("${redis.port}")
    public int port;
    @Value("${redis.username}")
    public String userName;
    @Value("${redis.password}")
    public String password;
    
    
    // --- Sample code of Redis cloud configuartion. You can ignore this. ---
    @Bean
    public RedisConnectionFactory connectToRedisCloud(){
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        redisStandaloneConfiguration.setHostName(host);
        redisStandaloneConfiguration.setPort(port);
        redisStandaloneConfiguration.setUsername(userName);
        redisStandaloneConfiguration.setPassword(password);
        return new LettuceConnectionFactory(redisStandaloneConfiguration);
    }

    @Bean
    public RedisTemplate<Object, Object> redisTemplate() {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectToRedisCloud());
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        return redisTemplate;
    }
    // --- Sample code of Redis cloud configuartion. You can ignore this. ---
}



해당 글에서는 Redis cloud에 연결하여 사용하는 법에 대해서는 다루지 않을 것이다. 그냥 예시 코드일 뿐이므로 아래 코드는 크게 신경쓰지 않아도 된다. 그저 Spring의 Value annotation을 통해 ENV 값을 가져오고 있음을 보여주기 위한 것이다.

application.properties

spring.config.import=optional:file:.env[.properties]

# Redis cloud setting
redis.username=${REDIS_ACCESS_KEY}
redis.password=${REDIS_SECRET_KEY}
redis.host=${REDIS_HOST}
redis.port=${REDIS_PORT}

 



스프링에 ENV 값을 주입하기 위해 application.properties에 위와 같이 작성한다.

.env

REDIS_ACCESS_KEY=
REDIS_SECRET_KEY=
REDIS_HOST=
REDIS_PORT=

 


로컬 코드에서는 애플리케이션을 동작하기 위해 .env 파일 각 변수의 우변에 필요한 secret을 입력하여 구동할 것이다. 하지만 배포 환경에서는 해당 변수값을 넣어주지 못하므로 해당 ENV 값이 존재하지 않아서 애플리케이션이 redis configuration 과정을 실패할 것이다. 이 환경 변수 값을 넣어주기 위해 아래 AWS Secret Manager를 활용해보자.

AWS Secret Manager


AWS에 접속하여 AWS Secrets Manager로 들어간다. (계정이 없다면 생성해주자. 해당 계정은 Serets Manager의 IAM 권한이 있어야 한다.)



우측의 새 보안 암호 저장을 누른다.



암호 유형을 다른 유형의 보안 암호를 선택하고 키 값 페어에 환경변수 값을 작성한다. key와 value는 .env 파일에서 작성했던 key와 value 값을 그대로 넣어준다. 


우변에 secret 내용을 그대로 입력한 후 다음을 누른다.

 



그 후 보안 암호 이름을 임의로 생성하고 다음을 눌러 암호 생성을 완료하면 된다.

보안 암호가 생성이 완료되면 보안 암호 세부 정보에서  보안 암호 ARN이 생성될 것이다. 해당 ARN은 ECS의 Task Definition에서 활용될 것이다.



Docker image build and push


도커를 사용하여 작성한 애플리케이션을 빌드하여 이미지를 만들고 docker hub에 업로드한다. 본 글은 docker에 대한 글이 아니므로 해당 과정은 진행되었다고 가정하고 자세한 내용은 생략한다. 

만약 docker로 이미지를 빌드하고 docker hub에 업로드하는 것이 궁금하다면 아래 글을 참고하길 바란다.

https://bascat-code.tistory.com/4

 

Spring Boot 프로젝트 Docker image를 Docker Hub에 업로드하기

서론 Spring 프로젝트를 도커 이미지로 만들어 클라우드에서 구동하려고 했으나 이에 관련하여 one way로 깔끔하게 정리되어있는 문서를 보지 못한 것 같아서 정리해두려고 한다. 또한 코드를 업데

bascat-code.tistory.com

 

ECS Task Definition


이제 생성한 암호를 활용할 차례다. Elastic Container Service에 들어가서 태스트 정의로 들어간다. 

 



새 태스크 정의 생성을 누른다. 기존에 사용하고 있던 태스크 정의가 있다면 그 태스트 정의를 누르고 새 개정 생성을 눌러도 무방하다.

 




태스크 역할로 ecsTaskExecutionRole을 선택한다. 여기서 중요한 점은 **해당 role은 Secrets Manager에 접근할 수 있는 권한이 있어야 한다는 점이다.** 만약 해당 role에 Secrets manager 접근 권한을 설정하는 방법을 알고 싶다면 아래 문단 "IAM 역할 설정"을 참고하자.

 


컨테이너 세부 정보에서 docker image 주소를 붙여넣는다. dockerhub를 사용하고 있다면 이미지 저장소의 이름을 그대로 붙여넣으면 된다.

AWS Secrets Manager 환경 변수 가져오기


중요한 것은 이 부분이다.



키에는 ENV의 키를 그대로 넣어주고 값 유형은 ValueFrom을 선택한다.

값에는 아래와 같은 형식으로 작성해준다.

 

ARN주소:ENV키::



무슨 말인지 이해가 안 된다면 아래 예시를 확인해보자. JSON으로 Task definition을 보면 아래와 같이 나온다.

 


"arn주소:(AWS Secrets Manager의 Secret key)::" 와 같은 형태로 작성해야 한다. 뒤의 ":(AWS Secrets Manager의 Secret key)::"를 빼먹으면 ECS는 Secret 정보 전체를 가져오게 되므로 ENV값이 제대로 배포 서버에 들어가지 못한다. 반드시 뒷부분에 키를 붙여줘야한다.

그 외에는 특별히 건들 것이 없으므로 생성을 눌러줘서 태스크 정의 생성을 완료한다.

배포 



ECS 클러스터로 들어가 서비스를 생성하여 배포를 실행한다. 배포가 완료되어 로그에 어플리케이션이 정상적으로 작동하는지 확인하자. 

앞서 언급했던 대로 ECS로 배포하는 자세한 방법은 본 글에서는 다루지 않을 것이다. 배포하는 방법이 궁금하다면 아래 글을 참고하자.

 

https://bascat-code.tistory.com/22

 

Docker image를 AWS ECS에 등록하여 서버 띄우기

서론 지난 편에서 Spring boot에서 Docker image를 만들고 Docker image를 Docker Hub에 등록하는 것까지 해봤다. 이번에는 등록한 이미지를 끌어와 ECS에서 구동하는 법을 실습해본다. 해당 실습은 AWS 계정이

bascat-code.tistory.com

 

주의: IAM 역할 설정


만약 ecsTaskExecutionRole에 Secrets Manager에 대한 접근권한이 없다면 ENV를 가져오는 과정은 정상적으로 진행되지 않을 수 있다. 아래와 같이 IAM 역할을 설정하여 ECS가 AWS Secrets Manager에 접근할 수 있게 해주자.

IAM으로 들어가서 역할로 들어간다. (해당 AWS 계정이 IAM 접근, 역할 생성 및 수정 권한이 있어야 한다.)

 



ecsTaskExecutionRole을 클릭하여 권한을 확인한다.

 



Secret Manager 접근 권한이 없다면 별도로 등록해줘야한다. 권한 추가의 인라인 정책 생성을 누른다. 


![업로드중..]

서비스 선택에서 Secrets Manager를 검색하여 선택하고 액세스 수준은 GetSecretValue를 누르면 된다.

리소스 ARN의 ARN추가를 누른다.
![업로드중..]

ARN 지정에서 방금 전 생성했던 Secret의 ARN을 붙여넣고 추가한다.
![업로드중..]

리소스 위치와 리전은 현재 계정과 리전을 포함하게만 하면 된다.

만약 개발 용도 및 테스트 용도라면 아래와 같이 모두를 선택해도 된다. 다만, 모든 secret에 접근하는 권한을 가지므로 실제 배포에 사용되는 계정이라면 이 선택은 하지 않는 것이 좋다.
![업로드중..]

정책 이름을 임의로 입력하고 권한 추가를 완료한다. 아래와 같이 나타날 것이다.

![업로드중..]