Files
wgServer/internal/wireguard/portforward.go
2025-10-16 16:27:36 +07:00

178 lines
5.4 KiB
Go
Raw Permalink 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.

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
}