From 0eb7f4526e4a2d6023498884bb6b7c88f157aaea Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Sat, 30 Apr 2022 13:21:18 +0200 Subject: [PATCH] feat: DNS check settings for SSL generation --- .../migration.sql | 23 +++ prisma/schema.prisma | 1 + src/lib/letsencrypt/index.ts | 151 ++++++++++-------- src/lib/locales/en.json | 5 +- src/routes/settings/index.json.ts | 13 +- src/routes/settings/index.svelte | 21 ++- 6 files changed, 145 insertions(+), 69 deletions(-) create mode 100644 prisma/migrations/20220430111953_ssl_dns_check_settings/migration.sql diff --git a/prisma/migrations/20220430111953_ssl_dns_check_settings/migration.sql b/prisma/migrations/20220430111953_ssl_dns_check_settings/migration.sql new file mode 100644 index 000000000..cf57379ca --- /dev/null +++ b/prisma/migrations/20220430111953_ssl_dns_check_settings/migration.sql @@ -0,0 +1,23 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_Setting" ( + "id" TEXT NOT NULL PRIMARY KEY, + "fqdn" TEXT, + "isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT false, + "dualCerts" BOOLEAN NOT NULL DEFAULT false, + "minPort" INTEGER NOT NULL DEFAULT 9000, + "maxPort" INTEGER NOT NULL DEFAULT 9100, + "proxyPassword" TEXT NOT NULL, + "proxyUser" TEXT NOT NULL, + "proxyHash" TEXT, + "isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false, + "isDNSCheckEnabled" BOOLEAN NOT NULL DEFAULT true, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); +INSERT INTO "new_Setting" ("createdAt", "dualCerts", "fqdn", "id", "isAutoUpdateEnabled", "isRegistrationEnabled", "maxPort", "minPort", "proxyHash", "proxyPassword", "proxyUser", "updatedAt") SELECT "createdAt", "dualCerts", "fqdn", "id", "isAutoUpdateEnabled", "isRegistrationEnabled", "maxPort", "minPort", "proxyHash", "proxyPassword", "proxyUser", "updatedAt" FROM "Setting"; +DROP TABLE "Setting"; +ALTER TABLE "new_Setting" RENAME TO "Setting"; +CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index c75ebeee5..58e4fd8a2 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -19,6 +19,7 @@ model Setting { proxyUser String proxyHash String? isAutoUpdateEnabled Boolean @default(false) + isDNSCheckEnabled Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } diff --git a/src/lib/letsencrypt/index.ts b/src/lib/letsencrypt/index.ts index 7682ecb27..f443625ae 100644 --- a/src/lib/letsencrypt/index.ts +++ b/src/lib/letsencrypt/index.ts @@ -109,6 +109,7 @@ export async function generateSSLCerts(): Promise { include: { destinationDocker: true, settings: true }, orderBy: { createdAt: 'desc' } }); + const { fqdn, isDNSCheckEnabled } = await db.prisma.setting.findFirst(); for (const application of applications) { try { if (application.fqdn && application.destinationDockerId) { @@ -147,7 +148,6 @@ export async function generateSSLCerts(): Promise { } } const services = await listServicesWithIncludes(); - for (const service of services) { try { if (service.fqdn && service.destinationDockerId) { @@ -171,7 +171,6 @@ export async function generateSSLCerts(): Promise { console.log(`Error during generateSSLCerts with ${service.fqdn}: ${error}`); } } - const { fqdn } = await db.prisma.setting.findFirst(); if (fqdn) { const domain = getDomain(fqdn); const isHttps = fqdn.startsWith('https://'); @@ -193,73 +192,99 @@ export async function generateSSLCerts(): Promise { file.endsWith('.pem') && certificates.push(file.replace(/\.pem$/, '')); } } - const resolver = new dns.Resolver({ timeout: 2000 }); - resolver.setServers(['8.8.8.8', '1.1.1.1']); - let ipv4, ipv6; - try { - ipv4 = await (await asyncExecShell(`curl -4s https://ifconfig.io`)).stdout; - } catch (error) {} - try { - ipv6 = await (await asyncExecShell(`curl -6s https://ifconfig.io`)).stdout; - } catch (error) {} - for (const ssl of ssls) { - if (!dev) { - if ( - certificates.includes(ssl.domain) || - certificates.includes(ssl.domain.replace('www.', '')) - ) { - // console.log(`Certificate for ${ssl.domain} already exists`); - } else { - // Checking DNS entry before generating certificate - if (ipv4 || ipv6) { - let domains4 = []; - let domains6 = []; - try { - domains4 = await resolver.resolve4(ssl.domain); - } catch (error) {} - try { - domains6 = await resolver.resolve6(ssl.domain); - } catch (error) {} - if (domains4.length > 0 || domains6.length > 0) { - if ( - (ipv4 && domains4.includes(ipv4.replace('\n', ''))) || - (ipv6 && domains6.includes(ipv6.replace('\n', ''))) - ) { - console.log('Generating SSL for', ssl.domain); - return await letsEncrypt(ssl.domain, ssl.id, ssl.isCoolify); + if (isDNSCheckEnabled) { + const resolver = new dns.Resolver({ timeout: 2000 }); + resolver.setServers(['8.8.8.8', '1.1.1.1']); + let ipv4, ipv6; + try { + ipv4 = await (await asyncExecShell(`curl -4s https://ifconfig.io`)).stdout; + } catch (error) {} + try { + ipv6 = await (await asyncExecShell(`curl -6s https://ifconfig.io`)).stdout; + } catch (error) {} + for (const ssl of ssls) { + if (!dev) { + if ( + certificates.includes(ssl.domain) || + certificates.includes(ssl.domain.replace('www.', '')) + ) { + // console.log(`Certificate for ${ssl.domain} already exists`); + } else { + // Checking DNS entry before generating certificate + if (ipv4 || ipv6) { + let domains4 = []; + let domains6 = []; + try { + domains4 = await resolver.resolve4(ssl.domain); + } catch (error) {} + try { + domains6 = await resolver.resolve6(ssl.domain); + } catch (error) {} + if (domains4.length > 0 || domains6.length > 0) { + if ( + (ipv4 && domains4.includes(ipv4.replace('\n', ''))) || + (ipv6 && domains6.includes(ipv6.replace('\n', ''))) + ) { + console.log('Generating SSL for', ssl.domain); + return await letsEncrypt(ssl.domain, ssl.id, ssl.isCoolify); + } } } + console.log('DNS settings is incorrect for', ssl.domain, 'skipping.'); + } + } else { + if ( + certificates.includes(ssl.domain) || + certificates.includes(ssl.domain.replace('www.', '')) + ) { + console.log(`Certificate for ${ssl.domain} already exists`); + } else { + // Checking DNS entry before generating certificate + if (ipv4 || ipv6) { + let domains4 = []; + let domains6 = []; + try { + domains4 = await resolver.resolve4(ssl.domain); + } catch (error) {} + try { + domains6 = await resolver.resolve6(ssl.domain); + } catch (error) {} + if (domains4.length > 0 || domains6.length > 0) { + if ( + (ipv4 && domains4.includes(ipv4.replace('\n', ''))) || + (ipv6 && domains6.includes(ipv6.replace('\n', ''))) + ) { + console.log('Generating SSL for', ssl.domain); + return; + } + } + } + console.log('DNS settings is incorrect for', ssl.domain, 'skipping.'); + } + } + } + } else { + if (!dev) { + for (const ssl of ssls) { + if ( + certificates.includes(ssl.domain) || + certificates.includes(ssl.domain.replace('www.', '')) + ) { + } else { + console.log('Generating SSL for', ssl.domain); + return await letsEncrypt(ssl.domain, ssl.id, ssl.isCoolify); } - console.log('DNS settings is incorrect for', ssl.domain, 'skipping.'); } } else { - if ( - certificates.includes(ssl.domain) || - certificates.includes(ssl.domain.replace('www.', '')) - ) { - console.log(`Certificate for ${ssl.domain} already exists`); - } else { - // Checking DNS entry before generating certificate - if (ipv4 || ipv6) { - let domains4 = []; - let domains6 = []; - try { - domains4 = await resolver.resolve4(ssl.domain); - } catch (error) {} - try { - domains6 = await resolver.resolve6(ssl.domain); - } catch (error) {} - if (domains4.length > 0 || domains6.length > 0) { - if ( - (ipv4 && domains4.includes(ipv4.replace('\n', ''))) || - (ipv6 && domains6.includes(ipv6.replace('\n', ''))) - ) { - console.log('Generating SSL for', ssl.domain); - return; - } - } + for (const ssl of ssls) { + if ( + certificates.includes(ssl.domain) || + certificates.includes(ssl.domain.replace('www.', '')) + ) { + console.log(`Certificate for ${ssl.domain} already exists`); + } else { + console.log('Generating SSL for', ssl.domain); } - console.log('DNS settings is incorrect for', ssl.domain, 'skipping.'); } } } diff --git a/src/lib/locales/en.json b/src/lib/locales/en.json index d7da2e60c..d89db86ef 100644 --- a/src/lib/locales/en.json +++ b/src/lib/locales/en.json @@ -308,7 +308,10 @@ "coolify_proxy_settings": "Coolify Proxy Settings", "credential_stat_explainer": "Credentials for stats page.", "auto_update_enabled": "Auto update enabled?", - "auto_update_enabled_explainer": "Enable automatic updates for Coolify. It will be done automatically behind the scenes, if there is no build process running." + "auto_update_enabled_explainer": "Enable automatic updates for Coolify. It will be done automatically behind the scenes, if there is no build process running.", + "generate_www_non_www_ssl": "It will generate certificates for both www and non-www.
You need to have both DNS entries set in advance.

Service needs to be restarted.", + "is_dns_check_enabled": "DNS check enabled?", + "is_dns_check_enabled_explainer": "Enable DNS check during SSL certificate generation.
It will check if the DNS entries are set correctly, before trying to get a new cert from Let's Encrypt." }, "team": { "pending_invitations": "Pending invitations", diff --git a/src/routes/settings/index.json.ts b/src/routes/settings/index.json.ts index 210f12782..5546d782f 100644 --- a/src/routes/settings/index.json.ts +++ b/src/routes/settings/index.json.ts @@ -64,13 +64,20 @@ export const post: RequestHandler = async (event) => { }; if (status === 401) return { status, body }; - const { fqdn, isRegistrationEnabled, dualCerts, minPort, maxPort, isAutoUpdateEnabled } = - await event.request.json(); + const { + fqdn, + isRegistrationEnabled, + dualCerts, + minPort, + maxPort, + isAutoUpdateEnabled, + isDNSCheckEnabled + } = await event.request.json(); try { const { id } = await db.listSettings(); await db.prisma.setting.update({ where: { id }, - data: { isRegistrationEnabled, dualCerts, isAutoUpdateEnabled } + data: { isRegistrationEnabled, dualCerts, isAutoUpdateEnabled, isDNSCheckEnabled } }); if (fqdn) { await db.prisma.setting.update({ where: { id }, data: { fqdn } }); diff --git a/src/routes/settings/index.svelte b/src/routes/settings/index.svelte index dce1362d7..8bfefbed3 100644 --- a/src/routes/settings/index.svelte +++ b/src/routes/settings/index.svelte @@ -43,6 +43,7 @@ let isRegistrationEnabled = settings.isRegistrationEnabled; let dualCerts = settings.dualCerts; let isAutoUpdateEnabled = settings.isAutoUpdateEnabled; + let isDNSCheckEnabled = settings.isDNSCheckEnabled; let minPort = settings.minPort; let maxPort = settings.maxPort; @@ -78,7 +79,15 @@ if (name === 'isAutoUpdateEnabled') { isAutoUpdateEnabled = !isAutoUpdateEnabled; } - await post(`/settings.json`, { isRegistrationEnabled, dualCerts, isAutoUpdateEnabled }); + if (name === 'isDNSCheckEnabled') { + isDNSCheckEnabled = !isDNSCheckEnabled; + } + await post(`/settings.json`, { + isRegistrationEnabled, + dualCerts, + isAutoUpdateEnabled, + isDNSCheckEnabled + }); return toast.push(t.get('application.settings_saved')); } catch ({ error }) { return errorNotification(error); @@ -176,13 +185,21 @@ /> +
+ changeSettings('isDNSCheckEnabled')} + /> +
!isFqdnSet && changeSettings('dualCerts')} />