178 lines
5.4 KiB
Go
178 lines
5.4 KiB
Go
package wireguard
|
||
|
||
import (
|
||
"fmt"
|
||
"log"
|
||
"os/exec"
|
||
|
||
"wg-panel/internal/database"
|
||
)
|
||
|
||
// isPortAvailable проверяет доступен ли порт
|
||
func isPortAvailable(db *database.Database, port int, protocol string) bool {
|
||
protocols := []string{protocol}
|
||
if protocol == "both" {
|
||
protocols = []string{"tcp", "udp", "both"}
|
||
}
|
||
|
||
for _, client := range db.Clients {
|
||
for _, pf := range client.PortForwards {
|
||
if pf.Port == port {
|
||
// Проверяем конфликт протоколов
|
||
if pf.Protocol == "both" || protocol == "both" {
|
||
return false
|
||
}
|
||
for _, p := range protocols {
|
||
if pf.Protocol == p {
|
||
return false
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return true
|
||
}
|
||
|
||
// AddPortForward добавляет проброс порта для клиента
|
||
func AddPortForward(db *database.Database, client *database.Client, port int, protocol, description string) error {
|
||
// Проверяем что порт свободен
|
||
if !isPortAvailable(db, port, protocol) {
|
||
return fmt.Errorf("порт %d/%s уже используется", port, protocol)
|
||
}
|
||
|
||
// Добавляем в список
|
||
portForward := database.PortForward{
|
||
Port: port,
|
||
Protocol: protocol,
|
||
Description: description,
|
||
}
|
||
client.PortForwards = append(client.PortForwards, portForward)
|
||
|
||
// Применяем правила iptables если клиент активен
|
||
if client.Enabled {
|
||
if err := applyPortForwardRules(client, portForward); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// updatePortForward обновляет проброс порта
|
||
func updatePortForward(client *database.Client, port int, protocol, newDescription string) error {
|
||
for i, pf := range client.PortForwards {
|
||
if pf.Port == port && pf.Protocol == protocol {
|
||
client.PortForwards[i].Description = newDescription
|
||
return nil
|
||
}
|
||
}
|
||
return fmt.Errorf("проброс порта не найден")
|
||
}
|
||
|
||
// RemovePortForward удаляет проброс порта
|
||
func RemovePortForward(client *database.Client, port int, protocol string) error {
|
||
// Находим и удаляем проброс
|
||
for i, pf := range client.PortForwards {
|
||
if pf.Port == port && pf.Protocol == protocol {
|
||
// Удаляем правила iptables
|
||
if client.Enabled {
|
||
removePortForwardRules(client, pf)
|
||
}
|
||
|
||
// Удаляем из списка
|
||
client.PortForwards = append(client.PortForwards[:i], client.PortForwards[i+1:]...)
|
||
return nil
|
||
}
|
||
}
|
||
|
||
return fmt.Errorf("проброс порта не найден")
|
||
}
|
||
|
||
// applyPortForwardRules применяет правила iptables для проброса порта
|
||
func applyPortForwardRules(client *database.Client, pf database.PortForward) error {
|
||
log.Printf(" 🔀 Применяю проброс порта %d (%s)", pf.Port, pf.Protocol)
|
||
|
||
netInterface := database.GetDefaultInterface()
|
||
|
||
protocols := []string{pf.Protocol}
|
||
if pf.Protocol == "both" {
|
||
protocols = []string{"tcp", "udp"}
|
||
}
|
||
|
||
for _, proto := range protocols {
|
||
commands := [][]string{
|
||
// DNAT только для пакетов приходящих с внешнего интерфейса
|
||
{"iptables", "-t", "nat", "-A", "PREROUTING", "-i", netInterface, "-p", proto,
|
||
"--dport", fmt.Sprintf("%d", pf.Port), "-j", "DNAT",
|
||
"--to-destination", fmt.Sprintf("%s:%d", client.Address, pf.Port)},
|
||
|
||
// Разрешаем FORWARD для этого порта
|
||
{"iptables", "-I", "FORWARD", "1", "-p", proto, "-d", client.Address,
|
||
"--dport", fmt.Sprintf("%d", pf.Port), "-j", "ACCEPT"},
|
||
}
|
||
|
||
for _, cmdArgs := range commands {
|
||
cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
|
||
output, err := cmd.CombinedOutput()
|
||
if err != nil {
|
||
log.Printf(" ⚠️ Ошибка: %v (output: %s)", err, string(output))
|
||
return err
|
||
}
|
||
}
|
||
}
|
||
|
||
log.Printf(" ✅ Проброс порта настроен")
|
||
return nil
|
||
}
|
||
|
||
// removePortForwardRules удаляет правила iptables для проброса порта
|
||
func removePortForwardRules(client *database.Client, pf database.PortForward) error {
|
||
netInterface := database.GetDefaultInterface()
|
||
|
||
protocols := []string{pf.Protocol}
|
||
if pf.Protocol == "both" {
|
||
protocols = []string{"tcp", "udp"}
|
||
}
|
||
|
||
for _, proto := range protocols {
|
||
commands := [][]string{
|
||
{"iptables", "-t", "nat", "-D", "PREROUTING", "-i", netInterface, "-p", proto,
|
||
"--dport", fmt.Sprintf("%d", pf.Port), "-j", "DNAT",
|
||
"--to-destination", fmt.Sprintf("%s:%d", client.Address, pf.Port)},
|
||
|
||
{"iptables", "-D", "FORWARD", "-p", proto, "-d", client.Address,
|
||
"--dport", fmt.Sprintf("%d", pf.Port), "-j", "ACCEPT"},
|
||
}
|
||
|
||
for _, cmdArgs := range commands {
|
||
cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
|
||
cmd.Run() // Игнорируем ошибки при удалении
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// ApplyAllPortForwards применяет все пробросы портов для клиента
|
||
func ApplyAllPortForwards(client *database.Client) error {
|
||
if !client.Enabled {
|
||
return nil
|
||
}
|
||
|
||
for _, pf := range client.PortForwards {
|
||
if err := applyPortForwardRules(client, pf); err != nil {
|
||
log.Printf(" ⚠️ Ошибка проброса порта %d: %v", pf.Port, err)
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// removeAllPortForwards удаляет все пробросы портов для клиента
|
||
func removeAllPortForwards(client *database.Client) error {
|
||
for _, pf := range client.PortForwards {
|
||
removePortForwardRules(client, pf)
|
||
}
|
||
return nil
|
||
}
|