diff --git a/src/certificates.js b/src/certificates.js index 20c042d05..11759c4e3 100644 --- a/src/certificates.js +++ b/src/certificates.js @@ -304,12 +304,13 @@ function validateCertificate(cert, key, fqdn) { return null; } -function setFallbackCertificate(cert, key, callback) { +function setFallbackCertificate(cert, key, fqdn, callback) { assert.strictEqual(typeof cert, 'string'); assert.strictEqual(typeof key, 'string'); + assert.strictEqual(typeof fqdn, 'string'); assert.strictEqual(typeof callback, 'function'); - var error = validateCertificate(cert, key, '*.' + config.fqdn()); + var error = validateCertificate(cert, key, '*.' + fqdn); if (error) return callback(new CertificatesError(CertificatesError.INVALID_CERT, error.message)); // backup the cert @@ -320,7 +321,7 @@ function setFallbackCertificate(cert, key, callback) { if (!safe.fs.writeFileSync(path.join(paths.NGINX_CERT_DIR, 'host.cert'), cert)) return callback(new CertificatesError(CertificatesError.INTERNAL_ERROR, safe.error.message)); if (!safe.fs.writeFileSync(path.join(paths.NGINX_CERT_DIR, 'host.key'), key)) return callback(new CertificatesError(CertificatesError.INTERNAL_ERROR, safe.error.message)); - exports.events.emit(exports.EVENT_CERT_CHANGED, '*.' + config.fqdn()); + exports.events.emit(exports.EVENT_CERT_CHANGED, '*.' + fqdn); nginx.reload(function (error) { if (error) return callback(new CertificatesError(CertificatesError.INTERNAL_ERROR, error)); diff --git a/src/cloudron.js b/src/cloudron.js index 99d8eff54..01e098700 100644 --- a/src/cloudron.js +++ b/src/cloudron.js @@ -195,8 +195,8 @@ function dnsSetup(dnsConfig, domain, zoneName, callback) { domains.get(domain, function (error, result) { if (error && error.reason !== DomainError.NOT_FOUND) return callback(new SettingsError(SettingsError.INTERNAL_ERROR, error)); - if (!result) domains.add(domain, zoneName, dnsConfig, done); - else domains.update(domain, dnsConfig, done); + if (!result) domains.add(domain, zoneName, dnsConfig, null, done); + else domains.update(domain, dnsConfig, null, done); }); } @@ -858,8 +858,8 @@ function migrate(options, callback) { if (error && error.reason !== DomainError.NOT_FOUND) return callback(new CloudronError(CloudronError.INTERNAL_ERROR, error)); var func; - if (!result) func = domains.add.bind(null, options.domain, options.zoneName, dnsConfig); - else func = domains.update.bind(null, options.domain, dnsConfig); + if (!result) func = domains.add.bind(null, options.domain, options.zoneName, dnsConfig, null); + else func = domains.update.bind(null, options.domain, dnsConfig, null); func(function (error) { if (error && error.reason === DomainError.BAD_FIELD) return callback(new CloudronError(CloudronError.BAD_FIELD, error.message)); diff --git a/src/domains.js b/src/domains.js index 3ac1e2b24..a5cf3e639 100644 --- a/src/domains.js +++ b/src/domains.js @@ -17,6 +17,7 @@ module.exports = exports = { }; var assert = require('assert'), + certificates = require('./certificates.js'), DatabaseError = require('./databaseerror.js'), domaindb = require('./domaindb.js'), sysinfo = require('./sysinfo.js'), @@ -84,15 +85,21 @@ function verifyDnsConfig(config, domain, zoneName, ip, callback) { } -function add(domain, zoneName, config, callback) { +function add(domain, zoneName, config, fallbackCertificate, callback) { assert.strictEqual(typeof domain, 'string'); assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof config, 'object'); + assert.strictEqual(typeof fallbackCertificate, 'object'); assert.strictEqual(typeof callback, 'function'); if (!tld.isValid(domain)) return callback(new DomainError(DomainError.BAD_FIELD, 'Invalid domain')); if (!tld.isValid(zoneName)) return callback(new DomainError(DomainError.BAD_FIELD, 'Invalid zoneName')); + if (fallbackCertificate) { + let error = certificates.validateCertificate(fallbackCertificate.cert, fallbackCertificate.key, domain); + if (error) return callback(new DomainError(DomainError.BAD_FIELD, error.message)); + } + sysinfo.getPublicIp(function (error, ip) { if (error) return callback(new DomainError(DomainError.INTERNAL_ERROR, 'Error getting IP:' + error.message)); @@ -108,7 +115,13 @@ function add(domain, zoneName, config, callback) { if (error && error.reason === DatabaseError.ALREADY_EXISTS) return callback(new DomainError(DomainError.ALREADY_EXISTS)); if (error) return callback(new DomainError(DomainError.INTERNAL_ERROR, error)); - return callback(null); + if (!fallbackCertificate) return callback(); + + // cert validation already happened above no need to check all errors again + certificates.setFallbackCertificate(fallbackCertificate.cert, fallbackCertificate.key, domain, function (error) { + if (error) return callback(new DomainError(DomainError.INTERNAL_ERROR, error)); + callback(); + }); }); }); }); @@ -136,15 +149,21 @@ function getAll(callback) { }); } -function update(domain, config, callback) { +function update(domain, config, fallbackCertificate, callback) { assert.strictEqual(typeof domain, 'string'); assert.strictEqual(typeof config, 'object'); + assert.strictEqual(typeof fallbackCertificate, 'object'); assert.strictEqual(typeof callback, 'function'); domaindb.get(domain, function (error, result) { if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new DomainError(DomainError.NOT_FOUND)); if (error) return callback(new DomainError(DomainError.INTERNAL_ERROR, error)); + if (fallbackCertificate) { + let error = certificates.validateCertificate(fallbackCertificate.cert, fallbackCertificate.key, domain); + if (error) return callback(new DomainError(DomainError.BAD_FIELD, error.message)); + } + sysinfo.getPublicIp(function (error, ip) { if (error) return callback(new DomainError(DomainError.INTERNAL_ERROR, 'Error getting IP:' + error.message)); @@ -160,7 +179,13 @@ function update(domain, config, callback) { if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new DomainError(DomainError.NOT_FOUND)); if (error) return callback(new DomainError(DomainError.INTERNAL_ERROR, error)); - return callback(null); + if (!fallbackCertificate) return callback(); + + // cert validation already happened above no need to check all errors again + certificates.setFallbackCertificate(fallbackCertificate.cert, fallbackCertificate.key, domain, function (error) { + if (error) return callback(new DomainError(DomainError.INTERNAL_ERROR, error)); + callback(); + }); }); }); }); diff --git a/src/routes/domains.js b/src/routes/domains.js index 496bd886f..47638ab5a 100644 --- a/src/routes/domains.js +++ b/src/routes/domains.js @@ -20,8 +20,11 @@ function add(req, res, next) { if (typeof req.body.domain !== 'string') return next(new HttpError(400, 'domain must be a string')); if (typeof req.body.config !== 'object') return next(new HttpError(400, 'config must be an object')); if ('zoneName' in req.body && typeof req.body.zoneName !== 'string') return next(new HttpError(400, 'zoneName must be a string')); + if ('fallbackCertificate' in req.body && typeof req.body.fallbackCertificate !== 'object') return next(new HttpError(400, 'fallbackCertificate must be a object with cert and key strings')); + if (req.body.fallbackCertificate && (!req.body.cert || typeof req.body.cert !== 'string')) return next(new HttpError(400, 'fallbackCertificate.cert must be a string')); + if (req.body.fallbackCertificate && (!req.body.key || typeof req.body.key !== 'string')) return next(new HttpError(400, 'fallbackCertificate.key must be a string')); - domains.add(req.body.domain, req.body.zoneName || req.body.domain, req.body.config, function (error) { + domains.add(req.body.domain, req.body.zoneName || req.body.domain, req.body.config, req.body.fallbackCertificate || null, function (error) { if (error && error.reason === DomainError.ALREADY_EXISTS) return next(new HttpError(400, error.message)); if (error && error.reason === DomainError.BAD_FIELD) return next(new HttpError(400, error.message)); if (error && error.reason === DomainError.INVALID_PROVIDER) return next(new HttpError(400, error.message)); @@ -55,8 +58,11 @@ function update(req, res, next) { assert.strictEqual(typeof req.body, 'object'); if (typeof req.body.config !== 'object') return next(new HttpError(400, 'config must be an object')); + if ('fallbackCertificate' in req.body && typeof req.body.fallbackCertificate !== 'object') return next(new HttpError(400, 'fallbackCertificate must be a object with cert and key strings')); + if (req.body.fallbackCertificate && (!req.body.cert || typeof req.body.cert !== 'string')) return next(new HttpError(400, 'fallbackCertificate.cert must be a string')); + if (req.body.fallbackCertificate && (!req.body.key || typeof req.body.key !== 'string')) return next(new HttpError(400, 'fallbackCertificate.key must be a string')); - domains.update(req.params.domain, req.body.config, function (error) { + domains.update(req.params.domain, req.body.config, req.body.fallbackCertificate || null, function (error) { if (error && error.reason === DomainError.NOT_FOUND) return next(new HttpError(404, error.message)); if (error && error.reason === DomainError.BAD_FIELD) return next(new HttpError(400, error.message)); if (error && error.reason === DomainError.INVALID_PROVIDER) return next(new HttpError(400, error.message)); diff --git a/src/routes/settings.js b/src/routes/settings.js index 64648cc0d..64273bb4d 100644 --- a/src/routes/settings.js +++ b/src/routes/settings.js @@ -33,7 +33,6 @@ exports = module.exports = { getAppstoreConfig: getAppstoreConfig, setAppstoreConfig: setAppstoreConfig, - setFallbackCertificate: setFallbackCertificate, setAdminCertificate: setAdminCertificate }; @@ -294,21 +293,6 @@ function setAppstoreConfig(req, res, next) { }); } -// default fallback cert -function setFallbackCertificate(req, res, next) { - assert.strictEqual(typeof req.body, 'object'); - - if (!req.body.cert || typeof req.body.cert !== 'string') return next(new HttpError(400, 'cert must be a string')); - if (!req.body.key || typeof req.body.key !== 'string') return next(new HttpError(400, 'key must be a string')); - - certificates.setFallbackCertificate(req.body.cert, req.body.key, function (error) { - if (error && error.reason === CertificatesError.INVALID_CERT) return next(new HttpError(400, error.message)); - if (error) return next(new HttpError(500, error)); - - next(new HttpSuccess(202, {})); - }); -} - // only webadmin cert, until it can be treated just like a normal app function setAdminCertificate(req, res, next) { assert.strictEqual(typeof req.body, 'object'); diff --git a/src/server.js b/src/server.js index e18b03df9..8350a4908 100644 --- a/src/server.js +++ b/src/server.js @@ -208,7 +208,6 @@ function initializeExpressSync() { router.get ('/api/v1/settings/email_status', settingsScope, routes.user.requireAdmin, routes.settings.getEmailStatus); router.get ('/api/v1/settings/backup_config', settingsScope, routes.user.requireAdmin, routes.settings.getBackupConfig); router.post('/api/v1/settings/backup_config', settingsScope, routes.user.requireAdmin, routes.settings.setBackupConfig); - router.post('/api/v1/settings/certificate', settingsScope, routes.user.requireAdmin, routes.settings.setFallbackCertificate); router.post('/api/v1/settings/admin_certificate', settingsScope, routes.user.requireAdmin, routes.settings.setAdminCertificate); router.get ('/api/v1/settings/time_zone', settingsScope, routes.user.requireAdmin, routes.settings.getTimeZone);