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

Add SSL & auth support to MongoDB client #1299

Merged
merged 9 commits into from Mar 28, 2017
Merged

Add SSL & auth support to MongoDB client #1299

merged 9 commits into from Mar 28, 2017

Conversation

tomconte
Copy link
Contributor

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:

docker run --name dev-mongo -d mongo --auth --replSet bigchain-rs

Create root user:

docker exec -it dev-mongo mongo admin
rs.initiate()
db.createUser({ user: 'tconte', pwd: 'Pass1234', roles: [ { role: "root", db: "admin" } ] });

(exit mongo shell)

docker exec -it dev-mongo mongo -u tconte -p Pass1234 admin
use bigchain
db.createUser({ user: 'bcdb', pwd: 'Pass1234', roles: [ { role: "root", db: "admin" } ] });

Now the bcdb user in the bigchain database has root role.

A couple (optional) parameters can be added to .bigchaindb to provide the new values, e.g.

        "ssl": false,
        "login": "bcdb",
        "password": "Pass1234",

@krish7919
Copy link
Contributor

krish7919 commented Mar 17, 2017

@tomconte To clarify, this is between the pymongo driver in BigchainDB and the MongoDB instance?

@codecov-io
Copy link

codecov-io commented Mar 17, 2017

Codecov Report

Merging #1299 into master will increase coverage by <.01%.
The diff coverage is 100%.

@@            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.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 0edb1c1...0471080. Read the comment docs.

@tomconte
Copy link
Contributor Author

@krish7919 yes, so that BigchainDB can connect to a secured MongoDB instance.

@ttmc
Copy link
Contributor

ttmc commented Mar 17, 2017

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
the source of which is in docs/server/source/server-reference/configuration.md.
We'll also have to document how to set up and use authentication (as you did above in this pull request, but we don't consider the set of GitHub comments on pull requests to be our official documentation 😄 ).
I could do that documentation if you like.

Codecov is also complaining about a decline in unit test coverage...

@ttmc ttmc requested review from vrde and ttmc March 17, 2017 09:21
@tomconte
Copy link
Contributor Author

Looks like I need to work on these tests :)

Copy link
Contributor

@vrde vrde left a 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 💃.

@@ -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):
Copy link
Contributor

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.

@@ -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 \
Copy link
Contributor

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):
Copy link
Contributor

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 \
Copy link
Contributor

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
Copy link
Contributor

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

@vrde
Copy link
Contributor

vrde commented Mar 27, 2017

@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.

@tomconte
Copy link
Contributor Author

@vrde many thanks! The "Allow edits from maintainers" option is enabled on this PR.

@vrde
Copy link
Contributor

vrde commented Mar 28, 2017

A couple (optional) parameters can be added to .bigchaindb to provide the new values, e.g.

I addressed it in 699e615, but I noticed I was adding too much scope, so I reverted it in 0471080. I've created #1346 to keep track of this issue.

@vrde vrde merged commit 0471080 into bigchaindb:master Mar 28, 2017
@vrde vrde mentioned this pull request Mar 28, 2017
@chrisckchang
Copy link

chrisckchang commented Apr 25, 2017

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.

@ttmc
Copy link
Contributor

ttmc commented Apr 26, 2017

@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) )

@vrde
Copy link
Contributor

vrde commented Apr 26, 2017

@chrisckchang wrote:

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.

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 mongod it is supposed to talk with, then the driver should not try to connect to other instances, and BigchainDB should not fail to start.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants