@f5devcentral/f5-cloud-libs-consul
Advanced tools
Comparing version 1.0.0 to 1.1.0
@@ -20,2 +20,5 @@ /** | ||
const util = require('util'); | ||
const path = require('path'); | ||
const fs = require('fs'); | ||
const q = require('q'); | ||
@@ -27,2 +30,3 @@ const CloudProvider = require('@f5devcentral/f5-cloud-libs').cloudProvider; | ||
const cryptoUtil = require('@f5devcentral/f5-cloud-libs').cryptoUtil; | ||
const localKeyUtil = require('@f5devcentral/f5-cloud-libs').localKeyUtil; | ||
@@ -78,9 +82,20 @@ let logger; | ||
* | ||
* @param {Object} [providerOptions] - Provider specific options. | ||
* @param {String} [providerOptions.secret] - Base64 encoded Consul credentials. | ||
* @param {Object} [options] - Options for this instance. | ||
* @param {Object} [providerOptions] - Provider specific options. | ||
* @param {String} [providerOptions.secret] - Base64 encoded Consul credentials. | ||
* @param {String} [providerOptions.caBundle] - Absolute TMSH path to a bundle of one or more | ||
* trusted CA certificates in PEM format that | ||
* overrides the default Nodejs certificates. | ||
* | ||
* "/Common/myCert.pem" | ||
* | ||
* @param {Boolean} [providerOptions.rejectUnauthorized=true] - The server certificate is verified | ||
* against the list of supplied/default | ||
* CAs when fetching nodes. | ||
* @param {Object} [options] - Options for this instance. | ||
* | ||
* @returns {Promise} A promise which will be resolved when init is complete. | ||
*/ | ||
ConsulCloudProvider.prototype.init = function init(providerOptions, options) { | ||
const promises = []; | ||
this.initOptions = options || {}; | ||
@@ -93,9 +108,29 @@ this.providerOptions = providerOptions || {}; | ||
this.nodeProvider = new GenericNodeProvider({ logger: this.logger }); | ||
if (this.providerOptions.caBundle) { | ||
this.caBundleTmshPath = this.providerOptions.caBundle; | ||
// Check to make sure the CA bundle exists | ||
promises.push( | ||
getCertFilePath(this.providerOptions.caBundle) | ||
.catch((err) => { | ||
err.message = `caBundle: ${err.message}`; // eslint-disable-line no-param-reassign | ||
return q.reject(err); | ||
}) | ||
); | ||
} | ||
return this.nodeProvider.init({ | ||
propertyPathId: '', | ||
propertyPathIpPrivate: 'Address', | ||
propertyPathIpPublic: 'Address' | ||
}); | ||
this.rejectUnauthorized = true; | ||
if (typeof this.providerOptions.rejectUnauthorized === 'boolean') { | ||
this.rejectUnauthorized = this.providerOptions.rejectUnauthorized; | ||
} | ||
return q.all(promises) | ||
.then(() => { | ||
this.nodeProvider = new GenericNodeProvider({ logger: this.logger }); | ||
return this.nodeProvider.init({ | ||
propertyPathId: '', | ||
propertyPathIpPrivate: 'Address', | ||
propertyPathIpPublic: 'Address' | ||
}); | ||
}); | ||
}; | ||
@@ -129,10 +164,38 @@ | ||
ConsulCloudProvider.prototype.getNodesFromUri = function getNodesFromUri(uri, options) { | ||
const promises = []; | ||
const opts = options || {}; | ||
opts.headers = opts.headers || {}; | ||
if (this.token) { | ||
opts.headers = opts.headers || {}; | ||
opts.headers['X-Consul-Token'] = this.token; | ||
} | ||
return this.nodeProvider.getNodesFromUri(uri, opts) | ||
if (this.caBundleTmshPath) { | ||
const caPromise = getCertFilePath(this.caBundleTmshPath) | ||
.then((caBundleFilePath) => { | ||
const deferred = q.defer(); | ||
fs.readFile(caBundleFilePath, (err, data) => { | ||
if (err) { | ||
deferred.reject(err); | ||
} | ||
deferred.resolve(data); | ||
}); | ||
return deferred.promise; | ||
}) | ||
.then((caBundleData) => { | ||
opts.ca = caBundleData; | ||
}) | ||
.catch((err) => { | ||
err.message = `caBundle: ${err.message}`; // eslint-disable-line no-param-reassign | ||
return q.reject(err); | ||
}); | ||
promises.push(caPromise); | ||
} | ||
opts.rejectUnauthorized = this.rejectUnauthorized; | ||
return q.all(promises) | ||
.then(() => { | ||
return this.nodeProvider.getNodesFromUri(uri, opts); | ||
}) | ||
.then((nodes) => { | ||
@@ -148,2 +211,24 @@ return nodes.map((node) => { | ||
/** | ||
* @param {String} name - Absolute TMSH path to certificate. | ||
* | ||
* "/Common/myCert.pem" | ||
* | ||
* @returns {Promise} A promise which will be resolved with a file path to the certificate. | ||
*/ | ||
function getCertFilePath(tmshPath) { | ||
if (!path.isAbsolute(tmshPath)) { | ||
return q.reject(new Error('TMSH path must be an absolute path')); | ||
} | ||
const splitPath = path.normalize(tmshPath).split(path.sep); | ||
return localKeyUtil.getKeyFilePath(splitPath[1], 'certificate', splitPath.slice(2).join(':')) | ||
.then((filePath) => { | ||
if (typeof filePath !== 'string') { | ||
return q.reject(new Error('certificate does not exist')); | ||
} | ||
return q.resolve(filePath); | ||
}); | ||
} | ||
module.exports = ConsulCloudProvider; |
{ | ||
"name": "@f5devcentral/f5-cloud-libs-consul", | ||
"version": "1.0.0", | ||
"version": "1.1.0", | ||
"description": "Hashicorp Consul implementation of f5-cloud-libs provider specific code", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -20,2 +20,3 @@ /** | ||
const q = require('q'); | ||
const fs = require('fs'); | ||
@@ -25,8 +26,27 @@ const cloudUtil = require('@f5devcentral/f5-cloud-libs').util; | ||
const origRunShellCommand = cloudUtil.runShellCommand; | ||
const origReadFile = fs.readFile; | ||
const caBundle = '/foo/bar/myCert.pem'; | ||
const providerOptions = { | ||
secret: cloudUtil.createBufferFrom('password12345').toString('base64') | ||
secret: cloudUtil.createBufferFrom('password12345').toString('base64'), | ||
caBundle, | ||
rejectUnauthorized: false | ||
}; | ||
const responseCertDir = ':foo:bar:myCert.pem_27774_1\n:foo:bar:myCert.pem_26654_1\n:foo:myCert.pem_16654_1'; | ||
let testProvider; | ||
function mockRunShellCommand(response) { | ||
cloudUtil.runShellCommand = function runShellCommand() { | ||
return response; | ||
}; | ||
} | ||
function mockReadFile(response) { | ||
fs.readFile = function readFile(path, callback) { | ||
callback(null, response); | ||
}; | ||
} | ||
// Our tests cause too many event listeners. Turn off the check. | ||
@@ -37,2 +57,4 @@ process.setMaxListeners(0); | ||
setUp(callback) { | ||
mockRunShellCommand(q(responseCertDir)); | ||
mockReadFile(Buffer.from('foo bar')); | ||
testProvider = new ConsulCloudProvider(); | ||
@@ -43,6 +65,4 @@ callback(); | ||
tearDown(callback) { | ||
Object.keys(require.cache).forEach((key) => { | ||
delete require.cache[key]; | ||
}); | ||
cloudUtil.runShellCommand = origRunShellCommand; | ||
fs.readFile = origReadFile; | ||
callback(); | ||
@@ -64,6 +84,12 @@ }, | ||
testInitSuccess(test) { | ||
test.expect(1); | ||
test.expect(2); | ||
testProvider.init() | ||
.then(() => { | ||
test.strictEqual(testProvider.rejectUnauthorized, true); | ||
test.ok(true); | ||
}) | ||
.catch((err) => { | ||
test.ok(false, err); | ||
}) | ||
.finally(() => { | ||
test.done(); | ||
@@ -74,3 +100,3 @@ }); | ||
testProviderOptions(test) { | ||
test.expect(2); | ||
test.expect(4); | ||
testProvider.init(providerOptions) | ||
@@ -80,5 +106,34 @@ .then(() => { | ||
test.strictEqual(testProvider.token, 'password12345'); | ||
test.strictEqual( | ||
testProvider.caBundleTmshPath, | ||
testProvider.providerOptions.caBundle | ||
); | ||
test.strictEqual(testProvider.rejectUnauthorized, false); | ||
}) | ||
.catch((err) => { | ||
test.ok(false, err); | ||
}) | ||
.finally(() => { | ||
test.done(); | ||
}); | ||
} | ||
}, | ||
testBadCaCertPath(test) { | ||
mockRunShellCommand(q.reject(new Error('Error: Command failed: ls -1t ' | ||
+ '/config/filestore/files_d/foo_d/certificate_d/\nls: cannot access ' | ||
+ '\'/config/filestore/files_d/foo_d/certificate_d/\': No such file or directory'))); | ||
test.expect(1); | ||
testProvider.init(providerOptions) | ||
.then(() => { | ||
test.ok(false, 'should have thrown "Command failed" error'); | ||
}) | ||
.catch((err) => { | ||
test.notStrictEqual(err.message.indexOf('Command failed: ls -1t'), -1); | ||
}) | ||
.finally(() => { | ||
test.done(); | ||
}); | ||
}, | ||
}, | ||
@@ -100,7 +155,15 @@ | ||
'X-Consul-Token': 'password12345' | ||
} | ||
}, | ||
ca: Buffer.from('foo bar'), | ||
rejectUnauthorized: false | ||
}); | ||
test.done(); | ||
return q([]); | ||
}; | ||
testProvider.getNodesFromUri('https://example.com'); | ||
testProvider.getNodesFromUri('https://example.com') | ||
.catch((err) => { | ||
test.ok(false, err); | ||
}) | ||
.finally(() => { | ||
test.done(); | ||
}); | ||
}, | ||
@@ -117,5 +180,7 @@ | ||
Hello: 'World' | ||
} | ||
}, | ||
ca: Buffer.from('foo bar'), | ||
rejectUnauthorized: false | ||
}); | ||
test.done(); | ||
return q([]); | ||
}; | ||
@@ -127,3 +192,9 @@ testProvider.getNodesFromUri('https://example.com', { | ||
} | ||
}); | ||
}) | ||
.catch((err) => { | ||
test.ok(false, err); | ||
}) | ||
.finally(() => { | ||
test.done(); | ||
}); | ||
}, | ||
@@ -175,2 +246,7 @@ | ||
]); | ||
}) | ||
.catch((err) => { | ||
test.ok(false, err); | ||
}) | ||
.finally(() => { | ||
test.done(); | ||
@@ -177,0 +253,0 @@ }); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
146209
9
4038
2
2