docker-composeで認証ありmongoDBを立ててPyMongoでアクセスしてみる

# はじめに

docker-composeでmongoDBを認証ありで立てたときに、PyMongoでどうやってアクセスすれば...となったりしたので、まとめてみました。

本稿では以下順でご紹介していきます。

1. docker-composeで認証ありmongoDBをつくる
1. ユーザーを追加する(PyMongo)
1. 作成したユーザーでmongoDBを制御する(PyMongo)


環境情報は以下となります。

+ Python : 3.8.0
+ PyMongo : 3.12.1
+ MongoDB : 5.0.4

※本稿ではすでにPythonやdockerなどの環境が揃っているものとして進めます。

# 1.docker-composeで認証ありmongoDBをつくる

MongoDBのdockerコンテナ 「mongo」を作成します。　　

## 使用コード

dokcer-compose.yml

```Docker
version: '3'
services:
    mongo:
        image: mongo:5.0.4
        container_name: mongo
        ports:
            - 27017:27017
        volumes:
            - ./mongo/db:/data/db
            - ./mongo/configdb:/data/configdb
        environment:
            MONGO_INITDB_ROOT_USERNAME: root     # ここがポイント!
            MONGO_INITDB_ROOT_PASSWORD: root
            TZ: Asia/Tokyo
```

## 実行コマンド

上記docker-compose.ymlのある場所で以下コマンドを実行してください。

```
$docker-compose up --build
```
 

## 説明



__□ポイント__

ポイントは`MONGO_INITDB_ROOT_USERNAME`と`MONGO_INITDB_ROOT_PASSWORD`です。　　 
ここで指定したユーザー名とパスワードがスーパーユーザーとなります。　　


本稿ではスーパーユーザー設定は以下としてます。 　 

+ ユーザー名 : root
+ パスワード : root

通常mongoDBに認証をつけるには「`authorization: enabled`を指定」や「`--auth`をつけてmongo実行」などする必要があるみたいですが、docker-composeの場合は上記定義を書けば立ち上げ時点から認証有効になるようです。  

# 2.ユーザーを追加する(PyMongo)

スーパーユーザーを使うことでもアクセスはできますが、セキュリティのためにも今回はアクセス用のユーザーを用意しましょう。

## 使用コード

add_user.py

```python
from pymongo import MongoClient

host_name = "localhost"
port_num  = 27017
user_name = "py_user"     # 作成ユーザーの名前
user_pwd  = "py_pwd"      # 作成ユーザーのパスワード
db_name   = "sample-db"   # 対象データベース

# スーパーユーザーでアクセス
client = MongoClient(
    host = host_name,
    port = port_num,
    username = "root",      # ここがポイント1個目!
    password = "root",
)

# 「sample-db」データベースにユーザーを追加
db = client[db_name]
db.command(                 # ここがポイント2個目!
  'createUser', 
  user_name,
  pwd=user_pwd,
  roles=['readWrite'],
)

# user確認
for user in db.command('usersInfo').get('users'):
    print(user)

client.close()
```

## 実行コマンド

上記Pythonを実行して以下結果が得られれば成功です。 

```
$python add_user.py
{'_id': 'sample-db.py_user', 'userId': UUID('82aee8a5-58a0-4314-979a-48131dd8eec3'), 'user': 'py_user', 'db': 'sample-db', 'roles': [{'role': 'readWrite', 'db': 'sample-db'}], 'mechanisms': ['SCRAM-SHA-1', 'SCRAM-SHA-256']}
```
※userIdは環境依存です。

## 説明

__□ポイント1個目__

手順1にてdokcerコンテナを立てた時点で認証が有効なので、docker-composeで定義したスーパーユーザーでアクセスする必要があります。　　
そこで`MongoClient`にて「username = "root"」「password = "root"」を指定することで、スーパーユーザーとして接続するようにしています。

__□ポイント2個目__

ユーザーを追加するApiは以下となります。  

```python
db = client['<ユーザーを追加する対象DB名>']
db.command(
    'createUser', 
    '<追加したいユーザー名>', 
    pwd='<追加ユーザーのパスワード>',
    roles=['<権限>'],
)
```


※補足  

ユーザー追加方法として以下方法もあります。
しかし、`adduser`はPyMongo4.0で削除予定とのことで非推奨のようです。

```python
db = client[db_name]
db.adduser(
    '<追加したいユーザー名>', 
    pwd='<追加ユーザーのパスワード>',
    roles=['<権限>']
)
```





# 3.作成したユーザーでmongoDBを制御する(PyMongo)

先ほど作成したユーザーで実際に制御してみます。

## 使用コード

accsess_insert.py

```python
from pymongo import MongoClient

host_name = "localhost"
port_num  = 27017
user_name = "py_user"     # 作成ユーザーの名前
user_pwd  = "py_pwd"      # 作成ユーザーのパスワード
db_name   = "sample-db"   # 対象データベース
co_name   = "sample-co"

# 作成ユーザーでアクセス
client = MongoClient(
    host = host_name,
    port = port_num,
    username = user_name,
    password = user_pwd,
    authSource = db_name,    # ここがポイント!
)

db = client[db_name]
co = db[co_name]

# データを追加
data1 = {
    "name":"TestBook",
    "price":420,
}
co.insert_one(data1)

data2 = {
    "name":"ProgramText",
    "price":1500,
    "category":"IT",
}
co.insert_one(data2)

# 全件表示
for data in co.find():
    print(data)

client.close()
```

## 実行コマンド

上記Pythonを実行して以下結果が得られれば成功です。

```
$python accsess_insert.py
{'_id': ObjectId('619f9fdda0c5b80b201aa550'), 'name': 'TestBook', 'price': 420}
{'_id': ObjectId('619f9fdda0c5b80b201aa551'), 'name': 'ProgramText', 'price': 1500, 'category': 'IT'}
```

## 説明

__□ポイント__

`MongoClient`の指定に`authSource`を追加しました。

`authSource`には認証のために参照するデータベースを指定できます。  
今回は「sample-db」を指定することで、手順2で作成したユーザー「py_user」として接続できるようになります。

ちなみに、`authSource`を省略した場合は「admin」データベースが参照先となります。　　
(例えば手順2のときは省略しているのでadminを参照してます)


※補足  

アクセスユーザーを指定する方法として以下方法もあります。
しかし、`authenticate`は今では非推奨のようです。

```python
client = MongoClient(host_name, port_num)
client[db_name].authenticate(user_name, user_pwd)
co = client[db_name][co_name]
```

# 最後に

何も考えずdockerでmongoDBを作ると認証を忘れがちだと思うので、認証をつけるよう気をつけましょう。

余談ですが、バージョンアップに従い非推奨Apiが増えていたので、公式ドキュメントは読まないといけないなと実感しました...(今更ですが)　　

# 参考

[PyMongo Documentation](https://pymongo.readthedocs.io/en/stable/)