mirror of
https://github.com/fosrl/pangolin.git
synced 2026-03-04 09:46:40 +00:00
Merge branch 'main' into dev
This commit is contained in:
2
.github/workflows/cicd.yml
vendored
2
.github/workflows/cicd.yml
vendored
@@ -8,7 +8,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
name: Build and Release
|
name: Build and Release
|
||||||
runs-on: ubuntu-latest
|
runs-on: amd64-runner
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
|
|||||||
20
Makefile
20
Makefile
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
major_tag := $(shell echo $(tag) | cut -d. -f1)
|
major_tag := $(shell echo $(tag) | cut -d. -f1)
|
||||||
minor_tag := $(shell echo $(tag) | cut -d. -f1,2)
|
minor_tag := $(shell echo $(tag) | cut -d. -f1,2)
|
||||||
build-release:
|
build-release-arm:
|
||||||
@if [ -z "$(tag)" ]; then \
|
@if [ -z "$(tag)" ]; then \
|
||||||
echo "Error: tag is required. Usage: make build-release tag=<tag>"; \
|
echo "Error: tag is required. Usage: make build-release tag=<tag>"; \
|
||||||
exit 1; \
|
exit 1; \
|
||||||
@@ -25,6 +25,24 @@ build-release:
|
|||||||
--tag fosrl/pangolin:postgresql-$(minor_tag) \
|
--tag fosrl/pangolin:postgresql-$(minor_tag) \
|
||||||
--tag fosrl/pangolin:postgresql-$(tag) \
|
--tag fosrl/pangolin:postgresql-$(tag) \
|
||||||
--push .
|
--push .
|
||||||
|
docker buildx build \
|
||||||
|
--build-arg BUILD=enterprise
|
||||||
|
--build-arg DATABASE=sqlite \
|
||||||
|
--platform linux/arm64,linux/amd64 \
|
||||||
|
--tag fosrl/pangolin:ee-latest \
|
||||||
|
--tag fosrl/pangolin:ee-$(major_tag) \
|
||||||
|
--tag fosrl/pangolin:ee-$(minor_tag) \
|
||||||
|
--tag fosrl/pangolin:ee-$(tag) \
|
||||||
|
--push .
|
||||||
|
docker buildx build \
|
||||||
|
--build-arg BUILD=enterprise
|
||||||
|
--build-arg DATABASE=pg \
|
||||||
|
--platform linux/arm64,linux/amd64 \
|
||||||
|
--tag fosrl/pangolin:ee-postgresql-latest \
|
||||||
|
--tag fosrl/pangolin:ee-postgresql-$(major_tag) \
|
||||||
|
--tag fosrl/pangolin:ee-postgresql-$(minor_tag) \
|
||||||
|
--tag fosrl/pangolin:ee-postgresql-$(tag) \
|
||||||
|
--push .
|
||||||
|
|
||||||
build-arm:
|
build-arm:
|
||||||
docker buildx build --platform linux/arm64 -t fosrl/pangolin:latest .
|
docker buildx build --platform linux/arm64 -t fosrl/pangolin:latest .
|
||||||
|
|||||||
3784
package-lock.json
generated
3784
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -17,19 +17,28 @@ import { and, eq, isNotNull, or, inArray, sql } from "drizzle-orm";
|
|||||||
import { decryptData } from "@server/lib/encryption";
|
import { decryptData } from "@server/lib/encryption";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import NodeCache from "node-cache";
|
import NodeCache from "node-cache";
|
||||||
|
import logger from "@server/logger";
|
||||||
|
|
||||||
const encryptionKeyPath =
|
let encryptionKeyPath = "";
|
||||||
config.getRawPrivateConfig().server.encryption_key_path;
|
let encryptionKeyHex = "";
|
||||||
|
let encryptionKey: Buffer;
|
||||||
|
function loadEncryptData() {
|
||||||
|
if (encryptionKey) {
|
||||||
|
return; // already loaded
|
||||||
|
}
|
||||||
|
|
||||||
if (!fs.existsSync(encryptionKeyPath)) {
|
encryptionKeyPath = config.getRawPrivateConfig().server.encryption_key_path;
|
||||||
throw new Error(
|
|
||||||
"Encryption key file not found. Please generate one first."
|
if (!fs.existsSync(encryptionKeyPath)) {
|
||||||
);
|
throw new Error(
|
||||||
|
"Encryption key file not found. Please generate one first."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptionKeyHex = fs.readFileSync(encryptionKeyPath, "utf8").trim();
|
||||||
|
encryptionKey = Buffer.from(encryptionKeyHex, "hex");
|
||||||
}
|
}
|
||||||
|
|
||||||
const encryptionKeyHex = fs.readFileSync(encryptionKeyPath, "utf8").trim();
|
|
||||||
const encryptionKey = Buffer.from(encryptionKeyHex, "hex");
|
|
||||||
|
|
||||||
// Define the return type for clarity and type safety
|
// Define the return type for clarity and type safety
|
||||||
export type CertificateResult = {
|
export type CertificateResult = {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -49,6 +58,9 @@ export async function getValidCertificatesForDomains(
|
|||||||
domains: Set<string>,
|
domains: Set<string>,
|
||||||
useCache: boolean = true
|
useCache: boolean = true
|
||||||
): Promise<Array<CertificateResult>> {
|
): Promise<Array<CertificateResult>> {
|
||||||
|
|
||||||
|
loadEncryptData(); // Ensure encryption key is loaded
|
||||||
|
|
||||||
const finalResults: CertificateResult[] = [];
|
const finalResults: CertificateResult[] = [];
|
||||||
const domainsToQuery = new Set<string>();
|
const domainsToQuery = new Set<string>();
|
||||||
|
|
||||||
@@ -69,7 +81,8 @@ export async function getValidCertificatesForDomains(
|
|||||||
|
|
||||||
// 2. If all domains were resolved from the cache, return early
|
// 2. If all domains were resolved from the cache, return early
|
||||||
if (domainsToQuery.size === 0) {
|
if (domainsToQuery.size === 0) {
|
||||||
return decryptFinalResults(finalResults);
|
const decryptedResults = decryptFinalResults(finalResults);
|
||||||
|
return decryptedResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Prepare domains for the database query
|
// 3. Prepare domains for the database query
|
||||||
@@ -126,23 +139,32 @@ export async function getValidCertificatesForDomains(
|
|||||||
for (const domain of domainsToQuery) {
|
for (const domain of domainsToQuery) {
|
||||||
let foundCert: (typeof potentialCerts)[0] | undefined = undefined;
|
let foundCert: (typeof potentialCerts)[0] | undefined = undefined;
|
||||||
|
|
||||||
// Priority 1: Check for an exact match
|
// Priority 1: Check for an exact match (non-wildcard)
|
||||||
if (exactMatches.has(domain)) {
|
if (exactMatches.has(domain)) {
|
||||||
foundCert = exactMatches.get(domain);
|
foundCert = exactMatches.get(domain);
|
||||||
}
|
}
|
||||||
// Priority 2: Check for a wildcard match on the parent domain
|
// Priority 2: Check for a wildcard certificate that matches the exact domain
|
||||||
else {
|
else {
|
||||||
const parts = domain.split(".");
|
if (wildcardMatches.has(domain)) {
|
||||||
if (parts.length > 1) {
|
foundCert = wildcardMatches.get(domain);
|
||||||
const parentDomain = parts.slice(1).join(".");
|
}
|
||||||
if (wildcardMatches.has(parentDomain)) {
|
// Priority 3: Check for a wildcard match on the parent domain
|
||||||
foundCert = wildcardMatches.get(parentDomain);
|
else {
|
||||||
|
const parts = domain.split(".");
|
||||||
|
if (parts.length > 1) {
|
||||||
|
const parentDomain = parts.slice(1).join(".");
|
||||||
|
if (wildcardMatches.has(parentDomain)) {
|
||||||
|
foundCert = wildcardMatches.get(parentDomain);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a certificate was found, format it, add to results, and cache it
|
// If a certificate was found, format it, add to results, and cache it
|
||||||
if (foundCert) {
|
if (foundCert) {
|
||||||
|
logger.debug(
|
||||||
|
`Creating result cert for ${domain} using cert from ${foundCert.domain}`
|
||||||
|
);
|
||||||
const resultCert: CertificateResult = {
|
const resultCert: CertificateResult = {
|
||||||
id: foundCert.certId,
|
id: foundCert.certId,
|
||||||
domain: foundCert.domain, // The actual domain of the cert record
|
domain: foundCert.domain, // The actual domain of the cert record
|
||||||
@@ -163,7 +185,8 @@ export async function getValidCertificatesForDomains(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return decryptFinalResults(finalResults);
|
const decryptedResults = decryptFinalResults(finalResults);
|
||||||
|
return decryptedResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
function decryptFinalResults(
|
function decryptFinalResults(
|
||||||
|
|||||||
@@ -172,6 +172,12 @@ export function readPrivateConfigFile() {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// test if the config file is there
|
||||||
|
if (!fs.existsSync(privateConfigFilePath1)) {
|
||||||
|
// load the default values of the zod schema and return those
|
||||||
|
return privateConfigSchema.parse({});
|
||||||
|
}
|
||||||
|
|
||||||
const loadConfig = (configPath: string) => {
|
const loadConfig = (configPath: string) => {
|
||||||
try {
|
try {
|
||||||
const yamlContent = fs.readFileSync(configPath, "utf8");
|
const yamlContent = fs.readFileSync(configPath, "utf8");
|
||||||
|
|||||||
@@ -224,6 +224,7 @@ export async function getTraefikConfig(
|
|||||||
}
|
}
|
||||||
// get the valid certs for these domains
|
// get the valid certs for these domains
|
||||||
validCerts = await getValidCertificatesForDomains(domains, true); // we are caching here because this is called often
|
validCerts = await getValidCertificatesForDomains(domains, true); // we are caching here because this is called often
|
||||||
|
logger.debug(`Valid certs for domains: ${JSON.stringify(validCerts)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const config_output: any = {
|
const config_output: any = {
|
||||||
|
|||||||
@@ -292,11 +292,33 @@ hybridRouter.get(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let encryptionKeyPath = "";
|
||||||
|
let encryptionKeyHex = "";
|
||||||
|
let encryptionKey: Buffer;
|
||||||
|
function loadEncryptData() {
|
||||||
|
if (encryptionKey) {
|
||||||
|
return; // already loaded
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptionKeyPath = privateConfig.getRawPrivateConfig().server.encryption_key_path;
|
||||||
|
|
||||||
|
if (!fs.existsSync(encryptionKeyPath)) {
|
||||||
|
throw new Error(
|
||||||
|
"Encryption key file not found. Please generate one first."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptionKeyHex = fs.readFileSync(encryptionKeyPath, "utf8").trim();
|
||||||
|
encryptionKey = Buffer.from(encryptionKeyHex, "hex");
|
||||||
|
}
|
||||||
|
|
||||||
// Get valid certificates for given domains (supports wildcard certs)
|
// Get valid certificates for given domains (supports wildcard certs)
|
||||||
hybridRouter.get(
|
hybridRouter.get(
|
||||||
"/certificates/domains",
|
"/certificates/domains",
|
||||||
async (req: Request, res: Response, next: NextFunction) => {
|
async (req: Request, res: Response, next: NextFunction) => {
|
||||||
try {
|
try {
|
||||||
|
loadEncryptData(); // Ensure encryption key is loaded
|
||||||
|
|
||||||
const parsed = getCertificatesByDomainsQuerySchema.safeParse(
|
const parsed = getCertificatesByDomainsQuerySchema.safeParse(
|
||||||
req.query
|
req.query
|
||||||
);
|
);
|
||||||
@@ -425,20 +447,6 @@ hybridRouter.get(
|
|||||||
filtered.push(cert);
|
filtered.push(cert);
|
||||||
}
|
}
|
||||||
|
|
||||||
const encryptionKeyPath =
|
|
||||||
privateConfig.getRawPrivateConfig().server.encryption_key_path;
|
|
||||||
|
|
||||||
if (!fs.existsSync(encryptionKeyPath)) {
|
|
||||||
throw new Error(
|
|
||||||
"Encryption key file not found. Please generate one first."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const encryptionKeyHex = fs
|
|
||||||
.readFileSync(encryptionKeyPath, "utf8")
|
|
||||||
.trim();
|
|
||||||
const encryptionKey = Buffer.from(encryptionKeyHex, "hex");
|
|
||||||
|
|
||||||
const result = filtered.map((cert) => {
|
const result = filtered.map((cert) => {
|
||||||
// Decrypt and save certificate file
|
// Decrypt and save certificate file
|
||||||
const decryptedCert = decryptData(
|
const decryptedCert = decryptData(
|
||||||
|
|||||||
Reference in New Issue
Block a user