Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix mongo connections #63053

Merged
merged 5 commits into from
Nov 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog/63058.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix mongo authentication for mongo ext_pillar and mongo returner

This fix also include the ability to use the mongo connection string for mongo ext_pillar
84 changes: 55 additions & 29 deletions salt/pillar/mongo.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,39 @@
===============================

The module shares the same base mongo connection variables as
:py:mod:`salt.returners.mongo_return`. These variables go in your master
:py:mod:`salt.returners.mongo_future_return`. These variables go in your master
config file.

* ``mongo.db`` - The mongo database to connect to. Defaults to ``'salt'``.
* ``mongo.host`` - The mongo host to connect to. Supports replica sets by
specifying all hosts in the set, comma-delimited. Defaults to ``'salt'``.
* ``mongo.port`` - The port that the mongo database is running on. Defaults
to ``27017``.
* ``mongo.user`` - The username for connecting to mongo. Only required if
you are using mongo authentication. Defaults to ``''``.
* ``mongo.password`` - The password for connecting to mongo. Only required
if you are using mongo authentication. Defaults to ``''``.
.. code-block:: yaml

mongo.db: <database name>
mongo.host: <server ip address>
mongo.user: <MongoDB username>
mongo.password: <MongoDB user password>
mongo.port: 27017

Or single URI:

.. code-block:: yaml

mongo.uri: URI

where uri is in the format:

.. code-block:: text

mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]

Example:

.. code-block:: text

mongodb://db1.example.net:27017/mydatabase
mongodb://db1.example.net:27017,db2.example.net:2500/?replicaSet=test
mongodb://db1.example.net:27017,db2.example.net:2500/?replicaSet=test&connectTimeoutMS=300000

More information on URI format can be found in
https://docs.mongodb.com/manual/reference/connection-string/

Configuring the Mongo ext_pillar
================================
Expand Down Expand Up @@ -57,6 +77,8 @@
import logging
import re

import salt.exceptions

try:
import pymongo

Expand All @@ -65,15 +87,6 @@
HAS_PYMONGO = False


__opts__ = {
"mongo.db": "salt",
"mongo.host": "salt",
"mongo.password": "",
"mongo.port": 27017,
"mongo.user": "",
}


def __virtual__():
if not HAS_PYMONGO:
return False
Expand Down Expand Up @@ -116,20 +129,33 @@ def ext_pillar(
careful with other fields in the document as they must be string
serializable. Defaults to ``None``.
"""
host = __opts__["mongo.host"]
port = __opts__["mongo.port"]
log.info("connecting to %s:%s for mongo ext_pillar", host, port)
conn = pymongo.MongoClient(host, port)

log.debug("using database '%s'", __opts__["mongo.db"])
mdb = conn[__opts__["mongo.db"]]

uri = __opts__.get("mongo.uri")
host = __opts__.get("mongo.host")
port = __opts__.get("mongo.port")
user = __opts__.get("mongo.user")
password = __opts__.get("mongo.password")
db = __opts__.get("mongo.db")

if uri:
if uri and host:
raise salt.exceptions.SaltConfigurationError(
"Mongo ext_pillar expects either uri or host configuration. Both were"
" provided"
)
pymongo.uri_parser.parse_uri(uri)
conn = pymongo.MongoClient(uri)
log.info("connecting to %s for mongo ext_pillar", uri)
mdb = conn.get_database()

else:
log.info("connecting to %s:%s for mongo ext_pillar", host, port)
conn = pymongo.MongoClient(
host=host, port=port, username=user, password=password
)

if user and password:
log.debug("authenticating as '%s'", user)
mdb.authenticate(user, password)
log.debug("using database '%s'", db)
mdb = conn[db]

# Do the regex string replacement on the minion id
if re_pattern:
Expand Down
6 changes: 2 additions & 4 deletions salt/returners/mongo_future_return.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,17 +178,15 @@ def _get_conn(ret):
mdb = conn.get_database()
else:
if PYMONGO_VERSION > _LooseVersion("2.3"):
conn = pymongo.MongoClient(host, port)
conn = pymongo.MongoClient(host, port, username=user, password=password)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need the username and password for the above client declarations? Line 177 here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, otherwise pymongo throws the following exception:

pymongo.errors.OperationFailure: command insert requires authentication, full error: {'ok': 0.0, 'errmsg': 'command insert requires authentication', 'code': 13, 'codeName': 'Unauthorized'}

else:
if uri:
raise salt.exceptions.SaltConfigurationError(
"pymongo <= 2.3 does not support uri format"
)
conn = pymongo.Connection(host, port)
conn = pymongo.Connection(host, port, username=user, password=password)

mdb = conn[db_]
if user and password:
mdb.authenticate(user, password)

if indexes:
if PYMONGO_VERSION > _LooseVersion("2.3"):
Expand Down
30 changes: 30 additions & 0 deletions tests/pytests/unit/pillar/test_mongo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import pytest

import salt.exceptions
import salt.pillar.mongo as mongo
from tests.support.mock import patch


@pytest.fixture
def configure_loader_modules():

return {
mongo: {
"__opts__": {
"mongo.uri": "mongodb://root:pass@localhost27017/salt?authSource=admin"
}
}
}


def test_config_exception():
opts = {
"mongo.host": "localhost",
"mongo.port": 27017,
"mongo.user": "root",
"mongo.password": "pass",
"mongo.uri": "mongodb://root:pass@localhost27017/salt?authSource=admin",
}
with patch.dict(mongo.__opts__, opts):
with pytest.raises(salt.exceptions.SaltConfigurationError):
mongo.ext_pillar("minion1", {})
31 changes: 31 additions & 0 deletions tests/pytests/unit/returners/test_mongo_future_return.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import pytest

import salt.exceptions
import salt.returners.mongo_future_return as mongo
from tests.support.mock import patch


@pytest.fixture
def configure_loader_modules():

return {
mongo: {
"__opts__": {
"mongo.uri": "mongodb://root:pass@localhost27017/salt?authSource=admin"
}
}
}


@patch("salt.returners.mongo_future_return.PYMONGO_VERSION", "4.3.2", create=True)
def test_config_exception():
opts = {
"mongo.host": "localhost",
"mongo.port": 27017,
"mongo.user": "root",
"mongo.password": "pass",
"mongo.uri": "mongodb://root:pass@localhost27017/salt?authSource=admin",
}
with patch.dict(mongo.__opts__, opts):
with pytest.raises(salt.exceptions.SaltConfigurationError):
mongo.returner({})