diff --git a/migrations/20211006200150-domains-ensure-fallbackCertificate.js b/migrations/20211006200150-domains-ensure-fallbackCertificate.js new file mode 100644 index 000000000..492060da7 --- /dev/null +++ b/migrations/20211006200150-domains-ensure-fallbackCertificate.js @@ -0,0 +1,33 @@ +'use strict'; + +const async = require('async'), + reverseProxy = require('../src/reverseproxy.js'), + safe = require('safetydance'); + +const NGINX_CERT_DIR = '/home/yellowtent/platformdata/nginx/cert'; + +// ensure fallbackCertificate of domains are present in database and the cert dir. it seems a bad migration lost them. +// https://forum.cloudron.io/topic/5683/data-argument-must-be-of-type-received-null-error-during-restore-process +exports.up = function(db, callback) { + db.all('SELECT * FROM domains', [ ], function (error, domains) { + if (error) return callback(error); + + async.eachSeries(domains, async function (domain, iteratorDone) { + let fallbackCertificate = safe.JSON.parse(domain.fallbackCertificateJson); + if (!fallbackCertificate || !fallbackCertificate.cert || !fallbackCertificate.key) { + let error; + [error, fallbackCertificate] = await safe(reverseProxy.generateFallbackCertificate(domain.domain)); + if (error) return iteratorDone(error); + } + + if (!safe.fs.writeFileSync(`${NGINX_CERT_DIR}/${domain.domain}.host.cert`, fallbackCertificate.cert, 'utf8')) return iteratorDone(safe.error); + if (!safe.fs.writeFileSync(`${NGINX_CERT_DIR}/${domain.domain}.host.key`, fallbackCertificate.key, 'utf8')) return iteratorDone(safe.error); + + db.runSql('UPDATE domains SET fallbackCertificateJson=? WHERE domain=?', [ JSON.stringify(fallbackCertificate), domain.domain ], iteratorDone); + }, callback); + }); +}; + +exports.down = function(db, callback) { + callback(); +}; diff --git a/src/domains.js b/src/domains.js index d6293fa81..3e4578f5c 100644 --- a/src/domains.js +++ b/src/domains.js @@ -142,7 +142,6 @@ async function add(domain, data, auditSource) { if (error) throw error; } else { fallbackCertificate = await reverseProxy.generateFallbackCertificate(domain); - if (fallbackCertificate.error) throw fallbackCertificate.error; } let error = validateTlsConfig(tlsConfig, provider); diff --git a/src/reverseproxy.js b/src/reverseproxy.js index ef189537f..d54a33656 100644 --- a/src/reverseproxy.js +++ b/src/reverseproxy.js @@ -192,36 +192,37 @@ async function reload() { if (error) throw new BoxError(BoxError.NGINX_ERROR, `Error reloading nginx: ${error.message}`); } +// this is used in migration - 20211006200150-domains-ensure-fallbackCertificate.js async function generateFallbackCertificate(domain) { assert.strictEqual(typeof domain, 'string'); const certFilePath = path.join(os.tmpdir(), `${domain}-${crypto.randomBytes(4).readUInt32LE(0)}.cert`); const keyFilePath = path.join(os.tmpdir(), `${domain}-${crypto.randomBytes(4).readUInt32LE(0)}.key`); - let opensslConf = safe.fs.readFileSync('/etc/ssl/openssl.cnf', 'utf8'); + const opensslConf = safe.fs.readFileSync('/etc/ssl/openssl.cnf', 'utf8'); // SAN must contain all the domains since CN check is based on implementation if SAN is found. -checkhost also checks only SAN if present! let opensslConfWithSan; - let cn = domain; + const cn = domain; debug(`generateFallbackCertificateSync: domain=${domain} cn=${cn}`); opensslConfWithSan = `${opensslConf}\n[SAN]\nsubjectAltName=DNS:${domain},DNS:*.${cn}\n`; - let configFile = path.join(os.tmpdir(), 'openssl-' + crypto.randomBytes(4).readUInt32LE(0) + '.conf'); + const configFile = path.join(os.tmpdir(), 'openssl-' + crypto.randomBytes(4).readUInt32LE(0) + '.conf'); safe.fs.writeFileSync(configFile, opensslConfWithSan, 'utf8'); // the days field is chosen to be less than 825 days per apple requirement (https://support.apple.com/en-us/HT210176) - let certCommand = util.format(`openssl req -x509 -newkey rsa:2048 -keyout ${keyFilePath} -out ${certFilePath} -days 800 -subj /CN=*.${cn} -extensions SAN -config ${configFile} -nodes`); - if (!safe.child_process.execSync(certCommand)) return { error: new BoxError(BoxError.OPENSSL_ERROR, safe.error.message) }; + const certCommand = util.format(`openssl req -x509 -newkey rsa:2048 -keyout ${keyFilePath} -out ${certFilePath} -days 800 -subj /CN=*.${cn} -extensions SAN -config ${configFile} -nodes`); + if (!safe.child_process.execSync(certCommand)) throw new BoxError(BoxError.OPENSSL_ERROR, safe.error.message); safe.fs.unlinkSync(configFile); const cert = safe.fs.readFileSync(certFilePath, 'utf8'); - if (!cert) return { error: new BoxError(BoxError.FS_ERROR, safe.error.message) }; + if (!cert) throw new BoxError(BoxError.FS_ERROR, safe.error.message); safe.fs.unlinkSync(certFilePath); const key = safe.fs.readFileSync(keyFilePath, 'utf8'); - if (!key) return { error: new BoxError(BoxError.FS_ERROR, safe.error.message) }; + if (!key) throw new BoxError(BoxError.FS_ERROR, safe.error.message); safe.fs.unlinkSync(keyFilePath); - return { cert: cert, key: key, error: null }; + return { cert, key }; } async function setFallbackCertificate(domain, fallback) { diff --git a/src/test/reverseproxy-test.js b/src/test/reverseproxy-test.js index 22db228f3..eb0a12a05 100644 --- a/src/test/reverseproxy-test.js +++ b/src/test/reverseproxy-test.js @@ -131,7 +131,6 @@ describe('Reverse Proxy', function () { it('can generate fallback certs', async function () { result = await reverseProxy.generateFallbackCertificate(domainObject.domain); expect(result).to.be.ok(); - expect(result.error).to.be(null); }); it('can validate the certs', function () {