catch netfilter.d event

This commit is contained in:
Vladimir Avtsenov 2024-09-06 16:50:56 +03:00
parent 1015423a8f
commit 797b85b03a
5 changed files with 163 additions and 77 deletions

View File

@ -11,7 +11,7 @@ Realized features:
- [X] IP integration
- [X] IPTables rules to IPSet
- [X] Catch interface up/down
- [ ] Catch `netfilter.d` event
- [X] Catch `netfilter.d` event
- [ ] Rule composer (CRUD)
- [ ] GORM integration
- [X] Listing of interfaces

View File

@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"net"
"strings"
"sync"
"time"
@ -103,6 +104,55 @@ func (a *App) Listen(ctx context.Context) []error {
done := make(chan struct{})
netlink.LinkSubscribe(link, done)
exitListenerLoop := false
listener, err := net.Listen("unix", "/opt/var/run/kvas2-go.sock")
if err != nil {
handleError(fmt.Errorf("error while serve UNIX socket: %v", err))
}
defer listener.Close()
go func() {
for {
if exitListenerLoop {
break
}
conn, err := listener.Accept()
if err != nil {
log.Error().Err(err).Msg("error while listening unix socket")
}
go func(conn net.Conn) {
defer conn.Close()
buf := make([]byte, 1024)
n, err := conn.Read(buf)
if err != nil {
return
}
args := strings.Split(string(buf[:n]), ":")
if len(args) == 3 && args[0] == "netfilter.d" {
log.Debug().Str("table", args[2]).Msg("netfilter.d event")
if a.dnsOverrider.Enabled {
err := a.dnsOverrider.PutIPTable(args[2])
if err != nil {
log.Error().Err(err).Msg("error while fixing iptables after netfilter.d")
}
}
for _, group := range a.Groups {
if group.ifaceToIPSet.Enabled {
err := group.ifaceToIPSet.PutIPTable(args[2])
if err != nil {
log.Error().Err(err).Msg("error while fixing iptables after netfilter.d")
}
}
}
}
}(conn)
}
}()
Loop:
for {
select {

View File

@ -25,6 +25,85 @@ type IfaceToIPSet struct {
ipRoute *netlink.Route
}
func (r *IfaceToIPSet) PutIPTable(table string) error {
var err error
if !r.SoftwareMode {
if table == "all" || table == "mangle" {
err = r.IPTables.ClearChain("mangle", r.ChainName)
if err != nil {
return fmt.Errorf("failed to clear chain: %w", err)
}
for _, iptablesArgs := range [][]string{
// Source: https://github.com/qzeleza/kvas/blob/3fdbbd1ace7b57b11bf88d8db3882d94a1d6e01c/opt/etc/ndm/ndm#L194-L206
{"-m", "set", "!", "--match-set", r.IPSetName, "dst", "-j", "RETURN"},
{"-j", "CONNMARK", "--restore-mark"},
{"-m", "mark", "--mark", strconv.Itoa(int(r.mark)), "-j", "RETURN"},
// This command not working
// {"--syn", "-j", "MARK", "--set-mark", strconv.Itoa(int(mark))},
{"-m", "conntrack", "--ctstate", "NEW", "-j", "MARK", "--set-mark", strconv.Itoa(int(r.mark))},
{"-j", "CONNMARK", "--save-mark"},
} {
err = r.IPTables.AppendUnique("mangle", r.ChainName, iptablesArgs...)
if err != nil {
return fmt.Errorf("failed to append rule: %w", err)
}
}
err = r.IPTables.AppendUnique("mangle", "PREROUTING", "-m", "set", "--match-set", r.IPSetName, "dst", "-j", r.ChainName)
if err != nil {
return fmt.Errorf("failed to append rule to PREROUTING: %w", err)
}
err = r.IPTables.AppendUnique("mangle", "OUTPUT", "-m", "set", "--match-set", r.IPSetName, "dst", "-j", r.ChainName)
if err != nil {
return fmt.Errorf("failed to append rule to OUTPUT: %w", err)
}
}
} else {
if table == "all" || table == "mangle" {
preroutingChainName := fmt.Sprintf("%s_PREROUTING", r.ChainName)
err = r.IPTables.ClearChain("mangle", preroutingChainName)
if err != nil {
return fmt.Errorf("failed to clear chain: %w", err)
}
err = r.IPTables.AppendUnique("mangle", preroutingChainName, "-m", "set", "--match-set", r.IPSetName, "dst", "-j", "MARK", "--set-mark", strconv.Itoa(int(r.mark)))
if err != nil {
return fmt.Errorf("failed to create rule: %w", err)
}
err = r.IPTables.AppendUnique("mangle", "PREROUTING", "-j", preroutingChainName)
if err != nil {
return fmt.Errorf("failed to append rule to PREROUTING: %w", err)
}
}
}
if table == "all" || table == "nat" {
postroutingChainName := fmt.Sprintf("%s_POSTROUTING", r.ChainName)
err = r.IPTables.ClearChain("nat", postroutingChainName)
if err != nil {
return fmt.Errorf("failed to clear chain: %w", err)
}
err = r.IPTables.AppendUnique("nat", postroutingChainName, "-o", r.IfaceName, "-j", "MASQUERADE")
if err != nil {
return fmt.Errorf("failed to create rule: %w", err)
}
err = r.IPTables.AppendUnique("nat", "POSTROUTING", "-j", postroutingChainName)
if err != nil {
return fmt.Errorf("failed to append rule to POSTROUTING: %w", err)
}
}
return nil
}
func (r *IfaceToIPSet) IfaceHandle() error {
// Find interface
iface, err := netlink.LinkByName(r.IfaceName)
@ -98,71 +177,9 @@ func (r *IfaceToIPSet) ForceEnable() error {
}
// IPTables rules
if !r.SoftwareMode {
err = r.IPTables.ClearChain("mangle", r.ChainName)
err = r.PutIPTable("all")
if err != nil {
return fmt.Errorf("failed to clear chain: %w", err)
}
for _, iptablesArgs := range [][]string{
// Source: https://github.com/qzeleza/kvas/blob/3fdbbd1ace7b57b11bf88d8db3882d94a1d6e01c/opt/etc/ndm/ndm#L194-L206
{"-m", "set", "!", "--match-set", r.IPSetName, "dst", "-j", "RETURN"},
{"-j", "CONNMARK", "--restore-mark"},
{"-m", "mark", "--mark", strconv.Itoa(int(r.mark)), "-j", "RETURN"},
// This command not working
// {"--syn", "-j", "MARK", "--set-mark", strconv.Itoa(int(mark))},
{"-m", "conntrack", "--ctstate", "NEW", "-j", "MARK", "--set-mark", strconv.Itoa(int(r.mark))},
{"-j", "CONNMARK", "--save-mark"},
} {
err = r.IPTables.AppendUnique("mangle", r.ChainName, iptablesArgs...)
if err != nil {
return fmt.Errorf("failed to append rule: %w", err)
}
}
err = r.IPTables.AppendUnique("mangle", "PREROUTING", "-m", "set", "--match-set", r.IPSetName, "dst", "-j", r.ChainName)
if err != nil {
return fmt.Errorf("failed to append rule to PREROUTING: %w", err)
}
err = r.IPTables.AppendUnique("mangle", "OUTPUT", "-m", "set", "--match-set", r.IPSetName, "dst", "-j", r.ChainName)
if err != nil {
return fmt.Errorf("failed to append rule to OUTPUT: %w", err)
}
} else {
preroutingChainName := fmt.Sprintf("%s_PREROUTING", r.ChainName)
err = r.IPTables.ClearChain("mangle", preroutingChainName)
if err != nil {
return fmt.Errorf("failed to clear chain: %w", err)
}
err = r.IPTables.AppendUnique("mangle", preroutingChainName, "-m", "set", "--match-set", r.IPSetName, "dst", "-j", "MARK", "--set-mark", strconv.Itoa(int(r.mark)))
if err != nil {
return fmt.Errorf("failed to create rule: %w", err)
}
err = r.IPTables.AppendUnique("mangle", "PREROUTING", "-j", preroutingChainName)
if err != nil {
return fmt.Errorf("failed to append rule to PREROUTING: %w", err)
}
}
postroutingChainName := fmt.Sprintf("%s_POSTROUTING", r.ChainName)
err = r.IPTables.ClearChain("nat", postroutingChainName)
if err != nil {
return fmt.Errorf("failed to clear chain: %w", err)
}
err = r.IPTables.AppendUnique("nat", postroutingChainName, "-o", r.IfaceName, "-j", "MASQUERADE")
if err != nil {
return fmt.Errorf("failed to create rule: %w", err)
}
err = r.IPTables.AppendUnique("nat", "POSTROUTING", "-j", postroutingChainName)
if err != nil {
return fmt.Errorf("failed to append rule to POSTROUTING: %w", err)
return nil
}
// Mapping mark with table
@ -180,6 +197,7 @@ func (r *IfaceToIPSet) ForceEnable() error {
return nil
}
r.Enabled = true
return nil
}
@ -244,6 +262,7 @@ func (r *IfaceToIPSet) Disable() []error {
r.ipRule = nil
}
r.Enabled = false
return errs
}

View File

@ -15,7 +15,8 @@ type PortRemap struct {
Enabled bool
}
func (r *PortRemap) ForceEnable() error {
func (r *PortRemap) PutIPTable(table string) error {
if table == "all" || table == "nat" {
err := r.IPTables.ClearChain("nat", r.ChainName)
if err != nil {
return fmt.Errorf("failed to clear chain: %w", err)
@ -30,6 +31,16 @@ func (r *PortRemap) ForceEnable() error {
if err != nil {
return fmt.Errorf("failed to linking chain: %w", err)
}
}
return nil
}
func (r *PortRemap) ForceEnable() error {
err := r.PutIPTable("all")
if err != nil {
return err
}
r.Enabled = true
return nil

View File

@ -0,0 +1,6 @@
#!/bin/sh
SOCKET_PATH="/opt/var/run/kvas2-go.sock"
if [ ! -S "$SOCKET_PATH" ]; then
exit
fi
echo -n "netfilter.d:${type}:${table}" | socat - UNIX-CONNECT:"${SOCKET_PATH}"