feat: custom TLD virtual hosts with SNI certs, dynamic PAC, and TLD validation
CI / Build & Test (push) Successful in 3m15s
CI / Build & Test (push) Successful in 3m15s
- Add isValidVhostHostname() to dashboard.js with embedded REAL_TLDS and REAL_SLD_TLDS blocklists; enforces 3-label minimum (two-tier TLD requirement), valid label characters, and blocks real public TLDs/SLDs (e.g. .com, co.uk) - Replace hardcoded .hole.sail validation in vhost submit handler with new validator - Update Add Virtual Host modal hint text and add inline format explanation - Update applyPAC() in background.js to accept a tlds array, generating one dnsDomainIs clause per unique two-label base domain; .hole.sail always included - Store virtualHosts in extensionState and pass derived TLD list to applyPAC at every getState response and retryGetStateForConnectProxy call - Replace single upfront *.hole.sail cert in https-proxy.js with SNICallback that lazily generates a wildcard cert per two-label base domain on first connection; baseline *.hole.sail cert still pre-generated at startup - Add chrome.permissions.request() in background.js send handler to grant host permissions for new TLDs dynamically after successful setVirtualHost - Add optional_host_permissions: ["*://*/*"] and "permissions" to manifest.json to enable runtime host permission grants for custom TLDs
This commit is contained in:
+47
-17
@@ -5,6 +5,7 @@
|
||||
|
||||
const path = require('bare-path');
|
||||
const fs = require('bare-fs');
|
||||
const tls = require('tls');
|
||||
|
||||
let https = null;
|
||||
let http = null;
|
||||
@@ -45,26 +46,55 @@ function start(port, certsDirOrCA, callback) {
|
||||
return proxyServer;
|
||||
}
|
||||
|
||||
// Wildcard cert for *.hole.sail — Chrome accepts wildcards when base domain has 2+ labels
|
||||
const certDomain = '*.hole.sail';
|
||||
let cert = null;
|
||||
let key = null;
|
||||
if (typeof certsDirOrCA === 'object' && certsDirOrCA.getOrCreateDomainCert) {
|
||||
const result = certsDirOrCA.getOrCreateDomainCert(certDomain, [
|
||||
{ type: 2, value: '*.hole.sail' },
|
||||
{ type: 2, value: 'hole.sail' }
|
||||
]);
|
||||
if (result) {
|
||||
cert = typeof result.cert === 'string' ? Buffer.from(result.cert) : result.cert;
|
||||
key = typeof result.key === 'string' ? Buffer.from(result.key) : result.key;
|
||||
}
|
||||
}
|
||||
if (!cert || !key) {
|
||||
if (callback) callback(new Error('Could not get wildcard cert for ' + certDomain));
|
||||
if (typeof certsDirOrCA !== 'object' || !certsDirOrCA.getOrCreateDomainCert) {
|
||||
if (callback) callback(new Error('certsDirOrCA must provide getOrCreateDomainCert'));
|
||||
return null;
|
||||
}
|
||||
|
||||
const opts = { cert, key };
|
||||
// Pre-generate the baseline *.hole.sail cert so the server can start immediately.
|
||||
// Additional TLD certs are generated lazily via SNICallback on first connection.
|
||||
const baseResult = certsDirOrCA.getOrCreateDomainCert('*.hole.sail', [
|
||||
{ type: 2, value: '*.hole.sail' },
|
||||
{ type: 2, value: 'hole.sail' }
|
||||
]);
|
||||
if (!baseResult) {
|
||||
if (callback) callback(new Error('Could not get baseline wildcard cert for *.hole.sail'));
|
||||
return null;
|
||||
}
|
||||
|
||||
// SNI callback — lazily issues a wildcard cert per two-label base domain
|
||||
function sniCallback(servername, cb) {
|
||||
const parts = (servername || '').split('.');
|
||||
// Need at least 3 labels (host.second.tld) to derive a 2-label base domain
|
||||
const baseDomain = parts.length >= 3 ? parts.slice(-2).join('.') : 'hole.sail';
|
||||
const wildcard = '*.' + baseDomain;
|
||||
debugLog('sniCallback: servername=', servername, 'baseDomain=', baseDomain, 'wildcard=', wildcard);
|
||||
const result = certsDirOrCA.getOrCreateDomainCert(wildcard, [
|
||||
{ type: 2, value: wildcard },
|
||||
{ type: 2, value: baseDomain }
|
||||
]);
|
||||
if (!result) {
|
||||
if (process.stderr) process.stderr.write('[https-proxy] SNI: no cert for ' + wildcard + '\n');
|
||||
return cb(new Error('No cert for ' + wildcard));
|
||||
}
|
||||
try {
|
||||
const ctx = tls.createSecureContext({
|
||||
cert: typeof result.cert === 'string' ? result.cert : result.cert.toString(),
|
||||
key: typeof result.key === 'string' ? result.key : result.key.toString()
|
||||
});
|
||||
cb(null, ctx);
|
||||
} catch (e) {
|
||||
if (process.stderr) process.stderr.write('[https-proxy] SNI createSecureContext error: ' + e.message + '\n');
|
||||
cb(e);
|
||||
}
|
||||
}
|
||||
|
||||
const opts = {
|
||||
cert: typeof baseResult.cert === 'string' ? Buffer.from(baseResult.cert) : baseResult.cert,
|
||||
key: typeof baseResult.key === 'string' ? Buffer.from(baseResult.key) : baseResult.key,
|
||||
SNICallback: sniCallback
|
||||
};
|
||||
|
||||
let callbackCalled = false;
|
||||
function done(err) {
|
||||
if (callbackCalled) return;
|
||||
|
||||
Reference in New Issue
Block a user