reverseproxy: rework cert logic

9c8f78a059 already fixed many of the cert issues.

However, some issues were caught in the CI:

* The TLS addon has to be rebuilt and not just restarted. For this reason, we now
  move things to a directory instead of mounting files. This way the container is just restarted.

* Cleanups must be driven by the database and not the filesystem . Deleting files on disk or after a restore,
  the certs are left dangling forever in the db.

* Separate the db cert logic and disk cert logic. This way we can sync as many times as we want and whenever we want.
This commit is contained in:
Girish Ramakrishnan
2022-11-28 22:32:34 +01:00
parent c844be5be1
commit 89127e1df7
12 changed files with 279 additions and 348 deletions

View File

@@ -64,9 +64,6 @@ exports = module.exports = {
appendLogLine,
getCertificate,
getLocationsSync,
start,
stop,
restart,
@@ -139,6 +136,11 @@ exports = module.exports = {
LOCATION_TYPE_REDIRECT: 'redirect',
LOCATION_TYPE_ALIAS: 'alias',
// should probably be in table as well
LOCATION_TYPE_DASHBOARD: 'dashboard',
LOCATION_TYPE_MAIL: 'mail',
LOCATION_TYPE_DIRECTORY_SERVER: 'directoryserver',
// respositories, match with appstore
REPOSITORY_CORE: 'core',
REPOSITORY_COMMUNITY: 'community',
@@ -204,6 +206,7 @@ const APPS_FIELDS_PREFIXED = [ 'apps.id', 'apps.appStoreId', 'apps.installationS
'apps.storageVolumeId', 'apps.storageVolumePrefix', 'apps.ts', 'apps.healthTime', '(apps.icon IS NOT NULL) AS hasIcon', '(apps.appStoreIcon IS NOT NULL) AS hasAppStoreIcon' ].join(',');
// const PORT_BINDINGS_FIELDS = [ 'hostPort', 'type', 'environmentVariable', 'appId' ].join(',');
const LOCATION_FIELDS = [ 'appId', 'subdomain', 'domain', 'type', 'certificateJson' ];
const CHECKVOLUME_CMD = path.join(__dirname, 'scripts/checkvolume.sh');
@@ -1797,11 +1800,23 @@ async function setCertificate(app, data, auditSource) {
const result = await database.query('UPDATE locations SET certificateJson=? WHERE location=? AND domain=?', [ certificate ? JSON.stringify(certificate) : null, subdomain, domain ]);
if (result.affectedRows === 0) throw new BoxError(BoxError.NOT_FOUND, 'Location not found');
app = await get(app.id); // refresh app object
await reverseProxy.setUserCertificate(app, dns.fqdn(subdomain, domain), certificate);
const location = await getLocation(subdomain, domain); // fresh location object
await reverseProxy.setUserCertificate(app, location);
await eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId: app.id, app, subdomain, domain, cert });
}
async function getLocation(subdomain, domain) {
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof domain, 'string');
const result = await database.query(`SELECT ${LOCATION_FIELDS} FROM locations WHERE subdomain=? AND domain=?`, [ subdomain, domain ]);
if (result.length === 0) return null;
result[0].certificate = safe.JSON.parse(result[0].certificateJson);
result[0].fqdn = dns.fqdn(subdomain, domain);
return result[0];
}
async function setLocation(app, data, auditSource) {
assert.strictEqual(typeof app, 'object');
assert.strictEqual(typeof data, 'object');
@@ -2049,15 +2064,6 @@ async function appendLogLine(app, line) {
if (!safe.fs.appendFileSync(logFilePath, line)) console.error(`Could not append log line for app ${app.id} at ${logFilePath}: ${safe.error.message}`);
}
async function getCertificate(subdomain, domain) {
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof domain, 'string');
const result = await database.query('SELECT certificateJson FROM locations WHERE subdomain=? AND domain=?', [ subdomain, domain ]);
if (result.length === 0) return null;
return safe.JSON.parse(result[0].certificateJson);
}
// does a re-configure when called from most states. for install/clone errors, it re-installs with an optional manifest
// re-configure can take a dockerImage but not a manifest because re-configure does not clean up addons
async function repair(app, data, auditSource) {
@@ -2866,14 +2872,3 @@ async function restoreConfig(app) {
await update(app.id, data);
}
function getLocationsSync(app) {
assert.strictEqual(typeof app, 'object');
const locations = [{ subdomain: app.subdomain, domain: app.domain, fqdn: app.fqdn, certificate: app.certificate, type: exports.LOCATION_TYPE_PRIMARY }]
.concat(app.secondaryDomains.map(sd => { return { subdomain: sd.subdomain, domain: sd.domain, certificate: sd.certificate, fqdn: sd.fqdn, type: exports.LOCATION_TYPE_SECONDARY }; }))
.concat(app.redirectDomains.map(rd => { return { subdomain: rd.subdomain, domain: rd.domain, certificate: rd.certificate, fqdn: rd.fqdn, type: exports.LOCATION_TYPE_REDIRECT }; }))
.concat(app.aliasDomains.map(ad => { return { subdomain: ad.subdomain, domain: ad.domain, certificate: ad.certificate, fqdn: ad.fqdn, type: exports.LOCATION_TYPE_ALIAS }; }));
return locations;
}