From 23e12c9c44e1d1761836a640835a3a22cb8fadb3 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 1 Mar 2022 13:07:34 +0100 Subject: [PATCH] fix: ssl + sslrenew --- src/lib/haproxy/configuration.ts | 37 +++----- .../{letsencrypt.ts => letsencrypt/index.ts} | 88 ++++++++++++++++++- src/lib/queues/index.ts | 6 +- src/lib/queues/ssl.ts | 71 +-------------- src/routes/settings/index.svelte | 3 +- 5 files changed, 103 insertions(+), 102 deletions(-) rename src/lib/{letsencrypt.ts => letsencrypt/index.ts} (57%) diff --git a/src/lib/haproxy/configuration.ts b/src/lib/haproxy/configuration.ts index 257bfe376..a881d9f31 100644 --- a/src/lib/haproxy/configuration.ts +++ b/src/lib/haproxy/configuration.ts @@ -6,7 +6,6 @@ import crypto from 'crypto'; import * as db from '$lib/database'; import { checkContainer, checkHAProxy } from '.'; import { asyncExecShell, getDomain, getEngine } from '$lib/common'; -import { letsEncrypt } from '$lib/letsencrypt'; const url = dev ? 'http://localhost:5555' : 'http://coolify-haproxy:5555'; @@ -115,7 +114,6 @@ export async function haproxyInstance() { export async function configureHAProxy() { const haproxy = await haproxyInstance(); await checkHAProxy(haproxy); - const ssls = []; const data = { applications: [], services: [], @@ -147,7 +145,6 @@ export async function configureHAProxy() { redirectValue, redirectTo: isWWW ? domain : 'www.' + domain }); - if (isHttps) ssls.push({ domain, id, isCoolify: false }); } if (previews) { const host = getEngine(engine); @@ -171,7 +168,6 @@ export async function configureHAProxy() { redirectValue, redirectTo: isWWW ? previewDomain : 'www.' + previewDomain }); - if (isHttps) ssls.push({ domain: previewDomain, id, isCoolify: false }); } } } @@ -202,17 +198,18 @@ export async function configureHAProxy() { const isHttps = fqdn.startsWith('https://'); const isWWW = fqdn.includes('www.'); const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`; - data.services.push({ - id, - port, - publicPort, - domain, - isRunning, - isHttps, - redirectValue, - redirectTo: isWWW ? domain : 'www.' + domain - }); - if (isHttps) ssls.push({ domain, id, isCoolify: false }); + if (isRunning) { + data.services.push({ + id, + port, + publicPort, + domain, + isRunning, + isHttps, + redirectValue, + redirectTo: isWWW ? domain : 'www.' + domain + }); + } } } const { fqdn } = await db.prisma.setting.findFirst(); @@ -229,7 +226,6 @@ export async function configureHAProxy() { redirectValue, redirectTo: isWWW ? domain : 'www.' + domain }); - if (!dev && isHttps) ssls.push({ domain, id: 'coolify', isCoolify: true }); } const output = mustache.render(template, data); const newHash = crypto.createHash('md5').update(output).digest('hex'); @@ -249,13 +245,4 @@ export async function configureHAProxy() { } else { // console.log('HAProxy configuration is up to date'); } - if (ssls.length > 0) { - for (const ssl of ssls) { - if (!dev) { - await letsEncrypt(ssl.domain, ssl.id, ssl.isCoolify); - } else { - // console.log('Generate ssl for', ssl.domain); - } - } - } } diff --git a/src/lib/letsencrypt.ts b/src/lib/letsencrypt/index.ts similarity index 57% rename from src/lib/letsencrypt.ts rename to src/lib/letsencrypt/index.ts index b3846eba7..43e5d8a63 100644 --- a/src/lib/letsencrypt.ts +++ b/src/lib/letsencrypt/index.ts @@ -1,7 +1,7 @@ -import { dev } from '$app/env'; -import { forceSSLOffApplication, forceSSLOnApplication } from '$lib/haproxy'; -import { asyncExecShell, getEngine } from './common'; +import { asyncExecShell, getDomain, getEngine } from '$lib/common'; +import { checkContainer } from '$lib/haproxy'; import * as db from '$lib/database'; +import { dev } from '$app/env'; import cuid from 'cuid'; import getPort, { portNumbers } from 'get-port'; @@ -13,7 +13,7 @@ export async function letsEncrypt(domain, id = null, isCoolify = false) { const nakedDomain = domain.replace('www.', ''); const wwwDomain = `www.${nakedDomain}`; const randomCuid = cuid(); - const randomPort = 9000; + const randomPort = await getPort({ port: portNumbers(minPort, maxPort) }); let host; let dualCerts = false; @@ -72,3 +72,83 @@ export async function letsEncrypt(domain, id = null, isCoolify = false) { } } } + +export async function generateSSLCerts() { + const ssls = []; + const applications = await db.prisma.application.findMany({ + include: { destinationDocker: true, settings: true } + }); + for (const application of applications) { + const { + fqdn, + id, + destinationDocker: { engine, network }, + settings: { previews } + } = application; + const isRunning = await checkContainer(engine, id); + const domain = getDomain(fqdn); + const isHttps = fqdn.startsWith('https://'); + if (isRunning) { + if (isHttps) ssls.push({ domain, id, isCoolify: false }); + } + if (previews) { + const host = getEngine(engine); + const { stdout } = await asyncExecShell( + `DOCKER_HOST=${host} docker container ls --filter="status=running" --filter="network=${network}" --filter="name=${id}-" --format="{{json .Names}}"` + ); + const containers = stdout + .trim() + .split('\n') + .filter((a) => a) + .map((c) => c.replace(/"/g, '')); + if (containers.length > 0) { + for (const container of containers) { + let previewDomain = `${container.split('-')[1]}.${domain}`; + if (isHttps) ssls.push({ domain: previewDomain, id, isCoolify: false }); + } + } + } + } + const services = await db.prisma.service.findMany({ + include: { + destinationDocker: true, + minio: true, + plausibleAnalytics: true, + vscodeserver: true, + wordpress: true + } + }); + + for (const service of services) { + const { + fqdn, + id, + type, + destinationDocker: { engine } + } = service; + const found = db.supportedServiceTypesAndVersions.find((a) => a.name === type); + if (found) { + const domain = getDomain(fqdn); + const isHttps = fqdn.startsWith('https://'); + const isRunning = await checkContainer(engine, id); + if (isRunning) { + if (isHttps) ssls.push({ domain, id, isCoolify: false }); + } + } + } + const { fqdn } = await db.prisma.setting.findFirst(); + if (fqdn) { + const domain = getDomain(fqdn); + const isHttps = fqdn.startsWith('https://'); + if (isHttps) ssls.push({ domain, id: 'coolify', isCoolify: true }); + } + if (ssls.length > 0) { + for (const ssl of ssls) { + if (!dev) { + await letsEncrypt(ssl.domain, ssl.id, ssl.isCoolify); + } else { + console.log('Generate ssl for', ssl.domain); + } + } + } +} diff --git a/src/lib/queues/index.ts b/src/lib/queues/index.ts index 28a080ed3..1ce9411c4 100644 --- a/src/lib/queues/index.ts +++ b/src/lib/queues/index.ts @@ -86,9 +86,9 @@ const cron = async () => { ); await queue.proxy.add('proxy', {}, { repeat: { every: 10000 } }); - // await queue.ssl.add('ssl', {}, { repeat: { every: 10000 } }); - // await queue.cleanup.add('cleanup', {}, { repeat: { every: 600000 } }); - // await queue.sslRenew.add('sslRenew', {}, { repeat: { every: 1800000 } }); + await queue.ssl.add('ssl', {}, { repeat: { every: 60000 } }); + await queue.cleanup.add('cleanup', {}, { repeat: { every: 600000 } }); + await queue.sslRenew.add('sslRenew', {}, { repeat: { every: 1800000 } }); const events = { proxy: new QueueEvents('proxy', { ...connectionOptions }), diff --git a/src/lib/queues/ssl.ts b/src/lib/queues/ssl.ts index cbc7964c0..d14fc0822 100644 --- a/src/lib/queues/ssl.ts +++ b/src/lib/queues/ssl.ts @@ -1,76 +1,9 @@ -import { asyncExecShell, getDomain, getEngine } from '$lib/common'; -import { prisma } from '$lib/database'; -import { dockerInstance } from '$lib/docker'; -import { forceSSLOnApplication } from '$lib/haproxy'; -import * as db from '$lib/database'; -import { dev } from '$app/env'; -import getPort, { portNumbers } from 'get-port'; -import cuid from 'cuid'; +import { generateSSLCerts } from '$lib/letsencrypt'; export default async function () { try { - const data = await db.prisma.setting.findFirst(); - const { minPort, maxPort } = data; - - const publicPort = await getPort({ port: portNumbers(minPort, maxPort) }); - const randomCuid = cuid(); - const destinationDockers = await prisma.destinationDocker.findMany({}); - for (const destination of destinationDockers) { - if (destination.isCoolifyProxyUsed) { - const docker = dockerInstance({ destinationDocker: destination }); - const containers = await docker.engine.listContainers(); - const configurations = containers.filter( - (container) => container.Labels['coolify.managed'] - ); - for (const configuration of configurations) { - const parsedConfiguration = JSON.parse( - Buffer.from(configuration.Labels['coolify.configuration'], 'base64').toString() - ); - if (configuration.Labels['coolify.type'] === 'standalone-application') { - const { fqdn } = parsedConfiguration; - if (fqdn) { - const domain = getDomain(fqdn); - const isHttps = fqdn.startsWith('https://'); - if (isHttps) { - if (dev) { - console.log('DEV MODE: SSL is enabled'); - } else { - const host = getEngine(destination.engine); - await asyncExecShell( - `DOCKER_HOST=${host} docker run --rm --name certbot-${randomCuid} -p 9080:${publicPort} -v "coolify-letsencrypt:/etc/letsencrypt" certbot/certbot --logs-dir /etc/letsencrypt/logs certonly --standalone --preferred-challenges http --http-01-address 0.0.0.0 --http-01-port ${publicPort} -d ${domain} --agree-tos --non-interactive --register-unsafely-without-email` - ); - const { stderr } = await asyncExecShell( - `DOCKER_HOST=${host} docker run --rm -v "coolify-letsencrypt:/etc/letsencrypt" -v "coolify-ssl-certs:/app/ssl" alpine:latest cat /etc/letsencrypt/live/${domain}/fullchain.pem /etc/letsencrypt/live/${domain}/privkey.pem > /app/ssl/${domain}.pem` - ); - if (stderr) throw new Error(stderr); - } - } - } - } - } - } - } - const { fqdn } = await db.listSettings(); - if (fqdn) { - const domain = getDomain(fqdn); - const isHttps = fqdn.startsWith('https://'); - if (isHttps) { - if (dev) { - console.log('DEV MODE: SSL is enabled'); - } else { - await asyncExecShell( - `docker run --rm --name certbot-${randomCuid} -p 9080:${publicPort} -v "coolify-letsencrypt:/etc/letsencrypt" certbot/certbot --logs-dir /etc/letsencrypt/logs certonly --standalone --preferred-challenges http --http-01-address 0.0.0.0 --http-01-port ${publicPort} -d ${domain} --agree-tos --non-interactive --register-unsafely-without-email` - ); - - const { stderr } = await asyncExecShell( - `docker run --rm -v "coolify-letsencrypt:/etc/letsencrypt" -v "coolify-ssl-certs:/app/ssl" alpine:latest cat /etc/letsencrypt/live/${domain}/fullchain.pem /etc/letsencrypt/live/${domain}/privkey.pem > /app/ssl/${domain}.pem` - ); - if (stderr) throw new Error(stderr); - } - } - } + return await generateSSLCerts(); } catch (error) { - console.log(error); throw error; } } diff --git a/src/routes/settings/index.svelte b/src/routes/settings/index.svelte index 0db5122aa..bf6ee64c3 100644 --- a/src/routes/settings/index.svelte +++ b/src/routes/settings/index.svelte @@ -78,6 +78,7 @@ if (fqdn !== settings.fqdn) { await post(`/settings/check.json`, { fqdn }); await post(`/settings.json`, { fqdn }); + return window.location.reload(); } if (minPort !== settings.minPort || maxPort !== settings.maxPort) { await post(`/settings.json`, { minPort, maxPort }); @@ -98,7 +99,7 @@ {#if $session.teamId === '0'}
-
+
Global Settings