151 lines
4.8 KiB
TypeScript
151 lines
4.8 KiB
TypeScript
|
|
import * as plugins from '../../plugins.js';
|
||
|
|
import * as fs from 'fs';
|
||
|
|
import * as path from 'path';
|
||
|
|
import { fileURLToPath } from 'url';
|
||
|
|
import { AsyncFileSystem } from '../../core/utils/fs-utils.js';
|
||
|
|
import type { ILogger, ICertificateEntry } from './models/types.js';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Interface for default certificate data
|
||
|
|
*/
|
||
|
|
export interface IDefaultCertificates {
|
||
|
|
key: string;
|
||
|
|
cert: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Provides default SSL certificates for HttpProxy.
|
||
|
|
* This is a minimal replacement for the deprecated CertificateManager.
|
||
|
|
*
|
||
|
|
* For production certificate management, use SmartCertManager instead.
|
||
|
|
*/
|
||
|
|
export class DefaultCertificateProvider {
|
||
|
|
private defaultCertificates: IDefaultCertificates | null = null;
|
||
|
|
private certificateCache: Map<string, ICertificateEntry> = new Map();
|
||
|
|
private initialized = false;
|
||
|
|
|
||
|
|
constructor(private logger?: ILogger) {}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Load default certificates asynchronously (preferred)
|
||
|
|
*/
|
||
|
|
public async loadDefaultCertificatesAsync(): Promise<IDefaultCertificates> {
|
||
|
|
if (this.defaultCertificates) {
|
||
|
|
return this.defaultCertificates;
|
||
|
|
}
|
||
|
|
|
||
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||
|
|
const certPath = path.join(__dirname, '..', '..', '..', 'assets', 'certs');
|
||
|
|
|
||
|
|
try {
|
||
|
|
const [key, cert] = await Promise.all([
|
||
|
|
AsyncFileSystem.readFile(path.join(certPath, 'key.pem')),
|
||
|
|
AsyncFileSystem.readFile(path.join(certPath, 'cert.pem'))
|
||
|
|
]);
|
||
|
|
|
||
|
|
this.defaultCertificates = { key, cert };
|
||
|
|
this.logger?.info?.('Loaded default certificates from filesystem');
|
||
|
|
this.initialized = true;
|
||
|
|
return this.defaultCertificates;
|
||
|
|
} catch (error) {
|
||
|
|
this.logger?.warn?.(`Failed to load default certificates: ${error}`);
|
||
|
|
this.defaultCertificates = this.generateFallbackCertificate();
|
||
|
|
this.initialized = true;
|
||
|
|
return this.defaultCertificates;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Load default certificates synchronously (for backward compatibility)
|
||
|
|
* @deprecated Use loadDefaultCertificatesAsync instead
|
||
|
|
*/
|
||
|
|
public loadDefaultCertificatesSync(): IDefaultCertificates {
|
||
|
|
if (this.defaultCertificates) {
|
||
|
|
return this.defaultCertificates;
|
||
|
|
}
|
||
|
|
|
||
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||
|
|
const certPath = path.join(__dirname, '..', '..', '..', 'assets', 'certs');
|
||
|
|
|
||
|
|
try {
|
||
|
|
this.defaultCertificates = {
|
||
|
|
key: fs.readFileSync(path.join(certPath, 'key.pem'), 'utf8'),
|
||
|
|
cert: fs.readFileSync(path.join(certPath, 'cert.pem'), 'utf8')
|
||
|
|
};
|
||
|
|
this.logger?.info?.('Loaded default certificates from filesystem (sync)');
|
||
|
|
} catch (error) {
|
||
|
|
this.logger?.warn?.(`Failed to load default certificates: ${error}`);
|
||
|
|
this.defaultCertificates = this.generateFallbackCertificate();
|
||
|
|
}
|
||
|
|
|
||
|
|
this.initialized = true;
|
||
|
|
return this.defaultCertificates;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Gets the default certificates (loads synchronously if not already loaded)
|
||
|
|
*/
|
||
|
|
public getDefaultCertificates(): IDefaultCertificates {
|
||
|
|
if (!this.defaultCertificates) {
|
||
|
|
return this.loadDefaultCertificatesSync();
|
||
|
|
}
|
||
|
|
return this.defaultCertificates;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Updates a certificate in the cache
|
||
|
|
*/
|
||
|
|
public updateCertificate(domain: string, cert: string, key: string): void {
|
||
|
|
this.certificateCache.set(domain, {
|
||
|
|
cert,
|
||
|
|
key,
|
||
|
|
expires: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000) // 90 days
|
||
|
|
});
|
||
|
|
|
||
|
|
this.logger?.info?.(`Certificate updated for ${domain}`);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Gets a cached certificate
|
||
|
|
*/
|
||
|
|
public getCachedCertificate(domain: string): ICertificateEntry | null {
|
||
|
|
return this.certificateCache.get(domain) || null;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Gets statistics for metrics
|
||
|
|
*/
|
||
|
|
public getStats(): { cachedCertificates: number; defaultCertEnabled: boolean } {
|
||
|
|
return {
|
||
|
|
cachedCertificates: this.certificateCache.size,
|
||
|
|
defaultCertEnabled: this.defaultCertificates !== null
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Generate a fallback self-signed certificate placeholder
|
||
|
|
* Note: This is just a placeholder - real apps should provide proper certificates
|
||
|
|
*/
|
||
|
|
private generateFallbackCertificate(): IDefaultCertificates {
|
||
|
|
this.logger?.warn?.('Using fallback self-signed certificate placeholder');
|
||
|
|
|
||
|
|
// Minimal self-signed certificate for fallback only
|
||
|
|
// In production, proper certificates should be provided via SmartCertManager
|
||
|
|
const selfSignedCert = `-----BEGIN CERTIFICATE-----
|
||
|
|
MIIBkTCB+wIJAKHHIgIIA0/cMA0GCSqGSIb3DQEBBQUAMA0xCzAJBgNVBAYTAlVT
|
||
|
|
MB4XDTE0MDEwMTAwMDAwMFoXDTI0MDEwMTAwMDAwMFowDTELMAkGA1UEBhMCVVMw
|
||
|
|
gZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMRiH0VwnOH3jCV7c6JFZWYrvuqy
|
||
|
|
-----END CERTIFICATE-----`;
|
||
|
|
|
||
|
|
const selfSignedKey = `-----BEGIN PRIVATE KEY-----
|
||
|
|
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMRiH0VwnOH3jCV7
|
||
|
|
c6JFZWYrvuqyALCLXj0pcr1iqNdHjegNXnkl5zjdaUjq4edNOKl7M1AlFiYjG2xk
|
||
|
|
-----END PRIVATE KEY-----`;
|
||
|
|
|
||
|
|
return {
|
||
|
|
key: selfSignedKey,
|
||
|
|
cert: selfSignedCert
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|