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: {
|
||||
server: Server
|
||||
certName: string
|
||||
@@ -300,9 +306,12 @@ export async function issueCertificateWithCloudflareDns(params: {
|
||||
|
||||
params.onStep?.("import")
|
||||
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 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"))
|
||||
await clientRos.importCertificate({
|
||||
fileName: certRouterFile,
|
||||
@@ -316,7 +325,21 @@ export async function issueCertificateWithCloudflareDns(params: {
|
||||
trusted: true,
|
||||
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 {
|
||||
params.onStep?.("cleanup")
|
||||
for (const item of txtCleanups) {
|
||||
|
||||
@@ -571,21 +571,12 @@ export class MikrotikClient {
|
||||
return this.post("/certificate/import", body, 60_000)
|
||||
}
|
||||
|
||||
async applyCertificateToServices(
|
||||
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")
|
||||
|
||||
async applyCertificateToServices(certName: string): Promise<void> {
|
||||
const body = {
|
||||
disabled: "no",
|
||||
certificate: certName,
|
||||
}
|
||||
for (const serviceName of targets) {
|
||||
for (const serviceName of ["www-ssl", "api-ssl"]) {
|
||||
await this.patchIpService(serviceName, body)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user