fix(ipsec): показывать все привязки общего сертификата
This commit is contained in:
1 parent
60b2e099f0
commit
58f5c48a97
17 files changed
+1213
-56
No files matched your search
@@ -272,10 +272,10 @@ export function mapClients(state: IpsecServerState): IpsecClientDto[] {
|
||||
const findActive = (remoteId: string) => activeByRemote.get(remoteId)
|
||||
?? activeByRemote.get(remoteId.replace(/^(key-id|fqdn|user-fqdn|address):/, ""))
|
||||
|
||||
const identityByRemoteCert = new Map<string, RosIpsecIdentity>()
|
||||
const identityByRemoteCert = new Map<string, RosIpsecIdentity[]>()
|
||||
for (const i of state.identities) {
|
||||
const rc = (i["remote-certificate"] ?? "").trim()
|
||||
if (rc) identityByRemoteCert.set(rc, i)
|
||||
if (rc) identityByRemoteCert.set(rc, [...(identityByRemoteCert.get(rc) ?? []), i])
|
||||
}
|
||||
|
||||
const personalStaticIp = (mcNameRaw: string): string | undefined => {
|
||||
@@ -303,44 +303,46 @@ export function mapClients(state: IpsecServerState): IpsecClientDto[] {
|
||||
const referenced = identityByRemoteCert.has(certName)
|
||||
if (!isUser && !referenced && !(caName && signedBy === caName)) continue
|
||||
|
||||
const identity = identityByRemoteCert.get(certName)
|
||||
const comment = identity?.comment ?? ""
|
||||
const mcName = (identity?.["mode-config"] ?? "").trim()
|
||||
const cn = String(cert["common-name"] ?? "").trim()
|
||||
const subject = routerOsCertificateDn(cert)
|
||||
const bySubject = subject && subjectCounts.get(subject) === 1 ? activeByRemote.get(subject) : undefined
|
||||
const uniqueCn = cn && cnCounts.get(cn) === 1
|
||||
// A literal CN is not a typed remote-id. It may also resemble another
|
||||
// certificate's complete DN, in which case the bare-CN alias is unsafe.
|
||||
const byCommonName = uniqueCn && !subjectCounts.has(cn) ? activeByRemote.get(cn) : undefined
|
||||
const active = bySubject ?? byCommonName
|
||||
?? (identity?.["remote-id"] ? findActive(String(identity["remote-id"]).trim()) : undefined)
|
||||
// Multiple established sessions confirm presence, but no single lease/uptime.
|
||||
const single = active?.length === 1 ? active[0] : undefined
|
||||
out.push({
|
||||
id: `${server.id}:${identity ? "identity" : "cert"}:${String(identity?.[".id"] ?? cert[".id"] ?? certName)}`,
|
||||
rosId: `${identity ? "identity" : "cert"}:${String(identity?.[".id"] ?? cert[".id"] ?? certName)}`,
|
||||
serverId: String(server.id),
|
||||
serverName,
|
||||
name: identity ? identityDisplayName(identity, state.certs) : (cn || certName),
|
||||
authMethod: "certificate",
|
||||
kind: "cert",
|
||||
certificateName: certName,
|
||||
certName,
|
||||
signedBy: signedBy || undefined,
|
||||
commonName: cn || undefined,
|
||||
remoteId: (identity?.["remote-id"] ?? "").trim() || undefined,
|
||||
staticIp: personalStaticIp(mcName),
|
||||
modeConfigName: mcName || undefined,
|
||||
peerName: (identity?.peer ?? "").trim() || undefined,
|
||||
online: Boolean(active),
|
||||
activeAddress: single?.["dynamic-address"] || single?.["remote-address"] || single?.address || undefined,
|
||||
activeSince: single?.uptime || single?.established || undefined,
|
||||
disabled: asBool(identity?.disabled),
|
||||
comment: comment || undefined,
|
||||
managed: isUser || isIpsecManagedComment(comment),
|
||||
})
|
||||
claimSession(active)
|
||||
const identities = identityByRemoteCert.get(certName) ?? [undefined]
|
||||
for (const identity of identities) {
|
||||
const comment = identity?.comment ?? ""
|
||||
const mcName = (identity?.["mode-config"] ?? "").trim()
|
||||
const cn = String(cert["common-name"] ?? "").trim()
|
||||
const subject = routerOsCertificateDn(cert)
|
||||
const bySubject = subject && subjectCounts.get(subject) === 1 ? activeByRemote.get(subject) : undefined
|
||||
const uniqueCn = cn && cnCounts.get(cn) === 1
|
||||
// A literal CN is not a typed remote-id. It may also resemble another
|
||||
// certificate's complete DN, in which case the bare-CN alias is unsafe.
|
||||
const byCommonName = uniqueCn && !subjectCounts.has(cn) ? activeByRemote.get(cn) : undefined
|
||||
const active = bySubject ?? byCommonName
|
||||
?? (identity?.["remote-id"] ? findActive(String(identity["remote-id"]).trim()) : undefined)
|
||||
// Multiple established sessions confirm presence, but no single lease/uptime.
|
||||
const single = active?.length === 1 ? active[0] : undefined
|
||||
out.push({
|
||||
id: `${server.id}:${identity ? "identity" : "cert"}:${String(identity?.[".id"] ?? cert[".id"] ?? certName)}`,
|
||||
rosId: `${identity ? "identity" : "cert"}:${String(identity?.[".id"] ?? cert[".id"] ?? certName)}`,
|
||||
serverId: String(server.id),
|
||||
serverName,
|
||||
name: identity ? identityDisplayName(identity, state.certs) : (cn || certName),
|
||||
authMethod: "certificate",
|
||||
kind: "cert",
|
||||
certificateName: certName,
|
||||
certName,
|
||||
signedBy: signedBy || undefined,
|
||||
commonName: cn || undefined,
|
||||
remoteId: (identity?.["remote-id"] ?? "").trim() || undefined,
|
||||
staticIp: personalStaticIp(mcName),
|
||||
modeConfigName: mcName || undefined,
|
||||
peerName: (identity?.peer ?? "").trim() || undefined,
|
||||
online: Boolean(active),
|
||||
activeAddress: single?.["dynamic-address"] || single?.["remote-address"] || single?.address || undefined,
|
||||
activeSince: single?.uptime || single?.established || undefined,
|
||||
disabled: asBool(identity?.disabled),
|
||||
comment: comment || undefined,
|
||||
managed: isUser || isIpsecManagedComment(comment),
|
||||
})
|
||||
claimSession(active)
|
||||
}
|
||||
}
|
||||
|
||||
// 2. PSK-identity (managed или IKEv2 remote-access).
|
||||
@@ -397,7 +399,7 @@ export function resolveIpsecClientTarget(state: IpsecServerState, token: string)
|
||||
const clients = mapClients(state)
|
||||
const identity = kind === "cert" ? undefined : state.identities.find(i => i[".id"] === raw && clients.some(c => c.rosId === "identity:" + raw))
|
||||
const certRow = kind === "identity" ? undefined : state.certs.find(c => (c[".id"] === raw || c.name === raw) && clients.some(user => user.kind === "cert" && user.certName === c.name))
|
||||
return { identity, certRow, ambiguous: Boolean(!kind && identity && certRow) }
|
||||
return { identity, certRow, ambiguous: Boolean((!kind && identity && certRow) || (certRow && state.identities.filter(i => (i["remote-certificate"] ?? "").trim() === certRow.name).length > 1)) }
|
||||
}
|
||||
|
||||
export function mapServerSummary(state: IpsecServerState, clients: IpsecClientDto[]): IpsecServerSummaryDto {
|
||||
|
||||
@@ -197,4 +197,20 @@ aliasesState.active[1]!.state = "dying"
|
||||
assert.equal(mapClients(aliasesState)[0]?.activeAddress, aliasesState.active[0]!["dynamic-address"]); checks++
|
||||
aliasesState.active[0]!.state = "starting"
|
||||
assert.equal(mapClients(aliasesState)[0]?.online, false); checks++
|
||||
const sharedCertState = { ...namespaceState, certs: [namespaceState.certs[0]!], identities: [0,1].map(n => ({ ".id": "*SHARED" + n, peer: "ike", "auth-method": "digital-signature", "remote-certificate": "ipsec-user-plain", "mode-config": "mc" + n, comment: ipsecManagedComment("user=Client" + n) })), modeConfigs: [0,1].map(n => ({ ".id": "*MC" + n, name: "mc" + n, address: "198.18.114." + (250+n) })), active: [{ ".id": "*SHAREDSESSION", id: "CN=alice", state: "established", "dynamic-address": "198.18.114.250" }] } as typeof state
|
||||
for (const reverse of [false,true]) {
|
||||
if (reverse) sharedCertState.identities.reverse()
|
||||
const rows = mapClients(sharedCertState)
|
||||
assert.equal(rows.length,2)
|
||||
assert(rows.every(c => c.sessionAmbiguous && !c.online && !c.activeAddress))
|
||||
assert.equal(rows.find(c => c.rosId === "identity:*SHARED0")?.staticIp,"198.18.114.250")
|
||||
assert.equal(rows.find(c => c.rosId === "identity:*SHARED1")?.staticIp,"198.18.114.251"); checks++
|
||||
}
|
||||
assert(resolveIpsecClientTarget(sharedCertState,"cert:*PLAIN").ambiguous); checks++
|
||||
for(const id of ["*SHARED0","*SHARED1"]) {assert.equal(resolveIpsecClientTarget(sharedCertState,"identity:"+id).identity?.[".id"],id);checks++}
|
||||
sharedCertState.identities.forEach((i,n) => { i["remote-id"] = "fqdn:client"+n+".example" })
|
||||
sharedCertState.active[0]!.id="client0.example"
|
||||
const distinctRows=mapClients(sharedCertState)
|
||||
assert.equal(distinctRows.filter(c => c.online).length,1)
|
||||
assert(distinctRows.every(c => !c.sessionAmbiguous)); checks++
|
||||
console.log(`ipsec-state: ${checks} checks passed`)
|
||||
Reference in new issue
Block a user