fix: ssl + sslrenew

This commit is contained in:
Andras Bacsai 2022-03-01 13:07:34 +01:00
parent 6da78cd3e5
commit 23e12c9c44
5 changed files with 103 additions and 102 deletions

View File

@ -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);
}
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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 }),

View File

@ -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;
}
}

View File

@ -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'}
<div class="mx-auto max-w-4xl px-6">
<form on:submit|preventDefault={handleSubmit} class="grid grid-flow-row gap-2 py-4">
<div class="flex space-x-1 py-6">
<div class="flex space-x-1 pb-6">
<div class="title font-bold">Global Settings</div>
<button
type="submit"