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
Add SSL & auth support to MongoDB client #1299
Conversation
@tomconte To clarify, this is between the pymongo driver in BigchainDB and the MongoDB instance? |
Codecov Report
@@ Coverage Diff @@
## master #1299 +/- ##
==========================================
+ Coverage 98.19% 98.19% +<.01%
==========================================
Files 54 54
Lines 2433 2444 +11
==========================================
+ Hits 2389 2400 +11
Misses 44 44 Continue to review full report at Codecov.
|
@krish7919 yes, so that BigchainDB can connect to a secured MongoDB instance. |
I'm not super-familiar with the code for establishing the connection to MongoDB, so I think I'll ask @vrde to take a look at this pull request too. We'll also have to update the docs page about the BigchainDB Server settings, i.e. https://docs.bigchaindb.com/projects/server/en/latest/server-reference/configuration.html Codecov is also complaining about a decline in unit test coverage... |
Looks like I need to work on these tests :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, thanks for your PR, looks pretty good! There are a couple of things to change, then we are ready to go 💃.
bigchaindb/backend/connection.py
Outdated
@@ -16,7 +16,7 @@ | |||
|
|||
|
|||
def connect(backend=None, host=None, port=None, name=None, max_tries=None, | |||
connection_timeout=None, replicaset=None): | |||
connection_timeout=None, replicaset=None, ssl=False, login=None, password=None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The params in the function signature have higher priority than the one in the configuration. This means that if your config defines 'ssl': True
, but the connect
function is called with ssl=False
, then False
has priority. It's better to have ssl=None
here. See next comment for the other part of the explanation.
bigchaindb/backend/connection.py
Outdated
@@ -50,6 +50,10 @@ def connect(backend=None, host=None, port=None, name=None, max_tries=None, | |||
# to handle these these additional args. In case of RethinkDBConnection | |||
# it just does not do anything with it. | |||
replicaset = replicaset or bigchaindb.config['database'].get('replicaset') | |||
ssl = bigchaindb.config['database'].get('ssl') if bigchaindb.config['database'].get('ssl') is not None \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should do the trick:
ssl = ssl if ssl is not None else bigchaindb.config['database'].get('ssl', False)
It says: if ssl
has a value different than none, then use that value, read the configuration otherwise. If in the configuration the value is not defined, assume False
.
@@ -16,7 +16,7 @@ | |||
|
|||
class MongoDBConnection(Connection): | |||
|
|||
def __init__(self, replicaset=None, **kwargs): | |||
def __init__(self, replicaset=None, ssl=False, login=None, password=None, **kwargs): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as before ssl=None
.
@@ -28,6 +28,10 @@ def __init__(self, replicaset=None, **kwargs): | |||
|
|||
super().__init__(**kwargs) | |||
self.replicaset = replicaset or bigchaindb.config['database']['replicaset'] | |||
self.ssl = bigchaindb.config['database'].get('ssl') if bigchaindb.config['database'].get('ssl') is not None \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ssl = ssl if ssl is not None else bigchaindb.config['database'].get('ssl', False)
if self.login is not None and self.password is not None: | ||
client[self.dbname].authenticate(self.login, self.password) | ||
|
||
return client | ||
|
||
# `initialize_replica_set` might raise `ConnectionFailure` or `OperationFailure`. | ||
except (pymongo.errors.ConnectionFailure, | ||
pymongo.errors.OperationFailure) as exc: | ||
raise ConnectionError() from exc |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately pymongo
doesn't provide a specific exception when the authentication fails, but a generic OperationFailure
. This might trigger a retry, but it's not the correct behavior: if credentials are wrong we want BigchainDB to fail and give an explicit error message to the user.
You don't have to care about it in this PR, some of us can do it after we merge your PR 😄
See #1336
@tomconte thanks for updating your PR. If you allow us to change your PR I can fix the test coverage and get this merged soon. |
@vrde many thanks! The "Allow edits from maintainers" option is enabled on this PR. |
Hi folks, not sure if this is the right place for this comment but the best practice for connecting to replica sets is to include the host&port information for each primary-electable replica set member (no arbiter or hidden secondary nodes) in the connection string. You may want to consider supporting the MongoDB connection URI, which can be passed directly into the driver: https://docs.mongodb.com/manual/reference/connection-string/. For example, Meteor.js allows users to pass an entire URI via CLI: https://github.com/meteor/meteor/blob/master/tools/cli/commands.js#L374. You can then use the pymongo uri_parser lib if you need specific parts of the URI. |
@vrde Maybe we could create a new issue to look into using the MongoDB connection URI as suggested by @chrisckchang (so that idea doesn't get lost in the comments of a merged PR)? ( #1299 (comment) ) |
@chrisckchang wrote:
AFAIK having multiple servers in the connection string allows the client to have multiple choices if one server is not reachable. Since BigchainDB is a federated system, when the driver fails to connect to the |
This a first stab at providing additional MongoDB connection options (issue #1281).
Turning SSL on is just an option to add in the
MongoClient()
constructor.Authentication needs to be performed at the database level, so that is a separate call to
authenticate()
.Note: currently the MongoDB backend requires admin access (not sure this is a good idea BTW, this might not be possible in shared/hosted environments), so the user must have the right privileges/role.
Here is how to setup a test MongoDB instance:
Run MongoDB with auth:
Create root user:
(exit mongo shell)
Now the
bcdb
user in thebigchain
database hasroot
role.A couple (optional) parameters can be added to
.bigchaindb
to provide the new values, e.g.