Require apex and www TLS for storefront SSL status.

Per-row ensure and SSL refresh treat storefront as valid only when both names match, and toast messages list each host explicitly.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-08-09 17:57:24 +03:30
co-authored by Cursor
parent a9d5f2e48d
commit 158523df7b
3 changed files with 86 additions and 34 deletions
+4 -3
View File
@@ -1,14 +1,15 @@
import * as tls from 'tls';
const DEFAULT_TIMEOUT_MS = 2500;
const DEFAULT_TIMEOUT_MS = 4000;
function peerMatchesHost(host: string, socket: tls.TLSSocket): boolean {
const cert = socket.getPeerCertificate();
// detailed=true so SAN is present for checkServerIdentity
const cert = socket.getPeerCertificate(true);
if (!cert || Object.keys(cert).length === 0) {
return false;
}
try {
const err = tls.checkServerIdentity(host, cert);
const err = tls.checkServerIdentity(host, cert as tls.PeerCertificate);
return !err;
} catch {
return false;
+72 -29
View File
@@ -156,7 +156,12 @@ export class DomainAdminService {
const targets: Array<{ id: bigint; host: string; deploySlug: string | null }> = [];
for (const domain of candidates) {
const ok = await probeTlsHost(domain.host);
const apexOk = await probeTlsHost(domain.host);
const wwwHost = domain.host.startsWith('www.')
? domain.host
: `www.${domain.host}`;
const wwwOk = await probeTlsHost(wwwHost);
const ok = apexOk && wwwOk;
if (ok) {
if (!domain.sslEnabled) {
await this.prisma.domain.update({
@@ -195,16 +200,20 @@ export class DomainAdminService {
slug: domain.deploySlug,
});
const ok = await probeTlsHost(domain.host);
const wwwHost = domain.host.startsWith('www.')
? domain.host
: `www.${domain.host}`;
const wwwOk = ok && (await probeTlsHost(wwwHost));
await this.prisma.domain.update({
where: { id: domain.id },
data: { sslEnabled: ok },
data: { sslEnabled: wwwOk },
});
if (ok) {
if (wwwOk) {
issued.push(domain.host);
} else {
failed.push({
host: domain.host,
error: 'Certbot finished but TLS probe still failed',
error: 'Certbot finished but TLS probe still failed (apex or www)',
});
}
} catch (err) {
@@ -224,8 +233,8 @@ export class DomainAdminService {
}
/**
* Per-row SSL ensure: probe apex + business.* + customer.*.
* Issue storefront SSL on websites VM when apex fails (deploy_slug required).
* Per-row SSL ensure: probe apex + www + business.* + customer.*.
* Issue storefront SSL on websites VM when apex/www fail (deploy_slug required).
* Expand dashboard cert when business/customer fail.
* Skip hosts that already have valid TLS.
*/
@@ -239,6 +248,7 @@ export class DomainAdminService {
}
const apex = domain.host.trim().toLowerCase();
const wwwHost = apex.startsWith('www.') ? apex : `www.${apex}`;
const businessHost = `business.${apex}`;
const customerHost = `customer.${apex}`;
const slug = domain.deploySlug?.trim() || null;
@@ -246,40 +256,81 @@ export class DomainAdminService {
type HostStatus = 'ok' | 'issued' | 'failed' | 'skipped';
type HostResult = { host: string; status: HostStatus; detail?: string };
const hosts: { apex: HostResult; business: HostResult; customer: HostResult } = {
const hosts: {
apex: HostResult;
www: HostResult;
business: HostResult;
customer: HostResult;
} = {
apex: { host: apex, status: 'ok' },
www: { host: wwwHost, status: 'ok' },
business: { host: businessHost, status: 'ok' },
customer: { host: customerHost, status: 'ok' },
};
let apexOk = await probeTlsHost(apex);
let wwwOk = await probeTlsHost(wwwHost);
let businessOk = await probeTlsHost(businessHost);
let customerOk = await probeTlsHost(customerHost);
if (apexOk) {
const storefrontNeedsIssue = !apexOk || !wwwOk;
if (!storefrontNeedsIssue) {
hosts.apex = { host: apex, status: 'ok', detail: 'Already valid' };
hosts.www = { host: wwwHost, status: 'ok', detail: 'Already valid' };
} else if (!slug) {
hosts.apex = {
host: apex,
status: 'skipped',
detail: 'No storefront deploy — apex SSL is issued on the websites VM only when git/deploy is configured',
status: apexOk ? 'ok' : 'skipped',
detail: apexOk
? 'Already valid'
: 'No storefront deploy — apex SSL needs git/deploy on the websites VM',
};
hosts.www = {
host: wwwHost,
status: wwwOk ? 'ok' : 'skipped',
detail: wwwOk
? 'Already valid'
: 'No storefront deploy — www SSL needs git/deploy on the websites VM',
};
} else {
try {
await this.websiteDeployAgent.issueSsl({ host: apex, slug });
apexOk = await probeTlsHost(apex);
wwwOk = await probeTlsHost(wwwHost);
hosts.apex = apexOk
? { host: apex, status: 'issued', detail: 'Issued on websites VM' }
? {
host: apex,
status: 'issued',
detail: 'Issued on websites VM',
}
: {
host: apex,
status: 'failed',
detail: 'Certbot ran but HTTPS probe still failed — check DNS for apex and www',
detail: 'Certbot ran but HTTPS probe still failed — check DNS for apex',
};
hosts.www = wwwOk
? {
host: wwwHost,
status: 'issued',
detail: 'Issued on websites VM',
}
: {
host: wwwHost,
status: 'failed',
detail: 'Certbot ran but HTTPS probe still failed — check DNS for www',
};
} catch (err) {
const detail = err instanceof Error ? err.message : 'Storefront SSL issue failed';
hosts.apex = {
host: apex,
status: 'failed',
detail: err instanceof Error ? err.message : 'Storefront SSL issue failed',
status: apexOk ? 'ok' : 'failed',
detail: apexOk ? 'Already valid' : detail,
};
hosts.www = {
host: wwwHost,
status: wwwOk ? 'ok' : 'failed',
detail: wwwOk ? 'Already valid' : detail,
};
}
}
@@ -326,28 +377,20 @@ export class DomainAdminService {
}
}
const storefrontOk = apexOk && wwwOk;
const updated = await this.prisma.domain.update({
where: { id: domainId },
data: { sslEnabled: apexOk },
data: { sslEnabled: storefrontOk },
});
const parts = [hosts.apex, hosts.business, hosts.customer];
const parts = [hosts.apex, hosts.www, hosts.business, hosts.customer];
const failed = parts.filter((p) => p.status === 'failed');
const issued = parts.filter((p) => p.status === 'issued');
const skipped = parts.filter((p) => p.status === 'skipped');
let message: string;
if (failed.length === 0 && issued.length === 0 && skipped.length === 0) {
message = `SSL already valid for ${apex}, ${businessHost}, ${customerHost}`;
} else if (failed.length === 0) {
const bits = [
...issued.map((p) => `${p.host} issued`),
...skipped.map((p) => `${p.host} skipped`),
];
message = bits.join(' · ');
} else {
message = `SSL ensure partial: ${failed.map((p) => p.host).join(', ')} failed`;
}
// Always spell out each host so dashboard-only OK is never mistaken for storefront OK.
const message = parts
.map((p) => `${p.host}: ${p.status}${p.detail ? ` (${p.detail})` : ''}`)
.join(' · ');
return {
status: failed.length === 0 ? ('ok' as const) : ('partial' as const),
+10 -2
View File
@@ -88,12 +88,20 @@ export class InternalSslService implements OnModuleInit {
const started = Date.now();
try {
const domains = await this.listActiveDomains();
const sslByHost = await probeTlsHosts(domains.map((d) => d.host));
const hostsToProbe = domains.flatMap((d) => {
const apex = d.host.trim().toLowerCase();
if (!apex) return [];
const www = apex.startsWith('www.') ? apex : `www.${apex}`;
return [apex, www];
});
const sslByHost = await probeTlsHosts(hostsToProbe);
let updated = 0;
for (const domain of domains) {
const hostKey = domain.host.trim().toLowerCase();
const sslEnabled = sslByHost.get(hostKey) ?? false;
const wwwKey = hostKey.startsWith('www.') ? hostKey : `www.${hostKey}`;
const sslEnabled =
(sslByHost.get(hostKey) ?? false) && (sslByHost.get(wwwKey) ?? false);
if (sslEnabled === domain.sslEnabled) continue;
await this.prisma.domain.update({