본문 바로가기
Database/MongoDB

MongoDB 사용자 계정 생성 및 권한 관리

by Esther Baek 2022. 3. 27.

일반적으로 DB 서버에서 사용하는 계정의 종류는 크게 두 가지로 나눌 수 있습니다. DB 관리자가 사용하는 어드민용 계정과 실제 애플리케이션 서버에서 DB 접근 시 사용하는 사용자(서비스) 계정입니다. 오늘은 이 중 사용자 계정을 MongoDB에서 셋팅하는 방법을 살펴보려고 합니다.


이 글은 MongoDB 4.4 버전을 바탕으로 작성되었습니다.

어디에 생성할 것인가?

MongoDB에서 하나의 계정은 하나의 데이터베이스에 종속됩니다. 즉, DB 서버에서 계정이 글로벌하게 존재하는 것이 아니라 각 데이터베이스 내에 존재하게 됩니다. 다음과 같은 구조라 할 수 있습니다. 각 계정은 (db_name, user_name) 조합으로 식별되므로, 동일한 계정명으로 서로 다른 데이터베이스에 계정 생성이 가능합니다.

동일한 이름을 가진 userC 계정이 서로 다른 데이터베이스에 생성돼있다.

따라서 계정을 생성할 때에는 계정을 생성할 데이터베이스에 접속한 후 그 안에 계정을 생성합니다. 계정이 생성된 데이터베이스는 이 계정의 인증 데이터베이스(Authentication Database)가 됩니다. 만약 데이터베이스를 선택하지 않은 채 계정을 생성하면, MongoDB의 디폴트 데이터베이스인 test DB에 계정이 생성됩니다.

 

어드민 계정을 생성하는 경우에는 보통 주저없이 admin 데이터베이스에 생성하곤 합니다. admin 데이터베이스가 다른 데이터베이스보다 가지고 있는 빌트인 Role(권한)도 많고 이름에서도 알 수 있듯이 DB 서버 내부 정보 등이 저장돼있는 데이터베이스이기 때문이죠. 반면에 사용자 계정을 생성하려고 할 때는 조금 고민스러운 부분들이 있습니다. 앞서 언급한 것처럼, 계정 생성 시 우선 어느 데이터베이스에 계정을 생성할 것인지를 결정해야 하기 때문입니다. 대게 다음의 두 방식 중 하나를 사용하게 되는 것 같습니다.

 

  • 방식 1. 사용자 데이터베이스에 사용자 계정을 생성한다.
  • 방식 2. admin 데이터베이스에 사용자 계정을 생성한다.

만약 사용자 데이터베이스를 하나만 사용할 예정이라면 방식 (1)과 같이 사용자 데이터베이스 내 사용자 계정을 생성 해 사용해도 특별히 불편한 부분은 없습니다. 하나의 MongoDB 서버 내 사용자 데이터베이스를 여러 개 사용하는 경우도 있을 수 있는데, 이때 각 데이터베이스가 독립적으로 사용되고 사용자 계정도 제각기 사용되는 형태라면 마찬가지로 큰 이슈는 없어보입니다. 다만 show users 명령을 사용해 전체 사용자 DB 계정들의 정보를 간편하게 확인하고자할 때 데이터베이스를 계속 바꿔줘야하는 불편함이 있을 것 같네요. (물론 admin 데이터베이스에서 system.users 컬렉션 데이터를 직접 조회하면 한 번에 전체 유저 정보를 확인할 수 있긴 합니다. 🙂)

 

문제는 하나의 MongoDB 서버 내에서 데이터베이스를 여러 개 사용하고 사용자 계정도 여러 개인데, 각 사용자 계정이 하나 이상의 데이터베이스에 대한 권한이 필요한 경우입니다. 이 경우 방식 (1)과 같은 형태로 계정을 관리한다면 사용자 계정과 해당 계정에서 접근하는 데이터베이스들 간에 관계가 거미줄 같이 복잡하게 얽히게 됩니다. DB 서버를 사용하는 입장에서는 단순히 하나의 계정만 사용하므로 별 문제없어보일 수 있지만, DB 서버를 관리하는 입장에서는 이와 같이 복잡한 형태로 계정이 존재하는 경우 계정 관련 작업 시 실수할 가능성이 높아지고 계정들이 여러 개 데이터베이스에 흩어져 있다보니 관리 자체도 비효율적일 수 있습니다.

 

그래서 추천하는 방식이, 바로 방식 (2)와 같이 admin 데이터베이스에 사용자 계정을 생성하는 것입니다. admin 데이터베이스에 사용자 계정들을 생성하는 경우, 한 곳에서 전체 계정의 정보를 손쉽게 파악할 수 있으며 계정과 해당 계정이 접근하는 데이터베이스들의 관계 또한 심플해집니다. 그리고 여러 계정에 대해 권한 변경 등의 작업을 진행하는 경우에도 데이터베이스 이동 없이 편리하게 작업이 가능해집니다.

admin 데이터베이스에서 모든 사용자 계정 목록과 권한 정보를 손쉽게 확인할 수 있습니다.

 


어떤 권한을 줄 것인가?

사용자 계정을 생성할 때 어느 정도의 권한을 부여할 것인지도 결정해야 하는데, 이는 이 계정을 어떤 용도로 사용할 지에 따라 달라집니다. 다양한 용도들이 있겠지만 대표적으로는 세 가지로 분류할 수 있습니다.

 

  1. 스키마 변경 및 데이터 읽기쓰기용
  2. 데이터 읽기쓰기용
  3. 데이터 읽기전용

용도 (1)의 경우 일반적으로 개발 DB 서버에서 많이 사용되며, 용도 (2)와 (3)은 운영 DB 서버에서 주로 사용됩니다. 원하는 용도에 맞게 사용자 계정에 권한을 부여하기 위해서는 MongoDB에서 제공하는 권한에 어떤 것들이 있는지 알고있어야 합니다.

 

MongoDB에서는 기본적으로 Role을 바탕으로 MongoDB 시스템에 대한 액세스를 제어합니다. 이를 Role-Based Access Control(RBAC) 이라고 합니다. 따라서 사용자 계정에는 반드시 하나 이상의 Role이 부여되어야 합니다. Role은 간단히 말해서 권한 모음집이라고 생각할 수 있습니다. 어떤 대상에 대해 어떤 작업을 수행할 수 있는지에 대한 일련의 내용이 Role에 정의돼있다고 보면 됩니다. 공식적인 용어로 표현하면 작업 대상이 되는 것들을 "Resource"라고 하며, 가능한 작업 하나 하나를 "Privilege Action"이라고 합니다.

 

Role은 MongoDB에서 제공하는 "빌트인 Role"과 "사용자 정의 Role"이 존재합니다. 빌트인 Role에는 아주 다양한 Role들이 있는데, 여기에서는 위에 언급한 용도들에 필요한 Role들만 살펴보겠습니다. 전체 빌트인 Role에 대한 정보는 공식 메뉴얼을 참고바랍니다.

  • read Role
    read Role은 이름에서도 알 수 있듯이 데이터를 읽을 수 있는 권한을 제공합니다. read Role에 포함돼있는 세부 권한(Privilege Action)은 다음과 같습니다.
    - changeStream
    - collStats
    - dbHash
    - dbStats
    - find
    - killCursors
    - listIndexes
    - listCollections

  • readWrite Role
    readWrite Role은 데이터를 수정할 수 있는 권한을 제공합니다. readWrite Role에 포함돼있는 세부 권한(Privilege Action)은 다음과 같습니다.
    - collStats
    - convertToCapped
    - createCollection
    - dbHash
    - dbStats
    - dropCollection
    - createIndex
    - dropIndex
    - find
    - insert
    - killCursors
    - listIndexes
    - listCollections
    - remove
    - renameCollectionSameDB
    - update

만약 DB에서 데이터를 읽기만 한다면(용도 3) 계정에 read Role을 부여하면 됩니다. 그리고 데이터를 읽고 쓰거나(용도 2), 또는 스키마 변경까지도 수행할 수 있다면(용도 1) readWrite Role을 부여해주면 됩니다. readWrite Role의 세부 권한 목록을 보면 알 수 있듯이 컬렉션이나 인덱스를 생성하고 드랍하는 권한도 포함돼있으므로, 해당 Role이 부여되면 스키마 변경도 가능합니다.

 

그런데 이 부분은 DB 서버 관리자 입장에서는 조금 부담스러울 수 있습니다. 보통은 운영 DB에서 사용자 계정은 데이터를 읽고 쓸 수 있도록만 설정하기 때문입니다. 운영 DB에서 스키마 변경과 같은 작업은 DB 장애가 발생할 리스크가 있으므로 관리자(DBA)가 직접 수행합니다. 만약 운영 환경에서 애플리케이션 서버 설정 오류로 인해 MongoDB에서 컬렉션 초기화가 수행됐다면 (컬렉션 드랍 후 재생성), 계정에 readWrite Role이 부여돼있는 경우 해당 작업이 막힘없이 수행될 것이므로 이는 아주 큰 서비스 장애로 이어질 수도 있습니다. 그렇다면 이런 장애 상황을 초래할 수도 있는 조금 위험한 권한들을 Role에서 제거하면 되지 않을까요. 하지만 readWrite Role은 빌트인 Role이므로 직접 그 안에 포함돼있는 권한들을 조정할 수는 없습니다. 그럼 어떻게 하면 내가 원하는 권한들로만 설정할 수 있을까요?

맞습니다. 앞에서 살짝 언급했었던 "사용자 정의 Role"을 사용하면 됩니다.

 

사용자 정의 Role은 db.createRole 메소드를 사용해 생성할 수 있습니다. db.createRole 메소드는 두 개의 파라미터 값을 가집니다.

db.createRole(role, writeConcern)

"role"에는 생성하고자 하는 Role의 정의문을 입력하며, "writeConcern"에는 이 명령에 적용될 Write Concern 설정을 입력합니다. "writeConcern"은 필수 입력 파라미터는 아니라서 필요한 경우에만 적절한 값을 설정해주면 됩니다.

 

"role" 부분에는 다음의 포맷에 맞춰 값을 입력합니다.

{
  role: "<name>",
  privileges: [
     { resource: { <resource> }, actions: [ "<action>", ... ] },
     ...
  ],
  roles: [ ... ],
  authenticationRestrictions: [ ... ]
}
role 필수 생성하고자 하는 Role의 이름을 입력한다.
privileges 필수 Role에 부여할 권한 목록으로, Resource와 Privilege Action의 조합으로 구성된다.
roles 필수 생성하고자 하는 Role이 상속받는 role을 입력한다.
authenticationRestrictions 선택 생성될 Role에 적용되는 인증 제한 정보로, 연결할 수 있는(Client)/연결될 수 있는(Server) IP 주소 또는 CIDR 범위를 지정할 수 있다.

 

그럼 이제 빌트인 readWrite Role보다 좀 더 권한이 축소된 데이터 읽기쓰기용 Role을 만들어 보겠습니다. Role은 계정과 마찬가지로 데이터베이스에 종속됩니다. 그러므로 새로 생성할 계정이 접근해서 사용할 데이터베이스 내에 이 Role을 생성해야 합니다. 즉, Role은 계정처럼 admin 데이터베이스 하나에 전부 생성하는 것이 아니라, 각 사용자 데이터베이스에 생성해줍니다. 그렇게해야 사용자 계정에 접근하고자 하는 데이터베이스 별로 이 Role을 부여할 수 있기 때문입니다.

"데이터 읽기쓰기용"으로 제가 생성해본 사용자 정의 Role은 다음과 같습니다. 이 Role이 부여된 계정은 "products" DB내 모든 컬렉션들에 대해 actions에 명시된 권한들에서 허용한 작업을 수행할 수 있게 됩니다. 간단히 설명하면 데이터 읽기쓰기 및 컬렉션의 인덱스 정보, 컬렉션 통계 정보 등을 확인할 수 있습니다.

use products

db.createRole(
   {
     role: "appReadWrite",
     privileges: [
       { resource: { db: "products", collection: "" }, actions: [ "find", "update", "insert", "remove", "collStats", "dbStats", "listIndexes", "listCollections" ] }
     ],
       roles: []
   }
)

 

MongoDB에 사용자 데이터베이스가 여러 개이고, 하나의 사용자 계정에서 여러 데이터베이스에 접근할 수 있다면 각 데이터베이스에 Role을 생성합니다.

use products
db.createRole( { role: "appReadWrite", ... } )

use users
db.createRole( { role: "appReadWrite", ... } )

 

생성한 Role은 show roles 명령을 통해 잘 생성됐는지 확인할 수 있습니다.

mongo> use products
mongo> show roles
...
{
    "role" : "appReadWrite",
    "db" : "products",
    "isBuiltin" : false,
    "roles" : [ ],
    "inheritedRoles" : [ ]
}
...

참고로 데이터베이스를 드랍해도 해당 데이터베이스에 생성된 계정과 Role은 그대로 남아있습니다. 그래서 이를 완전히 정리하려면 db.dropUser() 또는 db.dropRole() 메소드를 사용하여 계정과 Role을 직접 제거해줘야 합니다.


사용자 계정 생성

사용자 계정에 부여할 Role도 생성했으니 이제 db.createUser() 메소드를 사용해 사용자 계정을 생성해 봅시다.

 

db.createUser 메소드도 두 개의 파라미터를 가집니다. "user"에는 생성하고자 하는 계정 정보를 입력하며, "writeConcern"은 동일하게 선택 사항입니다.

db.createUser(user, writeConcern)

 

"user" 부분에는 다음의 포맷에 맞춰 값을 입력합니다.

{
  user: "<name>",
  pwd: "<password>",
  customData: { <any information> },
  roles: [
    { role: "<role>", db: "<database>" } | "<role>",
    ...
  ],
  authenticationRestrictions: [
     {
       clientSource: ["<IP>" | "<CIDR range>", ...],
       serverAddress: ["<IP>" | "<CIDR range>", ...]
     },
     ...
  ],
  mechanisms: [ "<SCRAM-SHA-1|SCRAM-SHA-256>", ... ],
  passwordDigestor: "<server|client>"
}
user 필수 생성하고자 하는 계정명 입력
pwd 필수 패스워드 평문을 직접 명시하거나 passwordPrompt() 를 명시해 프롬프트로 입력할 수도 있다.
customData 선택 계정에 대한 추가적인 정보를 명시
roles 필수 계정에 부여할 Role을 명시, 여러 Role도 명시 가능
authenticationRestrictions 선택 생성될 계정에 적용되는 인증 제한 정보로, 연결할 수 있는(Client)/연결될 수 있는(Server) IP 주소 또는 CIDR 범위를 지정할 수 있다.
mechanisms 선택 계정에 설정할 SCRAM 인증 메커니즘 방식을 지정
passwordDigestor 선택 입력된 패스워드 평문은 md5 해싱이 적용되는데, 이를 클라이언트에서 할지 서버에서 할지 결정

 

적절하게 정보들을 입력해 사용자 계정을 생성합니다. 계정 생성은 admin 데이터베이스로 이동해 진행합니다. 다음은 "products" 및 "users" DB의 "appReadWrite" Role을 가지며, 10.100.x.x 로 시작하는 IP를 가진 서버에서만 접속할 수 있는 "esther" 계정을 생성하는 명령문입니다.

use admin
db.createUser(
   {
     user: "esther",
     pwd: "xxxxxxxx",
     roles: [ 
         { role: "appReadWrite", db: "products" },
         { role: "appReadWrite", db: "users" }
     ],
     authenticationRestrictions: [ {
        clientSource: ["10.100.0.0/16"]
     } ]
   }
);

 

show users 명령을 통해 데이터베이스 내 생성돼있는 사용자 계정 목록을 확인할 수 있습니다.

mongo> use admin
mongo> show users
...
{
    "_id" : "admin.esther",
    "userId" : UUID("e51fd3c3-d233-4f67-92c5-32bc9fb39b6f"),
    "user" : "esther",
    "db" : "admin",
    "roles" : [
        {
            "role" : "appReadWrite",
            "db" : "products"
        },
        {
            "role" : "appReadWrite",
            "db" : "users"
        }
    ],
    "mechanisms" : [
        "SCRAM-SHA-1"
    ]
}
...

 

MongoDB로 접속 시에는 계정에서 실제로 사용하는 데이터베이스와 계정의 인증 데이터베이스가 다르므로, 접속 URI에 authSource 옵션을 사용해 명시적으로 인증 데이터베이스를 지정해줍니다.

mongodb://esther:<password>@mongodb.example.com:27017/users?authSource=admin

 


마치며

MongoDB에서 사용자 계정과 권한을 어떤 방식으로 관리하면 좋을지에 대한 글은 못본 것 같아 한번 작성해보았습니다. 글을 작성하면서 저도 한번 더 내용이 정리된 느낌이네요. 특별한 내용은 아니지만, 이런 부분들이 궁금하셨던 분들에게는 작게나마 도움이 되었으면 좋겠습니다. 감사합니다.

 

 

 

 

참고

 

댓글