From d0ac89bf5ffbb53baded7623ad20feaff92d2f45 Mon Sep 17 00:00:00 2001 From: Luke Nimtz Date: Fri, 22 Sep 2017 12:25:00 -0700 Subject: [PATCH] Support for client-side SSL certs --- package.json | 1 + src/utils/request-api.js | 23 +++++++++++++++++-- test/request-api.spec.js | 48 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 9499fefb1..1ff43b163 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "hapi": "^16.5.2", "interface-ipfs-core": "~0.31.19", "ipfsd-ctl": "~0.23.0", + "pem": "^1.11.2", "pre-commit": "^1.2.2", "socket.io": "^2.0.3", "socket.io-client": "^2.0.3", diff --git a/src/utils/request-api.js b/src/utils/request-api.js index 5f78b1e07..3344a89c0 100644 --- a/src/utils/request-api.js +++ b/src/utils/request-api.js @@ -10,9 +10,13 @@ const getFilesStream = require('./get-files-stream') const streamToValue = require('./stream-to-value') const streamToJsonValue = require('./stream-to-json-value') const request = require('./request') +const HttpsAgent = require('https').Agent // -- Internal +// possible agent for client side certs +var certifiedHttpsAgent + function parseError (res, cb) { const error = new Error(`Server responded with ${res.statusCode}`) @@ -147,13 +151,28 @@ function requestAPI (config, options, callback) { return qsDefaultEncoder(data) } }) - const req = request(config.protocol)({ + + const reqOpts = { hostname: config.host, path: `${config['api-path']}${options.path}?${qs}`, port: config.port, method: method, headers: headers - }, onRes(options.buffer, callback)) + } + + if (config.key || config.cert || config.pfx || config.ca) { + certifiedHttpsAgent = certifiedHttpsAgent || new HttpsAgent() + Object.assign(reqOpts, { + key: config.key, + cert: config.cert, + pfx: config.pfx, + passphrase: config.passphrase, + ca: config.ca, + agent: certifiedHttpsAgent + }) + } + + const req = request(config.protocol)(reqOpts, onRes(options.buffer, callback)) req.on('error', (err) => { callback(err) diff --git a/test/request-api.spec.js b/test/request-api.spec.js index 6588ccbbd..161496e88 100644 --- a/test/request-api.spec.js +++ b/test/request-api.spec.js @@ -7,6 +7,7 @@ const expect = chai.expect chai.use(dirtyChai) const isNode = require('detect-node') const ipfsAPI = require('../src/index.js') +const async = require('async') describe('\'deal with HTTP weirdness\' tests', () => { it('does not crash if no content-type header is provided', (done) => { @@ -27,3 +28,50 @@ describe('\'deal with HTTP weirdness\' tests', () => { }) }) }) + +describe('client cert options', function () { + it('accepts client cert, key', function (done) { + // not sure how to automate client certs in browsers + if (!isNode) { return done() } + + async.waterfall([ + (cb) => { + require('pem').createCertificate({selfSigned: true}, cb) + }, + (serverPEM, cb) => { + require('pem').createCertificate({ + serviceKey: serverPEM.clientKey, + serviceCertificate: serverPEM.certificate, + commonName: 'test' + }, (err, clientPEM) => cb(err, serverPEM, clientPEM)) + }, + (serverPEM, clientPEM, cb) => { + const server = require('https').createServer({ + requestCert: true, + rejectUnauthorized: true, + ca: [serverPEM.certificate], + key: serverPEM.clientKey, + cert: serverPEM.certificate + }, (req, res) => { + res.end() + }) + + server.listen(6001, () => { + const ipfs = ipfsAPI({ + host: 'localhost', + port: '6001', + protocol: 'https', + key: clientPEM.clientKey, + cert: clientPEM.certificate, + ca: [serverPEM.certificate] + }) + + ipfs.id((err, res) => { + expect(err).to.not.exist() + server.close(cb) + }) + }) + } + ], done) + }) +})