diff --git a/.evergreen/config.yml b/.evergreen/config.yml index 73611cadee..a219be25cc 100644 --- a/.evergreen/config.yml +++ b/.evergreen/config.yml @@ -172,6 +172,15 @@ functions: rm -f ./prepare_atlas_connectivity.sh NODE_LTS_NAME='${NODE_LTS_NAME}' bash ${PROJECT_DIRECTORY}/.evergreen/run-atlas-tests.sh + run kerberos tests: + - command: shell.exec + type: test + params: + working_dir: src + script: | + KRB5_KEYTAB='${gssapi_auth_keytab_base64}' KRB5_PRINCIPAL='${gssapi_auth_principal}' \ + MONGODB_URI='${gssapi_auth_mongodb_uri}' \ + NODE_LTS_NAME='${NODE_LTS_NAME}' bash ${PROJECT_DIRECTORY}/.evergreen/run-kerberos-tests.sh run ldap tests: - command: shell.exec type: test @@ -1065,6 +1074,20 @@ tasks: commands: - func: install dependencies - func: run atlas tests + - name: test-auth-kerberos + tags: + - auth + - kerberos + commands: + - func: install dependencies + - func: run kerberos tests + - name: test-auth-ldap + tags: + - auth + - ldap + commands: + - func: install dependencies + - func: run ldap tests - name: test-tls-support tags: - tls-support @@ -1076,13 +1099,6 @@ tasks: VERSION: latest TOPOLOGY: server - func: run tls tests - - name: test-auth-ldap - tags: - - auth - - ldap - commands: - - func: install dependencies - - func: run ldap tests - name: test-latest-ocsp-valid-cert-server-staples tags: - ocsp @@ -1377,8 +1393,9 @@ buildvariants: - test-2.6-replica_set-unified - test-2.6-sharded_cluster-unified - test-atlas-connectivity - - test-tls-support + - test-auth-kerberos - test-auth-ldap + - test-tls-support - test-latest-ocsp-valid-cert-server-staples - test-latest-ocsp-invalid-cert-server-staples - test-latest-ocsp-valid-cert-server-does-not-staple @@ -1490,6 +1507,7 @@ buildvariants: - test-2.6-replica_set-unified - test-2.6-sharded_cluster-unified - test-atlas-connectivity + - test-auth-kerberos - test-auth-ldap - name: ubuntu-14.04-dubnium display_name: Ubuntu 14.04 Node Dubnium @@ -1565,8 +1583,9 @@ buildvariants: - test-3.2-replica_set-unified - test-3.2-sharded_cluster-unified - test-atlas-connectivity - - test-tls-support + - test-auth-kerberos - test-auth-ldap + - test-tls-support - test-latest-ocsp-valid-cert-server-staples - test-latest-ocsp-invalid-cert-server-staples - test-latest-ocsp-valid-cert-server-does-not-staple diff --git a/.evergreen/config.yml.in b/.evergreen/config.yml.in index 4326a45491..e47f01532a 100644 --- a/.evergreen/config.yml.in +++ b/.evergreen/config.yml.in @@ -211,6 +211,16 @@ functions: NODE_LTS_NAME='${NODE_LTS_NAME}' bash ${PROJECT_DIRECTORY}/.evergreen/run-atlas-tests.sh + "run kerberos tests": + - command: shell.exec + type: test + params: + working_dir: src + script: | + KRB5_KEYTAB='${gssapi_auth_keytab_base64}' KRB5_PRINCIPAL='${gssapi_auth_principal}' \ + MONGODB_URI='${gssapi_auth_mongodb_uri}' \ + NODE_LTS_NAME='${NODE_LTS_NAME}' bash ${PROJECT_DIRECTORY}/.evergreen/run-kerberos-tests.sh + "run ldap tests": - command: shell.exec type: test diff --git a/.evergreen/generate_evergreen_tasks.js b/.evergreen/generate_evergreen_tasks.js index c28eadef28..297892658f 100644 --- a/.evergreen/generate_evergreen_tasks.js +++ b/.evergreen/generate_evergreen_tasks.js @@ -106,11 +106,21 @@ TASKS.push( { name: 'test-atlas-connectivity', tags: ['atlas-connect'], + commands: [{ func: 'install dependencies' }, { func: 'run atlas tests' }] + }, + { + name: 'test-auth-kerberos', + tags: ['auth', 'kerberos'], commands: [ { func: 'install dependencies' }, - { func: 'run atlas tests' } + { func: 'run kerberos tests' } ] }, + { + name: 'test-auth-ldap', + tags: ['auth', 'ldap'], + commands: [{ func: 'install dependencies' }, { func: 'run ldap tests' }] + }, { name: 'test-tls-support', tags: ['tls-support'], @@ -126,11 +136,6 @@ TASKS.push( }, { func: 'run tls tests' } ] - }, - { - name: 'test-auth-ldap', - tags: ['auth', 'ldap'], - commands: [{ func: 'install dependencies' }, { func: 'run ldap tests' }] } ); diff --git a/.evergreen/run-kerberos-tests.sh b/.evergreen/run-kerberos-tests.sh new file mode 100644 index 0000000000..665d520efc --- /dev/null +++ b/.evergreen/run-kerberos-tests.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +set -o errexit # Exit the script with error if any of the commands fail + +export PROJECT_DIRECTORY="$(pwd)" +NODE_ARTIFACTS_PATH="${PROJECT_DIRECTORY}/node-artifacts" +export NVM_DIR="${NODE_ARTIFACTS_PATH}/nvm" +[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" + +# set up keytab +mkdir -p "$(pwd)/.evergreen" +touch "$(pwd)/.evergreen/krb5.conf.empty" +export KRB5_CONFIG="$(pwd)/.evergreen/krb5.conf.empty" +echo "Writing keytab" +# DON'T PRINT KEYTAB TO STDOUT +set +o verbose +if [[ "$OSTYPE" == "darwin"* ]]; then + echo ${KRB5_KEYTAB} | base64 -D > "$(pwd)/.evergreen/drivers.keytab" +else + echo ${KRB5_KEYTAB} | base64 -d > "$(pwd)/.evergreen/drivers.keytab" +fi +echo "Running kinit" +kinit -k -t "$(pwd)/.evergreen/drivers.keytab" -p ${KRB5_PRINCIPAL} + +npm install kerberos +npm run check:kerberos + +# destroy ticket +kdestroy diff --git a/package.json b/package.json index 2b0add9a81..17945a0a01 100644 --- a/package.json +++ b/package.json @@ -65,8 +65,9 @@ }, "scripts": { "atlas": "mocha --opts '{}' ./test/manual/atlas_connectivity.test.js", - "check:tls": "mocha --opts '{}' test/manual/tls_support.test.js", + "check:kerberos": "mocha --opts '{}' test/manual/kerberos.test.js", "check:ldap": "mocha --opts '{}' test/manual/ldap.test.js", + "check:tls": "mocha --opts '{}' test/manual/tls_support.test.js", "test": "npm run lint && mocha --recursive test/functional test/unit test/core", "test-nolint": "mocha --recursive test/functional test/unit test/core", "coverage": "istanbul cover mongodb-test-runner -- -t 60000 test/core test/unit test/functional", diff --git a/test/functional/kerberos.test.js b/test/functional/kerberos.test.js deleted file mode 100644 index bb9b2dc50d..0000000000 --- a/test/functional/kerberos.test.js +++ /dev/null @@ -1,438 +0,0 @@ -'use strict'; - -var format = require('util').format; -var test = require('./shared').assert; -var setupDatabase = require('./shared').setupDatabase; - -// You need to set up the kinit tab first -// https://wiki.mongodb.com/pages/viewpage.action?title=Testing+Kerberos&spaceKey=DH -// kinit -p drivers@LDAPTEST.10GEN.CC -// password: (not shown) - -describe('Kerberos', function() { - before(function() { - return setupDatabase(this.configuration); - }); - - /** - * @ignore - */ - it('Should Correctly Authenticate using kerberos with MongoClient', { - metadata: { requires: { topology: 'kerberos', os: '!win32' } }, - - // The actual test we wish to run - test: function(done) { - var configuration = this.configuration; - - // KDC Server - var server = 'ldaptest.10gen.cc'; - var principal = 'drivers@LDAPTEST.10GEN.CC'; - var urlEncodedPrincipal = encodeURIComponent(principal); - const url = format( - 'mongodb://%s@%s/kerberos?authMechanism=GSSAPI&gssapiServiceName=mongodb&maxPoolSize=1', - urlEncodedPrincipal, - server - ); - - const client = configuration.newClient(url); - client.connect(function(err, client) { - test.equal(null, err); - var db = client.db('kerberos'); - - db.collection('test') - .find() - .toArray(function(err, docs) { - test.equal(null, err); - test.ok(true, docs[0].kerberos); - - client.close(done); - }); - }); - } - }); - - /** - * @ignore - */ - it('Validate that SERVICE_REALM and CANONICALIZE_HOST_NAME is passed in', { - metadata: { requires: { topology: 'kerberos', os: '!win32' } }, - - // The actual test we wish to run - test: function(done) { - var configuration = this.configuration; - - // KDC Server - var server = 'ldaptest.10gen.cc'; - var principal = 'drivers@LDAPTEST.10GEN.CC'; - var urlEncodedPrincipal = encodeURIComponent(principal); - const url = format( - 'mongodb://%s@%s/kerberos?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:mongodb,CANONICALIZE_HOST_NAME:false,SERVICE_REALM:windows&maxPoolSize=1', - urlEncodedPrincipal, - server - ); - - const client = configuration.newClient(url); - client.connect(function(err, client) { - test.equal(null, err); - var db = client.db('kerberos'); - - db.collection('test') - .find() - .toArray(function(err, docs) { - test.equal(null, err); - test.ok(true, docs[0].kerberos); - - client.close(done); - }); - }); - } - }); - - /** - * @ignore - */ - it( - 'Should Correctly Authenticate using kerberos with MongoClient and authentication properties', - { - metadata: { requires: { topology: 'kerberos', os: '!win32' } }, - - // The actual test we wish to run - test: function(done) { - var configuration = this.configuration; - - // KDC Server - var server = 'ldaptest.10gen.cc'; - var principal = 'drivers@LDAPTEST.10GEN.CC'; - var urlEncodedPrincipal = encodeURIComponent(principal); - const url = format( - 'mongodb://%s@%s/kerberos?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:mongodb,CANONICALIZE_HOST_NAME:false&maxPoolSize=1', - urlEncodedPrincipal, - server - ); - - const client = configuration.newClient(url); - client.connect(function(err, client) { - test.equal(null, err); - var db = client.db('kerberos'); - - db.collection('test') - .find() - .toArray(function(err, docs) { - test.equal(null, err); - test.ok(true, docs[0].kerberos); - - client.close(done); - }); - }); - } - } - ); - - /** - * @ignore - */ - it('Should Correctly Authenticate using kerberos with MongoClient and then reconnect', { - metadata: { requires: { topology: 'kerberos', os: '!win32' } }, - - // The actual test we wish to run - test: function(done) { - var configuration = this.configuration; - - // KDC Server - var server = 'ldaptest.10gen.cc'; - var principal = 'drivers@LDAPTEST.10GEN.CC'; - var urlEncodedPrincipal = encodeURIComponent(principal); - const url = format( - 'mongodb://%s@%s/kerberos?authMechanism=GSSAPI&gssapiServiceName=mongodb&maxPoolSize=5', - urlEncodedPrincipal, - server - ); - - const client = configuration.newClient(url); - client.connect(function(err, client) { - test.equal(null, err); - - client - .db('kerberos') - .collection('test') - .findOne(function(err, doc) { - test.equal(null, err); - test.equal(true, doc.kerberos); - - client.topology.once('reconnect', function() { - // Await reconnect and re-authentication - client - .db('kerberos') - .collection('test') - .findOne(function(err, doc) { - test.equal(null, err); - test.equal(true, doc.kerberos); - - // Attempt disconnect again - client.topology.connections()[0].destroy(); - - // Await reconnect and re-authentication - client - .db('kerberos') - .collection('test') - .findOne(function(err, doc) { - test.equal(null, err); - test.equal(true, doc.kerberos); - - client.close(done); - }); - }); - }); - - // Force close - client.topology.connections()[0].destroy(); - }); - }); - } - }); - - /** - * @ignore - */ - it('Should Correctly Authenticate authenticate method manually', { - metadata: { requires: { topology: 'kerberos', os: '!win32' } }, - - // The actual test we wish to run - test: function(done) { - var configuration = this.configuration; - var MongoClient = configuration.require.MongoClient, - Server = configuration.require.Server; - - // KDC Server - var server = 'ldaptest.10gen.cc'; - var principal = 'drivers@LDAPTEST.10GEN.CC'; - - var client = new MongoClient(new Server(server, 27017), { - w: 1, - user: principal, - authMechanism: 'GSSAPI' - }); - client.connect(function(err, client) { - test.equal(null, err); - - // Await reconnect and re-authentication - client - .db('kerberos') - .collection('test') - .findOne(function(err, doc) { - test.equal(null, err); - test.equal(true, doc.kerberos); - - client.close(done); - }); - }); - } - }); - - /** - * @ignore - */ - it('Should Fail to Authenticate due to illegal service name', { - metadata: { requires: { topology: 'kerberos', os: '!win32' } }, - - // The actual test we wish to run - test: function(done) { - var configuration = this.configuration; - - // KDC Server - var server = 'ldaptest.10gen.cc'; - var principal = 'drivers@LDAPTEST.10GEN.CC'; - var urlEncodedPrincipal = encodeURIComponent(principal); - const url = format( - 'mongodb://%s@%s/test?authMechanism=GSSAPI&gssapiServiceName=mongodb2&maxPoolSize=1', - urlEncodedPrincipal, - server - ); - - const client = configuration.newClient(url); - client.connect(function(err) { - test.ok(err != null); - done(); - }); - } - }); - - /** - * @ignore - */ - it('Should Correctly Authenticate on Win32 using kerberos with MongoClient', { - metadata: { requires: { topology: 'kerberos', os: 'win32' } }, - - // The actual test we wish to run - test: function(done) { - var configuration = this.configuration; - - // KDC Server - var server = 'ldaptest.10gen.cc'; - var principal = 'drivers@LDAPTEST.10GEN.CC'; - var pass = process.env['LDAPTEST_PASSWORD']; - - if (pass == null) throw new Error('The env parameter LDAPTEST_PASSWORD must be set'); - var urlEncodedPrincipal = encodeURIComponent(principal); - const url = format( - 'mongodb://%s:%s@%s/kerberos?authMechanism=GSSAPI&maxPoolSize=1', - urlEncodedPrincipal, - pass, - server - ); - - const client = configuration.newClient(url); - client.connect(function(err, client) { - test.equal(null, err); - var db = client.db('kerberos'); - - db.collection('test') - .find() - .toArray(function(err, docs) { - test.equal(null, err); - test.ok(true, docs[0].kerberos); - - client.close(done); - }); - }); - } - }); - - /** - * @ignore - */ - it('Should Correctly Authenticate using kerberos on Win32 with MongoClient and then reconnect', { - metadata: { requires: { topology: 'kerberos', os: 'win32' } }, - - // The actual test we wish to run - test: function(done) { - var configuration = this.configuration; - - // KDC Server - var server = 'ldaptest.10gen.cc'; - var principal = 'drivers@LDAPTEST.10GEN.CC'; - var pass = process.env['LDAPTEST_PASSWORD']; - if (pass == null) throw new Error('The env parameter LDAPTEST_PASSWORD must be set'); - var urlEncodedPrincipal = encodeURIComponent(principal); - const url = format( - 'mongodb://%s:%s@%s/kerberos?authMechanism=GSSAPI&maxPoolSize=5', - urlEncodedPrincipal, - pass, - server - ); - - const client = configuration.newClient(url); - client.connect(function(err, client) { - test.equal(null, err); - - client - .db('kerberos') - .collection('test') - .findOne(function(err, doc) { - test.equal(null, err); - test.equal(true, doc.kerberos); - - client.topology.once('reconnect', function() { - // Await reconnect and re-authentication - client - .db('kerberos') - .collection('test') - .findOne(function(err, doc) { - test.equal(null, err); - test.equal(true, doc.kerberos); - - // Attempt disconnect again - client.topology.connections()[0].destroy(); - - // Await reconnect and re-authentication - client - .db('kerberos') - .collection('test') - .findOne(function(err, doc) { - test.equal(null, err); - test.equal(true, doc.kerberos); - - client.close(done); - }); - }); - }); - - // Force close - client.topology.connections()[0].destroy(); - }); - }); - } - }); - - /** - * @ignore - */ - it('Should Correctly Authenticate on Win32 authenticate method manually', { - metadata: { requires: { topology: 'kerberos', os: 'win32' } }, - - // The actual test we wish to run - test: function(done) { - var configuration = this.configuration; - var MongoClient = configuration.require.MongoClient, - Server = configuration.require.Server; - - // KDC Server - var server = 'ldaptest.10gen.cc'; - var principal = 'drivers@LDAPTEST.10GEN.CC'; - var pass = process.env['LDAPTEST_PASSWORD']; - if (pass == null) throw new Error('The env parameter LDAPTEST_PASSWORD must be set'); - var client = new MongoClient(new Server(server, 27017), { - w: 1, - user: principal, - password: pass, - authMechanism: 'GSSAPI' - }); - - client.connect(function(err, client) { - test.equal(null, err); - var db = client.db('kerberos'); - - db.collection('test') - .find() - .toArray(function(err, docs) { - test.equal(null, err); - test.ok(true, docs[0].kerberos); - - client.close(done); - }); - }); - } - }); - - /** - * @ignore - */ - it('Should Fail to Authenticate due to illegal service name on win32', { - metadata: { requires: { topology: 'kerberos', os: 'win32' } }, - - // The actual test we wish to run - test: function(done) { - var configuration = this.configuration; - - // KDC Server - var server = 'ldaptest.10gen.cc'; - var principal = 'drivers@LDAPTEST.10GEN.CC'; - var pass = process.env['LDAPTEST_PASSWORD']; - - if (pass == null) throw new Error('The env parameter LDAPTEST_PASSWORD must be set'); - var urlEncodedPrincipal = encodeURIComponent(principal); - const url = format( - 'mongodb://%s:%s@%s/kerberos?authMechanism=GSSAPI&gssapiServiceName=mongodb2&maxPoolSize=1', - urlEncodedPrincipal, - pass, - server - ); - - const client = configuration.newClient(url); - client.connect(function(err) { - test.ok(err != null); - done(); - }); - } - }); -}); diff --git a/test/manual/kerberos.test.js b/test/manual/kerberos.test.js new file mode 100644 index 0000000000..b551474016 --- /dev/null +++ b/test/manual/kerberos.test.js @@ -0,0 +1,86 @@ +'use strict'; +const MongoClient = require('../..').MongoClient; +const chai = require('chai'); +const expect = chai.expect; + +describe('Kerberos', function() { + if (process.env.MONGODB_URI == null) { + console.log('skipping Kerberos tests, MONGODB_URI environment variable is not defined'); + return; + } + let krb5Uri = process.env.MONGODB_URI; + if (process.platform === 'win32') { + console.log('Win32 run detected'); + if (process.env.LDAPTEST_PASSWORD == null) { + throw new Error('The env parameter LDAPTEST_PASSWORD must be set'); + } + const parts = krb5Uri.split('@', 2); + krb5Uri = `${parts[0]}:${process.env.LDAPTEST_PASSWORD}@${parts[1]}`; + } + + it('should authenticate with original uri', function(done) { + const client = new MongoClient(krb5Uri); + client.connect(function(err, client) { + expect(err).to.not.exist; + client + .db('kerberos') + .collection('test') + .find() + .toArray(function(err, docs) { + expect(err).to.not.exist; + expect(docs[0].kerberos).to.be.true; + + client.close(done); + }); + }); + }); + + it('validate that SERVICE_REALM and CANONICALIZE_HOST_NAME can be passed in', function(done) { + const client = new MongoClient( + `${krb5Uri}&authMechanismProperties=SERVICE_NAME:mongodb,CANONICALIZE_HOST_NAME:false,SERVICE_REALM:windows&maxPoolSize=1` + ); + client.connect(function(err, client) { + expect(err).to.not.exist; + client + .db('kerberos') + .collection('test') + .find() + .toArray(function(err, docs) { + expect(err).to.not.exist; + expect(docs[0].kerberos).to.be.true; + + client.close(done); + }); + }); + }); + + it('should authenticate with additional authentication properties', function(done) { + const client = new MongoClient( + `${krb5Uri}&authMechanismProperties=SERVICE_NAME:mongodb,CANONICALIZE_HOST_NAME:false&maxPoolSize=1` + ); + client.connect(function(err, client) { + expect(err).to.not.exist; + client + .db('kerberos') + .collection('test') + .find() + .toArray(function(err, docs) { + expect(err).to.not.exist; + expect(docs[0].kerberos).to.be.true; + + client.close(done); + }); + }); + }); + + it('should fail to authenticate with bad credentials', function(done) { + const client = new MongoClient( + krb5Uri.replace(encodeURIComponent(process.env.KRB5_PRINCIPAL), 'bad%40creds.cc') + ); + client.connect(function(err) { + expect(err).to.exist; + expect(err.message).to.match(/Authentication failed/); + done(); + }); + }); +});