diff --git a/src/apps.js b/src/apps.js index 47b85e1c6..df54ecd6e 100644 --- a/src/apps.js +++ b/src/apps.js @@ -40,8 +40,7 @@ exports = module.exports = { // exported for testing _validateHostname: validateHostname, _validatePortBindings: validatePortBindings, - _validateAccessRestriction: validateAccessRestriction, - _validateCertificate: validateCertificate + _validateAccessRestriction: validateAccessRestriction }; var addons = require('./addons.js'), @@ -60,14 +59,14 @@ 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'), split = require('split'), superagent = require('superagent'), taskmanager = require('./taskmanager.js'), util = require('util'), - validator = require('validator'), - x509 = require('x509'); + validator = require('validator'); var BACKUP_APP_CMD = path.join(__dirname, 'scripts/backupapp.sh'), RESTORE_APP_CMD = path.join(__dirname, 'scripts/restoreapp.sh'), @@ -199,44 +198,6 @@ function validateAccessRestriction(accessRestriction) { return null; } -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'); - } - - // 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('cert is not valid for this domain'); - - // 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 getDuplicateErrorDetails(location, portBindings, error) { assert.strictEqual(typeof location, 'string'); assert.strictEqual(typeof portBindings, 'object'); @@ -405,7 +366,7 @@ function configure(appId, location, portBindings, accessRestriction, oauthProxy, error = validateAccessRestriction(accessRestriction); if (error) return callback(new AppsError(AppsError.BAD_FIELD, error.message)); - error = validateCertificate(cert, key, config.appFqdn(location)); + error = settings.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/settings.js b/src/settings.js index ecc82991c..a0bbb22f5 100644 --- a/src/settings.js +++ b/src/settings.js @@ -26,6 +26,7 @@ exports = module.exports = { getDefaultSync: getDefaultSync, getAll: getAll, + validateCertificate: validateCertificate, setCertificate: setCertificate, AUTOUPDATE_PATTERN_KEY: 'autoupdate_pattern', @@ -47,6 +48,7 @@ var assert = require('assert'), settingsdb = require('./settingsdb.js'), shell = require('./shell.js'), util = require('util'), + x509 = require('x509'), _ = require('underscore'); var gDefaults = (function () { @@ -89,6 +91,7 @@ util.inherits(SettingsError, Error); SettingsError.INTERNAL_ERROR = 'Internal Error'; SettingsError.NOT_FOUND = 'Not Found'; SettingsError.BAD_FIELD = 'Bad Field'; +SettingsError.INVALID_CERT = 'Invalid certificate'; function setAutoupdatePattern(pattern, callback) { assert.strictEqual(typeof pattern, 'string'); @@ -271,12 +274,53 @@ function getAll(callback) { }); } -function setCertificate(certificate, key, callback) { - assert.strictEqual(typeof certificate, 'string'); +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'); + } + + // 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('cert is not valid for this domain'); + + // 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'); - if (!safe.fs.writeFileSync(path.join(paths.NGINX_CERT_DIR, 'host.cert'), certificate)) { + var error = validateCertificate(cert, key, '*.' + config.fqdn()); + if (error) return callback(new SettingsError(SettingsError.INVALID_CERT, error.message)); + + if (!safe.fs.writeFileSync(path.join(paths.NGINX_CERT_DIR, 'host.cert'), cert)) { return callback(new SettingsError(SettingsError.INTERNAL_ERROR, safe.error.message)); } diff --git a/src/test/apps-test.js b/src/test/apps-test.js index d883c87b6..29aefc218 100644 --- a/src/test/apps-test.js +++ b/src/test/apps-test.js @@ -52,84 +52,6 @@ describe('Apps', function () { database._clear(done); }); - 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(apps._validateCertificate(null, null, 'foobar.com')).to.be(null); - }); - - it('does not allow only cert', function () { - expect(apps._validateCertificate('cert', null, 'foobar.com')).to.be.an(Error); - }); - - it('does not allow only key', function () { - expect(apps._validateCertificate(null, 'key', 'foobar.com')).to.be.an(Error); - }); - - it('does not allow empty string for cert', function () { - expect(apps._validateCertificate('', 'key', 'foobar.com')).to.be.an(Error); - }); - - it('does not allow empty string for key', function () { - expect(apps._validateCertificate('cert', '', 'foobar.com')).to.be.an(Error); - }); - - it('does not allow invalid cert', function () { - expect(apps._validateCertificate('someinvalidcert', validKey0, 'foobar.com')).to.be.an(Error); - }); - - it('does not allow invalid key', function () { - expect(apps._validateCertificate(validCert0, 'invalidkey', 'foobar.com')).to.be.an(Error); - }); - - it('does not allow cert without matching domain', function () { - expect(apps._validateCertificate(validCert0, validKey0, 'cloudron.io')).to.be.an(Error); - }); - - it('allows valid cert with matching domain', function () { - expect(apps._validateCertificate(validCert0, validKey0, 'foobar.com')).to.be(null); - }); - - it('allows valid cert with matching domain (wildcard)', function () { - expect(apps._validateCertificate(validCert1, validKey1, 'abc.foobar.com')).to.be(null); - }); - - it('does now allow cert without matching domain (wildcard)', function () { - expect(apps._validateCertificate(validCert1, validKey1, 'foobar.com')).to.be.an(Error); - expect(apps._validateCertificate(validCert1, validKey1, 'bar.abc.foobar.com')).to.be.an(Error); - }); - - it('allows valid cert with matching domain (subdomain)', function () { - expect(apps._validateCertificate(validCert2, validKey2, 'baz.foobar.com')).to.be(null); - }); - - it('does not allow cert without matching domain (subdomain)', function () { - expect(apps._validateCertificate(validCert0, validKey0, 'baz.foobar.com')).to.be.an(Error); - }); - - it('does not allow invalid cert/key tuple', function () { - expect(apps._validateCertificate(validCert0, validKey1, 'foobar.com')).to.be.an(Error); - }); - }); - describe('validateHostname', function () { it('does not allow admin subdomain', function () { expect(apps._validateHostname(constants.ADMIN_LOCATION, 'cloudron.us')).to.be.an(Error); diff --git a/src/test/settings-test.js b/src/test/settings-test.js index d5f024e1f..f8beaeb6b 100644 --- a/src/test/settings-test.js +++ b/src/test/settings-test.js @@ -23,89 +23,172 @@ function cleanup(done) { } describe('Settings', function () { - before(setup); - after(cleanup); + describe('values', function () { + before(setup); + after(cleanup); - it('can get default timezone', function (done) { - settings.getTimeZone(function (error, tz) { - expect(error).to.be(null); - expect(tz.length).to.not.be(0); - done(); + it('can get default timezone', function (done) { + settings.getTimeZone(function (error, tz) { + expect(error).to.be(null); + expect(tz.length).to.not.be(0); + done(); + }); + }); + + it('can get default autoupdate_pattern', function (done) { + settings.getAutoupdatePattern(function (error, pattern) { + expect(error).to.be(null); + expect(pattern).to.be('00 00 1,3,5,23 * * *'); + done(); + }); + }); + + it ('can get default cloudron name', function (done) { + settings.getCloudronName(function (error, name) { + expect(error).to.be(null); + expect(name).to.be('Cloudron'); + done(); + }); + }); + + it('can get default cloudron avatar', function (done) { + settings.getCloudronAvatar(function (error, gravatar) { + expect(error).to.be(null); + expect(gravatar).to.be.a(Buffer); + done(); + }); + }); + + it('can get default developer mode', function (done) { + settings.getDeveloperMode(function (error, enabled) { + expect(error).to.be(null); + expect(enabled).to.equal(false); + done(); + }); + }); + + it('can set developer mode', function (done) { + settings.setDeveloperMode(true, function (error) { + expect(error).to.be(null); + done(); + }); + }); + + it('can get developer mode', function (done) { + settings.getDeveloperMode(function (error, enabled) { + expect(error).to.be(null); + expect(enabled).to.equal(true); + done(); + }); + }); + + it('can set dns config', function (done) { + settings.setDnsConfig({ provider: 'route53', accessKeyId: 'accessKeyId', secretAccessKey: 'secretAccessKey' }, function (error) { + expect(error).to.be(null); + done(); + }); + }); + + it('can get dns config', function (done) { + settings.getDnsConfig(function (error, dnsConfig) { + expect(error).to.be(null); + expect(dnsConfig.provider).to.be('route53'); + expect(dnsConfig.accessKeyId).to.be('accessKeyId'); + expect(dnsConfig.secretAccessKey).to.be('secretAccessKey'); + expect(dnsConfig.region).to.be('us-east-1'); + done(); + }); + }); + + it('can get all values', function (done) { + settings.getAll(function (error, allSettings) { + expect(error).to.be(null); + expect(allSettings[settings.TIME_ZONE_KEY]).to.be.a('string'); + expect(allSettings[settings.AUTOUPDATE_PATTERN_KEY]).to.be.a('string'); + expect(allSettings[settings.CLOUDRON_NAME_KEY]).to.be.a('string'); + done(); + }); }); }); - it('can get default autoupdate_pattern', function (done) { - settings.getAutoupdatePattern(function (error, pattern) { - expect(error).to.be(null); - expect(pattern).to.be('00 00 1,3,5,23 * * *'); - done(); - }); - }); + describe('validateCertificate', function () { + before(setup); + after(cleanup); - it ('can get default cloudron name', function (done) { - settings.getCloudronName(function (error, name) { - expect(error).to.be(null); - expect(name).to.be('Cloudron'); - done(); - }); - }); + /* + 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 + */ - it('can get default cloudron avatar', function (done) { - settings.getCloudronAvatar(function (error, gravatar) { - expect(error).to.be(null); - expect(gravatar).to.be.a(Buffer); - done(); - }); - }); + // 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-----'; - it('can get default developer mode', function (done) { - settings.getDeveloperMode(function (error, enabled) { - expect(error).to.be(null); - expect(enabled).to.equal(false); - done(); - }); - }); + // *.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-----'; - it('can set developer mode', function (done) { - settings.setDeveloperMode(true, function (error) { - expect(error).to.be(null); - done(); - }); - }); + // 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('can get developer mode', function (done) { - settings.getDeveloperMode(function (error, enabled) { - expect(error).to.be(null); - expect(enabled).to.equal(true); - done(); + it('allows both null', function () { + expect(settings.validateCertificate(null, null, 'foobar.com')).to.be(null); }); - }); - it('can set dns config', function (done) { - settings.setDnsConfig({ provider: 'route53', accessKeyId: 'accessKeyId', secretAccessKey: 'secretAccessKey' }, function (error) { - expect(error).to.be(null); - done(); + it('does not allow only cert', function () { + expect(settings.validateCertificate('cert', null, 'foobar.com')).to.be.an(Error); }); - }); - it('can get dns config', function (done) { - settings.getDnsConfig(function (error, dnsConfig) { - expect(error).to.be(null); - expect(dnsConfig.provider).to.be('route53'); - expect(dnsConfig.accessKeyId).to.be('accessKeyId'); - expect(dnsConfig.secretAccessKey).to.be('secretAccessKey'); - expect(dnsConfig.region).to.be('us-east-1'); - done(); + it('does not allow only key', function () { + expect(settings.validateCertificate(null, 'key', 'foobar.com')).to.be.an(Error); }); - }); - it('can get all values', function (done) { - settings.getAll(function (error, allSettings) { - expect(error).to.be(null); - expect(allSettings[settings.TIME_ZONE_KEY]).to.be.a('string'); - expect(allSettings[settings.AUTOUPDATE_PATTERN_KEY]).to.be.a('string'); - expect(allSettings[settings.CLOUDRON_NAME_KEY]).to.be.a('string'); - done(); + 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); }); }); });