MongoDB TLS 구성 - 계정 접근 IP 제한 설정 - customData - authenticationRestrictions

Share

Last Updated on 2월 2, 2024 by Jade(정현호)

안녕하세요

이번 글에서는 MongoDB접속시 TLS(SSL) 방식으로 접속하기 위한 MongoDB 서버에서의 구성에 관한 내용과 Client 접속 방법에 대한 내용과 계정에 설정할 수 있는 authenticationRestrictions 과 customData 기능에 대해서 확인해보도록 하겠습니다.

MongoDB TLS(SSL)

MongoDB를 사용하면서 TLS(SSL) 방식으로 접속하기 위해서는 MongoDB에서 먼저 구성(설정)이 되어있어야 Client에서 TLS 방식으로 접속할 수 있습니다. 

기본적으로 설치후에는 관련된 구성이나 config가 설정 되어있지는 않습니다. 최근 RDBMS에서도 접속 시 Non-SSL에서 TLS 방식의 접속이 많이 권장 구성됨에 따라 MongoDB도 TLS 접속 방식으로 사용이 필요할 것으로 생각되며, 유사 클라우드 관리형 서비스인 DocumentDB는 기본 설정이 TLS 사용인만큼 앞으로 데이터베이스(NoSQL 포함)에 접속 시 TLS(SSL) 방식 사용이 더 보편화 될 수 있습니다.
            

Server 구성

MongoDB Server에서 TLS 관한 구성 및 설정은 크게 키 생성conf 파일 설정으로 나눌 수 있습니다.

가장 정석적으로 사용하기 위해서 웹사이트의 TLS 사용을 위해 TLS/SSL 인증서를 구매해서 사용하는 것처럼 믿을 수 있는 인증기관에서 인증서 키를 발급받아 사용해야 합니다. 다만 DB 또는 데이터 저장소 또는 내부 네트워크망 내에서 통신에서의 TLS/SSL 사용은 내부에서 생성하는 사설 인증서(자가 서명)도 많이 사용됩니다.

이렇게 자기 스스로 인증서에 서명하여 최상위 인증서를 만든 것에 대해서 Self signed Certificate(SSC) 이라고 합니다.

포스팅에서는 사설 인증서를 생성하여 진행하도록 하겠으며 인증서 유효기간은 50년으로 생성하겠습니다.

# MongoData DIR
$ cd $DATADIR


# 생성
$ openssl req -newkey rsa:2048 -new -x509 -days 18250 -nodes -out mongodb-cert.crt -keyout mongodb-cert.key
Country Name (2 letter code) [XX]:KR
State or Province Name (full name) []:
Locality Name (eg, city) [Default City]:  Seoul 
Organization Name (eg, company) [Default Company Ltd]:회사명
Organizational Unit Name (eg, section) []:DBTeam
Common Name (eg, your name or your server's hostname) []:회사도메인
Email Address []:


# pem 생성
$ cat mongodb-cert.key mongodb-cert.crt > mongodb.pem


글에서는 생성 위치를 MongoData 디렉토리에서 진행하였으며 mongod.conf 에서의 파일 위치도 해당 위치를 지정하였습니다.
인증서 생성시 입력 항목은 위의 내용을 참조하여 대략적으로 입력하면 됩니다.

mongod.conf 파일에서는 network interfaces인 net: 항목에서 내용을 추가해서 설정을 진행합니다.

# network interfaces
net:
  port: 27017
  bindIp: 0.0.0.0
  tls:
    mode: requireTLS
    certificateKeyFile: /home/mongod/data/mongodb.pem
    allowConnectionsWithoutCertificates: false
    allowConnectionsWithoutCertificates: true
    allowInvalidCertificates : true
    allowInvalidHostnames : true


각 옵션은 다음과 같은 내용입니다.

  • mode : 모든 네트워크 연결시에 TLS 사용에 대한 설정이며, requireTLS 는 서버가 TLS 암호화 연결만 사용하고 허용하게 함으로 TLS 사용을 강제화 합니다.
  • allowConnectionsWithoutCertificates : default값은 TRUE이며, 인증서를 제시하지 않는 클라이언트에 대해 클라이언트 인증서 유효성 검사를 우회를 결정하는 옵션입니다.
  • allowInvalidCertificates : 클러스터의 다른 서버에서 TLS 인증서에 대한 유효성 검사를 활성화 또는 비활성화하고 잘못된 인증서를 사용하여 연결할 수 있도록 허용합니다. 클러스터나 ReplicaSet에서의 서버 간의 접속에 대한 내용입니다.
    클라이언트에서는 명시적으로 옵션을 지정하여 사용합니다.
  • allowInvalidHostnames : MongoDB는 TLS 인증서의 호스트 이름 유효성 검사를 비활성화하여 인증서의 호스트 이름이 지정된 호스트 이름과 일치하지 않는 경우에도 mongod가 MongoDB 인스턴스에 연결할 수 있도록 허용합니다.
    인증서 발급 시 도메인이 아닌 다른 도메인인 예를 들어 cname record 로 접속시에 발급한 도메인과 다르기 때문에 에러가 발생할 수 있습니다. 이런 경우 필요한 옵션입니다.


설정이 완료되었다면 MongoDB를 재시작합니다.
            

Client 에서 TLS 사용

TLS가 서버에서 requireTLS 모드로 활성화가 되었다면 TLS 사용이 강제화가 되었기 때문에 TLS 관련 필요 옵션을 반드시 사용해야 합니다.

기본 설정은 클라이언트에서 인증서를 제시하지 않아도 유효성 검사의 우회이며 그렇기 때문에 다음과 같이 옵션을 사용하여 접속할 수 있습니다.

mongosh --tls --host 아이피:27017 \
--tlsAllowInvalidCertificates \
--tlsAllowInvalidHostnames \
--authenticationDatabase=admin \
--username admin --password

or

mongosh "mongodb://admin@아이피:27017/admin?
&tls=true
&tlsCAFile=mongodb.pem
&tlsAllowInvalidCertificates=true
&tlsAllowInvalidHostnames=true" --authenticationDatabase "admin"
* 가로 길이에 따라 & 기준으로 임의 개행되어있습니다.
* 실제로 사용시 한줄로 이어서 사용해야합니다.


서버에서 생성한 인증서 키를 client 측에서 전달해서 사용할 수 있다면 다음과 같이 옵션을 지정하여 사용할 수 있습니다.

mongosh --tls --host 아이피:27017 \
--tlsCertificateKeyFile mongodb.pem \
--tlsCAFile mongodb.pem \
--tlsAllowInvalidHostnames \
--authenticationDatabase=admin \
--username admin --password

or

mongosh "mongodb://admin@아이피:27017/admin?
&tls=true
&tlsCertificateKeyFile=mongodb.pem
&tlsCAFile=mongodb.pem
&tlsAllowInvalidHostnames=true" --authenticationDatabase "admin"
* 가로 길이에 따라 & 기준으로 임의 개행되어있습니다.
* 실제로 사용시 한줄로 이어서 사용해야합니다.



파이썬에서는 pymongo 를 통해서 접속해서 사용할 수 있고 다음과 같이 client 정보에서 옵션을 추가해서 사용하시면 됩니다.

import pymongo

client = pymongo.MongoClient(
    host='아이피',
    port=27017,
    tls=True,
    tlsAllowInvalidHostnames=True,
    tlsAllowInvalidCertificates=True,
    authSource='admin',
    username='아이디',
    password='패스워드'
)

db = client["test"]
collection = db["testcol"]

for x in collection.find():
  print(x)


실행결과)
$ python3 py_mongo_test.py
{'_id': ObjectId('65a15f0220d1af7b7e45f936'), 'key1': 'value1'}



다만 Java MongoDB Driver 에서는 tlsAllowInvalidCertificates 이 없으며 서버로부터 키를 받아 Key Store에 등록이 필요합니다.

  • The JVM Trust Store and JVM Key Store
  • A Client-Specific Trust Store and Key Store


관련된 내용은 다음의 문서를 참조하시면 됩니다.

              

계정 속성

MongoDB 계정에는 몇 가지 설정 수 있는 필드(속성 또는 옵션)이 있습니다. 그 중에서 이번 글에서는 authenticationRestrictions 와 customData 그리고 이런 속성을 변경할 수 있는 db.updateUser 를 확인해보도록 하겠습니다.
            

authenticationRestrictions

authenticationRestrictions 필드는 서버가 생성된 사용자에게 적용하는 인증 제한입니다. 사용자가 서버에 연결하도록 허용되거나 서버가 사용자를 허용할 수 있는 IP 주소 및 CIDR 범위 목록을 지정합니다. 즉, IP ACL (Access Control List) 기능을 설정하여 사용할 수 있는 기능입니다.

authenticationRestricts 에는 clientSourceserverAddress 필드만 포함할 수 있으며, 두 필드의 의미는 다음과 같습니다.

  • clientSource: 사용자를 인증할 때, 서버는 클라이언트의 IP 주소가 설정 목록에 있거나 목록의 CIDR 범위에 속하는지 확인합니다.
    클라이언트의 IP 주소가 설정 목록내 존재하지 않으면, 서버는 사용자를 인증하지 않습니다. (접속 불가)
  • serverAddress : 클라이언트가 연결할 수 있는 IP 주소 또는 CIDR 범위의 목록입니다.
    서버는 지정된 목록의 IP 주소 서버에 접속하였는지를 확인합니다.
    예를 들어, 샤드 클러스터나 Replica Set 구성에서 serverAddress를 설정하여 특정 서버에만 특정 사용자의 접근을 허용할 수 있습니다. 사용자가 특정 서버에만 접속해서 조회 업무 등을 하는 것과 같은 구성에 사용할 수 있습니다.


authenticationRestricts는 계정 생성시 또는 계정 설정 변경(update)시 설정할 수 있습니다.

> use admin
> db.createUser(
   {
     user: "restricted",
     pwd: passwordPrompt(),      // Or  "<cleartext password>"
     roles: [ { role: "readWrite", db: "test" } ],
     authenticationRestrictions: [ {
        clientSource: ["192.0.2.0/32"],
        serverAddress: ["198.51.100.5"]
     } ]
   }
)


위의 예시는 restricted 계정명으로 계정 생성시 Client Source IP를 192.0.2.0/32(Class) 로 제한하며, 접속할 수 있는 서버 중에서 192.51.100.0.5 에만 접속할 수 있음을 설정한 예시입니다.

MySQL에서는 이전부터 사용된 IP@호스트 조합의 계정 유형을 사용함으로 기본적으로 IP ACL 기능이 제공된다고 할 수 있으며, MongoDB 에서의 authenticationRestrictions 도 이와 유사한 형태로 사용할 수 있다고 생각하면 될 것 같으며, 추가로 접속할 수 있는 서버의 IP까지 제한의 기능도 있다고 생각하면 될 것 같습니다.

기존에 생성된 계정을 업데이트하기 위해서는 다음과 같이 db.updateUser를 사용합니다.

> use admin
> db.updateUser("restricted",
{
   authenticationRestrictions : [ {
    clientSource: ["192.168.56.0/24"]
   } ]
}
)


admin 데이터베이스는 계정의 인증 데이터베이스(authentication database)가 admin이기 때문에 admin에서 진행한 것이며, 인증 데이터베이스(authentication database)가 다른 스키마(데이터베이스)라면 해당 데이터베이스에서 진행하시면 됩니다.

인증 데이터베이스(authentication database)에 관한 내용은 다음 포스팅을 확인해보시면 됩니다.


참고로 여러 대역대 또는 여러 IP를 지정하려면 다음과 예시 형태로 지정합니다.

> use admin

> db.updateUser("유저명",
{authenticationRestrictions : [ {
   clientSource: ["192.168.56.0/24","127.0.0.1","10.200.0.0/16"]
   } ]
})



db.updateUser 를 수행 후 조회하면 다음과 같이 계정 authenticationRestrictions 정보가 변경된 것을 확인할 수 있습니다.

> db.system.users.find({"user":"restricted"})
[
  {
    _id: 'admin.restricted',
    userId: new UUID("f910ef21-92ad-4589-9647-b4906bd59821"),
    user: 'restricted',
    db: 'admin',
        <..중략..>
      }
    },
    authenticationRestrictions: [ { clientSource: [ '192.168.56.0/24' ] } ],
    roles: [ { role: 'readWrite', db: 'test' } ]
  }
]


설정된 Source IP가 아닌 다른 IP로 접속 시도 시 다음과 에러 메세지와 함께 접속이 실패하게 됩니다.

MongoServerError: Authentication failed.
               

customData

customData는 관리자가 특정 사용자에 대해서 여러가지 데이터를 별도 기록할 수 있는 기능입니다. 계정 생성시 또는 계정 정보 변경 시 지정할 수 있습니다.

계정 생성시 다음과 같이 생성하여 정보를 입력할 수 있습니다.

## 계정 생성
> use admin
> db.createUser({
  user: "memo",
  pwd: passwordPrompt(), 
  customData : {"memo": "테스트 유저입니다."},
  roles: [
      { role: 'readWrite', db: 'test' } ]
}
)


## 계정 조회
> db.system.users.find({"user":"memo"})
[
  {
    _id: 'admin.memo',
    userId: new UUID("b54a2739-8047-4f7e-b034-4dbf102dc2b3"),
    user: 'memo',
    db: 'admin',
    <..중략..>
    customData: { memo: '테스트 유저입니다.' },
    roles: [ { role: 'readWrite', db: 'test' } ]
  }
]


customData 필드는 타입이 document입니다. 그래서 위와 같이 document 형태로 입력해야 합니다.
일반 문자열인 String 형태로 입력시에는 에러가 발생합니다.

db.updateUser 명령어를 통해서 customData 추가하거나 변경할 수 있습니다. 계정생성시 customData 필드를 사용하지 않았더라도 db.updateUser 를 통해서 추가할 수 있습니다.

## updateUser 수행
> use admin
> db.updateUser(
   "memo",
  {customData : {"name": "메모유저", "team": "메모장", "work": "메모장 개발업무", "create_date": "2024-01-01 09:00:00"}}
)

## 계정 조회
> db.system.users.find({"user":"memo"})
[
  {
    _id: 'admin.memo',
    userId: new UUID("b54a2739-8047-4f7e-b034-4dbf102dc2b3"),
    user: 'memo',
    db: 'admin',
    <..중략..>
    customData: {
      name: '메모유저',
      team: '메모장',
      work: '메모장 개발업무',
      create_date: '2024-01-01 09:00:00'
    },
    roles: [ { role: 'readWrite', db: 'test' } ]
  }
]


계정에 이러한 부가적인 관리 정보를 추가할 수 있는 기능은 MySQL에서도 유사하게 존재하며 이러한 기능을 통해서 계정 관리 시 유용하게 사용할 수 있습니다.


이렇게 입력된 정보는 다양하게 사용할 수 있을 것이라고 예상되며 사용하기 위해서 먼저 조회를 해야 하는데 JSON 형태의 데이터를 Shell 에서는 처리나 다루기가 복잡할 수 있고 프로그램 언어에서 조회하거나 처리하는 게 조금 더 수월할 것 같습니다.

파이썬에서 조회를 하면 다음과 코드를 코드를 통해서 system.users 컬렉션의 여러 데이터에서 필요한 customData 를 추출하여 사용할 수 있습니다.

import pymongo

client = pymongo.MongoClient(
    host='아이피',
    port=27017,
    tls=True,
    tlsAllowInvalidHostnames=True,
    tlsAllowInvalidCertificates=True,
    authSource='admin',
    username='아이디',
    password='패스워드'
)

db = client["admin"]
collection = db['system.users']

for customData in collection.find({'customData': {'$exists': True}},{'_id': 0,'user':1, 'customData': 1}):
    print(f"{customData['user']} {customData['customData']['create_date']}")


실행결과)
restricted 2024-01-02 09:00:00
memo 2024-01-01 09:00:00

이와 같이 계정에 필요한 별도 정보를 통해 계정 관리를 하는데 조금 더 유용하게 사용할 수 있을 것이라고 예상해봅니다.

이번 글에서는 MongoDB에서 TLS(SSL) 설정과 계정 관련 기능인 authenticationRestrictions 와 customData를 알아보았으며 여기에서 마무리하도록 하겠습니다.

긴 글 읽어 주셔서 감사합니다.
              

Reference

Reference URL
mongodb.com/configure-ssl
mongodb.com/allowConnectionsWithoutCertificates
db.createUser() — MongoDB Manual
mongodb.com/change-own-password-and-custom-data

연관된 다른 글

 

 

 

 

              

0
글에 대한 당신의 생각을 기다립니다. 댓글 의견 주세요!x