diff --git a/src/apps.js b/src/apps.js index 6a623aa9b..4d88a7741 100644 --- a/src/apps.js +++ b/src/apps.js @@ -48,6 +48,7 @@ var addons = require('./addons.js'), async = require('async'), backups = require('./backups.js'), BackupsError = require('./backups.js').BackupsError, + certificateManager = require('./certificatemanager.js'), config = require('./config.js'), constants = require('./constants.js'), DatabaseError = require('./databaseerror.js'), @@ -59,7 +60,6 @@ var addons = require('./addons.js'), path = require('path'), paths = require('./paths.js'), safe = require('safetydance'), - settings = require('./settings.js'), semver = require('semver'), shell = require('./shell.js'), spawn = require('child_process').spawn, @@ -340,7 +340,7 @@ function install(appId, appStoreId, manifest, location, portBindings, accessRest } } - error = settings.validateCertificate(cert, key, config.appFqdn(location)); + error = certificateManager.validateCertificate(cert, key, config.appFqdn(location)); if (error) return callback(new AppsError(AppsError.BAD_CERTIFICATE, error.message)); debug('Will install app with id : ' + appId); @@ -381,7 +381,7 @@ function configure(appId, location, portBindings, accessRestriction, oauthProxy, error = validateAccessRestriction(accessRestriction); if (error) return callback(new AppsError(AppsError.BAD_FIELD, error.message)); - error = settings.validateCertificate(cert, key, config.appFqdn(location)); + error = certificateManager.validateCertificate(cert, key, config.appFqdn(location)); if (error) return callback(new AppsError(AppsError.BAD_CERTIFICATE, error.message)); appdb.get(appId, function (error, app) { diff --git a/src/certificatemanager.js b/src/certificatemanager.js index 679e60066..40960a295 100644 --- a/src/certificatemanager.js +++ b/src/certificatemanager.js @@ -4,16 +4,19 @@ var acme = require('./cert/acme.js'), assert = require('assert'), - async = require('async'), config = require('./config.js'), debug = require('debug')('src/certificatemanager'), paths = require('./paths.js'), - sysinfo = require('./sysinfo.js'); + safe = require('safetydance'), + sysinfo = require('./sysinfo.js'), + util = require('util'), + x509 = require('x509'); exports = module.exports = { initialize: initialize, uninitialize: uninitialize, - autoRenew: autoRenew + autoRenew: autoRenew, + validateCertificate: validateCertificate }; function initialize(callback) { @@ -35,3 +38,43 @@ function uninitialize(callback) { function autoRenew() { debug('will automatically renew certs'); } + +// note: https://tools.ietf.org/html/rfc4346#section-7.4.2 (certificate_list) requires that the +// servers certificate appears first (and not the intermediate cert) +function validateCertificate(cert, key, fqdn) { + assert(cert === null || typeof cert === 'string'); + assert(key === null || typeof key === 'string'); + assert.strictEqual(typeof fqdn, 'string'); + + if (cert === null && key === null) return null; + if (!cert && key) return new Error('missing cert'); + if (cert && !key) return new Error('missing key'); + + var content; + try { + content = x509.parseCert(cert); + } catch (e) { + return new Error('invalid cert: ' + e.message); + } + + // check expiration + if (content.notAfter < new Date()) return new Error('cert expired'); + + function matchesDomain(domain) { + if (domain === fqdn) return true; + if (domain.indexOf('*') === 0 && domain.slice(2) === fqdn.slice(fqdn.indexOf('.') + 1)) return true; + + return false; + } + + // check domain + var domains = content.altNames.concat(content.subject.commonName); + if (!domains.some(matchesDomain)) return new Error(util.format('cert is not valid for this domain. Expecting %s in %j', fqdn, domains)); + + // http://httpd.apache.org/docs/2.0/ssl/ssl_faq.html#verify + var certModulus = safe.child_process.execSync('openssl x509 -noout -modulus', { encoding: 'utf8', input: cert }); + var keyModulus = safe.child_process.execSync('openssl rsa -noout -modulus', { encoding: 'utf8', input: key }); + if (certModulus !== keyModulus) return new Error('key does not match the cert'); + + return null; +} diff --git a/src/settings.js b/src/settings.js index b7212ded7..b6bfabbe7 100644 --- a/src/settings.js +++ b/src/settings.js @@ -29,7 +29,6 @@ exports = module.exports = { getDefaultSync: getDefaultSync, getAll: getAll, - validateCertificate: validateCertificate, setCertificate: setCertificate, setAdminCertificate: setAdminCertificate, @@ -44,6 +43,7 @@ exports = module.exports = { }; var assert = require('assert'), + certificateManager = require('./certificatemanager.js'), config = require('./config.js'), constants = require('./constants.js'), CronJob = require('cron').CronJob, @@ -56,7 +56,6 @@ var assert = require('assert'), settingsdb = require('./settingsdb.js'), shell = require('./shell.js'), util = require('util'), - x509 = require('x509'), _ = require('underscore'); var gDefaults = (function () { @@ -323,52 +322,12 @@ function getAll(callback) { }); } -// note: https://tools.ietf.org/html/rfc4346#section-7.4.2 (certificate_list) requires that the -// servers certificate appears first (and not the intermediate cert) -function validateCertificate(cert, key, fqdn) { - assert(cert === null || typeof cert === 'string'); - assert(key === null || typeof key === 'string'); - assert.strictEqual(typeof fqdn, 'string'); - - if (cert === null && key === null) return null; - if (!cert && key) return new Error('missing cert'); - if (cert && !key) return new Error('missing key'); - - var content; - try { - content = x509.parseCert(cert); - } catch (e) { - return new Error('invalid cert: ' + e.message); - } - - // check expiration - if (content.notAfter < new Date()) return new Error('cert expired'); - - function matchesDomain(domain) { - if (domain === fqdn) return true; - if (domain.indexOf('*') === 0 && domain.slice(2) === fqdn.slice(fqdn.indexOf('.') + 1)) return true; - - return false; - } - - // check domain - var domains = content.altNames.concat(content.subject.commonName); - if (!domains.some(matchesDomain)) return new Error(util.format('cert is not valid for this domain. Expecting %s in %j', fqdn, domains)); - - // http://httpd.apache.org/docs/2.0/ssl/ssl_faq.html#verify - var certModulus = safe.child_process.execSync('openssl x509 -noout -modulus', { encoding: 'utf8', input: cert }); - var keyModulus = safe.child_process.execSync('openssl rsa -noout -modulus', { encoding: 'utf8', input: key }); - if (certModulus !== keyModulus) return new Error('key does not match the cert'); - - return null; -} - function setCertificate(cert, key, callback) { assert.strictEqual(typeof cert, 'string'); assert.strictEqual(typeof key, 'string'); assert.strictEqual(typeof callback, 'function'); - var error = validateCertificate(cert, key, '*.' + config.fqdn()); + var error = certificateManager.validateCertificate(cert, key, '*.' + config.fqdn()); if (error) return callback(new SettingsError(SettingsError.INVALID_CERT, error.message)); // backup the cert @@ -397,7 +356,7 @@ function setAdminCertificate(cert, key, callback) { var certFilePath = path.join(paths.APP_CERTS_DIR, vhost + '.cert'); var keyFilePath = path.join(paths.APP_CERTS_DIR, vhost + '.key'); - var error = validateCertificate(cert, key, vhost); + var error = certificateManager.validateCertificate(cert, key, vhost); if (error) return callback(new SettingsError(SettingsError.INVALID_CERT, error.message)); // backup the cert diff --git a/src/test/certificatemanager-test.js b/src/test/certificatemanager-test.js new file mode 100644 index 000000000..174d9699e --- /dev/null +++ b/src/test/certificatemanager-test.js @@ -0,0 +1,90 @@ +/* jslint node:true */ +/* global it:false */ +/* global describe:false */ +/* global before:false */ +/* global after:false */ + +'use strict'; + +var certificateManager = require('../certificatemanager.js'), + expect = require('expect.js'); + +describe('CertificateManager', function () { + describe('validateCertificate', function () { + /* + Generate these with: + openssl genrsa -out server.key 512 + openssl req -new -key server.key -out server.csr -subj "/C=DE/ST=Berlin/L=Berlin/O=Nebulon/OU=CTO/CN=baz.foobar.com" + openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt + */ + + // foobar.com + var validCert0 = '-----BEGIN CERTIFICATE-----\nMIIBujCCAWQCCQCjLyTKzAJ4FDANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQGEwJE\nRTEPMA0GA1UECAwGQmVybGluMQ8wDQYDVQQHDAZCZXJsaW4xEDAOBgNVBAoMB05l\nYnVsb24xDDAKBgNVBAsMA0NUTzETMBEGA1UEAwwKZm9vYmFyLmNvbTAeFw0xNTEw\nMjgxMjM5MjZaFw0xNjEwMjcxMjM5MjZaMGQxCzAJBgNVBAYTAkRFMQ8wDQYDVQQI\nDAZCZXJsaW4xDzANBgNVBAcMBkJlcmxpbjEQMA4GA1UECgwHTmVidWxvbjEMMAoG\nA1UECwwDQ1RPMRMwEQYDVQQDDApmb29iYXIuY29tMFwwDQYJKoZIhvcNAQEBBQAD\nSwAwSAJBAMeYofgwHeNVmGkGe0gj4dnX2ciifDi7X2K/oVHp7mxuHjGMSYP9Z7b6\n+mu0IMf4OedwXStHBeO8mwjKxZmE7p8CAwEAATANBgkqhkiG9w0BAQsFAANBAJI7\nFUUHXjR63UFk8pgxp0c7hEGqj4VWWGsmo8oZnnX8jGVmQDKbk8o3MtDujfqupmMR\nMo7tSAFlG7zkm3GYhpw=\n-----END CERTIFICATE-----'; + var validKey0 = '-----BEGIN RSA PRIVATE KEY-----\nMIIBOwIBAAJBAMeYofgwHeNVmGkGe0gj4dnX2ciifDi7X2K/oVHp7mxuHjGMSYP9\nZ7b6+mu0IMf4OedwXStHBeO8mwjKxZmE7p8CAwEAAQJBAJS59Sb8o6i8JT9NJxvQ\nMQCkSJGqEaosZJ0uccSZ7aE48v+H7HiPzXAueitohcEif2Wp1EZ1RbRMURhznNiZ\neLECIQDxxqhakO6wc7H68zmpRXJ5ZxGUNbM24AMtpONAtEw9iwIhANNWtp6P74OV\ntvfOmtubbqw768fmGskFCOcp5oF8oF29AiBkTAf9AhCyjFwyAYJTEScq67HkLN66\njfVjkvpfFixmfwIgI+xldmZ5DCDyzQSthg7RrS0yUvRmMS1N6h1RNUl96PECIQDl\nit4lFcytbqNo1PuBZvzQE+plCjiJqXHYo3WCst1Jbg==\n-----END RSA PRIVATE KEY-----'; + + // *.foobar.com + var validCert1 = '-----BEGIN CERTIFICATE-----\nMIIBvjCCAWgCCQCg957GWuHtbzANBgkqhkiG9w0BAQsFADBmMQswCQYDVQQGEwJE\nRTEPMA0GA1UECAwGQmVybGluMQ8wDQYDVQQHDAZCZXJsaW4xEDAOBgNVBAoMB05l\nYnVsb24xDDAKBgNVBAsMA0NUTzEVMBMGA1UEAwwMKi5mb29iYXIuY29tMB4XDTE1\nMTAyODEzMDI1MFoXDTE2MTAyNzEzMDI1MFowZjELMAkGA1UEBhMCREUxDzANBgNV\nBAgMBkJlcmxpbjEPMA0GA1UEBwwGQmVybGluMRAwDgYDVQQKDAdOZWJ1bG9uMQww\nCgYDVQQLDANDVE8xFTATBgNVBAMMDCouZm9vYmFyLmNvbTBcMA0GCSqGSIb3DQEB\nAQUAA0sAMEgCQQC0FKf07ZWMcABFlZw+GzXK9EiZrlJ1lpnu64RhN99z7MXRr8cF\nnZVgY3jgatuyR5s3WdzUvye2eJ0rNicl2EZJAgMBAAEwDQYJKoZIhvcNAQELBQAD\nQQAw4bteMZAeJWl2wgNLw+wTwAH96E0jyxwreCnT5AxJLmgimyQ0XOF4FsssdRFj\nxD9WA+rktelBodJyPeTDNhIh\n-----END CERTIFICATE-----'; + var validKey1 = '-----BEGIN RSA PRIVATE KEY-----\nMIIBOQIBAAJBALQUp/TtlYxwAEWVnD4bNcr0SJmuUnWWme7rhGE333PsxdGvxwWd\nlWBjeOBq27JHmzdZ3NS/J7Z4nSs2JyXYRkkCAwEAAQJALV2eykcoC48TonQEPmkg\nbhaIS57syw67jMLsQImQ02UABKzqHPEKLXPOZhZPS9hsC/hGIehwiYCXMUlrl+WF\nAQIhAOntBI6qaecNjAAVG7UbZclMuHROUONmZUF1KNq6VyV5AiEAxRLkfHWy52CM\njOQrX347edZ30f4QczvugXwsyuU9A1ECIGlGZ8Sk4OBA8n6fAUcyO06qnmCJVlHg\npTUeOvKk5c9RAiBs28+8dCNbrbhVhx/yQr9FwNM0+ttJW/yWJ+pyNQhr0QIgJTT6\nxwCWYOtbioyt7B9l+ENy3AMSO3Uq+xmIKkvItK4=\n-----END RSA PRIVATE KEY-----'; + + // baz.foobar.com + var validCert2 = '-----BEGIN CERTIFICATE-----\nMIIBwjCCAWwCCQDIKtL9RCDCkDANBgkqhkiG9w0BAQsFADBoMQswCQYDVQQGEwJE\nRTEPMA0GA1UECAwGQmVybGluMQ8wDQYDVQQHDAZCZXJsaW4xEDAOBgNVBAoMB05l\nYnVsb24xDDAKBgNVBAsMA0NUTzEXMBUGA1UEAwwOYmF6LmZvb2Jhci5jb20wHhcN\nMTUxMDI4MTMwNTMzWhcNMTYxMDI3MTMwNTMzWjBoMQswCQYDVQQGEwJERTEPMA0G\nA1UECAwGQmVybGluMQ8wDQYDVQQHDAZCZXJsaW4xEDAOBgNVBAoMB05lYnVsb24x\nDDAKBgNVBAsMA0NUTzEXMBUGA1UEAwwOYmF6LmZvb2Jhci5jb20wXDANBgkqhkiG\n9w0BAQEFAANLADBIAkEAw7UWW/VoQePv2l92l3XcntZeyw1nBiHxk1axZwC6auOW\n2/zfA//Tg7fv4q5qKnV1n/71IiMAheeFcpfogY5rTwIDAQABMA0GCSqGSIb3DQEB\nCwUAA0EAtluL6dGNfOdNkzoO/UwzRaIvEm2reuqe+Ik4WR/k+DJ4igrmRCQqXwjW\nJaGYsFWsuk3QLOWQ9YgCKlcIYd+1/A==\n-----END CERTIFICATE-----'; + var validKey2 = '-----BEGIN RSA PRIVATE KEY-----\nMIIBOQIBAAJBAMO1Flv1aEHj79pfdpd13J7WXssNZwYh8ZNWsWcAumrjltv83wP/\n04O37+Kuaip1dZ/+9SIjAIXnhXKX6IGOa08CAwEAAQJAUPD3Y2cXDJFaJQXwhWnw\nqhzdLbvITUgCor5rNr+dWhE2MopGPpRHiabA1PeWEPx8CfblyTZGd8KUR/2W1c0r\naQIhAP4ZxB3+uhuzzMfyRrn/khr12pFn/FCIDbwnDbyUxLrTAiEAxSuVOFs+Mupt\nYCz/pPrDCx3eid0wyXRObbkLHOxJiBUCIBTp5fxaBNNW3xnt1OhmIo5Zgd3J4zh1\nmjvMMxM8Y1zFAiAxOP0qsZSoj1+41+MGY9fXaaCJ2F96m3+M4tpEYTTGNQIgdESZ\nz+hzHBeYVbWJpIR8uaNkx7wveUF90FpipXyeTsA=\n-----END RSA PRIVATE KEY-----'; + + it('allows both null', function () { + expect(certificateManager.validateCertificate(null, null, 'foobar.com')).to.be(null); + }); + + it('does not allow only cert', function () { + expect(certificateManager.validateCertificate('cert', null, 'foobar.com')).to.be.an(Error); + }); + + it('does not allow only key', function () { + expect(certificateManager.validateCertificate(null, 'key', 'foobar.com')).to.be.an(Error); + }); + + it('does not allow empty string for cert', function () { + expect(certificateManager.validateCertificate('', 'key', 'foobar.com')).to.be.an(Error); + }); + + it('does not allow empty string for key', function () { + expect(certificateManager.validateCertificate('cert', '', 'foobar.com')).to.be.an(Error); + }); + + it('does not allow invalid cert', function () { + expect(certificateManager.validateCertificate('someinvalidcert', validKey0, 'foobar.com')).to.be.an(Error); + }); + + it('does not allow invalid key', function () { + expect(certificateManager.validateCertificate(validCert0, 'invalidkey', 'foobar.com')).to.be.an(Error); + }); + + it('does not allow cert without matching domain', function () { + expect(certificateManager.validateCertificate(validCert0, validKey0, 'cloudron.io')).to.be.an(Error); + }); + + it('allows valid cert with matching domain', function () { + expect(certificateManager.validateCertificate(validCert0, validKey0, 'foobar.com')).to.be(null); + }); + + it('allows valid cert with matching domain (wildcard)', function () { + expect(certificateManager.validateCertificate(validCert1, validKey1, 'abc.foobar.com')).to.be(null); + }); + + it('does now allow cert without matching domain (wildcard)', function () { + expect(certificateManager.validateCertificate(validCert1, validKey1, 'foobar.com')).to.be.an(Error); + expect(certificateManager.validateCertificate(validCert1, validKey1, 'bar.abc.foobar.com')).to.be.an(Error); + }); + + it('allows valid cert with matching domain (subdomain)', function () { + expect(certificateManager.validateCertificate(validCert2, validKey2, 'baz.foobar.com')).to.be(null); + }); + + it('does not allow cert without matching domain (subdomain)', function () { + expect(certificateManager.validateCertificate(validCert0, validKey0, 'baz.foobar.com')).to.be.an(Error); + }); + + it('does not allow invalid cert/key tuple', function () { + expect(certificateManager.validateCertificate(validCert0, validKey1, 'foobar.com')).to.be.an(Error); + }); + }); +}); diff --git a/src/test/settings-test.js b/src/test/settings-test.js index 617dfc8d1..3f149d2d6 100644 --- a/src/test/settings-test.js +++ b/src/test/settings-test.js @@ -126,85 +126,4 @@ describe('Settings', function () { }); }); }); - - describe('validateCertificate', function () { - before(setup); - after(cleanup); - - /* - Generate these with: - openssl genrsa -out server.key 512 - openssl req -new -key server.key -out server.csr -subj "/C=DE/ST=Berlin/L=Berlin/O=Nebulon/OU=CTO/CN=baz.foobar.com" - openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt - */ - - // foobar.com - var validCert0 = '-----BEGIN CERTIFICATE-----\nMIIBujCCAWQCCQCjLyTKzAJ4FDANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQGEwJE\nRTEPMA0GA1UECAwGQmVybGluMQ8wDQYDVQQHDAZCZXJsaW4xEDAOBgNVBAoMB05l\nYnVsb24xDDAKBgNVBAsMA0NUTzETMBEGA1UEAwwKZm9vYmFyLmNvbTAeFw0xNTEw\nMjgxMjM5MjZaFw0xNjEwMjcxMjM5MjZaMGQxCzAJBgNVBAYTAkRFMQ8wDQYDVQQI\nDAZCZXJsaW4xDzANBgNVBAcMBkJlcmxpbjEQMA4GA1UECgwHTmVidWxvbjEMMAoG\nA1UECwwDQ1RPMRMwEQYDVQQDDApmb29iYXIuY29tMFwwDQYJKoZIhvcNAQEBBQAD\nSwAwSAJBAMeYofgwHeNVmGkGe0gj4dnX2ciifDi7X2K/oVHp7mxuHjGMSYP9Z7b6\n+mu0IMf4OedwXStHBeO8mwjKxZmE7p8CAwEAATANBgkqhkiG9w0BAQsFAANBAJI7\nFUUHXjR63UFk8pgxp0c7hEGqj4VWWGsmo8oZnnX8jGVmQDKbk8o3MtDujfqupmMR\nMo7tSAFlG7zkm3GYhpw=\n-----END CERTIFICATE-----'; - var validKey0 = '-----BEGIN RSA PRIVATE KEY-----\nMIIBOwIBAAJBAMeYofgwHeNVmGkGe0gj4dnX2ciifDi7X2K/oVHp7mxuHjGMSYP9\nZ7b6+mu0IMf4OedwXStHBeO8mwjKxZmE7p8CAwEAAQJBAJS59Sb8o6i8JT9NJxvQ\nMQCkSJGqEaosZJ0uccSZ7aE48v+H7HiPzXAueitohcEif2Wp1EZ1RbRMURhznNiZ\neLECIQDxxqhakO6wc7H68zmpRXJ5ZxGUNbM24AMtpONAtEw9iwIhANNWtp6P74OV\ntvfOmtubbqw768fmGskFCOcp5oF8oF29AiBkTAf9AhCyjFwyAYJTEScq67HkLN66\njfVjkvpfFixmfwIgI+xldmZ5DCDyzQSthg7RrS0yUvRmMS1N6h1RNUl96PECIQDl\nit4lFcytbqNo1PuBZvzQE+plCjiJqXHYo3WCst1Jbg==\n-----END RSA PRIVATE KEY-----'; - - // *.foobar.com - var validCert1 = '-----BEGIN CERTIFICATE-----\nMIIBvjCCAWgCCQCg957GWuHtbzANBgkqhkiG9w0BAQsFADBmMQswCQYDVQQGEwJE\nRTEPMA0GA1UECAwGQmVybGluMQ8wDQYDVQQHDAZCZXJsaW4xEDAOBgNVBAoMB05l\nYnVsb24xDDAKBgNVBAsMA0NUTzEVMBMGA1UEAwwMKi5mb29iYXIuY29tMB4XDTE1\nMTAyODEzMDI1MFoXDTE2MTAyNzEzMDI1MFowZjELMAkGA1UEBhMCREUxDzANBgNV\nBAgMBkJlcmxpbjEPMA0GA1UEBwwGQmVybGluMRAwDgYDVQQKDAdOZWJ1bG9uMQww\nCgYDVQQLDANDVE8xFTATBgNVBAMMDCouZm9vYmFyLmNvbTBcMA0GCSqGSIb3DQEB\nAQUAA0sAMEgCQQC0FKf07ZWMcABFlZw+GzXK9EiZrlJ1lpnu64RhN99z7MXRr8cF\nnZVgY3jgatuyR5s3WdzUvye2eJ0rNicl2EZJAgMBAAEwDQYJKoZIhvcNAQELBQAD\nQQAw4bteMZAeJWl2wgNLw+wTwAH96E0jyxwreCnT5AxJLmgimyQ0XOF4FsssdRFj\nxD9WA+rktelBodJyPeTDNhIh\n-----END CERTIFICATE-----'; - var validKey1 = '-----BEGIN RSA PRIVATE KEY-----\nMIIBOQIBAAJBALQUp/TtlYxwAEWVnD4bNcr0SJmuUnWWme7rhGE333PsxdGvxwWd\nlWBjeOBq27JHmzdZ3NS/J7Z4nSs2JyXYRkkCAwEAAQJALV2eykcoC48TonQEPmkg\nbhaIS57syw67jMLsQImQ02UABKzqHPEKLXPOZhZPS9hsC/hGIehwiYCXMUlrl+WF\nAQIhAOntBI6qaecNjAAVG7UbZclMuHROUONmZUF1KNq6VyV5AiEAxRLkfHWy52CM\njOQrX347edZ30f4QczvugXwsyuU9A1ECIGlGZ8Sk4OBA8n6fAUcyO06qnmCJVlHg\npTUeOvKk5c9RAiBs28+8dCNbrbhVhx/yQr9FwNM0+ttJW/yWJ+pyNQhr0QIgJTT6\nxwCWYOtbioyt7B9l+ENy3AMSO3Uq+xmIKkvItK4=\n-----END RSA PRIVATE KEY-----'; - - // baz.foobar.com - var validCert2 = '-----BEGIN CERTIFICATE-----\nMIIBwjCCAWwCCQDIKtL9RCDCkDANBgkqhkiG9w0BAQsFADBoMQswCQYDVQQGEwJE\nRTEPMA0GA1UECAwGQmVybGluMQ8wDQYDVQQHDAZCZXJsaW4xEDAOBgNVBAoMB05l\nYnVsb24xDDAKBgNVBAsMA0NUTzEXMBUGA1UEAwwOYmF6LmZvb2Jhci5jb20wHhcN\nMTUxMDI4MTMwNTMzWhcNMTYxMDI3MTMwNTMzWjBoMQswCQYDVQQGEwJERTEPMA0G\nA1UECAwGQmVybGluMQ8wDQYDVQQHDAZCZXJsaW4xEDAOBgNVBAoMB05lYnVsb24x\nDDAKBgNVBAsMA0NUTzEXMBUGA1UEAwwOYmF6LmZvb2Jhci5jb20wXDANBgkqhkiG\n9w0BAQEFAANLADBIAkEAw7UWW/VoQePv2l92l3XcntZeyw1nBiHxk1axZwC6auOW\n2/zfA//Tg7fv4q5qKnV1n/71IiMAheeFcpfogY5rTwIDAQABMA0GCSqGSIb3DQEB\nCwUAA0EAtluL6dGNfOdNkzoO/UwzRaIvEm2reuqe+Ik4WR/k+DJ4igrmRCQqXwjW\nJaGYsFWsuk3QLOWQ9YgCKlcIYd+1/A==\n-----END CERTIFICATE-----'; - var validKey2 = '-----BEGIN RSA PRIVATE KEY-----\nMIIBOQIBAAJBAMO1Flv1aEHj79pfdpd13J7WXssNZwYh8ZNWsWcAumrjltv83wP/\n04O37+Kuaip1dZ/+9SIjAIXnhXKX6IGOa08CAwEAAQJAUPD3Y2cXDJFaJQXwhWnw\nqhzdLbvITUgCor5rNr+dWhE2MopGPpRHiabA1PeWEPx8CfblyTZGd8KUR/2W1c0r\naQIhAP4ZxB3+uhuzzMfyRrn/khr12pFn/FCIDbwnDbyUxLrTAiEAxSuVOFs+Mupt\nYCz/pPrDCx3eid0wyXRObbkLHOxJiBUCIBTp5fxaBNNW3xnt1OhmIo5Zgd3J4zh1\nmjvMMxM8Y1zFAiAxOP0qsZSoj1+41+MGY9fXaaCJ2F96m3+M4tpEYTTGNQIgdESZ\nz+hzHBeYVbWJpIR8uaNkx7wveUF90FpipXyeTsA=\n-----END RSA PRIVATE KEY-----'; - - it('allows both null', function () { - expect(settings.validateCertificate(null, null, 'foobar.com')).to.be(null); - }); - - it('does not allow only cert', function () { - expect(settings.validateCertificate('cert', null, 'foobar.com')).to.be.an(Error); - }); - - it('does not allow only key', function () { - expect(settings.validateCertificate(null, 'key', 'foobar.com')).to.be.an(Error); - }); - - it('does not allow empty string for cert', function () { - expect(settings.validateCertificate('', 'key', 'foobar.com')).to.be.an(Error); - }); - - it('does not allow empty string for key', function () { - expect(settings.validateCertificate('cert', '', 'foobar.com')).to.be.an(Error); - }); - - it('does not allow invalid cert', function () { - expect(settings.validateCertificate('someinvalidcert', validKey0, 'foobar.com')).to.be.an(Error); - }); - - it('does not allow invalid key', function () { - expect(settings.validateCertificate(validCert0, 'invalidkey', 'foobar.com')).to.be.an(Error); - }); - - it('does not allow cert without matching domain', function () { - expect(settings.validateCertificate(validCert0, validKey0, 'cloudron.io')).to.be.an(Error); - }); - - it('allows valid cert with matching domain', function () { - expect(settings.validateCertificate(validCert0, validKey0, 'foobar.com')).to.be(null); - }); - - it('allows valid cert with matching domain (wildcard)', function () { - expect(settings.validateCertificate(validCert1, validKey1, 'abc.foobar.com')).to.be(null); - }); - - it('does now allow cert without matching domain (wildcard)', function () { - expect(settings.validateCertificate(validCert1, validKey1, 'foobar.com')).to.be.an(Error); - expect(settings.validateCertificate(validCert1, validKey1, 'bar.abc.foobar.com')).to.be.an(Error); - }); - - it('allows valid cert with matching domain (subdomain)', function () { - expect(settings.validateCertificate(validCert2, validKey2, 'baz.foobar.com')).to.be(null); - }); - - it('does not allow cert without matching domain (subdomain)', function () { - expect(settings.validateCertificate(validCert0, validKey0, 'baz.foobar.com')).to.be.an(Error); - }); - - it('does not allow invalid cert/key tuple', function () { - expect(settings.validateCertificate(validCert0, validKey1, 'foobar.com')).to.be.an(Error); - }); - }); });