Support CA fingerprint validation (#1499)

Co-authored-by: Aleh Zasypkin <aleh.zasypkin@gmail.com>
Co-authored-by: Ioannis Kakavas <ioannis@elastic.co>
This commit is contained in:
Tomas Della Vedova
2021-08-02 11:20:31 +02:00
committed by delvedor
parent 563b7746cd
commit a48ebc9442
12 changed files with 337 additions and 4 deletions

View File

@ -42,6 +42,7 @@ class Connection {
this.headers = prepareHeaders(opts.headers, opts.auth)
this.deadCount = 0
this.resurrectTimeout = 0
this.caFingerprint = opts.caFingerprint
this._openRequests = 0
this._status = opts.status || Connection.statuses.ALIVE
@ -123,10 +124,36 @@ class Connection {
callback(new RequestAbortedError(), null)
}
const onSocket = socket => {
/* istanbul ignore else */
if (!socket.isSessionReused()) {
socket.once('secureConnect', () => {
const issuerCertificate = getIssuerCertificate(socket)
/* istanbul ignore next */
if (issuerCertificate == null) {
onError(new Error('Invalid or malformed certificate'))
request.once('error', () => {}) // we need to catch the request aborted error
return request.abort()
}
// Check if fingerprint matches
/* istanbul ignore else */
if (this.caFingerprint !== issuerCertificate.fingerprint256) {
onError(new Error('Server certificate CA fingerprint does not match the value configured in caFingerprint'))
request.once('error', () => {}) // we need to catch the request aborted error
return request.abort()
}
})
}
}
request.on('response', onResponse)
request.on('timeout', onTimeout)
request.on('error', onError)
request.on('abort', onAbort)
if (this.caFingerprint != null) {
request.on('socket', onSocket)
}
// Disables the Nagle algorithm
request.setNoDelay(true)
@ -152,6 +179,7 @@ class Connection {
request.removeListener('timeout', onTimeout)
request.removeListener('error', onError)
request.removeListener('abort', onAbort)
request.removeListener('socket', onSocket)
cleanedListeners = true
}
}
@ -340,5 +368,25 @@ function prepareHeaders (headers = {}, auth) {
return headers
}
function getIssuerCertificate (socket) {
let certificate = socket.getPeerCertificate(true)
while (certificate && Object.keys(certificate).length > 0) {
// invalid certificate
if (certificate.issuerCertificate == null) {
return null
}
// We have reached the root certificate.
// In case of self-signed certificates, `issuerCertificate` may be a circular reference.
if (certificate.fingerprint256 === certificate.issuerCertificate.fingerprint256) {
break
}
// continue the loop
certificate = certificate.issuerCertificate
}
return certificate
}
module.exports = Connection
module.exports.internals = { prepareHeaders }
module.exports.internals = { prepareHeaders, getIssuerCertificate }