Skip to content

Commit

Permalink
enhance the lan security logic
Browse files Browse the repository at this point in the history
- apply the nft rules if only the default gw is private
- explicitly allow traffic to all ips except the default gw network
- except the router from the lan block
- create a buffer with rules instead of executing commands
- use the nft.Apply function for executing the buffer
  • Loading branch information
Omarabdul3ziz committed Jan 15, 2025
1 parent f94953c commit ede0811
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 54 deletions.
5 changes: 0 additions & 5 deletions cmds/modules/networkd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"github.com/threefoldtech/zos/pkg/environment"
"github.com/threefoldtech/zos/pkg/network/dhcp"
"github.com/threefoldtech/zos/pkg/network/mycelium"
"github.com/threefoldtech/zos/pkg/network/nft"
"github.com/threefoldtech/zos/pkg/network/public"
"github.com/threefoldtech/zos/pkg/network/types"
"github.com/threefoldtech/zos/pkg/zinit"
Expand Down Expand Up @@ -93,10 +92,6 @@ func action(cli *cli.Context) error {
return errors.Wrap(err, "failed to host firewall rules")
}

if err := nft.DropTrafficToLAN(); err != nil {
return fmt.Errorf("failed to drop traffic to lan: %w", err)
}

public.SetPersistence(root)

pub, err := public.LoadPublicConfig()
Expand Down
4 changes: 4 additions & 0 deletions pkg/network/ndmz/dualstack.go
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,10 @@ func applyFirewall() error {
return errors.Wrap(err, "failed to apply nft rule set")
}

if err := nft.DropTrafficToLAN(dmzNamespace); err != nil {
return errors.Wrap(err, "failed to drop traffic to lan")
}

return nil
}

Expand Down
97 changes: 48 additions & 49 deletions pkg/network/nft/nft.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package nft

import (
"bytes"
"fmt"
"io"
"net"
"os/exec"

"github.com/rs/zerolog/log"
Expand Down Expand Up @@ -36,59 +36,49 @@ func Apply(r io.Reader, ns string) error {
return nil
}

func applyNftRule(rule []string) error {
if len(rule) == 0 {
return errors.New("invalid nft rule")
}
cmd := exec.Command(rule[0], rule[1:]...)

out, err := cmd.CombinedOutput()
if err != nil {
log.Error().Err(err).Str("output", string(out)).Msg("error during nft")
if eerr, ok := err.(*exec.ExitError); ok {
return errors.Wrapf(err, "failed to execute nft: %v", string(eerr.Stderr))
}
return errors.Wrap(err, "failed to execute nft")
}
return nil
}

// DropTrafficToLAN drops all the outgoing traffic to any peers on
// the same lan network, but allow dicovery port for ygg/myc by accepting
// traffic to/from dest/src ports.
func DropTrafficToLAN() error {
rules := [][]string{
// @th,0,16 and @th,16,16 is raw expression for sport/dport in transport header
// used due to limitation on the installed nft v0.9.1
{
"nft", "add", "rule", "inet", "filter", "forward",
"meta", "l4proto", "{tcp, udp}", "@th,0,16", "{9651, 9650}", "accept",
},
{
"nft", "add", "rule", "inet", "filter", "forward",
"meta", "l4proto", "{tcp, udp}", "@th,16,16", "{9651, 9650}", "accept",
},
// @th,0,16 and @th,16,16 is raw expression for sport/dport in transport header
// used due to limitation on the installed nft v0.9.1
func DropTrafficToLAN(namesapce string) error {
dgw, err := getDefaultGW()
if err != nil {
return fmt.Errorf("failed to find default gateway: %w", err)
}
mac, err := getDefaultGwMac()
log.Debug().Str("mac", mac.String()).Err(err).Msg("default gw return")
rules = append(rules, []string{
"nft", "add", "rule", "inet", "filter", "forward",
"ether", "daddr", "!=", mac.String(), "drop",
})

for _, rule := range rules {
if err := applyNftRule(rule); err != nil {
return fmt.Errorf("failed to apply nft rule: %w", err)
}

if !dgw.IP.IsPrivate() {
log.Warn().Msg("skip LAN security. default gateway is public")
return nil
}

return nil
ipAddr := dgw.IP.String()
netAddr := getNetworkRange(dgw)
macAddr := dgw.HardwareAddr.String()
log.Debug().
Str("ipAddr", ipAddr).
Str("netAddr", netAddr).
Str("macAddr", macAddr).
Msg("drop traffic to lan with the default gateway")

var buf bytes.Buffer
buf.WriteString("table inet filter {\n")
buf.WriteString(" chain forward {\n")
buf.WriteString(" meta l4proto { tcp, udp } @th,0,16 { 9651, 9650 } accept;\n")
buf.WriteString(" meta l4proto { tcp, udp } @th,16,16 { 9651, 9650 } accept;\n")
buf.WriteString(fmt.Sprintf(" ip daddr %s accept;\n", ipAddr))
buf.WriteString(fmt.Sprintf(" ip daddr != %s accept;\n", netAddr))
buf.WriteString(fmt.Sprintf(" ether daddr != %s drop;\n", macAddr))
buf.WriteString(" }\n")
buf.WriteString("}\n")

return Apply(&buf, namesapce)
}

func getDefaultGwMac() (net.HardwareAddr, error) {
func getDefaultGW() (netlink.Neigh, error) {
routes, err := netlink.RouteList(nil, netlink.FAMILY_V4)
if err != nil {
return nil, fmt.Errorf("failed to list routes: %v", err)
return netlink.Neigh{}, fmt.Errorf("failed to list routes: %v", err)
}

var defaultRoute *netlink.Route
Expand All @@ -100,23 +90,32 @@ func getDefaultGwMac() (net.HardwareAddr, error) {
}

if defaultRoute == nil {
return nil, fmt.Errorf("default route not found")
return netlink.Neigh{}, fmt.Errorf("default route not found")
}

if defaultRoute.Gw == nil {
return nil, fmt.Errorf("default route has no gateway")
return netlink.Neigh{}, fmt.Errorf("default route has no gateway")
}

neighs, err := netlink.NeighList(0, netlink.FAMILY_V4)
if err != nil {
return nil, fmt.Errorf("failed to list neighbors: %v", err)
return netlink.Neigh{}, fmt.Errorf("failed to list neighbors: %v", err)
}

for _, neigh := range neighs {
if neigh.IP.Equal(defaultRoute.Gw) {
return neigh.HardwareAddr, nil
return neigh, nil
}
}

return nil, errors.New("failed to get default gw")
return netlink.Neigh{}, errors.New("failed to get default gw")
}

func getNetworkRange(ip netlink.Neigh) string {
mask := ip.IP.DefaultMask()
network := ip.IP.Mask(mask)
ones, _ := mask.Size()
networkRange := fmt.Sprintf("%s/%d", network.String(), ones)

return networkRange
}

0 comments on commit ede0811

Please sign in to comment.