fix: Improved tcp proxy monitoring for databases/ftp

This commit is contained in:
Andras Bacsai 2022-04-14 00:04:46 +02:00
parent ce2757f514
commit 2bd3802a6f
8 changed files with 86 additions and 19 deletions

View File

@ -9,6 +9,7 @@ import { default as ProdPrisma } from '@prisma/client';
import type { Database, DatabaseSettings } from '@prisma/client';
import generator from 'generate-password';
import forge from 'node-forge';
import getPort, { portNumbers } from 'get-port';
export function generatePassword(length = 24): string {
return generator.generate({
@ -251,3 +252,29 @@ export function generateDatabaseConfiguration(database: Database & { settings: D
};
}
}
export async function getFreePort() {
const data = await prisma.setting.findFirst();
const { minPort, maxPort } = data;
const dbUsed = await (
await prisma.database.findMany({
where: { publicPort: { not: null } },
select: { publicPort: true }
})
).map((a) => a.publicPort);
const wpFtpUsed = await (
await prisma.wordpress.findMany({
where: { ftpPublicPort: { not: null } },
select: { ftpPublicPort: true }
})
).map((a) => a.ftpPublicPort);
const wpUsed = await (
await prisma.wordpress.findMany({
where: { mysqlPublicPort: { not: null } },
select: { mysqlPublicPort: true }
})
).map((a) => a.mysqlPublicPort);
const usedPorts = [...dbUsed, ...wpFtpUsed, ...wpUsed];
return await getPort({ port: portNumbers(minPort, maxPort), exclude: usedPorts });
}

View File

@ -7,6 +7,7 @@ import builder from './builder';
import logger from './logger';
import cleanup from './cleanup';
import proxy from './proxy';
import proxyTcpHttp from './proxyTcpHttp';
import ssl from './ssl';
import sslrenewal from './sslrenewal';
@ -29,17 +30,20 @@ const connectionOptions = {
const cron = async (): Promise<void> => {
new QueueScheduler('proxy', connectionOptions);
new QueueScheduler('proxyTcpHttp', connectionOptions);
new QueueScheduler('cleanup', connectionOptions);
new QueueScheduler('ssl', connectionOptions);
new QueueScheduler('sslRenew', connectionOptions);
const queue = {
proxy: new Queue('proxy', { ...connectionOptions }),
proxyTcpHttp: new Queue('proxyTcpHttp', { ...connectionOptions }),
cleanup: new Queue('cleanup', { ...connectionOptions }),
ssl: new Queue('ssl', { ...connectionOptions }),
sslRenew: new Queue('sslRenew', { ...connectionOptions })
};
await queue.proxy.drain();
await queue.proxyTcpHttp.drain();
await queue.cleanup.drain();
await queue.ssl.drain();
await queue.sslRenew.drain();
@ -54,6 +58,16 @@ const cron = async (): Promise<void> => {
}
);
new Worker(
'proxyTcpHttp',
async () => {
await proxyTcpHttp();
},
{
...connectionOptions
}
);
new Worker(
'ssl',
async () => {
@ -85,6 +99,7 @@ const cron = async (): Promise<void> => {
);
await queue.proxy.add('proxy', {}, { repeat: { every: 10000 } });
await queue.proxyTcpHttp.add('proxyTcpHttp', {}, { repeat: { every: 10000 } });
await queue.ssl.add('ssl', {}, { repeat: { every: dev ? 10000 : 60000 } });
if (!dev) await queue.cleanup.add('cleanup', {}, { repeat: { every: 300000 } });
await queue.sslRenew.add('sslRenew', {}, { repeat: { every: 1800000 } });

View File

@ -0,0 +1,34 @@
import { ErrorHandler, generateDatabaseConfiguration, prisma } from '$lib/database';
import { checkContainer, startTcpProxy } from '$lib/haproxy';
export default async function (): Promise<void | {
status: number;
body: { message: string; error: string };
}> {
try {
const databasesWithPublicPort = await prisma.database.findMany({
where: { publicPort: { not: null } },
include: { settings: true, destinationDocker: true }
});
for (const database of databasesWithPublicPort) {
const { destinationDockerId, destinationDocker, publicPort, id } = database;
if (destinationDockerId) {
const { privatePort } = generateDatabaseConfiguration(database);
await startTcpProxy(destinationDocker, id, publicPort, privatePort);
}
}
const wordpressWithFtp = await prisma.wordpress.findMany({
where: { ftpPublicPort: { not: null } },
include: { service: { include: { destinationDocker: true } } }
});
for (const ftp of wordpressWithFtp) {
const { service, ftpPublicPort, id } = ftp;
const { destinationDockerId, destinationDocker } = service;
if (destinationDockerId) {
await startTcpProxy(destinationDocker, `${id}-ftp`, ftpPublicPort, 22);
}
}
} catch (error) {
return ErrorHandler(error.response?.body || error);
}
}

View File

@ -34,6 +34,7 @@
name="rootUserPassword"
bind:value={database.rootUserPassword}
/>
<Explainer text="Could be changed while the database is running." />
</div>
<div class="grid grid-cols-2 items-center">
<label for="dbUser" class="text-base font-bold text-stone-100">User</label>

View File

@ -1,7 +1,7 @@
import { getUserDetails } from '$lib/common';
import * as db from '$lib/database';
import { ErrorHandler, stopDatabase } from '$lib/database';
import { deleteProxy } from '$lib/haproxy';
import { stopTcpHttpProxy } from '$lib/haproxy';
import type { RequestHandler } from '@sveltejs/kit';
export const del: RequestHandler = async (event) => {
@ -12,7 +12,7 @@ export const del: RequestHandler = async (event) => {
const database = await db.getDatabase({ id, teamId });
if (database.destinationDockerId) {
const everStarted = await stopDatabase(database);
if (everStarted) await deleteProxy({ id });
if (everStarted) await stopTcpHttpProxy(database.destinationDocker, database.publicPort);
}
await db.removeDatabase({ id });
return { status: 200 };

View File

@ -1,20 +1,16 @@
import { getUserDetails } from '$lib/common';
import * as db from '$lib/database';
import { generateDatabaseConfiguration, ErrorHandler } from '$lib/database';
import { generateDatabaseConfiguration, ErrorHandler, getFreePort } from '$lib/database';
import { startTcpProxy, stopTcpHttpProxy } from '$lib/haproxy';
import type { RequestHandler } from '@sveltejs/kit';
import getPort, { portNumbers } from 'get-port';
export const post: RequestHandler = async (event) => {
const { status, body, teamId } = await getUserDetails(event);
if (status === 401) return { status, body };
const { id } = event.params;
const data = await db.prisma.setting.findFirst();
const { minPort, maxPort } = data;
const { isPublic, appendOnly = true } = await event.request.json();
const publicPort = await getPort({ port: portNumbers(minPort, maxPort) });
const publicPort = await getFreePort();
try {
await db.setDatabase({ id, isPublic, appendOnly });

View File

@ -4,9 +4,7 @@ import { promises as fs } from 'fs';
import yaml from 'js-yaml';
import type { RequestHandler } from '@sveltejs/kit';
import { startHttpProxy } from '$lib/haproxy';
import getPort, { portNumbers } from 'get-port';
import { getDomain } from '$lib/components/common';
import { ErrorHandler, getServiceImage } from '$lib/database';
import { ErrorHandler, getFreePort, getServiceImage } from '$lib/database';
import { makeLabelForServices } from '$lib/buildPacks/common';
import type { ComposeFile } from '$lib/types/composeFile';
@ -28,13 +26,10 @@ export const post: RequestHandler = async (event) => {
serviceSecret
} = service;
const data = await db.prisma.setting.findFirst();
const { minPort, maxPort } = data;
const network = destinationDockerId && destinationDocker.network;
const host = getEngine(destinationDocker.engine);
const publicPort = await getPort({ port: portNumbers(minPort, maxPort) });
const publicPort = await getFreePort();
const consolePort = 9001;
const apiPort = 9000;

View File

@ -2,7 +2,7 @@ import { dev } from '$app/env';
import { asyncExecShell, getEngine, getUserDetails } from '$lib/common';
import { decrypt, encrypt } from '$lib/crypto';
import * as db from '$lib/database';
import { generateDatabaseConfiguration, ErrorHandler, generatePassword } from '$lib/database';
import { ErrorHandler, generatePassword, getFreePort } from '$lib/database';
import { checkContainer, startTcpProxy, stopTcpHttpProxy } from '$lib/haproxy';
import type { ComposeFile } from '$lib/types/composeFile';
import type { RequestHandler } from '@sveltejs/kit';
@ -16,11 +16,10 @@ export const post: RequestHandler = async (event) => {
if (status === 401) return { status, body };
const { id } = event.params;
const data = await db.prisma.setting.findFirst();
const { minPort, maxPort } = data;
const { ftpEnabled } = await event.request.json();
const publicPort = await getPort({ port: portNumbers(minPort, maxPort) });
const publicPort = await getFreePort();
let ftpUser = cuid();
let ftpPassword = generatePassword();