Home 인프라를 사용할 때, 민감정보를 어떻게 관리해야 할까?
Post
Cancel

인프라를 사용할 때, 민감정보를 어떻게 관리해야 할까?

1. 글을 작성하게 된 계기


프로젝트를 진행하며 민감한 정보를 어떻게 관리했는지에 대해 정리하기 위해 글을 작성하게 되었습니다.





2. 문제 상황


AWS MongoDB를 사용하는 것은 비용이 부담 되었고, 과거 RDB를 설치형으로 운영한 경험 이 있었기 때문에, 데이터베이스를 직접 설치해 운영 하기로 의사결정이 됐습니다.

  1. 비용 부담
  2. 설치형으로 데이터베이스 관리





이때, Docker Compose를 사용했는데, 민감 정보를 어떻게 관리 할지에 대한 고민이 생겼습니다. 즉, 아래 USERNAME/PASSWORD를 어떻게 관리할지에 대한 고민이 생긴 것이죠.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
services:
  mongodb:
    image: mongo:5.0
    container_name: mongodb
    restart: always
    ports:
      - 27017:27017
    volumes:
      - /home/ec2-user/docker/data:/data/db
      - ./init-mongo.js:/docker-entrypoint-initdb.d/init-mongo.js:ro
    environment:
      - MONGO_INITDB_ROOT_USERNAME=${USERNAME}
      - MONGO_INITDB_ROOT_PASSWORD=${PASSWORD}
      - MONGO_INITDB_DATABASE=mongodb
    env_file:
      - .env





처음에는 인스턴스에 정보를 환경 변수 로 저장하려고 했지만, 이는 너무 위험해 보였습니다. 3자가 인스턴스에 접속하는 순간, 데이터베이스의 정보가 다 넘어갈 수 있으니까요. 따라서 다른 방법을 찾아야 했는데, 다행히 AWS에서는 이런 상황을 대비해 좋은 서비스를 제공하고 있었습니다. 어떻게 문제를 해결했는지 살펴보죠.

이 외에도 암호화와 같은 다양한 방법이 있지만, 이 또한 인스턴스에 접속하는 순간 모두 무효화 됩니다.







3. 해결


해결책은 간단한데, AWS 람다(Lambda)Secret Manager 를 사용하면 됩니다. 참고로 Secret Manager는 AWS에서 사용하는 민감한 정보들을 한 곳에서 중앙집중식으로 관리 할 수 있도록 도와주는 서비스입니다.

AWS Secrets Manager is a secrets management service that helps you protect access to your applications, services, and IT resources. This service enables you to easily rotate, manage, and retrieve database credentials, API keys, and other secrets throughout their lifecycle.





전체 과정은 아래와 같습니다. 1번에서 Secret Manager ID를 조회하는 것은 ID만 조회한 후, 이를 통해 Secret Manager에 저장해둔 민감 정보를 받아오기 위함입니다. 이 과정이 번거롭다면 바로 USERNAME과 PASSWORD를 받아도 괜찮습니다.

  1. 람다에서 Secret Manager ID 호출
  2. Secret Manager ID로 USERNAME/PASSWORD 조회 후 .env에 저장
  3. 쉘 스크립트로 Docker Compose 실행 후 .env 삭제





위 단계를 실행하기 전, 먼저 Docker Compose 파일을 생성합니다. 저는 MongoDB를 사용하기 때문에 다음과 같이 작성했습니다.

1
2
3
4
5
6
7
8
9
10
11
12
services:
  mongodb:
    image: mongo:5.0
    container_name: mongodb
    restart: always
    ports:
      - 27017:27017
    volumes:
      - /home/ec2-user/docker/data:/data/db
      - ./init-mongo.js:/docker-entrypoint-initdb.d/init-mongo.js:ro
    env_file:
      - .env

Secret Manager에 민감정보를 저장하는 과정은 생략하겠습니다.





3-1. Secret Manager ID 조회

먼저 Secret Manager의 ID를 받아오는 람다 함수를 생성합니다. Secret Manager의 ARN은 환경변수로 지정합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import os
import boto3
import json

def lambda_handler(event, context):
    secret_name = os.getenv('ARN')
    client = boto3.client('secretsmanager')
    response = client.get_secret_value(SecretId=secret_name)
    secret = json.loads(response['SecretString'])

    return {
        'statusCode': 200,
        'body': json.dumps(secret_name)
    }





람다를 호출하기 위해서는 IAM 정책이 필요하기 때문에, 아래 내용을 추가합니다.

1
2
3
4
5
6
7
8
9
10
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "secretsmanager:GetSecretValue",
      "Resource": "${ARN}"
    }
  ]
}







3-2. USERNAME/PASSWORD 조회

이제 Secret Manager ID를 통해 MongoDB에서 사용할 USERNAME과 PASSWORD를 받아온 후, .env 파일에 값을 저장해야 합니다. 파이썬 코드를 조금 작성해야 하는데, 이는 다음과 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import boto3
import json

def get_secret_value(secret_name, region_name):
    client = boto3.client('secretsmanager', region_name=region_name)
    try:
        get_secret_value_response = client.get_secret_value(SecretId=secret_name)
        secret = get_secret_value_response['SecretString']
        return json.loads(secret)
    except Exception as ex:
        print(ex.__cause__)
        return None

secret_name = '${SECRET_MANAGER_NAME}'
region_name = '${REGION}'
secret = get_secret_value(secret_name, region_name)







3-3. 쉘 스크립트 실행

마지막으로 쉘 스크립트를 작성한 후, 권한을 부여합니다. .env 파일을 삭제하는 이유는 한 번 실행한 후, 이 파일을 제거해 조금 더 안전하게 운영하기 위함입니다.

1
2
3
4
5
6
7
8
9
10
#!/bin/bash

# AWS Secrets Manager에서 비밀 정보 가져오기
python fetch_secrets.py

# Docker Compose 실행
docker-compose up -d

# .env 파일 삭제
rm .env
1
chmod +x fetch_secrets_and_start.sh





참고로 쉘 스크립트를 실행할 때, 보안에 조금 더 신경 쓰고 싶다면 다음과 같이 사용자/그룹을 지정할 수 있습니다.

1
sudo chown ${USERNAME}:${GROUP} ${SCRIPT_FILE}





이를 실행하면 다음과 같이 Docker Compose가 잘 실행되는 것을 볼 수 있습니다.

1
2
3
4
5
(myprojectenv) [root@ip-${IP} docker]# sh fetch_secrets_and_start.sh
Secrets written to .env file
WARN[0000] /home/ec2-user/docker/docker-compose.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion
[+] Running 1/0
 ✔ Container mongodb  Running                                                                                                                  0.0s





이후 MongoDB에 접속하면 접속이 잘 되는 것을 볼 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@ip-${IP} docker]# docker exec -it 2e0c394be2c3 bash
root@2e0c394be2c3:/# mongo --host localhost --port 27017 -u ${USERNAME} -p ${PASSWORD} --authenticationDatabase admin
MongoDB shell version v5.0.28
connecting to: mongodb://localhost:27017/?authSource=admin&compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("${UUID}") }
MongoDB server version: 5.0.28
================
Warning: the "mongo" shell has been superseded by "mongosh",
which delivers improved usability and compatibility.The "mongo" shell has been deprecated and will be removed in
an upcoming release.
For installation instructions, see
https://docs.mongodb.com/mongodb-shell/install/
================
---
The server generated these startup warnings when booting:
        2024-07-27T19:09:50.209+00:00: Soft rlimits for open file descriptors too low
        2024-07-27T19:09:50.209+00:00:         currentValue: 32768
        2024-07-27T19:09:50.209+00:00:         recommendedMinimum: 64000
---







4. 정리


어떻게 민감한 정보를 안전하게 관리할 수 있는지 살펴보았습니다. 다행히 AWS가 제공하는 좋은 서비스가 있기 때문에 간단하게 문제를 해결할 수 있었는데, 민감정보 관리를 효율적으로 하는 것은 정말 어려운 것 같습니다. 중앙집중식으로, 효율적으로 관리하면서도 보안까지 신경 쓰려면 고려할 것들이 너무 많아요. 여튼, 이 외에도 AWS Systems Manager Parameter Store 와 같은 다른 방법도 있는데, 이는 별도의 포스팅으로 다루도록 하겠습니다.


This post is licensed under CC BY 4.0 by the author.