Mongoose with Authentication causes timeout - node.js

So we are having an interesting problem. We wanted to add authentication at the MongoDB Layer for more security. But we not getting a favorable outcome.
Pre-Setup
Use mongo shell (against admin table) as root
Switch to desired database (applicationdb)
Execute db.createUser()
Validate user was created successfully
{
"_id" : "applicationdb.appuser",
"user" : "appuser",
"db" : "applicationdb",
"roles" : [
{
"role" : "readWrite",
"db" : "applicationdb"
}
]
}
Scenario 1:
Change mongodb.conf, auth=true
Restart the Mongod service
Connect mongoose using:
mongoose.connect('mongodb://appuser:password#xx.xxx.xxx.xxx:27017/applicationdb');
No errors received for connect, so try to perform a GET through Mongoose causes the operation to timeout without any error (at least that I could find)
Scenario 2:
Change mongodb.conf, auth=false
Restart the Mongod service
Connect mongoose using:
mongoose.connect('mongodb://xx.xxx.xxx.xxx:27017/applicationdb');
No errors received for connect, so try to perform a GET through Mongoose and it returns documents successfully
Why do we get this timeout and never a completed request when using authentication in MongoDB?
Any help would be great, we're at a loss on this one!

You need to restart the mongo service with the --auth option see here
If it doesn't works do this:
Try setting server options in mongoose with keepAlive set. See here and here.

Related

Issue in connecting mongodatabase

I am using mongoose version 4.13.6 and mongodb from compose, and below is my code for connecting to mongo database.
mongoose.createConnection('mongodb://[user]:[pass]#[host1]:[port1],[host2]:[port2]/dbnamme?ssl=true', {});
But when I run this am getting error,
MongoError: no primary found in replicaset
Dont know why is that, can anyone help me in this?
So the short answer is this:
... all drivers are not equal and some make assumptions when multiple hosts are specified. For example, the Meteor/Node.js MongoDB driver sees two hosts and assumes it is talking to a replicaset. Upon connecting the driver asks which host is master and then errors out because neither of them are. The simple fix for this is to use one host in the URI ..
https://www.compose.com/articles/connecting-to-the-new-mongodb-at-compose/#drivingtoyourfirstdatabase
So when you create a connection, simply use one of the connection URIs for the database you want to connect to like:
var uri = "mongodb://<username>:<password>#[host]:[port]/<db_name>?ssl=true";
mongoose.createConnection(uri);

dbAdmin user can not execute list command on MongoDB 3

I created a new Database called meanapp.and I add a user meanUser for readWrite role.
When I connect to the mongo DB via mongoose it connects but I got Error as
{ MongoError: not authorized on meanapp to execute command { listIndexes:"jobs", cursor:{}}
So , I tried to grant dbAdmin role to meanUser by executing
db.grantRolesToUser('meanUser',[{role:"dbAdmin", db: "meanapp" }] )
And tried restarting mongo deamon.
And db.getUser('meanUser') shows the dbAdmin role for that user.
Still same error.
Could any one faced this issue or I am missing something?
I refered mongo docs also I could get an idea of this issue.
Thanks for your effort.

Cannot fetch from or insert data to mongodb using mongoose

I wrote a node web app and created a mongoDb database on my local system. I was using the following code to connect to local mongodb from node js
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/db_name'); //local
And everything was working fine on my local machine. So I went on and created an mlab account and created a database. But when I tried to run the code by changing the connection string, connections are still established I believe. But the find and save requests are not invoking the callbacks, even no errors shows up. All requests are getting timed out.
var mongoose = require('mongoose');
mongoose.connect("mongodb://user:pass#ds036789.mlab.com:36789/db_name"); //mlab
Another thing I noticed is that I cannot ping ds036789.mlab.com. But TCP connections are succeeding when I tried the nc command
nc -w 3 -v ds036789.mlab.com 36789
I even tried deploying to azure. Which doesn't work either. Any help is much appreciated. Thanks.
EDIT:
Not being able to ping was due to the fact that I used azure hosting. It is expected. And I also found out that I get this error while trying to connect :
connection error: { [MongoError: auth failed] name: 'MongoError', ok: 0, errmsg: 'auth failed', code: 18 }
Credentials are correct though.
From the error mesasge it seems like you are using invalid auth details
This is most likely happen when you do not create username and password for individual database i.e, db_name in you case.
Check mLabs account and create username and password for db_name database and update your connection string.
According to the error information, as #Astro said, it seems to be caused by using invalid auth user/password which be created for database.
Did you create a new user for connecting the database, not account user for mlab? Such as the figures below shown.
Fig 1. A database user is required for connecting
Fig 2. Users list for the database
Hope it helps.
I figured out the issue, it wasn't an issue with the credentials. It was an issue with the mongoose version. The mongoose version I used didn't support the authentication. I had to remove the package and reinstall the latest version. with
node install mongoose#latest
Hope it helps someone. And thanks for the answers :)

Where will the database be created in mongodb?

mongos.connect("mongodb://localhost/Company")
On executing the above command as per the document if Company database exits then it will be connected to the nodejs or else database will be created and then connection is made.
My question is where will this newly created database exist in mongodb data folder or nodejs application folder
So your application is connecting to localhost where you have MongoDB server running on the default port (27017). If you are connected to a MongoDB cluster using a mongos process you will have to see where the mongod (database process itself) is/are running. Let's take the simple case where your mongod is running locally.
So I expect you have started your MongoDB instance with all default values, the "database files" are created in /data/db ( \data\db ).
This means that in your case, you should see the "Company" db files in this folder, something like :
/data/db/Company.0
/data/db/Company.ns
Let's now give you more informations about this:
When you start your database you start a "mongod", that use a parameter named dbpath (see http://docs.mongodb.org/manual/reference/configuration-options/#storage.dbPath ) that is defaulted to /data/db
You can override it to any existing folder to adapt to your environment.
So nothing is "created" inside your application, everything in done at the"mongodb" (database) level.

Trouble getting Mongoose to reconnect to nodes and send requests to secondaries

A common connection string for mongoose connecting to a replica set is something like follows
var connection = mongoose.createConnection("mongodb://db_1:27017/client_test,mongodb://db_2:27017/client_test", {
replSet : { rs_name : "rs0", poolSize : 5, socketOptions : { keepAlive : 1 } }
}, function(err) {
if (err) { throw err; }
});
The problem with that is if one of the two hosts is down, then it will fail to connect. If you only specify one host, then no requests end up getting sent to secondaries.
Here's my proof for that claim. If you specify one host, and setup your replica set so that there is one primary and an arbiter and then perform a query such as
myApi.find({}).slaveOk().read("s").exec(function(err, docs) {
console.log(docs)
})
It will return results. Well, since I am specifying "s" (secondary), this query should throw an error because there are no running secondaries. In addition, if you bring the secondary online and then do db.currentOp(true), you will never see any actual queries sent it's way.
The moment you alter the connection string to specify every host then you will see connections go to the secondary. The dilemma is that now, because you had to specify the additional host in the connection string, in the event a secondary was offline, it would fail to connect and we've now lost failover (or the entire point to replica sets)
I can't determine if this is a configuration mistake on my part, a bug in Mongoose, or a conceptual flaw in my understanding of the way replica sets function. From some of the docs, they seem to state that reading from secondaries is basically a bad idea, but the reason for doing so is usually issues with stale data. My issue doesn't have anything to do with stale date, I can't figure out a way to setup the system so that I can get queries to secondaries without losing failover capacity.
1.connection string just defines seed servers, mongodb driver tries to connect to these servers and get information about other servers in replicaSet( by calling rs.status()). You could have replicaSet with 5 nodes, but specify only one in connection string, but driver would be able to find four others if server from connection string is available.
2.My proposal is to use secondaryPreferred instead of just secondary, so that in case there is no secondary available, request would be done to primary.
Ok, I believe I have solved all of my problems. Here is what I learned.
Specify all possible replica nodes in your connection string, otherwise Mongoose will never send requests there. Mongoose has a specific format for this which is different than the node-mongodb-native driver. Example below.
In order to prevent it from hanging forever if one of the nodes is down on bootup you need to specify connectTimeoutMS in the 'replset' options, then it will only wait that long for responses from each nodes on initial connection. If the node comes online at a later date, it will still be available.
The host name entries in your mongodb replica setup need to match the hostname entries in the connection string from your application and all hostnames need to be accessible from all parties (mongo to mongo and application to mongo). In my case I had aliased the hostnames from mongo to mongo as mongo1:27017, mongo2:27017, and mongo3:27017. My application server used a connection string with IPs. Mongoose was attempting to re-initate the connection using the mongo1:27017 hostname (which my application server could not reach) rather than the IP address I specified in the connection string. This resulted in it never re-connecting to a node it lost contact with. It is possible had I used hostnames that the application could reach it still would have worked, but I think it's a best practice to make the connection string and the replica setup identical to remove possibly places for error.
On the mongodb node that you rs.initiate() you might need to update the hostname to be a value that all boxes (other mongodbs and application server can reach). By default it will likely end up with a hostname like localhost, which means something different on each machine. This can be from that boxes mongo shell like so.
Example:
// from mongo shell
conf = rs.conf()
conf.members[0].host = "mongo1:27017"
rs.reconfig(conf)
Final functioning connection string which successfully fails over between nodes, including throwing errors if a query is destined for a secondary but there aren't secondaries.
var connection = mongoose.createConnection("mongodb://mongo1:27017/client_test,mongo2:27017/client_test,mongo3:27017/client_test", {
replset : { rs_name : "rs0", poolSize : 5, socketOptions : { keepAlive : 1, connectTimeoutMS : 1000 } },
}, function(err) {
if (err) { throw err; }
});
Working replica setup
{
"_id" : "rs0",
"version" : 4,
"members" : [
{
"_id" : 0,
"host" : "mongo1:27017"
},
{
"_id" : 1,
"host" : "mongo2:27017"
},
{
"_id" : 2,
"host" : "mongo3:27017",
"arbiterOnly" : true
}
]
}
I had some issue similar to yours while dealing with replica, in my case I had 1 primary node with a priority of 10, 1 secondary priority of 0(for analytics) and an arbiter.
My writes would fail after reconnecting the primary instance and I went through a lot trying to fix it here's the most important thing I learnt:
When my primary is down or unreacheable, there has to be another member eligible to become primary.(At least 2members in my set has to have a priority >= 1).
If I have only arbiters, hidden, or members with a priority of 0,
queries will get stuck even after I reconnect my primary, my client is
unable to complete write queries. Read queries would still work, but
write wouldn't.
This is what I faced with mongoose, even with keepalive, autoreconnect and all the socket and connection timeout MS set.
Hopefully this helps.

Resources