feat(ipsec): enhance IPsec management with peer and certificate handling
Docker images / prepare-release (push) Successful in 8s
Docker images / backend-test (push) Successful in 2m39s
Docker images / frontend-image (push) Successful in 2m56s
Docker images / updater-image (push) Successful in 44s
Docker images / backend-image (push) Successful in 2m35s
Docker images / notify-webhook (push) Skipped
Docker images / publish-release (push) Successful in 15s
Docker images / prepare-release (push) Successful in 8s
Docker images / backend-test (push) Successful in 2m39s
Docker images / frontend-image (push) Successful in 2m56s
Docker images / updater-image (push) Successful in 44s
Docker images / backend-image (push) Successful in 2m35s
Docker images / notify-webhook (push) Skipped
Docker images / publish-release (push) Successful in 15s
- Added support for managing IPsec peers, including editing and deleting functionality. - Introduced a new UI component for displaying peers and their details within the IPsec server grid. - Updated the IPsec user creation form to allow binding identities to specific peers. - Enhanced backend routes and services to handle peer patching and deletion requests. - Improved data structures to accommodate multiple peers and certificates for each server. - Added tests to ensure proper functionality of new peer management features.
This commit is contained in:
1 parent
7755d77340
commit
7365d8d8fb
14 files changed
+953
-106
No files matched your search
@@ -16,8 +16,8 @@ import {
|
||||
IPSEC_COMMON_NAME,
|
||||
IPSEC_SERVER_CERT,
|
||||
clientCertName,
|
||||
identityDisplayName,
|
||||
isIpsecManagedComment,
|
||||
parseIpsecUserComment,
|
||||
} from "./ipsec-config.js"
|
||||
import {
|
||||
canonicalIpsecSnapshot,
|
||||
@@ -111,6 +111,8 @@ export function mapCertificate(c: RosCertRow): IpsecCertInfoDto {
|
||||
const name = String(c.name ?? "")
|
||||
const keyUsage = String(c["key-usage"] ?? "")
|
||||
const isCa = keyUsage.includes("key-cert-sign")
|
||||
const isServer = keyUsage.includes("tls-server")
|
||||
const isClient = keyUsage.includes("tls-client")
|
||||
const isUser = name.startsWith("ipsec-user-")
|
||||
return {
|
||||
name,
|
||||
@@ -120,7 +122,7 @@ export function mapCertificate(c: RosCertRow): IpsecCertInfoDto {
|
||||
expiresAt: c["invalid-after"] || undefined,
|
||||
trusted: asBool(c.trusted),
|
||||
hasPrivateKey: asBool(c["private-key"]),
|
||||
role: isCa ? "ca" : name === IPSEC_SERVER_CERT ? "server" : isUser ? "client" : "other",
|
||||
role: isCa ? "ca" : name === IPSEC_SERVER_CERT || isServer ? "server" : isUser || isClient ? "client" : "other",
|
||||
managed: isCa && name === IPSEC_CA_CERT
|
||||
|| name === IPSEC_SERVER_CERT
|
||||
|| isUser
|
||||
@@ -202,14 +204,9 @@ export async function fetchIpsecState(server: ServerRow): Promise<IpsecServerSta
|
||||
return { server, client, peers, identities, modeConfigs, pools, policies, nat, active, certs }
|
||||
}
|
||||
|
||||
/** Клиенты = managed-идентичности (созданы менеджером). */
|
||||
/** Клиенты = все identity роутера (managed + существующие RouterOS). */
|
||||
export function mapClients(state: IpsecServerState): IpsecClientDto[] {
|
||||
const server = state.server
|
||||
const certByCommonName = new Map<string, RosCertRow>()
|
||||
for (const c of state.certs) {
|
||||
const cn = String(c["common-name"] ?? "").trim()
|
||||
if (cn) certByCommonName.set(cn, c)
|
||||
}
|
||||
const mcByName = new Map(state.modeConfigs.map((m) => [(m.name ?? "").trim(), m]))
|
||||
const activeByRemote = new Map<string, RosIpsecActivePeer>()
|
||||
for (const a of state.active) {
|
||||
@@ -218,10 +215,10 @@ export function mapClients(state: IpsecServerState): IpsecClientDto[] {
|
||||
}
|
||||
|
||||
return state.identities
|
||||
.filter((i) => isIpsecManagedComment(i.comment))
|
||||
.map((i): IpsecClientDto => {
|
||||
const comment = i.comment ?? ""
|
||||
const name = parseIpsecUserComment(comment) ?? (comment || "client")
|
||||
const managed = isIpsecManagedComment(comment)
|
||||
const name = identityDisplayName(i, state.certs)
|
||||
const psk = (i["auth-method"] ?? "") === "pre-shared-key"
|
||||
const certName = (i["remote-certificate"] ?? "").trim()
|
||||
const cn = certName
|
||||
@@ -229,8 +226,8 @@ export function mapClients(state: IpsecServerState): IpsecClientDto[] {
|
||||
: ""
|
||||
const mcName = (i["mode-config"] ?? "").trim()
|
||||
const mc = mcByName.get(mcName)
|
||||
const staticIp = mc && (mc.name ?? "").startsWith("mc-ipsec-")
|
||||
? (mc.address ?? mc["address-prefix"] ?? "").replace(/\/\d+$/, "") || undefined
|
||||
const staticIp = mc
|
||||
? ((mc.address ?? mc["address-prefix"] ?? "").replace(/\/\d+$/, "").trim() || undefined)
|
||||
: undefined
|
||||
const active = (cn ? activeByRemote.get(cn) : undefined)
|
||||
?? (i["remote-id"] ? activeByRemote.get(i["remote-id"]) : undefined)
|
||||
@@ -252,7 +249,7 @@ export function mapClients(state: IpsecServerState): IpsecClientDto[] {
|
||||
activeSince: active?.established || undefined,
|
||||
disabled: asBool(i.disabled),
|
||||
comment: comment || undefined,
|
||||
managed: true,
|
||||
managed,
|
||||
}
|
||||
})
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
@@ -262,6 +259,8 @@ export function mapServerSummary(state: IpsecServerState, clients: IpsecClientDt
|
||||
const server = state.server
|
||||
const managedPeer = state.peers.find((p) => isIpsecManagedComment(p.comment))
|
||||
?? state.peers.find((p) => (p.name ?? "").trim() === IPSEC_COMMON_NAME)
|
||||
/** Для отображения: managed peer, иначе первый существующий peer роутера. */
|
||||
const primaryPeer = managedPeer ?? state.peers[0]
|
||||
const sharedMc = state.modeConfigs.find(
|
||||
(m) => (m.name ?? "").trim() === IPSEC_COMMON_NAME || (isIpsecManagedComment(m.comment) && !(m.name ?? "").startsWith("mc-ipsec-")),
|
||||
)
|
||||
@@ -270,7 +269,9 @@ export function mapServerSummary(state: IpsecServerState, clients: IpsecClientDt
|
||||
?? state.pools.find((p) => isIpsecManagedComment(p.comment))
|
||||
: undefined
|
||||
const caCert = state.certs.find((c) => String(c.name ?? "") === IPSEC_CA_CERT)
|
||||
?? state.certs.find((c) => String(c["key-usage"] ?? "").includes("key-cert-sign"))
|
||||
const serverCert = state.certs.find((c) => String(c.name ?? "") === IPSEC_SERVER_CERT)
|
||||
?? state.certs.find((c) => String(c["key-usage"] ?? "").includes("tls-server"))
|
||||
const natRuleManaged = state.nat.some((r) => isIpsecManagedComment(r.comment) && r.chain === "srcnat")
|
||||
return {
|
||||
serverId: String(server.id),
|
||||
@@ -278,7 +279,8 @@ export function mapServerSummary(state: IpsecServerState, clients: IpsecClientDt
|
||||
serverCountry: server.country ?? undefined,
|
||||
initialized: Boolean(managedPeer && caCert && serverCert),
|
||||
serverEndpoint: serverCert ? String(serverCert["common-name"] ?? "") || undefined : undefined,
|
||||
peer: managedPeer ? mapPeer(server, managedPeer) : undefined,
|
||||
peer: primaryPeer ? mapPeer(server, primaryPeer) : undefined,
|
||||
peers: state.peers.map((p) => mapPeer(server, p)),
|
||||
pool: pool ? mapPool(server, pool) : undefined,
|
||||
sharedModeConfig: sharedMc ? mapModeConfig(server, sharedMc) : undefined,
|
||||
caCert: caCert ? mapCertificate(caCert) : undefined,
|
||||
@@ -286,15 +288,14 @@ export function mapServerSummary(state: IpsecServerState, clients: IpsecClientDt
|
||||
natRuleManaged,
|
||||
clientsTotal: clients.length,
|
||||
clientsOnline: clients.filter((c) => c.online).length,
|
||||
certs: state.certs
|
||||
.filter((c) => {
|
||||
const n = String(c.name ?? "")
|
||||
return n === IPSEC_CA_CERT || n === IPSEC_SERVER_CERT || n.startsWith("ipsec-user-")
|
||||
})
|
||||
.map(mapCertificate),
|
||||
certs: state.certs.map(mapCertificate),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Snapshot для истории/отката намеренно остаётся managed-only (`MikrotikManager:ipsec`):
|
||||
* restore не должен трогать/пересоздавать уже существующие на роутере (не наших) объекты.
|
||||
*/
|
||||
export async function captureIpsecSnapshot(server: ServerRow): Promise<IpsecSnapshot> {
|
||||
const state = await fetchIpsecState(server)
|
||||
return canonicalIpsecSnapshot({
|
||||
|
||||
Reference in new issue
Block a user