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,
|
||||
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>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user