fix(backend): улучшить обработку сертификатов и добавить поддержку цепочек сертификатов
This commit is contained in:
@@ -228,6 +228,12 @@ async function ensureAcmeAccount(client: acme.Client): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function splitPemCertificates(pem: string): string[] {
|
||||||
|
return [...pem.matchAll(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g)]
|
||||||
|
.map((match) => match[0].trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
export async function issueCertificateWithCloudflareDns(params: {
|
export async function issueCertificateWithCloudflareDns(params: {
|
||||||
server: Server
|
server: Server
|
||||||
certName: string
|
certName: string
|
||||||
@@ -300,9 +306,12 @@ export async function issueCertificateWithCloudflareDns(params: {
|
|||||||
|
|
||||||
params.onStep?.("import")
|
params.onStep?.("import")
|
||||||
const safeBase = params.certName.replace(/[^a-zA-Z0-9._-]+/g, "_")
|
const safeBase = params.certName.replace(/[^a-zA-Z0-9._-]+/g, "_")
|
||||||
|
const chain = splitPemCertificates(certPem)
|
||||||
|
const leafPem = chain[0] ?? certPem
|
||||||
|
const chainRest = chain.slice(1)
|
||||||
const certFile = `${safeBase}.crt`
|
const certFile = `${safeBase}.crt`
|
||||||
const keyFile = `${safeBase}.key`
|
const keyFile = `${safeBase}.key`
|
||||||
const certRouterFile = await clientRos.uploadTextFile(certFile, certPem)
|
const certRouterFile = await clientRos.uploadTextFile(certFile, leafPem)
|
||||||
const keyRouterFile = await clientRos.uploadTextFile(keyFile, privateKey.toString("utf8"))
|
const keyRouterFile = await clientRos.uploadTextFile(keyFile, privateKey.toString("utf8"))
|
||||||
await clientRos.importCertificate({
|
await clientRos.importCertificate({
|
||||||
fileName: certRouterFile,
|
fileName: certRouterFile,
|
||||||
@@ -316,7 +325,21 @@ export async function issueCertificateWithCloudflareDns(params: {
|
|||||||
trusted: true,
|
trusted: true,
|
||||||
trustStore: trustStoreCsv,
|
trustStore: trustStoreCsv,
|
||||||
})
|
})
|
||||||
await clientRos.applyCertificateToServices(params.certName, effectiveTrustStores)
|
|
||||||
|
for (let index = 0; index < chainRest.length; index += 1) {
|
||||||
|
const isRoot = index === chainRest.length - 1
|
||||||
|
const caName = isRoot ? `root_${params.certName}` : `root_${params.certName}_${index + 1}`
|
||||||
|
const caFile = `${safeBase}_ca_${index + 1}.crt`
|
||||||
|
const caRouterFile = await clientRos.uploadTextFile(caFile, chainRest[index]!)
|
||||||
|
await clientRos.importCertificate({
|
||||||
|
fileName: caRouterFile,
|
||||||
|
name: caName,
|
||||||
|
trusted: true,
|
||||||
|
trustStore: trustStoreCsv,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
await clientRos.applyCertificateToServices(params.certName)
|
||||||
} finally {
|
} finally {
|
||||||
params.onStep?.("cleanup")
|
params.onStep?.("cleanup")
|
||||||
for (const item of txtCleanups) {
|
for (const item of txtCleanups) {
|
||||||
|
|||||||
@@ -571,21 +571,12 @@ export class MikrotikClient {
|
|||||||
return this.post("/certificate/import", body, 60_000)
|
return this.post("/certificate/import", body, 60_000)
|
||||||
}
|
}
|
||||||
|
|
||||||
async applyCertificateToServices(
|
async applyCertificateToServices(certName: string): Promise<void> {
|
||||||
certName: string,
|
|
||||||
trustStores: string[] = ["www", "api"],
|
|
||||||
): Promise<void> {
|
|
||||||
const stores = trustStores.length > 0 ? trustStores : ["www", "api"]
|
|
||||||
const targets: string[] = []
|
|
||||||
if (stores.includes("www")) targets.push("www-ssl")
|
|
||||||
if (stores.includes("api")) targets.push("api-ssl")
|
|
||||||
if (targets.length === 0) targets.push("www-ssl")
|
|
||||||
|
|
||||||
const body = {
|
const body = {
|
||||||
disabled: "no",
|
disabled: "no",
|
||||||
certificate: certName,
|
certificate: certName,
|
||||||
}
|
}
|
||||||
for (const serviceName of targets) {
|
for (const serviceName of ["www-ssl", "api-ssl"]) {
|
||||||
await this.patchIpService(serviceName, body)
|
await this.patchIpService(serviceName, body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user