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
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m38s
This commit is contained in:
@@ -551,7 +551,7 @@ function NetworkConfigManager() {
|
|||||||
remoteIp: remoteIp,
|
remoteIp: remoteIp,
|
||||||
serverId: templateServer1,
|
serverId: templateServer1,
|
||||||
serverId2: templateServer2,
|
serverId2: templateServer2,
|
||||||
ipsecPasswordId: templateType === 'IPSec' && templateIpsecPasswordId ? templateIpsecPasswordId : ''
|
ipsecPasswordId: (templateType === 'IPSec' || templateType === 'GRE') && templateIpsecPasswordId ? templateIpsecPasswordId : ''
|
||||||
};
|
};
|
||||||
|
|
||||||
const conflicts = checkInterfaceIpConflict(newInterface);
|
const conflicts = checkInterfaceIpConflict(newInterface);
|
||||||
@@ -917,12 +917,12 @@ function NetworkConfigManager() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Если создан новый пароль и открыт шаблон интерфейса, автоматически выбираем его
|
// Если создан новый пароль и открыт шаблон интерфейса, автоматически выбираем его
|
||||||
if (savedPasswordId && interfaceTemplateModalOpen && templateType === 'IPSec') {
|
if (savedPasswordId && interfaceTemplateModalOpen && (templateType === 'IPSec' || templateType === 'GRE')) {
|
||||||
setTemplateIpsecPasswordId(savedPasswordId);
|
setTemplateIpsecPasswordId(savedPasswordId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Если создан новый пароль и открыт модал интерфейса с типом IPSec, автоматически выбираем его
|
// Если создан новый пароль и открыт модал интерфейса с типом IPSec или GRE, автоматически выбираем его
|
||||||
if (savedPasswordId && interfaceModalOpen && editingInterface?.type === 'IPSec') {
|
if (savedPasswordId && interfaceModalOpen && (editingInterface?.type === 'IPSec' || editingInterface?.type === 'GRE')) {
|
||||||
setEditingInterface({ ...editingInterface, ipsecPasswordId: savedPasswordId });
|
setEditingInterface({ ...editingInterface, ipsecPasswordId: savedPasswordId });
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -1019,7 +1019,7 @@ function NetworkConfigManager() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// === Генерация кода MikroTik для настройки IP адресов интерфейсов ===
|
// === Генерация кода MikroTik для настройки IP адресов интерфейсов ===
|
||||||
const generateMikrotikInterfaceAddresses = () => {
|
const generateMikrotikInterfaceAddresses = async () => {
|
||||||
const interfacesWithBothServers = (config.tunnelInterfaces || []).filter(i =>
|
const interfacesWithBothServers = (config.tunnelInterfaces || []).filter(i =>
|
||||||
i.serverId && i.serverId2 && i.localIp && i.remoteIp
|
i.serverId && i.serverId2 && i.localIp && i.remoteIp
|
||||||
);
|
);
|
||||||
@@ -1028,6 +1028,23 @@ function NetworkConfigManager() {
|
|||||||
return [];
|
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 = {};
|
const interfacesByServer = {};
|
||||||
interfacesWithBothServers.forEach(iface => {
|
interfacesWithBothServers.forEach(iface => {
|
||||||
@@ -1057,7 +1074,9 @@ function NetworkConfigManager() {
|
|||||||
remoteIp: iface.remoteIp,
|
remoteIp: iface.remoteIp,
|
||||||
type: iface.type,
|
type: iface.type,
|
||||||
server2Name,
|
server2Name,
|
||||||
server2Info: server2
|
server2Info: server2,
|
||||||
|
ipsecPasswordId: iface.ipsecPasswordId || null,
|
||||||
|
ipsecPassword: iface.ipsecPasswordId ? passwordMap[iface.ipsecPasswordId] : null
|
||||||
});
|
});
|
||||||
|
|
||||||
// Для второго сервера - МЕНЯЕМ МЕСТАМИ: remoteIp становится localIp, localIp становится remoteIp
|
// Для второго сервера - МЕНЯЕМ МЕСТАМИ: remoteIp становится localIp, localIp становится remoteIp
|
||||||
@@ -1074,7 +1093,9 @@ function NetworkConfigManager() {
|
|||||||
remoteIp: iface.localIp, // На втором сервере localIp первого становится remoteIp
|
remoteIp: iface.localIp, // На втором сервере localIp первого становится remoteIp
|
||||||
type: iface.type,
|
type: iface.type,
|
||||||
server2Name: server1Name,
|
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 += `# Связь с сервером: ${iface.server2Name}\n`;
|
||||||
code += `# Local IP: ${iface.localIp}\n`;
|
code += `# Local IP: ${iface.localIp}\n`;
|
||||||
code += `# Remote IP: ${iface.remoteIp}\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 += `/ip address add address=${iface.localIp}/30 interface="${iface.interfaceName}" comment="Interface: ${iface.interfaceName} (${iface.type}) to ${iface.server2Name}"\n`;
|
||||||
code += '\n';
|
code += '\n';
|
||||||
});
|
});
|
||||||
@@ -1121,7 +1177,7 @@ function NetworkConfigManager() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// === Генерация кода MikroTik для рекурсивных маршрутов ===
|
// === Генерация кода MikroTik для рекурсивных маршрутов ===
|
||||||
const generateMikrotikRecursiveRoutes = () => {
|
const generateMikrotikRecursiveRoutes = async () => {
|
||||||
const recursiveGateways = (config.gateways || []).filter(gw => gw.type === 'recursive');
|
const recursiveGateways = (config.gateways || []).filter(gw => gw.type === 'recursive');
|
||||||
|
|
||||||
const blocks = [];
|
const blocks = [];
|
||||||
@@ -1241,7 +1297,7 @@ function NetworkConfigManager() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Генерируем блоки для IP адресов интерфейсов
|
// Генерируем блоки для IP адресов интерфейсов
|
||||||
const interfaceBlocks = generateMikrotikInterfaceAddresses();
|
const interfaceBlocks = await generateMikrotikInterfaceAddresses();
|
||||||
if (interfaceBlocks && interfaceBlocks.length > 0) {
|
if (interfaceBlocks && interfaceBlocks.length > 0) {
|
||||||
blocks.push(...interfaceBlocks);
|
blocks.push(...interfaceBlocks);
|
||||||
}
|
}
|
||||||
@@ -1250,10 +1306,18 @@ function NetworkConfigManager() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// === Обработчик кнопки "Код для MikroTik" ===
|
// === Обработчик кнопки "Код для MikroTik" ===
|
||||||
const handleGenerateMikrotikCode = () => {
|
const handleGenerateMikrotikCode = async () => {
|
||||||
const code = generateMikrotikRecursiveRoutes();
|
setLoading(true);
|
||||||
setGeneratedMikrotikCode(code);
|
try {
|
||||||
setMikrotikCodeModalOpen(true);
|
const code = await generateMikrotikRecursiveRoutes();
|
||||||
|
setGeneratedMikrotikCode(code);
|
||||||
|
setMikrotikCodeModalOpen(true);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error generating MikroTik code:', error);
|
||||||
|
notify.error('Ошибка при генерации кода MikroTik');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// === Копирование кода MikroTik ===
|
// === Копирование кода MikroTik ===
|
||||||
@@ -2744,11 +2808,11 @@ function NetworkConfigManager() {
|
|||||||
name="type"
|
name="type"
|
||||||
type="select"
|
type="select"
|
||||||
value={editingInterface.type}
|
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}
|
options={INTERFACE_TYPES}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{editingInterface.type === 'IPSec' && (
|
{(editingInterface.type === 'IPSec' || editingInterface.type === 'GRE') && (
|
||||||
<div className="col-md-6">
|
<div className="col-md-6">
|
||||||
<label className="form-label">IPSec пароль</label>
|
<label className="form-label">IPSec пароль</label>
|
||||||
<div className="input-group">
|
<div className="input-group">
|
||||||
@@ -2778,7 +2842,7 @@ function NetworkConfigManager() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-text">
|
<div className="form-text">
|
||||||
Выберите сохраненный IPSec пароль или создайте новый
|
Выберите сохраненный IPSec пароль или создайте новый (опционально для GRE)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -3043,7 +3107,7 @@ function NetworkConfigManager() {
|
|||||||
/>
|
/>
|
||||||
<div className="form-text">Оставьте пустым для автогенерации </div>
|
<div className="form-text">Оставьте пустым для автогенерации </div>
|
||||||
</div>
|
</div>
|
||||||
{templateType === 'IPSec' && (
|
{(templateType === 'IPSec' || templateType === 'GRE') && (
|
||||||
<div className="col-12">
|
<div className="col-12">
|
||||||
<label className="form-label">IPSec пароль</label>
|
<label className="form-label">IPSec пароль</label>
|
||||||
<div className="input-group">
|
<div className="input-group">
|
||||||
@@ -3073,7 +3137,7 @@ function NetworkConfigManager() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-text">
|
<div className="form-text">
|
||||||
Выберите сохраненный IPSec пароль или создайте новый
|
Выберите сохраненный IPSec пароль или создайте новый (опционально для GRE)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user