Search code examples
mongodbsails.jsdocker-composesails-mongo

How to create an authenticated mongo database in a mongo container with minimal config


I'm using Sails 0.12.3 and mongo 3.2.7

Here's my config/connections.js.

mongo: {
 adapter: 'sails-mongo',
 host: 'database',
 port: 27017,
 user: 'user', //optional
 password: 'password', //optional
 database: 'db' //optional
}

and my docker-compose.yml

version: '2'
services:
  myservice:
    //blabla
    links:
     - database
  database:
    image: 'mongo:latest'
    container_name: 'database'
    environment:
      MONGODB_PASSWORD: "password"
      MONGODB_USER: "user"
      MONGODB_DATABASE: "db"

The problem comes whenever I build the containers with docker-compose up --build, the error comes from the sails-mongo module.

error: A hook (`orm`) failed to load!
error: Error: Failed to connect to MongoDB.  Are you sure your configured Mongo instance is running?
Error details:
{ MongoError: Authentication failed.
at Function.MongoError.create (/app/node_modules/mongodb-core/lib/error.js:31:11)
at commandCallback (/app/node_modules/mongodb-core/lib/topologies/server.js:929:66)
at Callbacks.emit (/app/node_modules/mongodb-core/lib/topologies/server.js:116:3)
at .messageHandler (/app/node_modules/mongodb-core/lib/topologies/server.js:282:23)
at Socket.<anonymous> (/app/node_modules/mongodb-core/lib/connection/connection.js:273:22)
at emitOne (events.js:96:13)
at Socket.emit (events.js:188:7)
at readableAddChunk (_stream_readable.js:172:18)
at Socket.Readable.push (_stream_readable.js:130:10)
at TCP.onread (net.js:542:20)
name: 'MongoError',
message: 'Authentication failed.',
ok: 0,
code: 18,
errmsg: 'Authentication failed.' }

Is there any extra configuration I need to just run the containers? I understand that Mongo doesn't create a database unless there's some data to store but I'm not sure if this is related at all.


Solution

  • First lets address your problem: Authentication failed. sails-mongo cannot connect to mongodb. Why is that? I see that you provided some environment variables to the mongodb docker container:

    environment:
      MONGODB_PASSWORD: "password"
      MONGODB_USER: "user"
      MONGODB_DATABASE: "db"
    

    However this is not documented in https://hub.docker.com/_/mongo/. The mongodb container is ignoring this values, thus no authentication is configured. sails-mongo will try to authenticate, however this fails because no such user exists. You can test it by simply commenting out user and password in config/connections.js. Your service will be able to connect.

    In https://hub.docker.com/_/mongo/ it is documented how you enable authentication:

    docker run --name some-mongo -d mongo --auth
    

    Adding user:

    $ docker exec -it some-mongo mongo admin
    connecting to: admin
    > db.createUser({ user: 'jsmith', pwd: 'some-initial-password', roles: [ { role: "userAdminAnyDatabase", db: "admin" } ] });
    Successfully added user: {
        "user" : "jsmith",
        "roles" : [
            {
                "role" : "userAdminAnyDatabase",
                "db" : "admin"
            }
        ]
    }
    

    To automate this you could put the user creation in a shell script and trigger it in docker compose.


    There is another solution that you can apply. First ask yourself, why do you need authentication at all? Do you want to access the mongodb instance from any ip address? Then you need authentication. If not you might leave authentication disabled. If mongodb binds to localhost, nobody outside of localhost will be able to connect to it.

    With docker you can harden this even further: any access to the mongodb instance is forbidden. Only containers that are on the same network as mongodb may connect.

    Using docker-compose version 2 network feature:

    version: "2"
    
    services:
    
      my-app:
        image: myHubRepo/my-app:v1.X.Y
        container_name: my-app
        environment:
          - MONGODB=mongodb:27017
          - APP_PORT=80
          - STAGE=production
        expose:
          - "80"
        networks:
          - back-tier
        restart: always
    
      mongodb:
        image: mongo
        volumes:
          - mongodb-data:/data/db
        networks:
          - back-tier
        restart: always
    
    volumes:
      mongodb-data:
        driver: local
    
    networks:
      back-tier:
    

    Mongodb is not accessible from outside of its very own container despite from containers that are on the same network back-tier.

    Adjust env/production.js accordingly:

    module.exports = {
        connections: {
            prodMongoDb: {
                adapter: 'sails-mongo',
                url: 'mongodb://' + process.env.MONGODB + '/my_app'
            }
        },
        models: {
            connection: 'prodMongoDb'
        },
        port: process.env.APP_PORT || 8080,
        log: {
            level: "warn"
        }
    };
    

    For more information on networks with compose: https://docs.docker.com/compose/networking/