feat(NetworkConfigManager): extend IPSec password handling for GRE tunnels; update modal behavior and enhance MikroTik code generation for GRE with optional IPSec support
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m38s

This commit is contained in:
2026-01-22 19:22:34 +07:00
parent a77ba76ff8
commit 76a8b867f8
+82 -18
View File
@@ -551,7 +551,7 @@ function NetworkConfigManager() {
remoteIp: remoteIp,
serverId: templateServer1,
serverId2: templateServer2,
ipsecPasswordId: templateType === 'IPSec' && templateIpsecPasswordId ? templateIpsecPasswordId : ''
ipsecPasswordId: (templateType === 'IPSec' || templateType === 'GRE') && templateIpsecPasswordId ? templateIpsecPasswordId : ''
};
const conflicts = checkInterfaceIpConflict(newInterface);
@@ -917,12 +917,12 @@ function NetworkConfigManager() {
}
// Если создан новый пароль и открыт шаблон интерфейса, автоматически выбираем его
if (savedPasswordId && interfaceTemplateModalOpen && templateType === 'IPSec') {
if (savedPasswordId && interfaceTemplateModalOpen && (templateType === 'IPSec' || templateType === 'GRE')) {
setTemplateIpsecPasswordId(savedPasswordId);
}
// Если создан новый пароль и открыт модал интерфейса с типом IPSec, автоматически выбираем его
if (savedPasswordId && interfaceModalOpen && editingInterface?.type === 'IPSec') {
// Если создан новый пароль и открыт модал интерфейса с типом IPSec или GRE, автоматически выбираем его
if (savedPasswordId && interfaceModalOpen && (editingInterface?.type === 'IPSec' || editingInterface?.type === 'GRE')) {
setEditingInterface({ ...editingInterface, ipsecPasswordId: savedPasswordId });
}
} catch (error) {
@@ -1019,7 +1019,7 @@ function NetworkConfigManager() {
};
// === Генерация кода MikroTik для настройки IP адресов интерфейсов ===
const generateMikrotikInterfaceAddresses = () => {
const generateMikrotikInterfaceAddresses = async () => {
const interfacesWithBothServers = (config.tunnelInterfaces || []).filter(i =>
i.serverId && i.serverId2 && i.localIp && i.remoteIp
);
@@ -1028,6 +1028,23 @@ function NetworkConfigManager() {
return [];
}
// Загружаем пароли для интерфейсов с IPSec
const passwordIds = [...new Set(interfacesWithBothServers
.filter(i => i.ipsecPasswordId)
.map(i => i.ipsecPasswordId)
)];
const passwordMap = {};
for (const passwordId of passwordIds) {
try {
const response = await api.get(`/ipsec-passwords/${passwordId}`);
passwordMap[passwordId] = response.data?.password || '';
} catch (error) {
console.error(`Error loading IPSec password ${passwordId}:`, error);
passwordMap[passwordId] = '';
}
}
// Группируем по серверам
const interfacesByServer = {};
interfacesWithBothServers.forEach(iface => {
@@ -1057,7 +1074,9 @@ function NetworkConfigManager() {
remoteIp: iface.remoteIp,
type: iface.type,
server2Name,
server2Info: server2
server2Info: server2,
ipsecPasswordId: iface.ipsecPasswordId || null,
ipsecPassword: iface.ipsecPasswordId ? passwordMap[iface.ipsecPasswordId] : null
});
// Для второго сервера - МЕНЯЕМ МЕСТАМИ: remoteIp становится localIp, localIp становится remoteIp
@@ -1074,7 +1093,9 @@ function NetworkConfigManager() {
remoteIp: iface.localIp, // На втором сервере localIp первого становится remoteIp
type: iface.type,
server2Name: server1Name,
server2Info: server1
server2Info: server1,
ipsecPasswordId: iface.ipsecPasswordId || null,
ipsecPassword: iface.ipsecPasswordId ? passwordMap[iface.ipsecPasswordId] : null
});
});
@@ -1102,6 +1123,41 @@ function NetworkConfigManager() {
code += `# Связь с сервером: ${iface.server2Name}\n`;
code += `# Local IP: ${iface.localIp}\n`;
code += `# Remote IP: ${iface.remoteIp}\n`;
// Для GRE туннелей - создаем туннель, опционально с IPSec
if (iface.type === 'GRE') {
const remoteServerIp = iface.server2Info?.ip || iface.server2Name;
const localServerIp = server.ip || serverName;
if (iface.ipsecPasswordId && iface.ipsecPassword) {
// GRE туннель с IPSec
code += `# Создание GRE туннеля с IPSec\n`;
code += `/interface gre add name="${iface.interfaceName}" remote-address=${remoteServerIp} local-address=${localServerIp} keepalive=10s\n`;
code += '\n';
// Создаем IPSec peer
code += `# Настройка IPSec peer для ${iface.interfaceName}\n`;
code += `/ip ipsec peer add name="${iface.interfaceName}-peer" address=${remoteServerIp}/32 local-address=${localServerIp} exchange-mode=ike2 passive=no\n`;
code += '\n';
// Создаем IPSec identity
code += `# Настройка IPSec identity для ${iface.interfaceName}\n`;
code += `/ip ipsec identity add peer="${iface.interfaceName}-peer" auth-method=pre-shared-key secret="${iface.ipsecPassword}"\n`;
code += '\n';
// Создаем IPSec policy
code += `# Настройка IPSec policy для ${iface.interfaceName}\n`;
code += `/ip ipsec policy add src-address=${iface.localIp}/30 dst-address=${iface.remoteIp}/30 sa-src-address=${localServerIp} sa-dst-address=${remoteServerIp} peer="${iface.interfaceName}-peer" tunnel=yes\n`;
code += '\n';
} else {
// GRE туннель без IPSec (голый GRE)
code += `# Создание GRE туннеля (без IPSec)\n`;
code += `/interface gre add name="${iface.interfaceName}" remote-address=${remoteServerIp} local-address=${localServerIp} keepalive=10s\n`;
code += '\n';
}
}
// Добавляем IP адрес на интерфейс
code += `/ip address add address=${iface.localIp}/30 interface="${iface.interfaceName}" comment="Interface: ${iface.interfaceName} (${iface.type}) to ${iface.server2Name}"\n`;
code += '\n';
});
@@ -1121,7 +1177,7 @@ function NetworkConfigManager() {
};
// === Генерация кода MikroTik для рекурсивных маршрутов ===
const generateMikrotikRecursiveRoutes = () => {
const generateMikrotikRecursiveRoutes = async () => {
const recursiveGateways = (config.gateways || []).filter(gw => gw.type === 'recursive');
const blocks = [];
@@ -1241,7 +1297,7 @@ function NetworkConfigManager() {
}
// Генерируем блоки для IP адресов интерфейсов
const interfaceBlocks = generateMikrotikInterfaceAddresses();
const interfaceBlocks = await generateMikrotikInterfaceAddresses();
if (interfaceBlocks && interfaceBlocks.length > 0) {
blocks.push(...interfaceBlocks);
}
@@ -1250,10 +1306,18 @@ function NetworkConfigManager() {
};
// === Обработчик кнопки "Код для MikroTik" ===
const handleGenerateMikrotikCode = () => {
const code = generateMikrotikRecursiveRoutes();
setGeneratedMikrotikCode(code);
setMikrotikCodeModalOpen(true);
const handleGenerateMikrotikCode = async () => {
setLoading(true);
try {
const code = await generateMikrotikRecursiveRoutes();
setGeneratedMikrotikCode(code);
setMikrotikCodeModalOpen(true);
} catch (error) {
console.error('Error generating MikroTik code:', error);
notify.error('Ошибка при генерации кода MikroTik');
} finally {
setLoading(false);
}
};
// === Копирование кода MikroTik ===
@@ -2744,11 +2808,11 @@ function NetworkConfigManager() {
name="type"
type="select"
value={editingInterface.type}
onChange={(val) => setEditingInterface({ ...editingInterface, type: val, ipsecPasswordId: val !== 'IPSec' ? '' : editingInterface.ipsecPasswordId })}
onChange={(val) => setEditingInterface({ ...editingInterface, type: val, ipsecPasswordId: (val !== 'IPSec' && val !== 'GRE') ? '' : editingInterface.ipsecPasswordId })}
options={INTERFACE_TYPES}
/>
</div>
{editingInterface.type === 'IPSec' && (
{(editingInterface.type === 'IPSec' || editingInterface.type === 'GRE') && (
<div className="col-md-6">
<label className="form-label">IPSec пароль</label>
<div className="input-group">
@@ -2778,7 +2842,7 @@ function NetworkConfigManager() {
</button>
</div>
<div className="form-text">
Выберите сохраненный IPSec пароль или создайте новый
Выберите сохраненный IPSec пароль или создайте новый (опционально для GRE)
</div>
</div>
)}
@@ -3043,7 +3107,7 @@ function NetworkConfigManager() {
/>
<div className="form-text">Оставьте пустым для автогенерации </div>
</div>
{templateType === 'IPSec' && (
{(templateType === 'IPSec' || templateType === 'GRE') && (
<div className="col-12">
<label className="form-label">IPSec пароль</label>
<div className="input-group">
@@ -3073,7 +3137,7 @@ function NetworkConfigManager() {
</button>
</div>
<div className="form-text">
Выберите сохраненный IPSec пароль или создайте новый
Выберите сохраненный IPSec пароль или создайте новый (опционально для GRE)
</div>
</div>
)}