fix(ipsec): убрать неподдерживаемый параметр при добавлении сертификата
Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -48,6 +48,9 @@ const anakondra: RosCertificateRow = { ...client1, name: "anakondra", "common-na
|
||||
// reference-based важнее key-usage: у client1 в key-usage есть tls-server, но он client
|
||||
assert.equal(certificateRole(client1, ctx), "client")
|
||||
assert.equal(certificateRole(anakondra, ctx), "client")
|
||||
// без reference-контекста: подпись локальным CA делает серт клиентским, несмотря на tls-server в usage
|
||||
assert.equal(certificateRole(client1), "client")
|
||||
assert.equal(certificateRole(anakondra), "client")
|
||||
// без контекста — по key-usage (default содержит оба, поэтому server)
|
||||
assert.equal(certificateRole({ name: "x", "key-usage": "tls-client" }), "client")
|
||||
assert.equal(certificateRole({ name: "y", "key-usage": "digital-signature" }), "other")
|
||||
|
||||
@@ -50,6 +50,10 @@ export function certificateRole(
|
||||
if (isCaCertificate(row)) return "ca"
|
||||
if (name && new Set(ctx.peerCertNames ?? []).has(name)) return "server"
|
||||
if (name && new Set(ctx.identityCertNames ?? []).has(name)) return "client"
|
||||
// Серт, подписанный локальным CA (`ca` заполнен), но не используемый как peer-серт — клиентский.
|
||||
// `client1`/`anakondra` имеют дефолтный key-usage (tls-server+tls-client), поэтому по usage
|
||||
// они выглядели бы «server».
|
||||
if ((row.ca ?? "").trim() !== "") return "client"
|
||||
const usage = row["key-usage"] ?? ""
|
||||
if (usage.includes("tls-server")) return "server"
|
||||
if (usage.includes("tls-client")) return "client"
|
||||
|
||||
@@ -415,6 +415,13 @@ export function encodeRosId(rosId: string): string {
|
||||
|
||||
// ── MikrotikClient ─────────────────────────────────────────────────────────────
|
||||
|
||||
/** Имя параметра из ошибки RouterOS `unknown parameter <name>` (400) — для деградации запроса. */
|
||||
function unknownParameterName(error: MikrotikError): string | undefined {
|
||||
if (error.statusCode !== 400) return undefined
|
||||
const match = error.body.match(/unknown parameter\s+"?([A-Za-z0-9_-]+)"?/i)
|
||||
return match?.[1]
|
||||
}
|
||||
|
||||
export class MikrotikClient {
|
||||
constructor(private readonly params: MikrotikConnectParams) {}
|
||||
|
||||
@@ -691,7 +698,19 @@ export class MikrotikClient {
|
||||
|
||||
/** Создание ключевой пары + заявки: /certificate add (поля common-name, key-size, key-usage…). */
|
||||
async addCertificate(body: Record<string, string>, timeoutMs = 30_000): Promise<unknown> {
|
||||
return this.post("/certificate/add", body, timeoutMs)
|
||||
// Набор параметров `/certificate/add` зависит от версии RouterOS (напр. `comment`).
|
||||
// Деградируем: при 400 «unknown parameter X» убираем X из тела и повторяем.
|
||||
const payload: Record<string, string> = { ...body }
|
||||
for (let attempt = 0; attempt < 4; attempt += 1) {
|
||||
try {
|
||||
return await this.post("/certificate/add", payload, timeoutMs)
|
||||
} catch (error) {
|
||||
const param = error instanceof MikrotikError ? unknownParameterName(error) : undefined
|
||||
if (!param || !(param in payload)) throw error
|
||||
delete payload[param]
|
||||
}
|
||||
}
|
||||
throw new Error(`RouterOS: не удалось добавить сертификат (несовместимые параметры): ${Object.keys(body).join(", ")}`)
|
||||
}
|
||||
|
||||
/** Подпись сертификата локальным CA; sign небыстрый — увеличенный таймаут. */
|
||||
|
||||
Reference in New Issue
Block a user