mirror of
https://github.com/OneUptime/oneuptime
synced 2024-11-21 22:59:07 +00:00
refactor: Improve SSL ordering for domains in StatusPageCerts job
This commit is contained in:
parent
108940678f
commit
5c3dcf7bc9
@ -50,7 +50,7 @@ RunCron(
|
||||
},
|
||||
fn: async (span: Span): Promise<void> => {
|
||||
try {
|
||||
logger.info("Ordering SSL for domains which are not ordered yet");
|
||||
logger.debug("Ordering SSL for domains which are not ordered yet");
|
||||
|
||||
await StatusPageDomainService.orderSSLForDomainsWhichAreNotOrderedYet();
|
||||
Telemetry.endSpan(span);
|
||||
|
@ -125,6 +125,10 @@ export class Service extends DatabaseService<StatusPageDomain> {
|
||||
);
|
||||
}
|
||||
|
||||
logger.debug(
|
||||
"Ordering SSL for domain: " + statusPageDomain.fullDomain,
|
||||
);
|
||||
|
||||
await GreenlockUtil.orderCert({
|
||||
domain: statusPageDomain.fullDomain as string,
|
||||
validateCname: async (fullDomain: string) => {
|
||||
@ -132,6 +136,10 @@ export class Service extends DatabaseService<StatusPageDomain> {
|
||||
},
|
||||
});
|
||||
|
||||
logger.debug(
|
||||
"SSL ordered for domain: " + statusPageDomain.fullDomain,
|
||||
);
|
||||
|
||||
// update the order.
|
||||
await this.updateOneById({
|
||||
id: statusPageDomain.id!,
|
||||
@ -238,6 +246,8 @@ export class Service extends DatabaseService<StatusPageDomain> {
|
||||
try {
|
||||
// get the token from the domain.
|
||||
|
||||
logger.debug("Checking for CNAME " + fullDomain);
|
||||
|
||||
const statusPageDomain: StatusPageDomain | null = await this.findOneBy({
|
||||
query: {
|
||||
fullDomain: fullDomain,
|
||||
@ -257,6 +267,8 @@ export class Service extends DatabaseService<StatusPageDomain> {
|
||||
|
||||
const token: string = statusPageDomain.cnameVerificationToken!;
|
||||
|
||||
logger.debug("Checking for CNAME " + fullDomain + " with token " + token);
|
||||
|
||||
const result: HTTPErrorResponse | HTTPResponse<JSONObject> =
|
||||
await API.get(
|
||||
URL.fromString(
|
||||
@ -267,6 +279,9 @@ export class Service extends DatabaseService<StatusPageDomain> {
|
||||
),
|
||||
);
|
||||
|
||||
logger.debug("CNAME verification result");
|
||||
logger.debug(result);
|
||||
|
||||
if (result.isSuccess()) {
|
||||
await this.updateCnameStatusForStatusPageDomain({
|
||||
domain: fullDomain,
|
||||
@ -275,8 +290,6 @@ export class Service extends DatabaseService<StatusPageDomain> {
|
||||
|
||||
return true;
|
||||
}
|
||||
logger.debug("CNAME verification failed for http endpoint");
|
||||
logger.debug(result);
|
||||
|
||||
// try with https
|
||||
|
||||
@ -290,6 +303,9 @@ export class Service extends DatabaseService<StatusPageDomain> {
|
||||
),
|
||||
);
|
||||
|
||||
logger.debug("CNAME verification result for https");
|
||||
logger.debug(resultHttps);
|
||||
|
||||
if (resultHttps.isSuccess()) {
|
||||
await this.updateCnameStatusForStatusPageDomain({
|
||||
domain: fullDomain,
|
||||
@ -298,8 +314,6 @@ export class Service extends DatabaseService<StatusPageDomain> {
|
||||
|
||||
return true;
|
||||
}
|
||||
logger.debug("CNAME verification fails for https endpoint");
|
||||
logger.debug(resultHttps);
|
||||
|
||||
await this.updateCnameStatusForStatusPageDomain({
|
||||
domain: fullDomain,
|
||||
@ -419,6 +433,7 @@ export class Service extends DatabaseService<StatusPageDomain> {
|
||||
|
||||
for (const domain of domains) {
|
||||
try {
|
||||
logger.debug("Ordering SSL for domain: " + domain.fullDomain);
|
||||
await this.orderCert(domain);
|
||||
} catch (e) {
|
||||
logger.error(e);
|
||||
|
@ -24,64 +24,107 @@ export default class GreenlockUtil {
|
||||
validateCname: (domain: string) => Promise<boolean>;
|
||||
notifyDomainRemoved: (domain: string) => Promise<void>;
|
||||
}): Promise<void> {
|
||||
logger.debug("Renewing all certificates");
|
||||
return await Telemetry.startActiveSpan<Promise<void>>({
|
||||
name: "GreenlockUtil.renewAllCertsWhichAreExpiringSoon",
|
||||
fn: async (span: Span): Promise<void> => {
|
||||
try {
|
||||
logger.debug("Renewing all certificates");
|
||||
|
||||
// get all certificates which are expiring soon
|
||||
// get all certificates which are expiring soon
|
||||
|
||||
const certificates: AcmeCertificate[] = await AcmeCertificateService.findBy(
|
||||
{
|
||||
query: {
|
||||
expiresAt: QueryHelper.lessThanEqualTo(
|
||||
OneUptimeDate.addRemoveDays(
|
||||
OneUptimeDate.getCurrentDate(),
|
||||
40, // 40 days before expiry
|
||||
),
|
||||
),
|
||||
},
|
||||
limit: LIMIT_MAX,
|
||||
skip: 0,
|
||||
select: {
|
||||
domain: true,
|
||||
},
|
||||
sort: {
|
||||
expiresAt: SortOrder.Ascending,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
},
|
||||
);
|
||||
const certificates: AcmeCertificate[] =
|
||||
await AcmeCertificateService.findBy({
|
||||
query: {
|
||||
expiresAt: QueryHelper.lessThanEqualTo(
|
||||
OneUptimeDate.addRemoveDays(
|
||||
OneUptimeDate.getCurrentDate(),
|
||||
40, // 40 days before expiry
|
||||
),
|
||||
),
|
||||
},
|
||||
limit: LIMIT_MAX,
|
||||
skip: 0,
|
||||
select: {
|
||||
domain: true,
|
||||
},
|
||||
sort: {
|
||||
expiresAt: SortOrder.Ascending,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
// order certificate for each domain
|
||||
logger.debug(
|
||||
`Found ${certificates.length} certificates which are expiring soon`,
|
||||
);
|
||||
|
||||
for (const certificate of certificates) {
|
||||
if (!certificate.domain) {
|
||||
continue;
|
||||
}
|
||||
// order certificate for each domain
|
||||
|
||||
try {
|
||||
//validate cname
|
||||
const isValidCname: boolean = await data.validateCname(
|
||||
certificate.domain,
|
||||
);
|
||||
for (const certificate of certificates) {
|
||||
if (!certificate.domain) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isValidCname) {
|
||||
// if cname is not valid then remove the domain
|
||||
await GreenlockUtil.removeDomain(certificate.domain);
|
||||
await data.notifyDomainRemoved(certificate.domain);
|
||||
} else {
|
||||
await GreenlockUtil.orderCert({
|
||||
domain: certificate.domain,
|
||||
validateCname: data.validateCname,
|
||||
logger.debug(
|
||||
`Renewing certificate for domain: ${certificate.domain}`,
|
||||
);
|
||||
|
||||
try {
|
||||
//validate cname
|
||||
const isValidCname: boolean = await data.validateCname(
|
||||
certificate.domain,
|
||||
);
|
||||
|
||||
if (!isValidCname) {
|
||||
logger.debug(
|
||||
`CNAME is not valid for domain: ${certificate.domain}`,
|
||||
);
|
||||
|
||||
// if cname is not valid then remove the domain
|
||||
await GreenlockUtil.removeDomain(certificate.domain);
|
||||
await data.notifyDomainRemoved(certificate.domain);
|
||||
|
||||
logger.error(
|
||||
`Cname is not valid for domain: ${certificate.domain}`,
|
||||
);
|
||||
} else {
|
||||
logger.debug(
|
||||
`CNAME is valid for domain: ${certificate.domain}`,
|
||||
);
|
||||
|
||||
await GreenlockUtil.orderCert({
|
||||
domain: certificate.domain,
|
||||
validateCname: data.validateCname,
|
||||
});
|
||||
|
||||
logger.debug(
|
||||
`Certificate renewed for domain: ${certificate.domain}`,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error(
|
||||
`Error renewing certificate for domain: ${certificate.domain}`,
|
||||
);
|
||||
logger.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
Telemetry.endSpan(span);
|
||||
} catch (e) {
|
||||
logger.error("Error renewing all certificates");
|
||||
logger.error(e);
|
||||
|
||||
// record exception
|
||||
Telemetry.recordExceptionMarkSpanAsErrorAndEndSpan({
|
||||
span,
|
||||
exception: e,
|
||||
});
|
||||
|
||||
throw e;
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error(
|
||||
`Error renewing certificate for domain: ${certificate.domain}`,
|
||||
);
|
||||
logger.error(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public static async removeDomain(domain: string): Promise<void> {
|
||||
@ -134,6 +177,10 @@ export default class GreenlockUtil {
|
||||
},
|
||||
fn: async (span: Span): Promise<void> => {
|
||||
try {
|
||||
logger.debug(
|
||||
`GreenlockUtil - Ordering certificate for domain: ${data.domain}`,
|
||||
);
|
||||
|
||||
let { domain } = data;
|
||||
|
||||
domain = domain.trim().toLowerCase();
|
||||
@ -155,9 +202,14 @@ export default class GreenlockUtil {
|
||||
|
||||
//validate cname
|
||||
|
||||
logger.debug(`Validating cname for domain: ${domain}`);
|
||||
|
||||
const isValidCname: boolean = await data.validateCname(domain);
|
||||
|
||||
if (!isValidCname) {
|
||||
logger.debug(`CNAME is not valid for domain: ${domain}`);
|
||||
logger.debug(`Removing domain: ${domain}`);
|
||||
|
||||
await GreenlockUtil.removeDomain(domain);
|
||||
logger.error(`Cname is not valid for domain: ${domain}`);
|
||||
throw new BadDataException(
|
||||
@ -165,6 +217,8 @@ export default class GreenlockUtil {
|
||||
);
|
||||
}
|
||||
|
||||
logger.debug(`Cname is valid for domain: ${domain}`);
|
||||
|
||||
const client: acme.Client = new acme.Client({
|
||||
directoryUrl: acme.directory.letsencrypt.production,
|
||||
accountKey: acmeAccountKey,
|
||||
@ -175,6 +229,8 @@ export default class GreenlockUtil {
|
||||
commonName: domain,
|
||||
});
|
||||
|
||||
logger.debug(`Ordering certificate for domain: ${domain}`);
|
||||
|
||||
const certificate: string = await client.auto({
|
||||
csr: certificateRequest,
|
||||
email: LetsEncryptNotificationEmail.toString(),
|
||||
@ -188,6 +244,10 @@ export default class GreenlockUtil {
|
||||
// Satisfy challenge here
|
||||
/* http-01 */
|
||||
if (challenge.type === "http-01") {
|
||||
logger.debug(
|
||||
`Creating challenge for domain: ${authz.identifier.value}`,
|
||||
);
|
||||
|
||||
const acmeChallenge: AcmeChallenge = new AcmeChallenge();
|
||||
acmeChallenge.challenge = keyAuthorization;
|
||||
acmeChallenge.token = challenge.token;
|
||||
@ -199,6 +259,10 @@ export default class GreenlockUtil {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
logger.debug(
|
||||
`Challenge created for domain: ${authz.identifier.value}`,
|
||||
);
|
||||
}
|
||||
},
|
||||
challengeRemoveFn: async (
|
||||
@ -207,6 +271,10 @@ export default class GreenlockUtil {
|
||||
) => {
|
||||
// Clean up challenge here
|
||||
|
||||
logger.debug(
|
||||
`Removing challenge for domain: ${authz.identifier.value}`,
|
||||
);
|
||||
|
||||
if (challenge.type === "http-01") {
|
||||
await AcmeChallengeService.deleteBy({
|
||||
query: {
|
||||
@ -219,15 +287,24 @@ export default class GreenlockUtil {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
logger.debug(
|
||||
`Challenge removed for domain: ${authz.identifier.value}`,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
logger.debug(`Certificate ordered for domain: ${domain}`);
|
||||
|
||||
// get expires at date from certificate
|
||||
const cert: acme.CertificateInfo =
|
||||
acme.crypto.readCertificateInfo(certificate);
|
||||
const issuedAt: Date = cert.notBefore;
|
||||
const expiresAt: Date = cert.notAfter;
|
||||
|
||||
logger.debug(`Certificate expires at: ${expiresAt}`);
|
||||
logger.debug(`Certificate issued at: ${issuedAt}`);
|
||||
|
||||
// check if the certificate is already in the database.
|
||||
const existingCertificate: AcmeCertificate | null =
|
||||
await AcmeCertificateService.findOneBy({
|
||||
@ -243,6 +320,8 @@ export default class GreenlockUtil {
|
||||
});
|
||||
|
||||
if (existingCertificate) {
|
||||
logger.debug(`Updating certificate for domain: ${domain}`);
|
||||
|
||||
// update the certificate
|
||||
await AcmeCertificateService.updateBy({
|
||||
query: {
|
||||
@ -260,7 +339,10 @@ export default class GreenlockUtil {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
logger.debug(`Certificate updated for domain: ${domain}`);
|
||||
} else {
|
||||
logger.debug(`Creating certificate for domain: ${domain}`);
|
||||
// create the certificate
|
||||
const acmeCertificate: AcmeCertificate = new AcmeCertificate();
|
||||
|
||||
@ -276,6 +358,8 @@ export default class GreenlockUtil {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
logger.debug(`Certificate created for domain: ${domain}`);
|
||||
}
|
||||
|
||||
Telemetry.endSpan(span);
|
||||
|
@ -50,8 +50,10 @@ const Row: FunctionComponent<ComponentProps> = (
|
||||
<div
|
||||
className={`flex w-full border-b-2 border-gray-200 border-l-2 border-l-gray-400 border-r-2 border-r-gray-400`}
|
||||
>
|
||||
<div className="flex w-1/4 border-r-2 border-gray-300">
|
||||
<div className={`pl-${paddingCount} pt-2 pb-2 pr-2 flex`}>
|
||||
<div className="flex w-1/4 border-r-2 border-gray-300 overflow-hidden">
|
||||
<div
|
||||
className={`pl-${paddingCount} pt-2 pb-2 pr-2 flex overflow-hidden`}
|
||||
>
|
||||
<div className="w-5 h-5 ml-3 mt-1">
|
||||
{hasChildRows && (
|
||||
<Icon
|
||||
|
@ -11,9 +11,13 @@ const RowLabel: FunctionComponent<ComponentProps> = (
|
||||
return (
|
||||
// rectangle div with curved corners and text inside in tailwindcss
|
||||
|
||||
<div>
|
||||
<div className="text-sm text-gray-600 truncate">{props.title}</div>
|
||||
<div className="text-xs text-gray-500 truncate">{props.description}</div>
|
||||
<div className="overflow-hidden">
|
||||
<div className="text-sm text-gray-600 truncate overflow-hidden">
|
||||
{props.title}
|
||||
</div>
|
||||
<div className="text-xs text-gray-500 truncate overflow-hidden">
|
||||
{props.description}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -319,7 +319,7 @@ const TraceExplorer: FunctionComponent<ComponentProps> = (
|
||||
|
||||
const rootRow: GanttChartRow = {
|
||||
rowInfo: {
|
||||
title: <div>{rootSpan.name!}</div>,
|
||||
title: <div className="truncate">{rootSpan.name!}</div>,
|
||||
description: telemetryService ? (
|
||||
getRowDescription({
|
||||
telemetryService,
|
||||
|
Loading…
Reference in New Issue
Block a user