Files
MikrotikManager/packages/contracts/src/ipsec.ts
T
Denozordec 7755d77340
Docker images / prepare-release (push) Successful in 15s
Docker images / backend-test (push) Successful in 2m32s
Docker images / frontend-image (push) Successful in 4m19s
Docker images / updater-image (push) Successful in 50s
Docker images / backend-image (push) Successful in 2m40s
Docker images / notify-webhook (push) Skipped
Docker images / publish-release (push) Successful in 8s
feat(ipsec): управление IKEv2/IPsec VPN и клиентами из одного окна
- мастер инициализации сервера: CA и серверный сертификаты, peer/profile/proposal, пул, mode-config, policy-template, managed NAT masquerade
- клиенты по сертификату (RSA) и PSK: статический IP или из пула, онлайн-статус по active-peers
- скачивание .p12 и strongSwan .sswan с инструкцией, перекачка с новой passphrase
- история изменений (config_revisions, секция ipsec) и restore только managed-объектов
- привязка IPsec-клиентов к пользователям приложения по Common Name
- страница /ipsec с KPI и вкладками Клиенты/Сервер/CLI, сайдбар, command palette
2026-09-12 20:20:56 +07:00

192 lines
7.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { z } from "zod"
export const ipsecAuthMethodSchema = z.enum(["certificate", "pre-shared-key"])
/** Клиент IKEv2 — /ip/ipsec/identity (+ опциональный персональный mode-config). */
export const ipsecClientDtoSchema = z.object({
id: z.string().min(1),
rosId: z.string().min(1),
serverId: z.string().min(1),
serverName: z.string(),
/** Управляемое имя клиента (из managed-комментария identity). */
name: z.string(),
authMethod: ipsecAuthMethodSchema,
certificateName: z.string().optional(),
commonName: z.string().optional(),
remoteId: z.string().optional(),
/** Статический IP клиента (персональный mode-config), undefined — из общего пула. */
staticIp: z.string().optional(),
modeConfigName: z.string().optional(),
peerName: z.string().optional(),
online: z.boolean(),
activeAddress: z.string().optional(),
activeSince: z.string().optional(),
disabled: z.boolean(),
comment: z.string().optional(),
/** Создан менеджером (managed-комментарий). */
managed: z.boolean(),
})
/** Слушатель IKEv2 — /ip/ipsec/peer (passive). */
export const ipsecPeerDtoSchema = z.object({
id: z.string().min(1),
rosId: z.string().min(1),
serverId: z.string().min(1),
serverName: z.string(),
name: z.string(),
address: z.string().optional(),
exchangeMode: z.string().optional(),
passive: z.boolean().optional(),
certificate: z.string().optional(),
profile: z.string().optional(),
disabled: z.boolean(),
comment: z.string().optional(),
managed: z.boolean(),
})
export const ipsecModeConfigDtoSchema = z.object({
id: z.string().min(1),
rosId: z.string().min(1),
serverId: z.string().min(1),
name: z.string(),
/** Общий пул (shared) или персональный статический адрес (per-user). */
addressPool: z.string().optional(),
address: z.string().optional(),
splitDns: z.string().optional(),
staticDns: z.string().optional(),
comment: z.string().optional(),
managed: z.boolean(),
})
export const ipsecPoolDtoSchema = z.object({
id: z.string().min(1),
rosId: z.string().min(1),
serverId: z.string().min(1),
name: z.string(),
ranges: z.string(),
comment: z.string().optional(),
managed: z.boolean(),
})
export const ipsecCertInfoDtoSchema = z.object({
name: z.string(),
commonName: z.string().optional(),
keySize: z.string().optional(),
fingerprint: z.string().optional(),
expiresAt: z.string().optional(),
trusted: z.boolean().optional(),
hasPrivateKey: z.boolean().optional(),
role: z.enum(["ca", "server", "client", "other"]).optional(),
managed: z.boolean(),
})
/** Сводка IKEv2-сервера на одном роутере. */
export const ipsecServerSummaryDtoSchema = z.object({
serverId: z.string().min(1),
serverName: z.string(),
serverCountry: z.string().optional(),
/** Managed-набор (CA + серверный серт + peer + mode-config) инициализирован. */
initialized: z.boolean(),
serverEndpoint: z.string().optional(),
peer: ipsecPeerDtoSchema.optional(),
pool: ipsecPoolDtoSchema.optional(),
sharedModeConfig: ipsecModeConfigDtoSchema.optional(),
caCert: ipsecCertInfoDtoSchema.optional(),
serverCert: ipsecCertInfoDtoSchema.optional(),
natRuleManaged: z.boolean(),
clientsTotal: z.number().int().nonnegative(),
clientsOnline: z.number().int().nonnegative(),
certs: z.array(ipsecCertInfoDtoSchema).optional(),
})
export const ipsecListResponseSchema = z.object({
servers: z.array(ipsecServerSummaryDtoSchema),
clients: z.array(ipsecClientDtoSchema),
failures: z
.array(
z.object({
serverId: z.string(),
serverName: z.string().optional(),
error: z.string(),
}),
)
.optional(),
})
export const ipsecInitRequestSchema = z.object({
serverId: z.union([z.string(), z.number()]),
/** Домен или IP для CN/SAN серверного сертификата (к нему подключаются клиенты). */
serverEndpoint: z.string().min(1),
/** Подсеть пула клиентов; из неё pool ranges .2.254. */
poolCidr: z.string().regex(/^[0-9a-fA-F.:/]+$/).default("10.77.0.0/24"),
dns: z.string().optional(),
caDaysValid: z.number().int().positive().default(3650),
serverDaysValid: z.number().int().positive().default(3650),
clientDaysValid: z.number().int().positive().default(1825),
/** Managed srcnat masquerade, чтобы у клиентов был интернет. */
createNatRule: z.boolean().default(true),
})
export const ipsecUserCreateRequestSchema = z.object({
serverId: z.union([z.string(), z.number()]),
/** Отображаемое имя клиента; из него slug для серта/CN. */
name: z.string().min(1).max(64),
authMethod: ipsecAuthMethodSchema.default("certificate"),
psk: z.string().min(8).optional(),
remoteId: z.string().optional(),
/** Конкретный IP клиента; без — выдаётся из пула. */
staticIp: z.string().optional(),
/** Пароль на экспортируемый .p12. */
passphrase: z.string().min(4).optional(),
daysValid: z.number().int().positive().optional(),
})
export const ipsecUserPatchSchema = z.object({
name: z.string().min(1).max(64).optional(),
/** null — вернуть выдачу из пула. */
staticIp: z.string().nullable().optional(),
psk: z.string().min(8).optional(),
remoteId: z.string().optional(),
disabled: z.boolean().optional(),
})
export const ipsecCertExportRequestSchema = z.object({
serverId: z.union([z.string(), z.number()]),
clientId: z.string().min(1),
passphrase: z.string().min(4),
})
/** Бандл для авторизации клиента: .p12 (+ strongSwan .sswan + инструкция). */
export const ipsecCertBundleSchema = z.object({
user: z.string(),
serverEndpoint: z.string().optional(),
filename: z.string(),
contentB64: z.string(),
mime: z.string().default("application/x-pkcs12"),
passphrase: z.string().optional(),
sswanFilename: z.string().optional(),
sswanContent: z.string().optional(),
instructions: z.string().optional(),
})
export const ipsecUserCreatedSchema = z.object({
client: ipsecClientDtoSchema,
/** Одноразовый бандл сертификата (для cert-клиентов). */
bundle: ipsecCertBundleSchema.optional(),
})
export type IpsecAuthMethod = z.infer<typeof ipsecAuthMethodSchema>
export type IpsecClientDto = z.infer<typeof ipsecClientDtoSchema>
export type IpsecPeerDto = z.infer<typeof ipsecPeerDtoSchema>
export type IpsecModeConfigDto = z.infer<typeof ipsecModeConfigDtoSchema>
export type IpsecPoolDto = z.infer<typeof ipsecPoolDtoSchema>
export type IpsecCertInfoDto = z.infer<typeof ipsecCertInfoDtoSchema>
export type IpsecServerSummaryDto = z.infer<typeof ipsecServerSummaryDtoSchema>
export type IpsecListResponse = z.infer<typeof ipsecListResponseSchema>
export type IpsecInitRequest = z.infer<typeof ipsecInitRequestSchema>
export type IpsecUserCreateRequest = z.infer<typeof ipsecUserCreateRequestSchema>
export type IpsecUserPatch = z.infer<typeof ipsecUserPatchSchema>
export type IpsecCertExportRequest = z.infer<typeof ipsecCertExportRequestSchema>
export type IpsecCertBundle = z.infer<typeof ipsecCertBundleSchema>
export type IpsecUserCreated = z.infer<typeof ipsecUserCreatedSchema>