Last Updated on 12월 14, 2023 by Jade(정현호)
안녕하세요
이번 포스팅에서는 MongoDB 의 인증 과 계정 그리고 권한에 대한 내용을 살펴보려고 합니다.
Contents
인증(Authentication)
MongoDB 에 접속하기 위해 사용할 수 있는 인증 방식으로는 아래와 같은 종류가 있습니다.
- 커뮤니티 버전에서 사용 가능
- SCRAM(Salted Challenge Response Authentication Mechanism)
- x.509 인증서
- MongoDB Enterprise 에서 사용 가능
- Kerberos Authentication
- LDAP Proxy Authentication
참고) MongoDB Challenge and Response (MONGODB-CR) 는 MongoDB 3.6에서 Deprecated 되었음
SCRAM Authentication 이 MongoDB 에서 기본 인증 방식으로 사용됩니다.
이러한 서버 인증은 내부 인증 과 사용자 인증 으로 나눌 수 있습니다.
내부 인증
내부 인증(Internal authentication)은 클라이언트의 사용자 신원을 확인하는 것 외에도 레플리카 세트 및 샤드 클러스터의 구성원이 해당 레플리카 세트 또는 샤드 클러스터에 대한 구성원 자격을 확인 할 때 사용되는 인증입니다.
내부 인증에서 사용할 수 있는 인증 방식으로는 Keyfile 과 x.509 인증서 방식을 사용할 수 있습니다.
인증을 활성화하려면 MongoDB 서버의 설정 파일에서 인증 관련된 옵션을 활성화해야 합니다.
설치 후 MongoDB의 설정 파일인 /etc/mongod.conf 에서는 기본적으로는 인증 관련된 설정이 별도로 되어 있지 않습니다.
#security:
그래서 MongoDB 설치 후에 별도의 설정 변경을 하지 않았다면 인증 없이 로그인이 될 수 있으므로 인증 설정이 필요 합니다.
$ mongo 192.168.xxx.xxx MongoDB shell version v4.4.16 connecting to: mongodb://192.168.xxx.xxx:27017/test?compressors=disabled&gssapiServiceName=mongodb Implicit session: session { "id" : UUID("xxxxxxxxx-xxxxxx-4bc1-905d-8ca72ee56ecc") } MongoDB server version: 4.4.18 Welcome to the MongoDB shell. For interactive help, type "help". < ... 중략 ... > >
기본적으로 비어 있는 Security 항목에 대해서 주석 해제 및 내용을 아래와 같이 추가합니다.
security: authorization: enabled clusterAuthMode: keyFile keyFile : /etc/mongod.key
authorization: enabled 로 설정하면 MongoDB 서버 간의 통신에서 사용하기 위한 내부 인증과 Application 또는 Client 에서의 사용자 로그인을 위한 인증이 모두 활성화되게 됩니다.
clusterAuthMode 옵션은 클러스터 인증에 사용할 인증 모드로 기본값은 KeyFile 이며, 총 4개 인증모드가 있으며, 보통 keyFile 또는 x.509 인증방식을 선택합니다.
- keyFile : 인증을 위해 키 파일을 사용, 키 파일만 허용합니다.
- x.509 : 인증을 위해 x.509 인증서를 보내고 x.509 인증서만 수락합니다.
mongod 를 통해서 MongoDB를 시작할 경우 다음과 같이 옵션을 사용합니다.
mongod --keyFile /경로/경로/mongod.key --auth --clusterAuthMode keyFile \ ... ...
sendKeyFile 과 sendX509 는 롤링 업그레이드를 목적으로 주로 사용되며 의미는 아래와 같습니다.
- sendKeyFile : 인증을 위해 키 파일을 보내지만 키 파일과 x.509 인증서를 모두 수락할 수 있습니다.
- sendX509 : 인증을 위해 x.509 인증서를 보내지만 키 파일과 x.509 인증서를 모두 수락할 수 있습니다.
clusterAuthMode 기본 값인 KeyFile 을 사용하려면 파일을 생성하고 경로를 지정해야 합니다.
MongoDB 4.2 버전 부터 내부 멤버십 인증을 위한 키 파일은 YAML 형식을 사용하여 키 파일에 여러 키를 허용합니다.
- 단일 키 문자열(이전 버전과 동일)
- Multiple Key Strings(각 문자열은 따옴표로 묶어야 함)
- Multiple Key Sequence
keyFile 를 설정할 때 고려사항은 다음 과 같습니다.
- 키 길이는 6자에서 1024자 사이여야 하며 base64 세트의 문자만 포함할 수 있습니다. 복제 세트의 모든 구성원은 하나 이상의 공통 키를 공유해야 합니다.
- UNIX 시스템에서 키 파일에는 그룹 또는 전체 권한이 없어야 합니다. 즉 그룹 0, other 0 인 400이나 600 또는 700 으로 설정이 필요 합니다. (Windows 시스템에서는 키 파일 권한이 확인하지 않습니다.)
- 플랫폼 간 편의를 위해 공백 문자(예: x0d, x09 및 x20)를 제거하여 인식합니다.
위에서 설명된 플랫폼 간 편의성을 위해서 공백이 제거됨에 따라서, 다음 작업은 결과적으로 동일한 키로 인식되게 됩니다.
echo -e "mysecretkey" > key1 echo -e "my secret key" > key1 echo -e "my secret key\n" > key2 echo -e "my secret key" > key3 echo -e "my\r\nsecret\r\nkey\r\n" > key4
키 파일 생성은 아래와 같은 방법을 사용하여 생성할 수 있습니다.
다음 작업은 openssl을 사용하여 공유 암호로 사용할 복잡한 난수 1024 문자열을 생성합니다.
그런 다음 chmod 및 chown 를 사용하여 파일 소유권 및 권한을 변경합니다.
openssl rand -base64 756 > <path-to-keyfile> chown mongod:mongod <path-to-keyfile> chmod 600 <path-to-keyfile> ls -al <path-to-keyfile>
참고) MongoDB 를 패키지로 생성시 자동적으로 유저 및 그룹을 생성을 해주게 되며, 생성되는 이름은 OS 별로 아래와 같습니다.
- RHEL/CentOS/RHEL 에서는 mongod
- Ubuntu 에서는 mongodb
MongoDB 4.2 버전부터 YAML 형식을 사용하여 키 파일에 여러 키를 허용하며 방식은 Multiple Key Strings 또는 Multiple Key Sequence 를 사용할 수 있습니다.
- Multiple Key Strings 는 각 키 문자열이 따옴표로 묶어서 Multiple Key 를 지정합니다.
- Multiple Key Sequence 는 - 와 같은 일련의 문자열로 구분하여 지정하며 선태적으로 따옴표로 묶음을 사용할 수 있습니다.
Multiple Key Strings 와 Multiple Key Sequence 예시는 아래와 같으며, 아래 키는 예제 설명을 위한 키로 Production 에 사용하지 마십시오
• Multiple Key Strings
'UB2axQJYYiotj4dx+G5BVifi/KrC5pCFVJz6BmxLEdysvHwSMoYQkr949zjDkLsC uPJsJYphOSobXkAvmW04jYoa6BsNU2X5J5zXncr9clj1YFrtGBxLd0yk9gNbcH4r CEQdf9wS+iiCIeE2dokt8YQ+3Ra5rNkV2Culaw3F3nYoNzdAb9tBt64F1bGJGKzA bQVvYjUGXgHNJnxVV5HZamepCwInMbAftBym9b54yQWTq2I0oN1iKxrhmtGqHgiy 2H2Vm70XQHPlFw0FAWk7KOoyut13KPioHmDNy/Spzf/NbZe/tHXcEj/wS38pUKFp P9HtRG7PdnP01l7nkPgxl7Cv4xmOJPjY9U9E0E/YnvnjadFJ' 'UB2axQJYYiotj4dx+G5BVifi/KrC5pCFVJz6BmxLEdysvHwSMoYQkr949zjDkLsC uPJsJYphOSobXkAvmW04jYoa6BsNU2X5J5zXncr9clj1YFrtGBxLd0yk9gNbcH4r CEQdf9wS+iiCIeE2dokt8YQ+3Ra5rNkV2Culaw3F3nYoNzdAb9tBt64F1bGJGKzA bQVvYjUGXgHNJnxVV5HZamepCwInMbAftBym9b54yQWTq2I0oN1iKxrhmtGqHgiy 2H2Vm70XQHPlFw0FAWk7KOoyut13KPioHmDNy/Spzf/NbZe/tHXcEj/wS38pUKFp P9HtRG7PdnP01l7nkPgxl7Cv4xmOJPjY9U9E0E/YnvnjadFJ'
• Multiple Key Sequence
- 'UB2axQJYYiotj4dx+G5BVifi/KrC5pCFVJz6BmxLEdysvHwSMoYQkr949zjDkLsC uPJsJYphOSobXkAvmW04jYoa6BsNU2X5J5zXncr9clj1YFrtGBxLd0yk9gNbcH4r CEQdf9wS+iiCIeE2dokt8YQ+3Ra5rNkV2Culaw3F3nYoNzdAb9tBt64F1bGJGKzA bQVvYjUGXgHNJnxVV5HZamepCwInMbAftBym9b54yQWTq2I0oN1iKxrhmtGqHgiy 2H2Vm70XQHPlFw0FAWk7KOoyut13KPioHmDNy/Spzf/NbZe/tHXcEj/wS38pUKFp P9HtRG7PdnP01l7nkPgxl7Cv4xmOJPjY9U9E0E/YnvnjadFJ' - 'UB2axQJYYiotj4dx+G5BVifi/KrC5pCFVJz6BmxLEdysvHwSMoYQkr949zjDkLsC uPJsJYphOSobXkAvmW04jYoa6BsNU2X5J5zXncr9clj1YFrtGBxLd0yk9gNbcH4r CEQdf9wS+iiCIeE2dokt8YQ+3Ra5rNkV2Culaw3F3nYoNzdAb9tBt64F1bGJGKzA bQVvYjUGXgHNJnxVV5HZamepCwInMbAftBym9b54yQWTq2I0oN1iKxrhmtGqHgiy 2H2Vm70XQHPlFw0FAWk7KOoyut13KPioHmDNy/Spzf/NbZe/tHXcEj/wS38pUKFp P9HtRG7PdnP01l7nkPgxl7Cv4xmOJPjY9U9E0E/YnvnjadFJ'
샤딩 클러스터 사용 환경에서 이와 같이 Multiple Key 를 사용할 경우 구성원 간에 하나 이상의 키가 공통인 경우 구성원 인증이 가능 해집니다.
이를 이용하여 다운타임 없이 키를 롤링 업그레이드할 수 있습니다.
키 파일은 클러스터에 참여한 모든 MongoDB 서버와 MongoDB 라우터 서버가 가지고 있어야 합니다.
사용자 인증
MongoDB 에서 애플리케이션이나 MongoDB Shell 과 같은 클라이언트에서 접속 시 사용자 인증에 대한 내용으로 ID/Password 를 사용하게 됩니다.
사용자를 생성하게 되면 사용자의 자격 증명(user’s credentials) 은 하나의 인증 데이터베이스(authentication database) 에 저장되게 됩니다.
유저 생성시 특정 데이터베이스로 이동(접속) 후에 유저를 생성해야 하며, 이때의 데이터베이스를 인증 데이터베이스(authentication database) 라고 합니다.
유저를 생성한 데이터베이스 이외 다른 데이터베이스에 대해서도 권한을 부여받아서 작업을 할 수 있습니다.
관리자 계정 생성
MongoDB 를 생성하게 되면 관리자 계정이 생성이 되어 있지 않은 상태이며, 일반 사용자도 없는 상태입니다.
인증 없이 익명으로 로그인 한 다음에 제일 처음으로 관리자 계정을 생성을 해야 하며, admin 데이터베이스에서 진행하시면 됩니다.
mongo 명령어를 통해서 접속을 합니다.
$ mongo MongoDB shell version v4.4.18 connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb Implicit session: session { "id" : UUID("xxxxxx-xxxxxx-4cec-9219-2066faba288e") } MongoDB server version: 4.4.18 .. >
접속이 되었다면 아래와 같이 관리자 계정을 생성하시면 되며, 생성시에 패스워드를 입력 받는 방식과, 명령어 라인에 직접 작성하는 방식 2가지로 사용할 수 있습니다.
관리자 계정 생성시에는 admin 데이터베이스에서 진행하시면 됩니다.
> use admin > db.createUser( { user: "admin", pwd: passwordPrompt(), roles: [ "root" ] } ) Enter password: [패스워드 입력] Successfully added user: { "user" : "admin", "roles" : [ "root" ] } ### 또는 > use admin > db.createUser( { user: "admin", pwd: "AdminPassWd", roles: [ "root" ] } ) Successfully added user: { "user" : "admin", "roles" : [ "root" ] }
생성된 계정에 대한 조회는 db.getUsers() 명령어를 통해서 확인할 수 있습니다.
> use admin > db.getUsers() [ { "_id" : "admin.admin", "userId" : UUID("0d48b468-28a9-482f-9adc-3f1a12f48d4b"), "user" : "admin", "db" : "admin", "roles" : [ { "role" : "root", "db" : "admin" } ], "mechanisms" : [ "SCRAM-SHA-1", "SCRAM-SHA-256" ] } ]
패스워드 입력 후 아래와 같은 에러가 발생되었다면 일반적으로 다음과 같은 상황에서 발생할 수 있습니다.
uncaught exception: Error: couldn't add user: not master : _getErrorWithCode@src/mongo/shell/utils.js:25:13 DB.prototype.createUser@src/mongo/shell/db.js:1367:11 @(shell):1:1
- mongod.conf 파일에는 Replica Set이 설정이 되어 있으나 Initiates Replica Set을 하지 않았을 경우
- Replica Set이 구성 되어있으나 mongo 클라이언트(또는 mongosh)이 Primary 노드에 연결되어 있지 않은 경우
- Primary 노드에 연결되어 있더라도 사용자 추가를 위해 인증 정보가 올바르지 않은 경우
- 기타 다른 이유로 Primary 노드가 쓰기 작업을 수행하지 못하는 경우
추가 정보로 유저 삭제 명령어는 db.dropUser() 입니다.
> db.dropUser("유저명")
관리자 계정을 생성하였으며 일반 유저를 생성하기 전에 사용자 인증으로 접속되는지 확인해 보기 위해서 mongod.conf 에서 security 항목을 설정 후에 MongoDB 를 재시작 하고 접속해보도록 하겠습니다.
(security 주석 해제 및 추가 내용 입력)
security: authorization: enabled clusterAuthMode: keyFile keyFile : /etc/mongod.key
* 포스팅에서는 키 파일을 openssl 을 통해서 생성된 상태입니다.
/etc/mongod.conf 파일 수정이 완료되었다면 MongoDB 를 재시작 합니다. MongoDB 재시작이 완료되었다면 아래와 같이 접속을 합니다.
$ mongo -u admin -p MongoDB shell version v4.4.18 Enter password: ## 또는 ## $ mongo -u admin -p --authenticationDatabase admin MongoDB shell version v4.4.18 Enter password:
생성한 키는 클러스터 내 모든 노드에서 동일해야 합니다. 이 키는 Replica Set 또는 샤드 클러스터의 멤버를 인증하는 데 사용됩니다.
--keyFile 옵션을 사용하여 mongod를 시작할 때, Replica Set 또는 샤드 클러스터의 모든 멤버가 동일한 키 파일을 사용해야 합니다.
만약 MongoDB가 시작된 상태로 다른 노드로 키 파일이 복사하였다면 해당 노드의 MongoDB는 재시작이 필요 합니다.
일반 유저 생성
MongoDB 에서 일반 유저를 생성하기 위해서 위에서 설명한 내용 과 같이 특정 데이터베이스로 이동 후 생성해야 합니다.
아래의 예제에서는 testuser 라고 생성을 진행하였으며 인증 데이터베이스(authentication database) 는 test 데이터베이스가 되게 됩니다.
> use test > db.createUser( { user: "testuser", pwd: "testuser123", roles: [ "readWrite" ] } ) Successfully added user: { "user" : "testuser", "roles" : [ "readWrite" ] }
MongoDB 는 계정 생성시 사용되는 데이터베이스, 즉 인증 데이터베이스(authentication database)가 달라지면 다른 계정으로 인식이 되게 됩니다.
testuser 유저 라는 계정 생성시에 tdb1, tdb2, tdb3 과 같은 데이터베이스 별로 동일한 이름의 유저를 생성할 수 있습니다.
> use tdb1 > db.createUser( { user: "testuser", ... > use tdb2 > db.createUser( { user: "testuser", ... > use tdb3 > db.createUser( { user: "testuser", ...
이렇게 3개의 계정을 각각의 데이터베이스 별로 생성할 수 있으며, 각자 마다 다른 비밀번호를 지정할 수 있으며, MongoDB 에서는 이름이 같더라도 서로 다른 계정으로 인식되는 즉 3개의 계정으로 인식합니다.
계정은 생성한 데이터베이스 이외에 권한을 부여하여 다른 데이터베이스를 사용할 수 있습니다. 또는 계정 생성시에도 지정할 수 있습니다.
• 생성시
> use tdb1 > db.createUser( { user: "testuser", pwd: "testuser123", roles: [ "readWrite", {role:"read", db:"tdb2"} ] } )
• 생성 후 권한 부여
> use tdb1 > db.createUser( { user: "testuser2", pwd: "testuser123", roles: [ "readWrite" ] } ) > db.grantRolesToUser("testuser2",[ {role:"read", db:"tdb2"} ] )
유저 생성 후 db.getUsers() 명령어를 통해서 확인하면 testuser 와 testuser2 모두 tdb1, tdb2 에 대해서 권한을 가지고 있는 것을 확인할 수 있습니다.
> db.getUsers() [ { "_id" : "tdb1.testuser", "userId" : UUID("04d68049-3357-4959-9a8f-68dc8536ecbb"), "user" : "testuser", "db" : "tdb1", "roles" : [ { "role" : "readWrite", "db" : "tdb1" }, { "role" : "read", "db" : "tdb2" } ], "mechanisms" : [ "SCRAM-SHA-1", "SCRAM-SHA-256" ] }, { "_id" : "tdb1.testuser2", "userId" : UUID("c5226697-ae03-4b44-9365-21c3cb1a105a"), "user" : "testuser2", "db" : "tdb1", "roles" : [ { "role" : "readWrite", "db" : "tdb1" }, { "role" : "read", "db" : "tdb2" } ], "mechanisms" : [ "SCRAM-SHA-1", "SCRAM-SHA-256" ] } ]
인증 데이터베이스(authentication database) 와 무관하게 사용자를 조회하려고 할 때에는 admin 데이터베이스에서 db.system.users 컬렉션을 통해서 확인할 수 있습니다.
> use admin > db.system.users.find().pretty() { "_id" : "admin.admin", "userId" : UUID("0b43902a-6c5e-477e-8a2a-9b4ca09097e5"), "user" : "admin", "db" : "admin", "credentials" : { "SCRAM-SHA-1" : { < ... 중략 ... > "role" : "root", "db" : "admin" } ] } { "_id" : "tdb1.testuser", "userId" : UUID("ba88127e-cd1f-4feb-8db4-6f2597752df6"), "user" : "testuser", "db" : "tdb1", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, < ... 중략 ... > "roles" : [ { "role" : "readWrite", "db" : "tdb1" }, { "role" : "read", "db" : "tdb2" } ] } { "_id" : "tdb1.testuser2", "userId" : UUID("3e3a8e4c-a365-4949-a0f5-3e9cc3839379"), "user" : "testuser2", "db" : "tdb1", "credentials" : { "SCRAM-SHA-1" : { < ... 중략 ... > }, "roles" : [ { "role" : "readWrite", "db" : "tdb1" }, { "role" : "read", "db" : "tdb2" } ] }
계정생성 위치 선정
계정 생성에 관해서는 인증 데이터베이스(authentication database) 와 관련이 있고, 각각의 데이터베이스 별로 생성할 수 있음을 확인하였습니다.
그렇다면 어디에 생성하는 것이 좋을 것인지에 대한 고민이 있을 수 있습니다.
사용자 계정명과 패스워드가 같다면, 계정을 사용하는 애플리케이션 또는 사용자(개발자) 의 경우 하나의 계정으로 인식될 것이고 사용하는데 큰 불편함은 없을 것입니다.
하지만 관리하는 측면에서는 같은 계정명 이라도 엄연히 다른 계정이 다수 존재하다 보니 계정 관리 측면에서 어려움이나 고민이 될 것이라고 생각 합니다.
예를 들어 데이터베이스가 db1, db2, db3, db4 4개가 있는 상황에서
user1 은 db1 에서 생성하면서 권한을 db1, db2,db3 을 받거나
user1 이 db1,db2,db3 에서 각각 계정을 생성하면서 생성한 데이터베이스의 권한을 받거나
하는 등 여러 가지 조합으로 계정을 생성 관리할 수 있습니다.
이렇게 관리 측면에서 어려움이 있을 수 있기 때문에 사용자가 1개의 db를 사용하더라도 관리 비용과 여러 대의 MongoDB에 대해서 공통적인 운영 정책을 사용하기 위해서 정해진 한 곳(데이터베이스)에서 계정을 생성하고 권한을 할당하는 방법으로 진행이 권장됩니다.
그래서 관리 데이터베이스인 admin 데이터베이스에서 생성하는 것이 좋을 것이라 생각하며 admin 이 아니더라도 공통된 특정 데이터베이스에서만 계정을 생성하는 것으로 운영 방침을 하는 것이 권장됩니다.
Sharded Cluster 환경에서의 계정
MongoDB Sharded Cluster 환경에서 MongoS를 통해 접속한 일반적인 상황에서 생성되는 계정은 모두 Config ReplicaSet에 생성되며 각 샤드 서버(ReplicaSet) 에는 생성되지 않습니다.
각 샤드 서버에 생성되지 않더라도 MongoS를 통해서 접속하게 되면 인증을 성공하여 샤드 서버를 정상적으로 이용할 수 있습니다.
authentication(인증모드)가 활성화되어 있다면 각 샤드 서버(Replica Set)에 어드민(관리자) 계정은 Replica Set 단위로 MongoDB Instance의 유지 관리를 위해서 필요하며, 로컬 관리자 라고 부릅니다.
예를 들어 Primary 와 Secondary의 전환을 하거나 Secondary 중에서 서버의 점검 등을 위해서 인스턴스를 종료해야 하거나 하는 등과 같이 컨트롤을 해야 할 때는 관리자 계정으로 로그인이 필요함에 따라서 샤드 별 Replica Set에는 각각의 관리자(admin) 계정을 생성하는 것이 필요 합니다.
각 Replica Set 별로 Primary 멤버에 접속해서 생성을 하면 됩니다.
> use admin > db.createUser( { user: "admin", pwd: passwordPrompt(), roles: [ "root" ] } ) Enter password: [패스워드 입력]
일반적인 상황인 MongoS를 통해서 생성한 유저에 대한 목록이나 정보는 MongoS로 접속해서 조회하거나 Config 서버의 Replica Set에 접속하여 유저 정보를 조회합니다.
-- 유저 목록 조회 > use admin > db.getUsers()
권한과 Role
MongoDB 도 다른 RDBMS에서 처럼 Role을 지원하며 보통의 경우 Role 기반으로 권한을 부여하여 사용을 합니다.
RDBMS 와 차이가 있는 점은 RDBMS 에서는 보통 Role 에 권한을 추가하여 사용하지만, MongoDB 의 Role 에는 사전에 정의된 Actions 가 추가되는 방식을 사용하고 있습니다.
액션(Actions) 은 Role 에서 사용되기도 하고 명령어(command) 와 매핑 되어 사용됩니다.
액션의 종류는 여러가지가 있으며 아래에서 내용을 확인할 수 있습니다.
예를 들어 insert 명령은 bypassDocumentValidation 와 insert 액션이 필요하다는 것을 확인할 수 있습니다.
MongoDB의 Role 은 이와 같은 액션의 묶음을 의미합니다.
MongoDB에서 Role 은 이미 준비되어 있는 Built-In Role 과 사용자가 서비스나 요건에 맞는 새로운 Role을 정의해서 생성해서 사용하는 사용자 정의 Role 두가지가 있습니다.
MongoDB에서 Built-In Role 또는 사용자 정의 role을 사용 후 정책을 적용되게 하려면, authorization을 활성화해야 합니다.
authorization을 활성화하면, MongoDB는 role에 대한 권한을 제대로 적용하고, 부여 받은 권한 이외에 권한을 제한합니다.
이것은 MongoDB의 보안을 강화하는 중요한 단계로 MongoDB에서 authorization을 활성화하려면, MongoDB 인스턴스를 시작할 때 conf 파일에 authorization: enabled 와 keyFile 옵션을 추가해야 합니다.
명령어 라인에서 mongod 를 통해서 MongoDB 인스턴스를 기동할 때에는 --auth 와 --keyFile <path-to-keyfile> 옵션을 추가하여 시작을 합니다.
이렇게 하면 authorization을 활성화되면, 인증이 강제되며, Role에 대한 권한 적용을 할 수 있습니다.
• 적용 예시
## mongod.conf 사용시 security: authorization: enabled clusterAuthMode: keyFile keyFile : /var/lib/mongo/mongod.key ## 커맨드 라인에서 인스턴스 기동시 # -keyFile 과 --auth 옵션을 사용 mongod --keyFile /var/lib/mongo/mongod.key \ --dbpath /var/lib/mongo --directoryperdb \ --pidfilepath /var/run/mongodb/mongod.pid \ --timeZoneInfo /usr/share/zoneinfo \ --logpath /var/log/mongodb/mongod.log --logappend \ --fork --auth --port 27017 --bind_ip_all
Built-In Role
MongoDB의 Role 은 이와 같은 액션의 묶음을 의미하며, MongoDB 에는 많이 사용되는 권한의 Role 이 준비되어 있는 상태입니다.
미리 준비된 Built-In Role 목록과 Role 안에 포함된 세부적인 권한 정보는 아래 링크에서 확인할 수 있습니다.
위 문서에서 Role 에 실제로 매핑 되어 있는 액션(actions) 정보를 확인할 수 있습니다.
계정을 생성하면서 Role 권한을 할당한다면 아래와 같이 수행합니다.
> use tdb1 > db.createUser( { user: "testuser3", pwd: "testuser123", roles: [ "readWrite" ] } )
Built-In Role 을 이미 생성된 사용자에게 할당하려면 다음과 같이 실행합니다.
> db.grantRolesToUser("testuser3", [ {role:"readWrite", db:"tdb2"} ])
위에서 생성한 testuser3 유저 정보를 확인해 보면 tdb1 와 tdb2 에서 모두 Built-In Role인 readWrite Role 을 할당 받은 것을 확인할 수 있습니다.
> use tdb1 > db.getUser("testuser3") { "_id" : "tdb1.testuser3", "userId" : UUID("816d6751-d404-4838-81fe-ff963357bf5c"), "user" : "testuser3", "db" : "tdb1", "roles" : [ { "role" : "readWrite", "db" : "tdb1" }, { "role" : "readWrite", "db" : "tdb2" } ], "mechanisms" : [ "SCRAM-SHA-1", "SCRAM-SHA-256" ] }
또는 선택한 데이터베이스에서 생성된 전체 유저를 보고자 할 때는 아래와 같은 명령어를 사용합니다.
> show users
할당된 Role 을 사용자에서 다시 회수하려면 db.revokeRolesFromUser 명령어를 사용합니다
> use tdb1 > db.revokeRolesFromUser("testuser3",[ { role:"readWrite", db:"tdb2" } ] )
revoke 명령어 수행 후 testuser3 에 대해서 다시 조회해보면 tdb2 에 대한 권한이 회수가 된 것을 확인할 수 있습니다.
> db.getUser("testuser3") { "_id" : "tdb1.testuser3", "userId" : UUID("816d6751-d404-4838-81fe-ff963357bf5c"), "user" : "testuser3", "db" : "tdb1", "roles" : [ { "role" : "readWrite", "db" : "tdb1" } ], "mechanisms" : [ "SCRAM-SHA-1", "SCRAM-SHA-256" ] }
revokeRolesFromUser 을 통한 권한 회수는 Built-In Role 뿐만 아니라 사용자 정의 Role 도 모두 가능합니다.
사용자 정의 Role
MongoDB 에는 보편적으로 많이 사용되는 Role 에 대해서는 미리 선언 및 준비가 되어 있고 그 Role을 Built-In Role 이라고 합니다.
다만, MongoDB 도 그렇고 다른 RDBMS 를 사용할 경우에도 미리 정의된 Role 외에 다양한 서비스 요건이나 보안 정책 등에 따라서 다른 권한을 가진 Role 이 필요할 것입니다.
이럴 경우 별도의 Role 을 생성하여 사용할 수 있으며, 이렇게 별도로 생성한 Role 을 사용자 정의 Role(User-Defined Role) 이라고 합니다.
Role 에 대한 생성은 db.createRole 명령어를 통해서 할 수 있습니다.
사용자 정의 Role 을 생성할 때도 데이터베이스 위치가 중요하며, 이것은 유저를 생성하는 것과 유사한 측면이 있긴 합니다.
- admin 데이터베이스에서 생성하는 것과 특정 데이터베이스에서 생성하는 것이 다릅니다.
- admin 데이터베이스에서 생성하는 Role 은 모든 데이터베이스(전역적)에 대한 권한을 포함할 수 있습니다.
- 특정 데이터베이스에서 생성한 Role 은 해당 데이터베이스의 객체에 대해서만 권한만 가질 수 있습니다.
그래서 이 부분도 Role 을 어디서 생성할 것인지에 대한 고민이 필요 할 수 있으며, 유저 생성과 마찬가지로 관리 편의성 등을 위해서 admin 데이터베이스에서 생성을 해서 사용하는 편이 더 좋을 것이라고 생각 합니다.
아래는 admin 데이터베이스에서 생성한 유저 정의 Role 에 대한 예제로 tdb1, tdb2 데이터베이스에 대해서 find 을 할 수 있는 Role 을 생성하였습니다.
> use admin > db.createRole( { role: "rl_find_tdb1_tdb2", privileges: [ { resource: { db: "tdb1", collection: "" }, actions: [ "find" ] }, { resource: { db: "tdb2", collection: "" }, actions: [ "find" ] } ], roles: [] } )
만약 특정 데이터베이스에서 Role 을 생성하게 되면 어떻게 될까요?
위에서 설명한 내용과 같이 생성한 데이터베이스 객체에 대해서만 권한을 할당 받을 수 있으며, 다른 데이터베이스에 대해서는 불가 함에 따라서 아래와 같이 에러가 발생하게 됩니다.
> use tdb1 > db.createRole( { role: "rl_find_tdb1_tdb2", privileges: [ { resource: { db: "tdb1", collection: "" }, actions: [ "find" ] }, { resource: { db: "tdb2", collection: "" }, actions: [ "find" ] } ], roles: [] } ) uncaught exception: Error: Roles on the 'tdb1' database cannot be granted privileges that target other databases or the cluster :
부여할 수 있는 개별 Actions 에 대한 정보는 다음 문서를 참조하시면 됩니다.
insert 권한 관련
개별 권한 중에서 insert 의 경우 사용자 정의 role을 만드는 형태에 따라서 createCollection 권한이 없어도 insert 권한만으로 데이터 입력 시 컬렉션이 생성될 수 있습니다.
예를 들어 다음과 같이 test 데이터베이스(스키마)에 대해서 actions을 설정하여 생성하겠습니다.
> db.createRole( { role: "rl_dml_test", privileges: [ { resource: { db: "test", collection: "" },actions: [ "find", "update", "insert", "remove"] }, { resource: { db: "test", collection: "" },actions: [ "listCollections"] } ], roles: [] } )
test 데이터베이스(스키마)에 find, upload, insert, remove 권한 사용할 수 있도록 설정하였습니다.
생성된 role을 사용자에게 부여하고 insertOne 를 통해 데이터를 입력해보도록 하겠습니다.
> show collections; employees <-- 현재 해당 컬렉션만 존재 > db.t1.insertOne({"col1":"test"}) { acknowledged: true, insertedId: ObjectId("657b00aaccc6b655d9399b61") } <!!-- 컬렉션 생성과 데이터 입력이 정상 수행됨 > db.t1.find() [ { _id: ObjectId("657b00aaccc6b655d9399b61"), col1: 'test' } ]
createCollection 권한이 없고 존재하지 않은 컬렉션(t1)에 데이터 입력 시 정상적으로 수행되었습니다.
데이터가 입력하면서 컬렉션이 생성되는 것은 MongoDB의 동적 스키마 특성 때문입니다.
이를 컨트롤하기 위해서는 db.createCollection()을 사용하여 명시적으로 컬렉션을 생성하고 컬렉션 단위로 별도의 권한을 부여해야 합니다.
Role 생성시에 roles: [] 항목을 통해서 기존 Role 을 통한 새로운 Role 을 만드는 것도 가능 합니다.
> admin > db.createRole( { role: "rl_readwrite_tdb1_tdb2", privileges: [ ], roles: [ { role: "readWrite",db: "tdb1"}, { role: "readWrite",db: "tdb2"} ] } )
생성한 Role 의 삭제는 db.dropRole() 명령어를 사용합니다.
> use admin > db.dropRole("rl_readwrite_tdb1_tdb2") true
이렇게 생성된 Role 에 대해서 액션을 추가나 삭제를 하고자 할 경우 db.grantPrivilegesToRole() 와 db.revokePrivilegesFromRole() 명령어를 사용합니다.
생성된 Role 에 액션이 아닌 Role 을 통해서 권한을 추가나 삭제하려고 할 경우 db.grantRolesToRole() 와 db.revokeRolesFromRole() 명령어를 사용합니다.
Role 조회
별도로 생성한 User-Defined Role 에 대한 목록 및 상세 정보는 system.roles 컬렉션에서 확인할 수 있으며 db.getRole() 을 통해서 개별 Role에 대한 정보를 확인할 수 있습니다.
> use admin > db.system.roles.find().pretty() { "_id" : "admin.rl_readwrite_tdb1_tdb2", "role" : "rl_readwrite_tdb1_tdb2", "db" : "admin", "privileges" : [ { "resource" : { "db" : "tdb1", "collection" : "" }, "actions" : [ "find" ] }, { "resource" : { "db" : "tdb2", "collection" : "" }, "actions" : [ "find" ] } ], "roles" : [ { "role" : "readWrite", "db" : "tdb1" }, { "role" : "readWrite", "db" : "tdb2" } ] }
• db.getRole 사용예시
> db.getRole("role이름",{showPrivileges:true})
데이터베이스에서 할당된 Built-In Role 과 User-Defined Role 에 대한 리스트를 확인하고 싶을 때는 show roles 명령어를 사용하면 확인할 수 있습니다.
> use admin > show roles; { "role" : "__queryableBackup", "db" : "admin", "isBuiltin" : true, "roles" : [ ], "inheritedRoles" : [ ] } { "role" : "__system", "db" : "admin", "isBuiltin" : true, "roles" : [ ], "inheritedRoles" : [ ] } < .. 중략 .. >
Built-In Role 과 User-Defined Role 에 대한 상세한 정보를 확인하고 싶을 때는 db.getRole 명령어를 사용하면 됩니다.
> use admin > db.getRole("read",{ showPrivileges: true }) { "role" : "read", "db" : "admin", "isBuiltin" : true, "roles" : [ ], "inheritedRoles" : [ ], "privileges" : [ { "resource" : { "db" : "admin", "collection" : "" }, "actions" : [ "changeStream", "collStats", "dbHash", "dbStats", "find", "killCursors", "listCollections", "listIndexes", "planCacheRead" ] < ..중략 .. > > db.getRole("rl_readwrite_tdb1_tdb2",{ showPrivileges: true }) { "role" : "rl_readwrite_tdb1_tdb2", "db" : "admin", "isBuiltin" : false, "roles" : [ { "role" : "readWrite", "db" : "tdb1" }, { "role" : "readWrite", "db" : "tdb2" } ], < ..중략 .. >
여기까지 MongoDB 의 인증 과 계정, 권한에 관한 Role 에 대해서 확인해보았으며, 이번 포스팅은 여기에서 정리하도록 하겠습니다.
Reference
Reference URL
• mongodb.com/authentication
• mongodb.com/deploy-replica-set-with-keyfile-access-control
• mongodb.com/security-internal-authentication
• mongodb.com/rotate-key-sharded-cluster
• mongodb.com/built-in-roles
• mongodb.com/privilege-actions
• mongodb.com/mongodb-authentication
연관된 다른 글






Principal DBA(MySQL, AWS Aurora, Oracle)
핀테크 서비스인 핀다에서 데이터베이스를 운영하고 있어요(at finda.co.kr)
Previous - 당근마켓, 위메프, Oracle Korea ACS / Fedora Kor UserGroup 운영중
Database 외에도 NoSQL , Linux , Python, Cloud, Http/PHP CGI 등에도 관심이 있습니다
purityboy83@gmail.com / admin@hoing.io