diff --git a/src/apps.js b/src/apps.js index e170ef0f2..7ddca8a03 100644 --- a/src/apps.js +++ b/src/apps.js @@ -609,7 +609,7 @@ function install(data, user, auditSource, callback) { if (error) return callback(new AppsError(AppsError.BAD_FIELD, 'Bad location: ' + error.message)); if (cert && key) { - error = reverseProxy.validateCertificate(location, domainObject, cert, key); + error = reverseProxy.validateCertificate(location, domainObject, { cert, key }); if (error) return callback(new AppsError(AppsError.BAD_CERTIFICATE, error.message)); } @@ -652,7 +652,7 @@ function install(data, user, auditSource, callback) { // save cert to boxdata/certs if (cert && key) { - let error = reverseProxy.setAppCertificateSync(location, domainObject, cert, key); + let error = reverseProxy.setAppCertificateSync(location, domainObject, { cert, key }); if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, 'Error setting cert: ' + error.message)); } @@ -764,11 +764,11 @@ function configure(appId, data, user, auditSource, callback) { // save cert to boxdata/certs. TODO: move this to apptask when we have a real task queue if ('cert' in data && 'key' in data) { if (data.cert && data.key) { - error = reverseProxy.validateCertificate(location, domainObject, data.cert, data.key); + error = reverseProxy.validateCertificate(location, domainObject, { cert: data.cert, key: data.key }); if (error) return callback(new AppsError(AppsError.BAD_CERTIFICATE, error.message)); } - error = reverseProxy.setAppCertificateSync(location, domainObject, data.cert, data.key); + error = reverseProxy.setAppCertificateSync(location, domainObject, { cert: data.cert, key: data.key }); if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, 'Error setting cert: ' + error.message)); } diff --git a/src/domains.js b/src/domains.js index ede3ee9ab..31728bba1 100644 --- a/src/domains.js +++ b/src/domains.js @@ -219,7 +219,7 @@ function add(domain, zoneName, provider, dnsConfig, fallbackCertificate, tlsConf } if (fallbackCertificate) { - let error = reverseProxy.validateCertificate('test', { domain: domain, config: dnsConfig }, fallbackCertificate.cert, fallbackCertificate.key); + let error = reverseProxy.validateCertificate('test', { domain: domain, config: dnsConfig }, fallbackCertificate); if (error) return callback(new DomainsError(DomainsError.BAD_FIELD, error.message)); } else { fallbackCertificate = reverseProxy.generateFallbackCertificateSync({ domain: domain, config: dnsConfig }); @@ -311,7 +311,7 @@ function update(domain, zoneName, provider, dnsConfig, fallbackCertificate, tlsC } if (fallbackCertificate) { - let error = reverseProxy.validateCertificate('test', domainObject, fallbackCertificate.cert, fallbackCertificate.key); + let error = reverseProxy.validateCertificate('test', domainObject, fallbackCertificate); if (error) return callback(new DomainsError(DomainsError.BAD_FIELD, error.message)); } diff --git a/src/reverseproxy.js b/src/reverseproxy.js index 184255802..0fdf5830a 100644 --- a/src/reverseproxy.js +++ b/src/reverseproxy.js @@ -151,11 +151,12 @@ function providerMatchesSync(certFilePath, apiOptions) { // 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(location, domainObject, cert, key) { +function validateCertificate(location, domainObject, certificate) { assert.strictEqual(typeof location, 'string'); assert.strictEqual(typeof domainObject, 'object'); - assert.strictEqual(typeof cert, 'string'); - assert.strictEqual(typeof key, 'string'); + assert(certificate && typeof certificate, 'object'); + + const cert = certificate.cert, key = certificate.key; // check for empty cert and key strings if (!cert && key) return new ReverseProxyError(ReverseProxyError.INVALID_CERT, 'missing cert'); @@ -261,16 +262,15 @@ function getFallbackCertificate(domain, callback) { callback(null, { certFilePath, keyFilePath, type: 'fallback' }); } -function setAppCertificateSync(location, domainObject, cert, key) { +function setAppCertificateSync(location, domainObject, certificate) { assert.strictEqual(typeof location, 'string'); assert.strictEqual(typeof domainObject, 'object'); - assert.strictEqual(typeof cert, 'string'); - assert.strictEqual(typeof key, 'string'); + assert.strictEqual(typeof certificate, 'object'); let fqdn = domains.fqdn(location, domainObject); - if (cert && key) { - if (!safe.fs.writeFileSync(path.join(paths.APP_CERTS_DIR, `${fqdn}.user.cert`), cert)) return safe.error; - if (!safe.fs.writeFileSync(path.join(paths.APP_CERTS_DIR, `${fqdn}.user.key`), key)) return safe.error; + if (certificate.cert && certificate.key) { + if (!safe.fs.writeFileSync(path.join(paths.APP_CERTS_DIR, `${fqdn}.user.cert`), certificate.cert)) return safe.error; + if (!safe.fs.writeFileSync(path.join(paths.APP_CERTS_DIR, `${fqdn}.user.key`), certificate.key)) return safe.error; } else { // remove existing cert/key if (!safe.fs.unlinkSync(path.join(paths.APP_CERTS_DIR, `${fqdn}.user.cert`))) debug('Error removing cert: ' + safe.error.message); if (!safe.fs.unlinkSync(path.join(paths.APP_CERTS_DIR, `${fqdn}.user.key`))) debug('Error removing key: ' + safe.error.message); diff --git a/src/test/reverseproxy-test.js b/src/test/reverseproxy-test.js index 508d603fc..8134abaa2 100644 --- a/src/test/reverseproxy-test.js +++ b/src/test/reverseproxy-test.js @@ -74,54 +74,54 @@ describe('Certificates', function () { var validKey3 = '-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC+nx2TXe7i+YB8\np9E047z6tVDyr6/JHyxxz0S6PSlOh0sF+UzYrpmTSKyKtHMIv+N3ifUDpCs94n6v\nYnjG0eTaP6lq+3xJrYajYzOkB7c5O4ipgl9S3sibd/YtvruK1M+ukDZNHcD90MWV\nrOO754F3e3fj3oOjxoE+lcB05pt8+yu/ecTH3u+uvi3N4reQb27sZi6h3mDKLAXa\n3nBJ4xZsWTaiqQ4K0dFcTvskMpXEgaJ6mdROeRb1vuMiSIFDXHTEcbiyh7BXbKYY\ndLZetQdnjkUUwbvJVinK0pP0jP23L/PlVwtno3OBT+kp9Boh7wymgOlV/lQ14HYM\n4AASgkorAgMBAAECggEAdVSVLMcNqlGuv4vAHtDq2lpOaAKxrZbtkWPlxsisqzRl\nfljT7y+RQfHimkG16LXL+iFFWadsIlxOY/+1nZNGTPwQeNQwzVzs2ZbPC3DgW28E\nkGm56NVOHzu4oLGc2DhjWOxVMCRXTSN66sUPK/K0YunxgqXM2zrtBKvCWXI0VLlo\nN/UWAwHf4i0GWRl8u8PvxgMXlSW9p9l6gSsivWRMag9ADwRQ/NSKrRYkiOoRe3vz\nLxXARBvzeZXvOPVLGVRX4SIR7OmS8cC6Ol/rp1/ZFFID7aN+wdzphPSL1UNUriw4\nDv1mxz73SNakgeYSFBoWRS5BsJI01JoCoILsnhVCiQKBgQDyW+k5+j4K17fzwsmi\nyxZ0Nz/ncpkqxVrWYZM3pn7OVkb2NDArimEk53kmJ0hrT84kKJUYDx55R2TpnzpV\nMLmjxgs9TUrzZzsL/DP2ppkfE3OrPS+06OGa5GbURxD6KPvqDtOmU3oFyJ3f4YJR\nVK7RW+zO4sXEpHIxwdBXbYov1QKBgQDJWbt+W5M0sA2D5LrUBNMTvMdNnKH0syc2\nZlcIOdj6HuUIveYpBRq64Jn9VJpXMxQanwE+IUjCpPTa8wF0OA6MZPy6cfovqb8a\ni1/M/lvCoYVS3KHLcTOvTGD3xej0EUj13xWGNu8y3i7Z9/Bl21hEyjd0q0I5OqJx\no9Qa5TGR/wKBgBPfkYpdiMTe14i3ik09FgRFm4nhDcpCEKbPrYC8uF03Ge6KbQDF\nAh5ClN6aDggurRqt8Tvd0YPkZNP7aI8fxbk2PimystiuuFrNPX2WP6warjt2cvkE\nt6s522zAvxWkUrPor1ZONg1PXBLFrSf6J7OnNA3q7oina23FFM52fwRZAoGAZ7l7\nFffU2IKNI9HT0N7/YZ6RSVEUOXuFCsgjs5AhT5BUynERPTZs87I6gb9wltUwWRpq\nSHhbBDJ4FMa0jAtIq1hmvSF0EdOvJ9x+qJqr6JLOnMYd7zDMwFRna5yfigPRgx+9\n9dsc1CaTGiRYyg/5484MTWTgA51KC6Kq5IQHSj8CgYBr9rWgqM8hVCKSt1cMguQV\nTPaV97+u3kV2jFd/aVgDtCDIVvp5TPuqfskE1v3MsSjJ8hfHdYvyxZB8h8T4LlTD\n2HdxwCjVh2qirAvkar2b1mfA6R8msmVaIxBu4MqDcIPqR823klF7A8jSD3MGzYcU\nbnnxMdwgWQkmx0/6/90ZCg==\n-----END PRIVATE KEY-----\n'; it('does not allow empty string for cert', function () { - expect(reverseProxy.validateCertificate('', foobarDomain, '', 'key')).to.be.an(Error); + expect(reverseProxy.validateCertificate('', foobarDomain, { cert: '', key: 'key' })).to.be.an(Error); }); it('does not allow empty string for key', function () { - expect(reverseProxy.validateCertificate('', foobarDomain, 'cert', '')).to.be.an(Error); + expect(reverseProxy.validateCertificate('', foobarDomain, { cert: 'cert', key: '' })).to.be.an(Error); }); it('does not allow invalid cert', function () { - expect(reverseProxy.validateCertificate('', foobarDomain, 'someinvalidcert', validKey0)).to.be.an(Error); + expect(reverseProxy.validateCertificate('', foobarDomain, { cert: 'someinvalidcert', key: validKey0 })).to.be.an(Error); }); it('does not allow invalid key', function () { - expect(reverseProxy.validateCertificate('', foobarDomain, validCert0, 'invalidkey')).to.be.an(Error); + expect(reverseProxy.validateCertificate('', foobarDomain, { cert: validCert0, key: 'invalidkey' })).to.be.an(Error); }); it('does not allow cert without matching domain', function () { - expect(reverseProxy.validateCertificate('', { domain: 'cloudron.io' }, validCert0, validKey0)).to.be.an(Error); - expect(reverseProxy.validateCertificate('cloudron.io', foobarDomain, validCert0, validKey0)).to.be.an(Error); + expect(reverseProxy.validateCertificate('', { domain: 'cloudron.io' }, { cert: validCert0, key: validKey0 })).to.be.an(Error); + expect(reverseProxy.validateCertificate('cloudron.io', foobarDomain, { cert: validCert0, key: validKey0 })).to.be.an(Error); }); it('allows valid cert with matching domain', function () { - expect(reverseProxy.validateCertificate('', foobarDomain, validCert0, validKey0)).to.be(null); + expect(reverseProxy.validateCertificate('', foobarDomain, { cert: validCert0, key: validKey0 })).to.be(null); }); it('allows valid cert with matching domain (wildcard)', function () { - expect(reverseProxy.validateCertificate('abc', foobarDomain, validCert1, validKey1)).to.be(null); + expect(reverseProxy.validateCertificate('abc', foobarDomain, { cert: validCert1, key: validKey1 })).to.be(null); }); it('does now allow cert without matching domain (wildcard)', function () { - expect(reverseProxy.validateCertificate('', foobarDomain, validCert1, validKey1)).to.be.an(Error); - expect(reverseProxy.validateCertificate('bar.abc', foobarDomain, validCert1, validKey1)).to.be.an(Error); + expect(reverseProxy.validateCertificate('', foobarDomain, { cert: validCert1, key: validKey1 })).to.be.an(Error); + expect(reverseProxy.validateCertificate('bar.abc', foobarDomain, { cert: validCert1, key: validKey1 })).to.be.an(Error); }); it('allows valid cert with matching domain (subdomain)', function () { - expect(reverseProxy.validateCertificate('baz', foobarDomain, validCert2, validKey2)).to.be(null); + expect(reverseProxy.validateCertificate('baz', foobarDomain, { cert: validCert2, key: validKey2 })).to.be(null); }); it('does not allow cert without matching domain (subdomain)', function () { - expect(reverseProxy.validateCertificate('baz', foobarDomain, validCert0, validKey0)).to.be.an(Error); + expect(reverseProxy.validateCertificate('baz', foobarDomain, { cert: validCert0, key: validKey0 })).to.be.an(Error); }); it('does not allow invalid cert/key tuple', function () { - expect(reverseProxy.validateCertificate('', foobarDomain, validCert0, validKey1)).to.be.an(Error); + expect(reverseProxy.validateCertificate('', foobarDomain, { cert: validCert0, key: validKey1 })).to.be.an(Error); }); it('picks certificate in SAN', function () { - expect(reverseProxy.validateCertificate('', amazingDomain, validCert3, validKey3)).to.be(null); - expect(reverseProxy.validateCertificate('subdomain', amazingDomain, validCert3, validKey3)).to.be(null); + expect(reverseProxy.validateCertificate('', amazingDomain, { cert: validCert3, key: validKey3 })).to.be(null); + expect(reverseProxy.validateCertificate('subdomain', amazingDomain, { cert: validCert3, key: validKey3 })).to.be(null); }); }); @@ -139,8 +139,8 @@ describe('Certificates', function () { }); it('can validate the certs', function () { - expect(reverseProxy.validateCertificate('foo', domainObject, result.cert, result.key)).to.be(null); - expect(reverseProxy.validateCertificate('', domainObject, result.cert, result.key)).to.be(null); + expect(reverseProxy.validateCertificate('foo', domainObject, result)).to.be(null); + expect(reverseProxy.validateCertificate('', domainObject, result)).to.be(null); }); }); @@ -158,10 +158,10 @@ describe('Certificates', function () { }); it('can validate the certs', function () { - expect(reverseProxy.validateCertificate('foo', domainObject, result.cert, result.key)).to.be(null); - expect(reverseProxy.validateCertificate('', domainObject, result.cert, result.key)).to.be(null); + expect(reverseProxy.validateCertificate('foo', domainObject, result)).to.be(null); + expect(reverseProxy.validateCertificate('', domainObject, result)).to.be(null); - expect(reverseProxy.validateCertificate('foo', { domain: 'customer.cool.com', config: {} }, result.cert, result.key)).to.be.an(Error); + expect(reverseProxy.validateCertificate('foo', { domain: 'customer.cool.com', config: {} }, result)).to.be.an(Error); }); });