TLS(Transport Layer Security)를 적용한 MongoDB 접속

arky uhm·2025년 5월 22일

MongoDB

목록 보기
7/14

** 구성 환경
Docker Desktop for Windows (WSL 2 기반, Ubuntu 22.04.4 LTS 환경)
. 단일 MongoDB 인스턴스
. OpenSSL 3.0.13 (자체 서명된 CA 및 TLS 적용)
. MongoDB Compass 1.46.2, mongosh 2.5.0

TLS를 구성하려면 우선 인증서(Certificate)와 개인 키(Private Key)가 필요합니다. 일반적으로 상용 CA(인증 기관)에서 발급받거나, 테스트 및 개발 환경에서는 OpenSSL 등을 이용해 자체 서명된 인증서(Self-Signed Certificate)를 생성할 수 있습니다.


** 운영환경 : 반드시 상용 CA에서 발급받은 인증서를 사용하고, 신뢰할 수 없는 자체 서명 인증서는 보안 경고를 유발하므로 실제 서비스에서는 절대 사용해서는 안됩니다.

** 개발환경 : OpenSSL로 자체 서명 인증서를 만들어서 사용하고 이 방식으로 구성해보겠습니다.


자체 서명 인증서 생성(openssl req -x509)으로 SAN을 넣는 데 문제가 있을 수 있으므로, 인증서 서명 요청(CSR)을 생성하고, 그 CSR을 별도로 자체 CA로 서명하는 방식을 사용하는데 이 방법은 일반적인 CA 구성에서 많이 사용되며, SAN 포함에 더 안정적이므로 CA용 개인키 생성부터 시작하는 방법으로 진행합니다.

MongoDB 서버 인증서를 "공식적으로 보증"해 줄 "인증기관(CA)"을 먼저 정해야 하고, 이 CA가 있어야 나중에 MongoDB 서버의 인증서에 "이 인증서는 믿을 수 있다"는 도장을 찍어줄 수 있습니다. CA를 먼저 만들고, 그 CA의 서명을 통해 서버 인증서를 발급받는 이 과정은 일반적인 기업 환경에서 인증서가 발급되고 사용되는 방식과 매우 유사하며, 특히 SAN과 같은 확장 정보를 안정적으로 포함하는 데 가장 효과적인 방법입니다.

1. ca.key(CA개인키) 생성

  • 무엇을 하는가?: 인증기관(CA)의 "비밀 열쇠"(ca.key)를 만듭니다. 이 열쇠는 CA의 신원을 증명하고, 다른 인증서들을 서명할 때 사용되는 매우 중요한 파일입니다. 2048은 열쇠의 길이(비트 수)를 의미하며, 강력한 보안을 위해 사용됩니다.
  • 왜 먼저 하는가?: CA는 다른 모든 인증서(MongoDB 서버 인증서 등)를 서명하는 가장 상위의 신뢰 주체입니다. 즉, CA가 먼저 자신의 비밀 열쇠를 가지고 있어야, 나중에 "내가 보증한다"는 서명을 해줄 수 있기 때문에 가장 먼저 만들어야 합니다.
# mkdir -p /drives/c/mongodata/certs
# cd /drives/c/mongodata/certs
# openssl genrsa -out ca.key 2048

2. ca.pem(CA인증서) 생성

  • 무엇을 하는가?: 1단계에서 만든 CA의 비밀 열쇠(ca.key)를 사용하여 "인증기관(CA) 자신을 나타내는 인증서"(ca.pem)를 생성합니다. 이 인증서는 CA 자신의 신분증이며, 자기 자신(-x509 옵션)이 직접 서명했기 때문에 "자체 서명된 CA 인증서"라고 부릅니다.
  • 왜 필요한가?: Compass와 같은 클라이언트가 MongoDB 서버에 연결할 때, MongoDB 서버 인증서가 이 ca.pem에 의해 서명되었다는 것을 보고 신뢰하게 됩니다. 클라이언트는 이 ca.pem 파일을 "이 기관이 서명한 인증서는 믿을 수 있다"라고 등록(CAFile 옵션)하는 역할을 합니다.
# openssl req -new -x509 -key ca.key -out ca.pem -days 3650 -subj "/CN=MyRootCA"

3. mongodb.key(개인키) 생성

  • 무엇을 하는가?: 이제 MongoDB 서버의 "비밀 열쇠"(mongodb.key)를 만듭니다. 이 열쇠는 MongoDB 서버의 신원을 증명하고, 나중에 서버 인증서(mongodb.crt)와 묶여 mongodb.pem이 됩니다.
  • 왜 필요한가?: MongoDB 서버도 자신의 신원을 증명하기 위한 "비밀 열쇠"가 필요합니다. 이 열쇠 없이는 서버 인증서를 만들거나 TLS 통신을 할 수 없습니다.
# openssl genrsa -out mongodb.key 2048

4. mongodb.csr(MongoDB서버용 CSR) 생성

  • 별도 SANs((Subject Alternative Names)를 지정하지 않았다면, 인증서는 localhost라는 이름만 자신을 나타내는 유효한 호스트 이름으로 알고 있습니다. mongodb_single이라는 호스트 이름으로 접속하고 싶다면, 인증서에 해당 이름을 명시적으로 포함해야 하므로 OpenSSL 설정 파일(san.cnf)을 생성한다.
  • 무엇을 하는가?: MongoDB 서버의 비밀 열쇠(mongodb.key)를 사용하여 "인증서 서명 요청(CSR)" 파일(mongodb.csr)을 생성합니다. 이 CSR 파일 안에는 서버의 정보(국가, 조직, 호스트 이름 등)와 가장 중요한 Subject Alternative Name (SAN) 정보가 담겨 있습니다.
  • 왜 필요한가?: 앞서 설명했듯이, 자체 서명 방식으로 SAN을 넣는 데 문제가 있었기 때문에, CSR 방식으로 SAN을 정확하게 포함시킨 후, 이 CSR을 CA에 제출하여 서명을 받는 것이 더 안정적입니다. 이 CSR은 마치 "나(MongoDB 서버)를 위한 인증서를 만들어주세요!" 라고 CA에게 보내는 정식 요청서입니다.
# cd /drives/c/mongodata/certs
# vi san.cnf
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
req_extensions = v3_req

[dn]
C = KR
ST = Seoul
L = Seoul
O = MyCompany
OU = MyDept
CN = mongodb_single  # 접속하려는 호스트 이름 지정

[v3_req]
subjectAltName = @alt_names

[alt_names]
DNS.1 = mongodb_single  # Docker Compose 서비스 이름
DNS.2 = localhost  # localhost로도 접속할 수 있도록 추가
IP.1 = 127.0.0.1   # 로컬 IP로도 접속할 수 있도록 추가
# openssl req -new -key mongodb.key -out mongodb.csr -config san.cnf

5. 생성된 CSR의 SAN 확인

# openssl req -in mongodb.csr -text -noout | grep -A 5 "Subject Alternative Name"  
                X509v3 Subject Alternative Name:
                    DNS:mongodb_single, DNS:localhost, IP Address:127.0.0.1 
    Signature Algorithm: sha256WithRSAEncryption
    Signature Value:
        27:3a:5f:ea:ba:b7:51:1d:13:43:77:a8:42:72:dd:9b:96:39:
        3e:89:02:d0:9a:9c:bb:56:77:e8:9a:37:cb:52:83:c0:0e:47:
※ Subject Alternative Name 필드 아래에 DNS:mongodb_single, DNS:localhost, IP Address:127.0.0.1이 보여야 함

6. mongodb.crt(MongoDB 서버 인증서) 발급

  • 무엇을 하는가?: 2단계에서 만든 인증기관(CA)의 인증서(ca.pem)와 비밀 열쇠(ca.key)를 사용하여, 4단계에서 만든 MongoDB 서버의 CSR(mongodb.csr)을 "서명"합니다. 이렇게 서명된 파일이 바로 최종 "MongoDB 서버 인증서"(mongodb.crt)입니다.
  • 왜 필요한가?: 클라이언트는 이 mongodb.crt를 보고 MongoDB 서버의 신원을 확인합니다. CA가 서명했기 때문에, 클라이언트는 ca.pem을 신뢰하면 이 mongodb.crt도 자동으로 신뢰하게 됩니다. 이 단계에서 CSR에 포함된 SAN 정보가 최종 mongodb.crt에 정확히 반영됩니다.
# openssl x509 -req -in mongodb.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out mongodb.crt -days 365 -sha256 -extensions v3_req -extfile san.cnf
※ ca.srl도 동시에 발급됨 

7. 최종 mongodb.crt의 Subject Alternative Name (SAN) 확인

# openssl x509 -in mongodb.crt -text -noout | grep -A 5 "Subject Alternative Name:"
            X509v3 Subject Alternative Name:
                DNS:mongodb_single, DNS:localhost, IP Address:127.0.0.1
            X509v3 Subject Key Identifier:
                C5:64:0F:2E:AD:3F:3E:56:24:27:94:B5:F1:02:22:2E:58:0E:A8:4E
            X509v3 Authority Key Identifier:
                DirName:/CN=MyRootCA 

8. mongodb.pem(개인키+인증서) 생성

  • 무엇을 하는가?: MongoDB 서버의 개인 키(mongodb.key)와 서명된 서버 인증서(mongodb.crt)를 하나의 파일(mongodb.pem)로 합칩니다.
  • 왜 필요한가?: MongoDB는 tlsCertificateKeyFile 옵션으로 mongodb.pem처럼 개인 키와 인증서가 함께 묶인 파일을 사용합니다.
# cat mongodb.key mongodb.crt > mongodb.pem

9. 컨테이너 재기동 : docker-compose_single.yml 파일이 있는 위치에서

# cd /drives/c/mongodata
# docker-compose -f docker-compose_single.yml down -v
# docker-compose -f docker-compose_single.yml up -d

## down -v: 컨테이너뿐만 아니라 관련된 모든 Docker 볼륨까지 제거합니다. 혹시 모를 캐시된 설정이나 남아있는 데이터를 완전히 지우고 새롭게 시작하기 위해 사용하는데, 이전에 생성된 데이터나 로그도 함께 제거될 수 있으니 주의가 필요함

10. admin user 생성

$ mongosh "mongodb://localhost:27027/?tls=true&tlsCAFile=/etc/ssl/mongodb/ca.pem"
% use admin
% db.createUser(
   {
      user: "admin",
      pwd: "1234",
      roles: [
         { role: "userAdminAnyDatabase", db: "admin" }, // 사용자 관리 권한
         { role: "dbAdminAnyDatabase", db: "admin" },   // 모든 DB 관리 권한
         { role: "readWriteAnyDatabase", db: "admin" }  // 모든 DB 읽기/쓰기 권한
      ]
   }
);

11. MongoDB 접속

## Compass 접속 
. Connection URI : mongodb://admin:****@mongodb_single:27031/?tls=true&tlsCAFile=C%3A%5Cmongodata%5Ccerts%5Cca.pem&authSource=admin
  . Edit Connection > Advanced Connection Options
    . Authentication
      . Authentication Method : Username/Password 
      . Username : admin
      . Password : ****
      . Authentication Database : admin
    . TLS/SSL
      . SSL/TLS Connection : On
      . Certificate Authority (.pem) : C:\mongodata\certs\ca.pem 선택

## Mongosh 접속
$ mongosh "mongodb://admin:****@mongodb_single:27027/?tls=true&tlsCAFile=/etc/ssl/mongodb/ca.pem&authSource=admin"


✅ 사용된 yml파일

## docker-compose_single.yml
#version: "3.8"

services:
  mongodb_single:
    image: mongo:latest
    hostname: mongodb_single
    container_name: mongodb_single
    restart: always
    ports:
      - "27031:27027"
    volumes:
      - ./data/mongodb_single:/data/db
      - ./mongod_single.conf:/etc/mongod.conf
      - ./logs/mongodb_single:/var/log/mongodb
      - ./certs:/etc/ssl/mongodb      
    command: ["mongod", "--config", "/etc/mongod.conf"]
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

## mongod_single.conf
storage:
  dbPath: /data/db
systemLog:
  destination: file
  path: /var/log/mongodb/mongod.log
  logAppend: true
net:
  port: 27027
  bindIp: 0.0.0.0
  tls:
    mode: requireTLS
    certificateKeyFile: /etc/ssl/mongodb/mongodb.pem
    CAFile: /etc/ssl/mongodb/ca.pem
    allowConnectionsWithoutCertificates: true  
    ## 서버가 클라이언트로부터 인증서를 받지 않고도 연결을 허용하도록 함(서버만 인증서로 자신을 인증하고 클라이언트 인증서는 요구하지 않는 경우가 일반적)
security:
  authorization: enabled

0개의 댓글