UNPKG

openhim-core

Version:

The OpenHIM core application that provides logging and routing of http requests

229 lines (192 loc) 7.44 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.getTrustedClientCerts = getTrustedClientCerts; exports.getServerOptions = getServerOptions; exports.koaMiddleware = koaMiddleware; var _winston = require('winston'); var _winston2 = _interopRequireDefault(_winston); var _pem = require('pem'); var _pem2 = _interopRequireDefault(_pem); var _latest = require('ssl-root-cas/latest'); var _statsdClient = require('statsd-client'); var _statsdClient2 = _interopRequireDefault(_statsdClient); var _os = require('os'); var _os2 = _interopRequireDefault(_os); var _clients = require('../model/clients'); var _keystore = require('../model/keystore'); var _utils = require('../utils'); var utils = _interopRequireWildcard(_utils); var _config = require('../config'); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } _config.config.tlsClientLookup = _config.config.get('tlsClientLookup'); const statsdServer = _config.config.get('statsd'); const application = _config.config.get('application'); const domain = `${_os2.default.hostname()}.${application.name}.appMetrics`; const sdc = new _statsdClient2.default(statsdServer); /* * Fetches the trusted certificates, callsback with an array of certs. */ function getTrustedClientCerts(done) { return _keystore.KeystoreModel.findOne((err, keystore) => { if (err) { done(err, null); } const certs = _latest.rootCas; if (keystore.ca != null) { for (const cert of Array.from(keystore.ca)) { certs.push(cert.data); } } return done(null, certs); }); } /* * Gets server options object for use with a HTTPS node server * * mutualTLS is a boolean, when true mutual TLS authentication is enabled */ function getServerOptions(mutualTLS, done) { return _keystore.KeystoreModel.findOne((err, keystore) => { let options; if (err) { _winston2.default.error(`Could not fetch keystore: ${err}`); return done(err); } if (keystore != null) { options = { key: keystore.key, cert: keystore.cert.data // if key has password add it to the options };if (keystore.passphrase) { options.passphrase = keystore.passphrase; } } else { return done(new Error('Keystore does not exist')); } if (mutualTLS) { return exports.getTrustedClientCerts((err, certs) => { if (err) { _winston2.default.error(`Could not fetch trusted certificates: ${err}`); return done(err, null); } options.ca = certs; options.requestCert = true; options.rejectUnauthorized = false; // we test authority ourselves return done(null, options); }); } else { return done(null, options); } }); } /* * A promise returning function that lookups up a client via the given cert fingerprint, * if not found and config.tlsClientLookup.type is 'in-chain' then the function will * recursively walk up the certificate chain and look for clients with certificates * higher in the chain. */ function clientLookup(fingerprint, subjectCN, issuerCN) { return new Promise((resolve, reject) => { _winston2.default.debug(`Looking up client linked to cert with fingerprint ${fingerprint} with subject ${subjectCN} and issuer ${issuerCN}`); _clients.ClientModel.findOne({ certFingerprint: fingerprint }, (err, result) => { if (err) { return reject(err); } if (result != null) { // found a match return resolve(result); } if (subjectCN === issuerCN) { // top certificate reached return resolve(null); } if (_config.config.tlsClientLookup.type === 'in-chain') { // walk further up and cert chain and check return utils.getKeystore((err, keystore) => { if (err) { return reject(err); } let missedMatches = 0; // find the isser cert if (keystore.ca == null || keystore.ca.length < 1) { _winston2.default.info(`Issuer cn=${issuerCN} for cn=${subjectCN} not found in keystore.`); return resolve(null); } else { return Array.from(keystore.ca).map(cert => (cert => _pem2.default.readCertificateInfo(cert.data, (err, info) => { if (err) { return reject(err); } if (info.commonName === issuerCN) { const promise = clientLookup(cert.fingerprint, info.commonName, info.issuer.commonName); promise.then(resolve); } else { missedMatches++; } if (missedMatches === keystore.ca.length) { _winston2.default.info(`Issuer cn=${issuerCN} for cn=${subjectCN} not found in keystore.`); return resolve(null); } }))(cert)); } }); } else { if (_config.config.tlsClientLookup.type !== 'strict') { _winston2.default.warn('tlsClientLookup.type config option does not contain a known value, defaulting to \'strict\'. Available options are \'strict\' and \'in-chain\'.'); } return resolve(null); } }); }); } if (process.env.NODE_ENV === 'test') { exports.clientLookup = clientLookup; } /* * Koa middleware for mutual TLS authentication */ async function koaMiddleware(ctx, next) { let startTime; if (statsdServer.enabled) { startTime = new Date(); } if (ctx.authenticated != null) { await next(); } else if (ctx.req.client.authorized === true) { const cert = ctx.req.connection.getPeerCertificate(true); _winston2.default.info(`${cert.subject.CN} is authenticated via TLS.`); // lookup client by cert fingerprint and set them as the authenticated user try { ctx.authenticated = await clientLookup(cert.fingerprint, cert.subject.CN, cert.issuer.CN); } catch (err) { _winston2.default.error(`Failed to lookup client: ${err}`); } if (ctx.authenticated != null) { if (ctx.authenticated.clientID != null) { ctx.header['X-OpenHIM-ClientID'] = ctx.authenticated.clientID; } if (statsdServer.enabled) { sdc.timing(`${domain}.tlsAuthenticationMiddleware`, startTime); } ctx.authenticationType = 'tls'; await next(); } else { ctx.authenticated = null; _winston2.default.info(`Certificate Authentication Failed: the certificate's fingerprint ${cert.fingerprint} did not match any client's certFingerprint attribute, trying next auth mechanism if any...`); if (statsdServer.enabled) { sdc.timing(`${domain}.tlsAuthenticationMiddleware`, startTime); } await next(); } } else { ctx.authenticated = null; _winston2.default.info(`Could NOT authenticate via TLS: ${ctx.req.client.authorizationError}, trying next auth mechanism if any...`); if (statsdServer.enabled) { sdc.timing(`${domain}.tlsAuthenticationMiddleware`, startTime); } await next(); } } //# sourceMappingURL=tlsAuthentication.js.map