feat(integration): improve integration test connection (#3005)
This commit is contained in:
@@ -1,13 +1,18 @@
|
||||
import { X509Certificate } from "node:crypto";
|
||||
import fsSync from "node:fs";
|
||||
import fs from "node:fs/promises";
|
||||
import type { AgentOptions } from "node:https";
|
||||
import { Agent as HttpsAgent } from "node:https";
|
||||
import path from "node:path";
|
||||
import { rootCertificates } from "node:tls";
|
||||
import { checkServerIdentity, rootCertificates } from "node:tls";
|
||||
import axios from "axios";
|
||||
import { fetch } from "undici";
|
||||
|
||||
import { env } from "@homarr/common/env";
|
||||
import { LoggingAgent } from "@homarr/common/server";
|
||||
import type { InferSelectModel } from "@homarr/db";
|
||||
import { db } from "@homarr/db";
|
||||
import type { trustedCertificateHostnames } from "@homarr/db/schema";
|
||||
|
||||
const getCertificateFolder = () => {
|
||||
return env.NODE_ENV === "production"
|
||||
@@ -40,10 +45,23 @@ export const loadCustomRootCertificatesAsync = async () => {
|
||||
export const removeCustomRootCertificateAsync = async (fileName: string) => {
|
||||
const folder = getCertificateFolder();
|
||||
if (!folder) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
await fs.rm(path.join(folder, fileName));
|
||||
const existingFiles = await fs.readdir(folder, { withFileTypes: true });
|
||||
if (!existingFiles.some((file) => file.isFile() && file.name === fileName)) {
|
||||
throw new Error(`File ${fileName} does not exist`);
|
||||
}
|
||||
|
||||
const fullPath = path.join(folder, fileName);
|
||||
const content = await fs.readFile(fullPath, "utf8");
|
||||
|
||||
await fs.rm(fullPath);
|
||||
try {
|
||||
return new X509Certificate(content);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const addCustomRootCertificateAsync = async (fileName: string, content: string) => {
|
||||
@@ -61,25 +79,56 @@ export const addCustomRootCertificateAsync = async (fileName: string, content: s
|
||||
await fs.writeFile(path.join(folder, fileName), content);
|
||||
};
|
||||
|
||||
export const createCertificateAgentAsync = async () => {
|
||||
export const getTrustedCertificateHostnamesAsync = async () => {
|
||||
return await db.query.trustedCertificateHostnames.findMany();
|
||||
};
|
||||
|
||||
export const getAllTrustedCertificatesAsync = async () => {
|
||||
const customCertificates = await loadCustomRootCertificatesAsync();
|
||||
return rootCertificates.concat(customCertificates.map((cert) => cert.content));
|
||||
};
|
||||
|
||||
export const createCustomCheckServerIdentity = (
|
||||
trustedHostnames: InferSelectModel<typeof trustedCertificateHostnames>[],
|
||||
): typeof checkServerIdentity => {
|
||||
return (hostname, peerCertificate) => {
|
||||
const matchingTrustedHostnames = trustedHostnames.filter(
|
||||
(cert) => cert.thumbprint === peerCertificate.fingerprint256,
|
||||
);
|
||||
|
||||
// We trust the certificate if we have a matching hostname
|
||||
if (matchingTrustedHostnames.some((cert) => cert.hostname === hostname)) return undefined;
|
||||
|
||||
return checkServerIdentity(hostname, peerCertificate);
|
||||
};
|
||||
};
|
||||
|
||||
export const createCertificateAgentAsync = async (override?: {
|
||||
ca: string | string[];
|
||||
checkServerIdentity: typeof checkServerIdentity;
|
||||
}) => {
|
||||
return new LoggingAgent({
|
||||
connect: {
|
||||
ca: rootCertificates.concat(customCertificates.map((cert) => cert.content)),
|
||||
connect: override ?? {
|
||||
ca: await getAllTrustedCertificatesAsync(),
|
||||
checkServerIdentity: createCustomCheckServerIdentity(await getTrustedCertificateHostnamesAsync()),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const createHttpsAgentAsync = async () => {
|
||||
const customCertificates = await loadCustomRootCertificatesAsync();
|
||||
return new HttpsAgent({
|
||||
ca: rootCertificates.concat(customCertificates.map((cert) => cert.content)),
|
||||
});
|
||||
export const createHttpsAgentAsync = async (override?: Pick<AgentOptions, "ca" | "checkServerIdentity">) => {
|
||||
return new HttpsAgent(
|
||||
override ?? {
|
||||
ca: await getAllTrustedCertificatesAsync(),
|
||||
checkServerIdentity: createCustomCheckServerIdentity(await getTrustedCertificateHostnamesAsync()),
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
export const createAxiosCertificateInstanceAsync = async () => {
|
||||
export const createAxiosCertificateInstanceAsync = async (
|
||||
override?: Pick<AgentOptions, "ca" | "checkServerIdentity">,
|
||||
) => {
|
||||
return axios.create({
|
||||
httpsAgent: await createHttpsAgentAsync(),
|
||||
httpsAgent: await createHttpsAgentAsync(override),
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user