Feat linux firewall support (#805)

Update the client's engine to apply firewall rules received from the manager (results of ACL policy).
This commit is contained in:
Givi Khojanashvili 2023-05-29 18:00:18 +04:00 committed by GitHub
parent 2eb9a97fee
commit ba7a39a4fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 4143 additions and 1013 deletions

View File

@ -78,6 +78,9 @@ jobs:
- name: Generate RouteManager Test bin - name: Generate RouteManager Test bin
run: CGO_ENABLED=0 go test -c -o routemanager-testing.bin ./client/internal/routemanager/... run: CGO_ENABLED=0 go test -c -o routemanager-testing.bin ./client/internal/routemanager/...
- name: Generate nftables Manager Test bin
run: CGO_ENABLED=0 go test -c -o nftablesmanager-testing.bin ./client/firewall/nftables/...
- name: Generate Engine Test bin - name: Generate Engine Test bin
run: CGO_ENABLED=0 go test -c -o engine-testing.bin ./client/internal run: CGO_ENABLED=0 go test -c -o engine-testing.bin ./client/internal
@ -96,6 +99,9 @@ jobs:
- name: Run RouteManager tests in docker - name: Run RouteManager tests in docker
run: docker run -t --cap-add=NET_ADMIN --privileged --rm -v $PWD:/ci -w /ci/client/internal/routemanager --entrypoint /busybox/sh gcr.io/distroless/base:debug -c /ci/routemanager-testing.bin -test.timeout 5m -test.parallel 1 run: docker run -t --cap-add=NET_ADMIN --privileged --rm -v $PWD:/ci -w /ci/client/internal/routemanager --entrypoint /busybox/sh gcr.io/distroless/base:debug -c /ci/routemanager-testing.bin -test.timeout 5m -test.parallel 1
- name: Run nftables Manager tests in docker
run: docker run -t --cap-add=NET_ADMIN --privileged --rm -v $PWD:/ci -w /ci/client/firewall --entrypoint /busybox/sh gcr.io/distroless/base:debug -c /ci/nftablesmanager-testing.bin -test.timeout 5m -test.parallel 1
- name: Run Engine tests in docker - name: Run Engine tests in docker
run: docker run -t --cap-add=NET_ADMIN --privileged --rm -v $PWD:/ci -w /ci/client/internal --entrypoint /busybox/sh gcr.io/distroless/base:debug -c /ci/engine-testing.bin -test.timeout 5m -test.parallel 1 run: docker run -t --cap-add=NET_ADMIN --privileged --rm -v $PWD:/ci -w /ci/client/internal --entrypoint /busybox/sh gcr.io/distroless/base:debug -c /ci/engine-testing.bin -test.timeout 5m -test.parallel 1

View File

@ -13,22 +13,24 @@ type Rule interface {
GetRuleID() string GetRuleID() string
} }
// Direction is the direction of the traffic // RuleDirection is the traffic direction which a rule is applied
type Direction int type RuleDirection int
const ( const (
// DirectionSrc is the direction of the traffic from the source // RuleDirectionIN applies to filters that handlers incoming traffic
DirectionSrc Direction = iota RuleDirectionIN RuleDirection = iota
// DirectionDst is the direction of the traffic from the destination // RuleDirectionOUT applies to filters that handlers outgoing traffic
DirectionDst RuleDirectionOUT
) )
// Action is the action to be taken on a rule // Action is the action to be taken on a rule
type Action int type Action int
const ( const (
// ActionUnknown is a unknown action
ActionUnknown Action = iota
// ActionAccept is the action to accept a packet // ActionAccept is the action to accept a packet
ActionAccept Action = iota ActionAccept
// ActionDrop is the action to drop a packet // ActionDrop is the action to drop a packet
ActionDrop ActionDrop
) )
@ -39,10 +41,15 @@ const (
// Netbird client for ACL and routing functionality // Netbird client for ACL and routing functionality
type Manager interface { type Manager interface {
// AddFiltering rule to the firewall // AddFiltering rule to the firewall
//
// If comment argument is empty firewall manager should set
// rule ID as comment for the rule
AddFiltering( AddFiltering(
ip net.IP, ip net.IP,
port *Port, proto Protocol,
direction Direction, sPort *Port,
dPort *Port,
direction RuleDirection,
action Action, action Action,
comment string, comment string,
) (Rule, error) ) (Rule, error)

View File

@ -8,26 +8,43 @@ import (
"github.com/coreos/go-iptables/iptables" "github.com/coreos/go-iptables/iptables"
"github.com/google/uuid" "github.com/google/uuid"
log "github.com/sirupsen/logrus"
fw "github.com/netbirdio/netbird/client/firewall" fw "github.com/netbirdio/netbird/client/firewall"
) )
const ( const (
// ChainFilterName is the name of the chain that is used for filtering by the Netbird client // ChainInputFilterName is the name of the chain that is used for filtering incoming packets
ChainFilterName = "NETBIRD-ACL" ChainInputFilterName = "NETBIRD-ACL-INPUT"
// ChainOutputFilterName is the name of the chain that is used for filtering outgoing packets
ChainOutputFilterName = "NETBIRD-ACL-OUTPUT"
) )
// jumpNetbirdInputDefaultRule always added by manager to the input chain for all trafic from the Netbird interface
var jumpNetbirdInputDefaultRule = []string{"-j", ChainInputFilterName}
// jumpNetbirdOutputDefaultRule always added by manager to the output chain for all trafic from the Netbird interface
var jumpNetbirdOutputDefaultRule = []string{"-j", ChainOutputFilterName}
// dropAllDefaultRule in the Netbird chain
var dropAllDefaultRule = []string{"-j", "DROP"}
// Manager of iptables firewall // Manager of iptables firewall
type Manager struct { type Manager struct {
mutex sync.Mutex mutex sync.Mutex
ipv4Client *iptables.IPTables ipv4Client *iptables.IPTables
ipv6Client *iptables.IPTables ipv6Client *iptables.IPTables
wgIfaceName string
} }
// Create iptables firewall manager // Create iptables firewall manager
func Create() (*Manager, error) { func Create(wgIfaceName string) (*Manager, error) {
m := &Manager{} m := &Manager{
wgIfaceName: wgIfaceName,
}
// init clients for booth ipv4 and ipv6 // init clients for booth ipv4 and ipv6
ipv4Client, err := iptables.NewWithProtocol(iptables.ProtocolIPv4) ipv4Client, err := iptables.NewWithProtocol(iptables.ProtocolIPv4)
@ -38,118 +55,266 @@ func Create() (*Manager, error) {
ipv6Client, err := iptables.NewWithProtocol(iptables.ProtocolIPv6) ipv6Client, err := iptables.NewWithProtocol(iptables.ProtocolIPv6)
if err != nil { if err != nil {
return nil, fmt.Errorf("ip6tables is not installed in the system or not supported") log.Errorf("ip6tables is not installed in the system or not supported: %v", err)
} else {
m.ipv6Client = ipv6Client
} }
m.ipv6Client = ipv6Client
if err := m.Reset(); err != nil { if err := m.Reset(); err != nil {
return nil, fmt.Errorf("failed to reset firewall: %s", err) return nil, fmt.Errorf("failed to reset firewall: %v", err)
} }
return m, nil return m, nil
} }
// AddFiltering rule to the firewall // AddFiltering rule to the firewall
//
// If comment is empty rule ID is used as comment
func (m *Manager) AddFiltering( func (m *Manager) AddFiltering(
ip net.IP, ip net.IP,
port *fw.Port, protocol fw.Protocol,
direction fw.Direction, sPort *fw.Port,
dPort *fw.Port,
direction fw.RuleDirection,
action fw.Action, action fw.Action,
comment string, comment string,
) (fw.Rule, error) { ) (fw.Rule, error) {
m.mutex.Lock() m.mutex.Lock()
defer m.mutex.Unlock() defer m.mutex.Unlock()
client := m.client(ip)
ok, err := client.ChainExists("filter", ChainFilterName) client, err := m.client(ip)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to check if chain exists: %s", err)
}
if !ok {
if err := client.NewChain("filter", ChainFilterName); err != nil {
return nil, fmt.Errorf("failed to create chain: %s", err)
}
}
if port == nil || port.Values == nil || (port.IsRange && len(port.Values) != 2) {
return nil, fmt.Errorf("invalid port definition")
}
pv := strconv.Itoa(port.Values[0])
if port.IsRange {
pv += ":" + strconv.Itoa(port.Values[1])
}
specs := m.filterRuleSpecs("filter", ChainFilterName, ip, pv, direction, action, comment)
if err := client.AppendUnique("filter", ChainFilterName, specs...); err != nil {
return nil, err return nil, err
} }
rule := &Rule{
id: uuid.New().String(), var dPortVal, sPortVal string
specs: specs, if dPort != nil && dPort.Values != nil {
v6: ip.To4() == nil, // TODO: we support only one port per rule in current implementation of ACLs
dPortVal = strconv.Itoa(dPort.Values[0])
} }
return rule, nil if sPort != nil && sPort.Values != nil {
sPortVal = strconv.Itoa(sPort.Values[0])
}
ruleID := uuid.New().String()
if comment == "" {
comment = ruleID
}
specs := m.filterRuleSpecs(
"filter",
ip,
string(protocol),
sPortVal,
dPortVal,
direction,
action,
comment,
)
if direction == fw.RuleDirectionOUT {
ok, err := client.Exists("filter", ChainOutputFilterName, specs...)
if err != nil {
return nil, fmt.Errorf("check is output rule already exists: %w", err)
}
if ok {
return nil, fmt.Errorf("input rule already exists")
}
if err := client.Insert("filter", ChainOutputFilterName, 1, specs...); err != nil {
return nil, err
}
} else {
ok, err := client.Exists("filter", ChainInputFilterName, specs...)
if err != nil {
return nil, fmt.Errorf("check is input rule already exists: %w", err)
}
if ok {
return nil, fmt.Errorf("input rule already exists")
}
if err := client.Insert("filter", ChainInputFilterName, 1, specs...); err != nil {
return nil, err
}
}
return &Rule{
id: ruleID,
specs: specs,
dst: direction == fw.RuleDirectionOUT,
v6: ip.To4() == nil,
}, nil
} }
// DeleteRule from the firewall by rule definition // DeleteRule from the firewall by rule definition
func (m *Manager) DeleteRule(rule fw.Rule) error { func (m *Manager) DeleteRule(rule fw.Rule) error {
m.mutex.Lock() m.mutex.Lock()
defer m.mutex.Unlock() defer m.mutex.Unlock()
r, ok := rule.(*Rule) r, ok := rule.(*Rule)
if !ok { if !ok {
return fmt.Errorf("invalid rule type") return fmt.Errorf("invalid rule type")
} }
client := m.ipv4Client client := m.ipv4Client
if r.v6 { if r.v6 {
if m.ipv6Client == nil {
return fmt.Errorf("ipv6 is not supported")
}
client = m.ipv6Client client = m.ipv6Client
} }
return client.Delete("filter", ChainFilterName, r.specs...)
if r.dst {
return client.Delete("filter", ChainOutputFilterName, r.specs...)
}
return client.Delete("filter", ChainInputFilterName, r.specs...)
} }
// Reset firewall to the default state // Reset firewall to the default state
func (m *Manager) Reset() error { func (m *Manager) Reset() error {
m.mutex.Lock() m.mutex.Lock()
defer m.mutex.Unlock() defer m.mutex.Unlock()
if err := m.reset(m.ipv4Client, "filter", ChainFilterName); err != nil {
return fmt.Errorf("clean ipv4 firewall ACL chain: %w", err) if err := m.reset(m.ipv4Client, "filter"); err != nil {
return fmt.Errorf("clean ipv4 firewall ACL input chain: %w", err)
} }
if err := m.reset(m.ipv6Client, "filter", ChainFilterName); err != nil { if m.ipv6Client != nil {
return fmt.Errorf("clean ipv6 firewall ACL chain: %w", err) if err := m.reset(m.ipv6Client, "filter"); err != nil {
return fmt.Errorf("clean ipv6 firewall ACL input chain: %w", err)
}
} }
return nil return nil
} }
// reset firewall chain, clear it and drop it // reset firewall chain, clear it and drop it
func (m *Manager) reset(client *iptables.IPTables, table, chain string) error { func (m *Manager) reset(client *iptables.IPTables, table string) error {
ok, err := client.ChainExists(table, chain) ok, err := client.ChainExists(table, ChainInputFilterName)
if err != nil { if err != nil {
return fmt.Errorf("failed to check if chain exists: %w", err) return fmt.Errorf("failed to check if input chain exists: %w", err)
} }
if !ok { if ok {
specs := append([]string{"-i", m.wgIfaceName}, jumpNetbirdInputDefaultRule...)
if ok, err := client.Exists("filter", "INPUT", specs...); err != nil {
return err
} else if ok {
if err := client.Delete("filter", "INPUT", specs...); err != nil {
log.WithError(err).Errorf("failed to delete default input rule: %v", err)
}
}
}
ok, err = client.ChainExists(table, ChainOutputFilterName)
if err != nil {
return fmt.Errorf("failed to check if output chain exists: %w", err)
}
if ok {
specs := append([]string{"-o", m.wgIfaceName}, jumpNetbirdOutputDefaultRule...)
if ok, err := client.Exists("filter", "OUTPUT", specs...); err != nil {
return err
} else if ok {
if err := client.Delete("filter", "OUTPUT", specs...); err != nil {
log.WithError(err).Errorf("failed to delete default output rule: %v", err)
}
}
}
if err := client.ClearAndDeleteChain(table, ChainInputFilterName); err != nil {
log.Errorf("failed to clear and delete input chain: %v", err)
return nil return nil
} }
if err := client.ClearChain(table, ChainFilterName); err != nil {
return fmt.Errorf("failed to clear chain: %w", err) if err := client.ClearAndDeleteChain(table, ChainOutputFilterName); err != nil {
log.Errorf("failed to clear and delete input chain: %v", err)
return nil
} }
return client.DeleteChain(table, ChainFilterName)
return nil
} }
// filterRuleSpecs returns the specs of a filtering rule // filterRuleSpecs returns the specs of a filtering rule
func (m *Manager) filterRuleSpecs( func (m *Manager) filterRuleSpecs(
table string, chain string, ip net.IP, port string, table string, ip net.IP, protocol string, sPort, dPort string,
direction fw.Direction, action fw.Action, comment string, direction fw.RuleDirection, action fw.Action, comment string,
) (specs []string) { ) (specs []string) {
if direction == fw.DirectionSrc { switch direction {
case fw.RuleDirectionIN:
specs = append(specs, "-s", ip.String()) specs = append(specs, "-s", ip.String())
case fw.RuleDirectionOUT:
specs = append(specs, "-d", ip.String())
}
if protocol != "all" {
specs = append(specs, "-p", protocol)
}
if sPort != "" {
specs = append(specs, "--sport", sPort)
}
if dPort != "" {
specs = append(specs, "--dport", dPort)
} }
specs = append(specs, "-p", "tcp", "--dport", port)
specs = append(specs, "-j", m.actionToStr(action)) specs = append(specs, "-j", m.actionToStr(action))
return append(specs, "-m", "comment", "--comment", comment) return append(specs, "-m", "comment", "--comment", comment)
} }
// client returns corresponding iptables client for the given ip // rawClient returns corresponding iptables client for the given ip
func (m *Manager) client(ip net.IP) *iptables.IPTables { func (m *Manager) rawClient(ip net.IP) (*iptables.IPTables, error) {
if ip.To4() != nil { if ip.To4() != nil {
return m.ipv4Client return m.ipv4Client, nil
} }
return m.ipv6Client if m.ipv6Client == nil {
return nil, fmt.Errorf("ipv6 is not supported")
}
return m.ipv6Client, nil
}
// client returns client with initialized chain and default rules
func (m *Manager) client(ip net.IP) (*iptables.IPTables, error) {
client, err := m.rawClient(ip)
if err != nil {
return nil, err
}
ok, err := client.ChainExists("filter", ChainInputFilterName)
if err != nil {
return nil, fmt.Errorf("failed to check if chain exists: %w", err)
}
if !ok {
if err := client.NewChain("filter", ChainInputFilterName); err != nil {
return nil, fmt.Errorf("failed to create input chain: %w", err)
}
if err := client.AppendUnique("filter", ChainInputFilterName, dropAllDefaultRule...); err != nil {
return nil, fmt.Errorf("failed to create default drop all in netbird input chain: %w", err)
}
specs := append([]string{"-i", m.wgIfaceName}, jumpNetbirdInputDefaultRule...)
if err := client.AppendUnique("filter", "INPUT", specs...); err != nil {
return nil, fmt.Errorf("failed to create input chain jump rule: %w", err)
}
}
ok, err = client.ChainExists("filter", ChainOutputFilterName)
if err != nil {
return nil, fmt.Errorf("failed to check if chain exists: %w", err)
}
if !ok {
if err := client.NewChain("filter", ChainOutputFilterName); err != nil {
return nil, fmt.Errorf("failed to create output chain: %w", err)
}
if err := client.AppendUnique("filter", ChainOutputFilterName, dropAllDefaultRule...); err != nil {
return nil, fmt.Errorf("failed to create default drop all in netbird output chain: %w", err)
}
specs := append([]string{"-o", m.wgIfaceName}, jumpNetbirdOutputDefaultRule...)
if err := client.AppendUnique("filter", "OUTPUT", specs...); err != nil {
return nil, fmt.Errorf("failed to create output chain jump rule: %w", err)
}
}
return client, nil
} }
func (m *Manager) actionToStr(action fw.Action) string { func (m *Manager) actionToStr(action fw.Action) string {

View File

@ -1,105 +1,129 @@
package iptables package iptables
import ( import (
"fmt"
"net" "net"
"testing" "testing"
"time"
"github.com/coreos/go-iptables/iptables" "github.com/coreos/go-iptables/iptables"
"github.com/stretchr/testify/require"
fw "github.com/netbirdio/netbird/client/firewall" fw "github.com/netbirdio/netbird/client/firewall"
) )
func TestNewManager(t *testing.T) { func TestIptablesManager(t *testing.T) {
ipv4Client, err := iptables.NewWithProtocol(iptables.ProtocolIPv4) ipv4Client, err := iptables.NewWithProtocol(iptables.ProtocolIPv4)
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
manager, err := Create() // just check on the local interface
if err != nil { manager, err := Create("lo")
t.Fatal(err) require.NoError(t, err)
} time.Sleep(time.Second)
defer func() {
if err := manager.Reset(); err != nil {
t.Errorf("clear the manager state: %v", err)
}
time.Sleep(time.Second)
}()
var rule1 fw.Rule var rule1 fw.Rule
t.Run("add first rule", func(t *testing.T) { t.Run("add first rule", func(t *testing.T) {
ip := net.ParseIP("10.20.0.2") ip := net.ParseIP("10.20.0.2")
port := &fw.Port{Proto: fw.PortProtocolTCP, Values: []int{8080}} port := &fw.Port{Values: []int{8080}}
rule1, err = manager.AddFiltering(ip, port, fw.DirectionDst, fw.ActionAccept, "accept HTTP traffic") rule1, err = manager.AddFiltering(ip, "tcp", nil, port, fw.RuleDirectionOUT, fw.ActionAccept, "accept HTTP traffic")
if err != nil { require.NoError(t, err, "failed to add rule")
t.Errorf("failed to add rule: %v", err)
}
checkRuleSpecs(t, ipv4Client, true, rule1.(*Rule).specs...) checkRuleSpecs(t, ipv4Client, ChainOutputFilterName, true, rule1.(*Rule).specs...)
}) })
var rule2 fw.Rule var rule2 fw.Rule
t.Run("add second rule", func(t *testing.T) { t.Run("add second rule", func(t *testing.T) {
ip := net.ParseIP("10.20.0.3") ip := net.ParseIP("10.20.0.3")
port := &fw.Port{ port := &fw.Port{
Proto: fw.PortProtocolTCP,
Values: []int{8043: 8046}, Values: []int{8043: 8046},
} }
rule2, err = manager.AddFiltering( rule2, err = manager.AddFiltering(
ip, port, fw.DirectionDst, fw.ActionAccept, "accept HTTPS traffic from ports range") ip, "tcp", port, nil, fw.RuleDirectionIN, fw.ActionAccept, "accept HTTPS traffic from ports range")
if err != nil { require.NoError(t, err, "failed to add rule")
t.Errorf("failed to add rule: %v", err)
}
checkRuleSpecs(t, ipv4Client, true, rule2.(*Rule).specs...) checkRuleSpecs(t, ipv4Client, ChainInputFilterName, true, rule2.(*Rule).specs...)
}) })
t.Run("delete first rule", func(t *testing.T) { t.Run("delete first rule", func(t *testing.T) {
if err := manager.DeleteRule(rule1); err != nil { if err := manager.DeleteRule(rule1); err != nil {
t.Errorf("failed to delete rule: %v", err) require.NoError(t, err, "failed to delete rule")
} }
checkRuleSpecs(t, ipv4Client, false, rule1.(*Rule).specs...) checkRuleSpecs(t, ipv4Client, ChainOutputFilterName, false, rule1.(*Rule).specs...)
}) })
t.Run("delete second rule", func(t *testing.T) { t.Run("delete second rule", func(t *testing.T) {
if err := manager.DeleteRule(rule2); err != nil { if err := manager.DeleteRule(rule2); err != nil {
t.Errorf("failed to delete rule: %v", err) require.NoError(t, err, "failed to delete rule")
} }
checkRuleSpecs(t, ipv4Client, false, rule2.(*Rule).specs...) checkRuleSpecs(t, ipv4Client, ChainInputFilterName, false, rule2.(*Rule).specs...)
}) })
t.Run("reset check", func(t *testing.T) { t.Run("reset check", func(t *testing.T) {
// add second rule // add second rule
ip := net.ParseIP("10.20.0.3") ip := net.ParseIP("10.20.0.3")
port := &fw.Port{Proto: fw.PortProtocolUDP, Values: []int{5353}} port := &fw.Port{Values: []int{5353}}
_, err = manager.AddFiltering(ip, port, fw.DirectionDst, fw.ActionAccept, "accept Fake DNS traffic") _, err = manager.AddFiltering(ip, "udp", nil, port, fw.RuleDirectionOUT, fw.ActionAccept, "accept Fake DNS traffic")
if err != nil { require.NoError(t, err, "failed to add rule")
t.Errorf("failed to add rule: %v", err)
}
if err := manager.Reset(); err != nil { err = manager.Reset()
t.Errorf("failed to reset: %v", err) require.NoError(t, err, "failed to reset")
}
ok, err := ipv4Client.ChainExists("filter", ChainFilterName) ok, err := ipv4Client.ChainExists("filter", ChainInputFilterName)
if err != nil { require.NoError(t, err, "failed check chain exists")
t.Errorf("failed to drop chain: %v", err)
}
if ok { if ok {
t.Errorf("chain '%v' still exists after Reset", ChainFilterName) require.NoErrorf(t, err, "chain '%v' still exists after Reset", ChainInputFilterName)
} }
}) })
} }
func checkRuleSpecs(t *testing.T, ipv4Client *iptables.IPTables, mustExists bool, rulespec ...string) { func checkRuleSpecs(t *testing.T, ipv4Client *iptables.IPTables, chainName string, mustExists bool, rulespec ...string) {
exists, err := ipv4Client.Exists("filter", ChainFilterName, rulespec...) exists, err := ipv4Client.Exists("filter", chainName, rulespec...)
if err != nil { require.NoError(t, err, "failed to check rule")
t.Errorf("failed to check rule: %v", err) require.Falsef(t, !exists && mustExists, "rule '%v' does not exist", rulespec)
return require.Falsef(t, exists && !mustExists, "rule '%v' exist", rulespec)
} }
if !exists && mustExists { func TestIptablesCreatePerformance(t *testing.T) {
t.Errorf("rule '%v' does not exist", rulespec) for _, testMax := range []int{10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000} {
return t.Run(fmt.Sprintf("Testing %d rules", testMax), func(t *testing.T) {
} // just check on the local interface
if exists && !mustExists { manager, err := Create("lo")
t.Errorf("rule '%v' exist", rulespec) require.NoError(t, err)
return time.Sleep(time.Second)
defer func() {
if err := manager.Reset(); err != nil {
t.Errorf("clear the manager state: %v", err)
}
time.Sleep(time.Second)
}()
_, err = manager.client(net.ParseIP("10.20.0.100"))
require.NoError(t, err)
ip := net.ParseIP("10.20.0.100")
start := time.Now()
for i := 0; i < testMax; i++ {
port := &fw.Port{Values: []int{1000 + i}}
if i%2 == 0 {
_, err = manager.AddFiltering(ip, "tcp", nil, port, fw.RuleDirectionOUT, fw.ActionAccept, "accept HTTP traffic")
} else {
_, err = manager.AddFiltering(ip, "tcp", nil, port, fw.RuleDirectionIN, fw.ActionAccept, "accept HTTP traffic")
}
require.NoError(t, err, "failed to add rule")
}
t.Logf("execution avg per rule: %s", time.Since(start)/time.Duration(testMax))
})
} }
} }

View File

@ -4,6 +4,7 @@ package iptables
type Rule struct { type Rule struct {
id string id string
specs []string specs []string
dst bool
v6 bool v6 bool
} }

View File

@ -0,0 +1,435 @@
package nftables
import (
"bytes"
"encoding/binary"
"fmt"
"net"
"net/netip"
"strings"
"sync"
"github.com/google/nftables"
"github.com/google/nftables/expr"
"github.com/google/uuid"
"golang.org/x/sys/unix"
fw "github.com/netbirdio/netbird/client/firewall"
)
const (
// FilterTableName is the name of the table that is used for filtering by the Netbird client
FilterTableName = "netbird-acl"
// FilterInputChainName is the name of the chain that is used for filtering incoming packets
FilterInputChainName = "netbird-acl-input-filter"
// FilterOutputChainName is the name of the chain that is used for filtering outgoing packets
FilterOutputChainName = "netbird-acl-output-filter"
)
// Manager of iptables firewall
type Manager struct {
mutex sync.Mutex
conn *nftables.Conn
tableIPv4 *nftables.Table
tableIPv6 *nftables.Table
filterInputChainIPv4 *nftables.Chain
filterOutputChainIPv4 *nftables.Chain
filterInputChainIPv6 *nftables.Chain
filterOutputChainIPv6 *nftables.Chain
wgIfaceName string
}
// Create nftables firewall manager
func Create(wgIfaceName string) (*Manager, error) {
m := &Manager{
conn: &nftables.Conn{},
wgIfaceName: wgIfaceName,
}
if err := m.Reset(); err != nil {
return nil, err
}
return m, nil
}
// AddFiltering rule to the firewall
//
// If comment argument is empty firewall manager should set
// rule ID as comment for the rule
func (m *Manager) AddFiltering(
ip net.IP,
proto fw.Protocol,
sPort *fw.Port,
dPort *fw.Port,
direction fw.RuleDirection,
action fw.Action,
comment string,
) (fw.Rule, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
var (
err error
table *nftables.Table
chain *nftables.Chain
)
if direction == fw.RuleDirectionOUT {
table, chain, err = m.chain(
ip,
FilterOutputChainName,
nftables.ChainHookOutput,
nftables.ChainPriorityFilter,
nftables.ChainTypeFilter)
} else {
table, chain, err = m.chain(
ip,
FilterInputChainName,
nftables.ChainHookInput,
nftables.ChainPriorityFilter,
nftables.ChainTypeFilter)
}
if err != nil {
return nil, err
}
ifaceKey := expr.MetaKeyIIFNAME
if direction == fw.RuleDirectionOUT {
ifaceKey = expr.MetaKeyOIFNAME
}
expressions := []expr.Any{
&expr.Meta{Key: ifaceKey, Register: 1},
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: ifname(m.wgIfaceName),
},
}
if proto != "all" {
expressions = append(expressions, &expr.Payload{
DestRegister: 1,
Base: expr.PayloadBaseNetworkHeader,
Offset: uint32(9),
Len: uint32(1),
})
var protoData []byte
switch proto {
case fw.ProtocolTCP:
protoData = []byte{unix.IPPROTO_TCP}
case fw.ProtocolUDP:
protoData = []byte{unix.IPPROTO_UDP}
case fw.ProtocolICMP:
protoData = []byte{unix.IPPROTO_ICMP}
default:
return nil, fmt.Errorf("unsupported protocol: %s", proto)
}
expressions = append(expressions, &expr.Cmp{
Register: 1,
Op: expr.CmpOpEq,
Data: protoData,
})
}
// source address position
var adrLen, adrOffset uint32
if ip.To4() == nil {
adrLen = 16
adrOffset = 8
} else {
adrLen = 4
adrOffset = 12
}
// change to destination address position if need
if direction == fw.RuleDirectionOUT {
adrOffset += adrLen
}
ipToAdd, _ := netip.AddrFromSlice(ip)
add := ipToAdd.Unmap()
expressions = append(expressions,
&expr.Payload{
DestRegister: 1,
Base: expr.PayloadBaseNetworkHeader,
Offset: adrOffset,
Len: adrLen,
},
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: add.AsSlice(),
},
)
if sPort != nil && len(sPort.Values) != 0 {
expressions = append(expressions,
&expr.Payload{
DestRegister: 1,
Base: expr.PayloadBaseTransportHeader,
Offset: 0,
Len: 2,
},
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: encodePort(*sPort),
},
)
}
if dPort != nil && len(dPort.Values) != 0 {
expressions = append(expressions,
&expr.Payload{
DestRegister: 1,
Base: expr.PayloadBaseTransportHeader,
Offset: 2,
Len: 2,
},
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: encodePort(*dPort),
},
)
}
if action == fw.ActionAccept {
expressions = append(expressions, &expr.Verdict{Kind: expr.VerdictAccept})
} else {
expressions = append(expressions, &expr.Verdict{Kind: expr.VerdictDrop})
}
id := uuid.New().String()
userData := []byte(strings.Join([]string{id, comment}, " "))
_ = m.conn.InsertRule(&nftables.Rule{
Table: table,
Chain: chain,
Position: 0,
Exprs: expressions,
UserData: userData,
})
if err := m.conn.Flush(); err != nil {
return nil, err
}
list, err := m.conn.GetRules(table, chain)
if err != nil {
return nil, err
}
// Add the rule to the chain
rule := &Rule{id: id}
for _, r := range list {
if bytes.Equal(r.UserData, userData) {
rule.Rule = r
break
}
}
if rule.Rule == nil {
return nil, fmt.Errorf("rule not found")
}
return rule, nil
}
// chain returns the chain for the given IP address with specific settings
func (m *Manager) chain(
ip net.IP,
name string,
hook nftables.ChainHook,
priority nftables.ChainPriority,
cType nftables.ChainType,
) (*nftables.Table, *nftables.Chain, error) {
var err error
getChain := func(c *nftables.Chain, tf nftables.TableFamily) (*nftables.Chain, error) {
if c != nil {
return c, nil
}
return m.createChainIfNotExists(tf, name, hook, priority, cType)
}
if ip.To4() != nil {
if name == FilterInputChainName {
m.filterInputChainIPv4, err = getChain(m.filterInputChainIPv4, nftables.TableFamilyIPv4)
return m.tableIPv4, m.filterInputChainIPv4, err
}
m.filterOutputChainIPv4, err = getChain(m.filterOutputChainIPv4, nftables.TableFamilyIPv4)
return m.tableIPv4, m.filterOutputChainIPv4, err
}
if name == FilterInputChainName {
m.filterInputChainIPv6, err = getChain(m.filterInputChainIPv6, nftables.TableFamilyIPv6)
return m.tableIPv4, m.filterInputChainIPv6, err
}
m.filterOutputChainIPv6, err = getChain(m.filterOutputChainIPv6, nftables.TableFamilyIPv6)
return m.tableIPv4, m.filterOutputChainIPv6, err
}
// table returns the table for the given family of the IP address
func (m *Manager) table(family nftables.TableFamily) (*nftables.Table, error) {
if family == nftables.TableFamilyIPv4 {
if m.tableIPv4 != nil {
return m.tableIPv4, nil
}
table, err := m.createTableIfNotExists(nftables.TableFamilyIPv4)
if err != nil {
return nil, err
}
m.tableIPv4 = table
return m.tableIPv4, nil
}
if m.tableIPv6 != nil {
return m.tableIPv6, nil
}
table, err := m.createTableIfNotExists(nftables.TableFamilyIPv6)
if err != nil {
return nil, err
}
m.tableIPv6 = table
return m.tableIPv6, nil
}
func (m *Manager) createTableIfNotExists(family nftables.TableFamily) (*nftables.Table, error) {
tables, err := m.conn.ListTablesOfFamily(family)
if err != nil {
return nil, fmt.Errorf("list of tables: %w", err)
}
for _, t := range tables {
if t.Name == FilterTableName {
return t, nil
}
}
return m.conn.AddTable(&nftables.Table{Name: FilterTableName, Family: nftables.TableFamilyIPv4}), nil
}
func (m *Manager) createChainIfNotExists(
family nftables.TableFamily,
name string,
hooknum nftables.ChainHook,
priority nftables.ChainPriority,
chainType nftables.ChainType,
) (*nftables.Chain, error) {
table, err := m.table(family)
if err != nil {
return nil, err
}
chains, err := m.conn.ListChainsOfTableFamily(family)
if err != nil {
return nil, fmt.Errorf("list of chains: %w", err)
}
for _, c := range chains {
if c.Name == name && c.Table.Name == table.Name {
return c, nil
}
}
polAccept := nftables.ChainPolicyAccept
chain := &nftables.Chain{
Name: name,
Table: table,
Hooknum: hooknum,
Priority: priority,
Type: chainType,
Policy: &polAccept,
}
chain = m.conn.AddChain(chain)
ifaceKey := expr.MetaKeyIIFNAME
if name == FilterOutputChainName {
ifaceKey = expr.MetaKeyOIFNAME
}
expressions := []expr.Any{
&expr.Meta{Key: ifaceKey, Register: 1},
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: ifname(m.wgIfaceName),
},
&expr.Verdict{Kind: expr.VerdictDrop},
}
_ = m.conn.AddRule(&nftables.Rule{
Table: table,
Chain: chain,
Exprs: expressions,
})
if err := m.conn.Flush(); err != nil {
return nil, err
}
return chain, nil
}
// DeleteRule from the firewall by rule definition
func (m *Manager) DeleteRule(rule fw.Rule) error {
nativeRule, ok := rule.(*Rule)
if !ok {
return fmt.Errorf("invalid rule type")
}
if err := m.conn.DelRule(nativeRule.Rule); err != nil {
return err
}
return m.conn.Flush()
}
// Reset firewall to the default state
func (m *Manager) Reset() error {
m.mutex.Lock()
defer m.mutex.Unlock()
chains, err := m.conn.ListChains()
if err != nil {
return fmt.Errorf("list of chains: %w", err)
}
for _, c := range chains {
if c.Name == FilterInputChainName || c.Name == FilterOutputChainName {
m.conn.DelChain(c)
}
}
tables, err := m.conn.ListTables()
if err != nil {
return fmt.Errorf("list of tables: %w", err)
}
for _, t := range tables {
if t.Name == FilterTableName {
m.conn.DelTable(t)
}
}
return m.conn.Flush()
}
func encodePort(port fw.Port) []byte {
bs := make([]byte, 2)
binary.BigEndian.PutUint16(bs, uint16(port.Values[0]))
return bs
}
func ifname(n string) []byte {
b := make([]byte, 16)
copy(b, []byte(n+"\x00"))
return b
}

View File

@ -0,0 +1,137 @@
package nftables
import (
"fmt"
"net"
"net/netip"
"testing"
"time"
"github.com/google/nftables"
"github.com/google/nftables/expr"
"github.com/stretchr/testify/require"
"golang.org/x/sys/unix"
fw "github.com/netbirdio/netbird/client/firewall"
)
func TestNftablesManager(t *testing.T) {
// just check on the local interface
manager, err := Create("lo")
require.NoError(t, err)
time.Sleep(time.Second)
defer func() {
err = manager.Reset()
require.NoError(t, err, "failed to reset")
time.Sleep(time.Second)
}()
ip := net.ParseIP("100.96.0.1")
testClient := &nftables.Conn{}
rule, err := manager.AddFiltering(
ip,
fw.ProtocolTCP,
nil,
&fw.Port{Values: []int{53}},
fw.RuleDirectionIN,
fw.ActionDrop,
"",
)
require.NoError(t, err, "failed to add rule")
rules, err := testClient.GetRules(manager.tableIPv4, manager.filterInputChainIPv4)
require.NoError(t, err, "failed to get rules")
// 1 regular rule and other "drop all rule" for the interface
require.Len(t, rules, 2, "expected 1 rule")
ipToAdd, _ := netip.AddrFromSlice(ip)
add := ipToAdd.Unmap()
expectedExprs := []expr.Any{
&expr.Meta{Key: expr.MetaKeyIIFNAME, Register: 1},
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: ifname("lo"),
},
&expr.Payload{
DestRegister: 1,
Base: expr.PayloadBaseNetworkHeader,
Offset: uint32(9),
Len: uint32(1),
},
&expr.Cmp{
Register: 1,
Op: expr.CmpOpEq,
Data: []byte{unix.IPPROTO_TCP},
},
&expr.Payload{
DestRegister: 1,
Base: expr.PayloadBaseNetworkHeader,
Offset: 12,
Len: 4,
},
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: add.AsSlice(),
},
&expr.Payload{
DestRegister: 1,
Base: expr.PayloadBaseTransportHeader,
Offset: 2,
Len: 2,
},
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: []byte{0, 53},
},
&expr.Verdict{Kind: expr.VerdictDrop},
}
require.ElementsMatch(t, rules[0].Exprs, expectedExprs, "expected the same expressions")
err = manager.DeleteRule(rule)
require.NoError(t, err, "failed to delete rule")
rules, err = testClient.GetRules(manager.tableIPv4, manager.filterInputChainIPv4)
require.NoError(t, err, "failed to get rules")
require.Len(t, rules, 1, "expected 1 rules after deleteion")
err = manager.Reset()
require.NoError(t, err, "failed to reset")
}
func TestNFtablesCreatePerformance(t *testing.T) {
for _, testMax := range []int{10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000} {
t.Run(fmt.Sprintf("Testing %d rules", testMax), func(t *testing.T) {
// just check on the local interface
manager, err := Create("lo")
require.NoError(t, err)
time.Sleep(time.Second)
defer func() {
if err := manager.Reset(); err != nil {
t.Errorf("clear the manager state: %v", err)
}
time.Sleep(time.Second)
}()
ip := net.ParseIP("10.20.0.100")
start := time.Now()
for i := 0; i < testMax; i++ {
port := &fw.Port{Values: []int{1000 + i}}
if i%2 == 0 {
_, err = manager.AddFiltering(ip, "tcp", nil, port, fw.RuleDirectionOUT, fw.ActionAccept, "accept HTTP traffic")
} else {
_, err = manager.AddFiltering(ip, "tcp", nil, port, fw.RuleDirectionIN, fw.ActionAccept, "accept HTTP traffic")
}
require.NoError(t, err, "failed to add rule")
}
t.Logf("execution avg per rule: %s", time.Since(start)/time.Duration(testMax))
})
}
}

View File

@ -0,0 +1,16 @@
package nftables
import (
"github.com/google/nftables"
)
// Rule to handle management of rules
type Rule struct {
*nftables.Rule
id string
}
// GetRuleID returns the rule id
func (r *Rule) GetRuleID() string {
return r.id
}

View File

@ -1,14 +1,23 @@
package firewall package firewall
// PortProtocol is the protocol of the port // Protocol is the protocol of the port
type PortProtocol string type Protocol string
const ( const (
// PortProtocolTCP is the TCP protocol // ProtocolTCP is the TCP protocol
PortProtocolTCP PortProtocol = "tcp" ProtocolTCP Protocol = "tcp"
// PortProtocolUDP is the UDP protocol // ProtocolUDP is the UDP protocol
PortProtocolUDP PortProtocol = "udp" ProtocolUDP Protocol = "udp"
// ProtocolICMP is the ICMP protocol
ProtocolICMP Protocol = "icmp"
// ProtocolALL cover all supported protocols
ProtocolALL Protocol = "all"
// ProtocolUnknown unknown protocol
ProtocolUnknown Protocol = "unknown"
) )
// Port of the address for firewall rule // Port of the address for firewall rule
@ -18,7 +27,4 @@ type Port struct {
// Values contains one value for single port, multiple values for the list of ports, or two values for the range of ports // Values contains one value for single port, multiple values for the list of ports, or two values for the range of ports
Values []int Values []int
// Proto is the protocol of the port
Proto PortProtocol
} }

View File

@ -0,0 +1,27 @@
package uspfilter
import (
"net"
"github.com/google/gopacket"
fw "github.com/netbirdio/netbird/client/firewall"
)
// Rule to handle management of rules
type Rule struct {
id string
ip net.IP
ipLayer gopacket.LayerType
protoLayer gopacket.LayerType
direction fw.RuleDirection
sPort uint16
dPort uint16
drop bool
comment string
}
// GetRuleID returns the rule id
func (r *Rule) GetRuleID() string {
return r.id
}

View File

@ -0,0 +1,291 @@
package uspfilter
import (
"fmt"
"net"
"sync"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
fw "github.com/netbirdio/netbird/client/firewall"
"github.com/netbirdio/netbird/iface"
)
const layerTypeAll = 0
// IFaceMapper defines subset methods of interface required for manager
type IFaceMapper interface {
SetFiltering(iface.PacketFilter) error
}
// Manager userspace firewall manager
type Manager struct {
outgoingRules []Rule
incomingRules []Rule
rulesIndex map[string]int
wgNetwork *net.IPNet
decoders sync.Pool
mutex sync.RWMutex
}
// decoder for packages
type decoder struct {
eth layers.Ethernet
ip4 layers.IPv4
ip6 layers.IPv6
tcp layers.TCP
udp layers.UDP
icmp4 layers.ICMPv4
icmp6 layers.ICMPv6
decoded []gopacket.LayerType
parser *gopacket.DecodingLayerParser
}
// Create userspace firewall manager constructor
func Create(iface IFaceMapper) (*Manager, error) {
m := &Manager{
rulesIndex: make(map[string]int),
decoders: sync.Pool{
New: func() any {
d := &decoder{
decoded: []gopacket.LayerType{},
}
d.parser = gopacket.NewDecodingLayerParser(
layers.LayerTypeIPv4,
&d.eth, &d.ip4, &d.ip6, &d.icmp4, &d.icmp6, &d.tcp, &d.udp,
)
d.parser.IgnoreUnsupported = true
return d
},
},
}
if err := iface.SetFiltering(m); err != nil {
return nil, err
}
return m, nil
}
// AddFiltering rule to the firewall
//
// If comment argument is empty firewall manager should set
// rule ID as comment for the rule
func (m *Manager) AddFiltering(
ip net.IP,
proto fw.Protocol,
sPort *fw.Port,
dPort *fw.Port,
direction fw.RuleDirection,
action fw.Action,
comment string,
) (fw.Rule, error) {
r := Rule{
id: uuid.New().String(),
ip: ip,
ipLayer: layers.LayerTypeIPv6,
direction: direction,
drop: action == fw.ActionDrop,
comment: comment,
}
if ipNormalized := ip.To4(); ipNormalized != nil {
r.ipLayer = layers.LayerTypeIPv4
r.ip = ipNormalized
}
if sPort != nil && len(sPort.Values) == 1 {
r.sPort = uint16(sPort.Values[0])
}
if dPort != nil && len(dPort.Values) == 1 {
r.dPort = uint16(dPort.Values[0])
}
switch proto {
case fw.ProtocolTCP:
r.protoLayer = layers.LayerTypeTCP
case fw.ProtocolUDP:
r.protoLayer = layers.LayerTypeUDP
case fw.ProtocolICMP:
r.protoLayer = layers.LayerTypeICMPv4
if r.ipLayer == layers.LayerTypeIPv6 {
r.protoLayer = layers.LayerTypeICMPv6
}
case fw.ProtocolALL:
r.protoLayer = layerTypeAll
}
m.mutex.Lock()
var p int
if direction == fw.RuleDirectionIN {
m.incomingRules = append(m.incomingRules, r)
p = len(m.incomingRules) - 1
} else {
m.outgoingRules = append(m.outgoingRules, r)
p = len(m.outgoingRules) - 1
}
m.rulesIndex[r.id] = p
m.mutex.Unlock()
return &r, nil
}
// DeleteRule from the firewall by rule definition
func (m *Manager) DeleteRule(rule fw.Rule) error {
m.mutex.Lock()
defer m.mutex.Unlock()
r, ok := rule.(*Rule)
if !ok {
return fmt.Errorf("delete rule: invalid rule type: %T", rule)
}
p, ok := m.rulesIndex[r.id]
if !ok {
return fmt.Errorf("delete rule: no rule with such id: %v", r.id)
}
delete(m.rulesIndex, r.id)
var toUpdate []Rule
if r.direction == fw.RuleDirectionIN {
m.incomingRules = append(m.incomingRules[:p], m.incomingRules[p+1:]...)
toUpdate = m.incomingRules
} else {
m.outgoingRules = append(m.outgoingRules[:p], m.outgoingRules[p+1:]...)
toUpdate = m.outgoingRules
}
for i := 0; i < len(toUpdate); i++ {
m.rulesIndex[toUpdate[i].id] = i
}
return nil
}
// Reset firewall to the default state
func (m *Manager) Reset() error {
m.mutex.Lock()
defer m.mutex.Unlock()
m.outgoingRules = m.outgoingRules[:0]
m.incomingRules = m.incomingRules[:0]
m.rulesIndex = make(map[string]int)
return nil
}
// DropOutgoing filter outgoing packets
func (m *Manager) DropOutgoing(packetData []byte) bool {
return m.dropFilter(packetData, m.outgoingRules, false)
}
// DropIncoming filter incoming packets
func (m *Manager) DropIncoming(packetData []byte) bool {
return m.dropFilter(packetData, m.incomingRules, true)
}
// dropFilter imlements same logic for booth direction of the traffic
func (m *Manager) dropFilter(packetData []byte, rules []Rule, isIncomingPacket bool) bool {
m.mutex.RLock()
defer m.mutex.RUnlock()
d := m.decoders.Get().(*decoder)
defer m.decoders.Put(d)
if err := d.parser.DecodeLayers(packetData, &d.decoded); err != nil {
log.Tracef("couldn't decode layer, err: %s", err)
return true
}
if len(d.decoded) < 2 {
log.Tracef("not enough levels in network packet")
return true
}
ipLayer := d.decoded[0]
switch ipLayer {
case layers.LayerTypeIPv4:
if !m.wgNetwork.Contains(d.ip4.SrcIP) || !m.wgNetwork.Contains(d.ip4.DstIP) {
return false
}
case layers.LayerTypeIPv6:
if !m.wgNetwork.Contains(d.ip6.SrcIP) || !m.wgNetwork.Contains(d.ip6.DstIP) {
return false
}
default:
log.Errorf("unknown layer: %v", d.decoded[0])
return true
}
payloadLayer := d.decoded[1]
// check if IP address match by IP
for _, rule := range rules {
switch ipLayer {
case layers.LayerTypeIPv4:
if isIncomingPacket {
if !d.ip4.SrcIP.Equal(rule.ip) {
continue
}
} else {
if !d.ip4.DstIP.Equal(rule.ip) {
continue
}
}
case layers.LayerTypeIPv6:
if isIncomingPacket {
if !d.ip6.SrcIP.Equal(rule.ip) {
continue
}
} else {
if !d.ip6.DstIP.Equal(rule.ip) {
continue
}
}
}
if rule.protoLayer == layerTypeAll {
return rule.drop
}
if payloadLayer != rule.protoLayer {
continue
}
switch payloadLayer {
case layers.LayerTypeTCP:
if rule.sPort == 0 && rule.dPort == 0 {
return rule.drop
}
if rule.sPort != 0 && rule.sPort == uint16(d.tcp.SrcPort) {
return rule.drop
}
if rule.dPort != 0 && rule.dPort == uint16(d.tcp.DstPort) {
return rule.drop
}
case layers.LayerTypeUDP:
if rule.sPort == 0 && rule.dPort == 0 {
return rule.drop
}
if rule.sPort != 0 && rule.sPort == uint16(d.udp.SrcPort) {
return rule.drop
}
if rule.dPort != 0 && rule.dPort == uint16(d.udp.DstPort) {
return rule.drop
}
return rule.drop
case layers.LayerTypeICMPv4, layers.LayerTypeICMPv6:
return rule.drop
}
}
// default policy is DROP ALL
return true
}
// SetNetwork of the wireguard interface to which filtering applied
func (m *Manager) SetNetwork(network *net.IPNet) {
m.wgNetwork = network
}

View File

@ -0,0 +1,207 @@
package uspfilter
import (
"fmt"
"net"
"testing"
"time"
"github.com/stretchr/testify/require"
fw "github.com/netbirdio/netbird/client/firewall"
"github.com/netbirdio/netbird/iface"
)
type IFaceMock struct {
SetFilteringFunc func(iface.PacketFilter) error
}
func (i *IFaceMock) SetFiltering(iface iface.PacketFilter) error {
if i.SetFilteringFunc == nil {
return fmt.Errorf("not implemented")
}
return i.SetFilteringFunc(iface)
}
func TestManagerCreate(t *testing.T) {
ifaceMock := &IFaceMock{
SetFilteringFunc: func(iface.PacketFilter) error { return nil },
}
m, err := Create(ifaceMock)
if err != nil {
t.Errorf("failed to create Manager: %v", err)
return
}
if m == nil {
t.Error("Manager is nil")
}
}
func TestManagerAddFiltering(t *testing.T) {
isSetFilteringCalled := false
ifaceMock := &IFaceMock{
SetFilteringFunc: func(iface.PacketFilter) error {
isSetFilteringCalled = true
return nil
},
}
m, err := Create(ifaceMock)
if err != nil {
t.Errorf("failed to create Manager: %v", err)
return
}
ip := net.ParseIP("192.168.1.1")
proto := fw.ProtocolTCP
port := &fw.Port{Values: []int{80}}
direction := fw.RuleDirectionOUT
action := fw.ActionDrop
comment := "Test rule"
rule, err := m.AddFiltering(ip, proto, nil, port, direction, action, comment)
if err != nil {
t.Errorf("failed to add filtering: %v", err)
return
}
if rule == nil {
t.Error("Rule is nil")
return
}
if !isSetFilteringCalled {
t.Error("SetFiltering was not called")
return
}
}
func TestManagerDeleteRule(t *testing.T) {
ifaceMock := &IFaceMock{
SetFilteringFunc: func(iface.PacketFilter) error { return nil },
}
m, err := Create(ifaceMock)
if err != nil {
t.Errorf("failed to create Manager: %v", err)
return
}
ip := net.ParseIP("192.168.1.1")
proto := fw.ProtocolTCP
port := &fw.Port{Values: []int{80}}
direction := fw.RuleDirectionOUT
action := fw.ActionDrop
comment := "Test rule"
rule, err := m.AddFiltering(ip, proto, nil, port, direction, action, comment)
if err != nil {
t.Errorf("failed to add filtering: %v", err)
return
}
ip = net.ParseIP("192.168.1.1")
proto = fw.ProtocolTCP
port = &fw.Port{Values: []int{80}}
direction = fw.RuleDirectionIN
action = fw.ActionDrop
comment = "Test rule 2"
rule2, err := m.AddFiltering(ip, proto, nil, port, direction, action, comment)
if err != nil {
t.Errorf("failed to add filtering: %v", err)
return
}
err = m.DeleteRule(rule)
if err != nil {
t.Errorf("failed to delete rule: %v", err)
return
}
if idx, ok := m.rulesIndex[rule2.GetRuleID()]; !ok || len(m.incomingRules) != 1 || idx != 0 {
t.Errorf("rule2 is not in the rulesIndex")
}
err = m.DeleteRule(rule2)
if err != nil {
t.Errorf("failed to delete rule: %v", err)
return
}
if len(m.rulesIndex) != 0 || len(m.incomingRules) != 0 {
t.Errorf("rule1 still in the rulesIndex")
}
}
func TestManagerReset(t *testing.T) {
ifaceMock := &IFaceMock{
SetFilteringFunc: func(iface.PacketFilter) error { return nil },
}
m, err := Create(ifaceMock)
if err != nil {
t.Errorf("failed to create Manager: %v", err)
return
}
ip := net.ParseIP("192.168.1.1")
proto := fw.ProtocolTCP
port := &fw.Port{Values: []int{80}}
direction := fw.RuleDirectionOUT
action := fw.ActionDrop
comment := "Test rule"
_, err = m.AddFiltering(ip, proto, nil, port, direction, action, comment)
if err != nil {
t.Errorf("failed to add filtering: %v", err)
return
}
err = m.Reset()
if err != nil {
t.Errorf("failed to reset Manager: %v", err)
return
}
if len(m.rulesIndex) != 0 || len(m.outgoingRules) != 0 || len(m.incomingRules) != 0 {
t.Errorf("rules is not empty")
}
}
func TestUSPFilterCreatePerformance(t *testing.T) {
for _, testMax := range []int{10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000} {
t.Run(fmt.Sprintf("Testing %d rules", testMax), func(t *testing.T) {
// just check on the local interface
ifaceMock := &IFaceMock{
SetFilteringFunc: func(iface.PacketFilter) error { return nil },
}
manager, err := Create(ifaceMock)
require.NoError(t, err)
time.Sleep(time.Second)
defer func() {
if err := manager.Reset(); err != nil {
t.Errorf("clear the manager state: %v", err)
}
time.Sleep(time.Second)
}()
ip := net.ParseIP("10.20.0.100")
start := time.Now()
for i := 0; i < testMax; i++ {
port := &fw.Port{Values: []int{1000 + i}}
if i%2 == 0 {
_, err = manager.AddFiltering(ip, "tcp", nil, port, fw.RuleDirectionOUT, fw.ActionAccept, "accept HTTP traffic")
} else {
_, err = manager.AddFiltering(ip, "tcp", nil, port, fw.RuleDirectionIN, fw.ActionAccept, "accept HTTP traffic")
}
require.NoError(t, err, "failed to add rule")
}
t.Logf("execution avg per rule: %s", time.Since(start)/time.Duration(testMax))
})
}
}

View File

@ -0,0 +1,209 @@
package acl
import (
"fmt"
"net"
"strconv"
"sync"
log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/client/firewall"
"github.com/netbirdio/netbird/iface"
mgmProto "github.com/netbirdio/netbird/management/proto"
)
// iFaceMapper defines subset methods of interface required for manager
type iFaceMapper interface {
Name() string
IsUserspaceBind() bool
SetFiltering(iface.PacketFilter) error
}
// Manager is a ACL rules manager
type Manager interface {
ApplyFiltering(rules []*mgmProto.FirewallRule)
Stop()
}
// DefaultManager uses firewall manager to handle
type DefaultManager struct {
manager firewall.Manager
rulesPairs map[string][]firewall.Rule
mutex sync.Mutex
}
// ApplyFiltering firewall rules to the local firewall manager processed by ACL policy.
func (d *DefaultManager) ApplyFiltering(rules []*mgmProto.FirewallRule) {
d.mutex.Lock()
defer d.mutex.Unlock()
if d.manager == nil {
log.Debug("firewall manager is not supported, skipping firewall rules")
return
}
var (
applyFailed bool
newRulePairs = make(map[string][]firewall.Rule)
)
for _, r := range rules {
rules, err := d.protoRuleToFirewallRule(r)
if err != nil {
log.Errorf("failed to apply firewall rule: %+v, %v", r, err)
applyFailed = true
break
}
newRulePairs[rules[0].GetRuleID()] = rules
}
if applyFailed {
log.Error("failed to apply firewall rules, rollback ACL to previous state")
for _, rules := range newRulePairs {
for _, rule := range rules {
if err := d.manager.DeleteRule(rule); err != nil {
log.Errorf("failed to delete new firewall rule (id: %v) during rollback: %v", rule.GetRuleID(), err)
continue
}
}
}
return
}
for pairID, rules := range d.rulesPairs {
if _, ok := newRulePairs[pairID]; !ok {
for _, rule := range rules {
if err := d.manager.DeleteRule(rule); err != nil {
log.Errorf("failed to delete firewall rule: %v", err)
continue
}
}
delete(d.rulesPairs, pairID)
}
}
d.rulesPairs = newRulePairs
}
// Stop ACL controller and clear firewall state
func (d *DefaultManager) Stop() {
d.mutex.Lock()
defer d.mutex.Unlock()
if err := d.manager.Reset(); err != nil {
log.WithError(err).Error("reset firewall state")
}
}
func (d *DefaultManager) protoRuleToFirewallRule(r *mgmProto.FirewallRule) ([]firewall.Rule, error) {
ip := net.ParseIP(r.PeerIP)
if ip == nil {
return nil, fmt.Errorf("invalid IP address, skipping firewall rule")
}
protocol := convertToFirewallProtocol(r.Protocol)
if protocol == firewall.ProtocolUnknown {
return nil, fmt.Errorf("invalid protocol type: %d, skipping firewall rule", r.Protocol)
}
action := convertFirewallAction(r.Action)
if action == firewall.ActionUnknown {
return nil, fmt.Errorf("invalid action type: %d, skipping firewall rule", r.Action)
}
var port *firewall.Port
if r.Port != "" {
value, err := strconv.Atoi(r.Port)
if err != nil {
return nil, fmt.Errorf("invalid port, skipping firewall rule")
}
port = &firewall.Port{
Values: []int{value},
}
}
var rules []firewall.Rule
var err error
switch r.Direction {
case mgmProto.FirewallRule_IN:
rules, err = d.addInRules(ip, protocol, port, action, "")
case mgmProto.FirewallRule_OUT:
rules, err = d.addOutRules(ip, protocol, port, action, "")
default:
return nil, fmt.Errorf("invalid direction, skipping firewall rule")
}
if err != nil {
return nil, err
}
d.rulesPairs[rules[0].GetRuleID()] = rules
return rules, nil
}
func (d *DefaultManager) addInRules(ip net.IP, protocol firewall.Protocol, port *firewall.Port, action firewall.Action, comment string) ([]firewall.Rule, error) {
var rules []firewall.Rule
rule, err := d.manager.AddFiltering(ip, protocol, nil, port, firewall.RuleDirectionIN, action, comment)
if err != nil {
return nil, fmt.Errorf("failed to add firewall rule: %v", err)
}
rules = append(rules, rule)
if shouldSkipInvertedRule(protocol) {
return rules, nil
}
rule, err = d.manager.AddFiltering(ip, protocol, port, nil, firewall.RuleDirectionOUT, action, comment)
if err != nil {
return nil, fmt.Errorf("failed to add firewall rule: %v", err)
}
return append(rules, rule), nil
}
func (d *DefaultManager) addOutRules(ip net.IP, protocol firewall.Protocol, port *firewall.Port, action firewall.Action, comment string) ([]firewall.Rule, error) {
var rules []firewall.Rule
rule, err := d.manager.AddFiltering(ip, protocol, nil, port, firewall.RuleDirectionOUT, action, comment)
if err != nil {
return nil, fmt.Errorf("failed to add firewall rule: %v", err)
}
rules = append(rules, rule)
if shouldSkipInvertedRule(protocol) {
return rules, nil
}
rule, err = d.manager.AddFiltering(ip, protocol, port, nil, firewall.RuleDirectionIN, action, comment)
if err != nil {
return nil, fmt.Errorf("failed to add firewall rule: %v", err)
}
return append(rules, rule), nil
}
func convertToFirewallProtocol(protocol mgmProto.FirewallRuleProtocol) firewall.Protocol {
switch protocol {
case mgmProto.FirewallRule_TCP:
return firewall.ProtocolTCP
case mgmProto.FirewallRule_UDP:
return firewall.ProtocolUDP
case mgmProto.FirewallRule_ICMP:
return firewall.ProtocolICMP
case mgmProto.FirewallRule_ALL:
return firewall.ProtocolALL
default:
return firewall.ProtocolUnknown
}
}
func shouldSkipInvertedRule(protocol firewall.Protocol) bool {
return protocol == firewall.ProtocolALL || protocol == firewall.ProtocolICMP
}
func convertFirewallAction(action mgmProto.FirewallRuleAction) firewall.Action {
switch action {
case mgmProto.FirewallRule_ACCEPT:
return firewall.ActionAccept
case mgmProto.FirewallRule_DROP:
return firewall.ActionDrop
default:
return firewall.ActionUnknown
}
}

View File

@ -0,0 +1,27 @@
//go:build !linux
package acl
import (
"fmt"
"runtime"
"github.com/netbirdio/netbird/client/firewall"
"github.com/netbirdio/netbird/client/firewall/uspfilter"
)
// Create creates a firewall manager instance
func Create(iface iFaceMapper) (manager *DefaultManager, err error) {
if iface.IsUserspaceBind() {
// use userspace packet filtering firewall
fm, err := uspfilter.Create(iface)
if err != nil {
return nil, err
}
return &DefaultManager{
manager: fm,
rulesPairs: make(map[string][]firewall.Rule),
}, nil
}
return nil, fmt.Errorf("not implemented for this OS: %s", runtime.GOOS)
}

View File

@ -0,0 +1,36 @@
package acl
import (
log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/client/firewall"
"github.com/netbirdio/netbird/client/firewall/iptables"
"github.com/netbirdio/netbird/client/firewall/nftables"
"github.com/netbirdio/netbird/client/firewall/uspfilter"
)
// Create creates a firewall manager instance for the Linux
func Create(iface iFaceMapper) (manager *DefaultManager, err error) {
var fm firewall.Manager
if iface.IsUserspaceBind() {
// use userspace packet filtering firewall
if fm, err = uspfilter.Create(iface); err != nil {
log.Debugf("failed to create userspace filtering firewall: %s", err)
return nil, err
}
} else {
if fm, err = iptables.Create(iface.Name()); err != nil {
log.Debugf("failed to create iptables manager: %s", err)
// fallback to nftables
if fm, err = nftables.Create(iface.Name()); err != nil {
log.Errorf("failed to create nftables manager: %s", err)
return nil, err
}
}
}
return &DefaultManager{
manager: fm,
rulesPairs: make(map[string][]firewall.Rule),
}, nil
}

View File

@ -0,0 +1,92 @@
package acl
import (
"runtime"
"testing"
"github.com/golang/mock/gomock"
"github.com/netbirdio/netbird/client/internal/acl/mocks"
mgmProto "github.com/netbirdio/netbird/management/proto"
)
func TestDefaultManager(t *testing.T) {
// TODO: enable when other platform will be added
if runtime.GOOS != "linux" {
t.Skipf("ACL manager not supported in the: %s", runtime.GOOS)
return
}
fwRules := []*mgmProto.FirewallRule{
{
PeerIP: "10.93.0.1",
Direction: mgmProto.FirewallRule_OUT,
Action: mgmProto.FirewallRule_ACCEPT,
Protocol: mgmProto.FirewallRule_TCP,
Port: "80",
},
{
PeerIP: "10.93.0.2",
Direction: mgmProto.FirewallRule_OUT,
Action: mgmProto.FirewallRule_DROP,
Protocol: mgmProto.FirewallRule_UDP,
Port: "53",
},
}
ctrl := gomock.NewController(t)
defer ctrl.Finish()
iface := mocks.NewMockIFaceMapper(ctrl)
iface.EXPECT().IsUserspaceBind().Return(false)
iface.EXPECT().Name().Return("lo")
// we receive one rule from the management so for testing purposes ignore it
acl, err := Create(iface)
if err != nil {
t.Errorf("create ACL manager: %v", err)
return
}
defer acl.Stop()
t.Run("apply firewall rules", func(t *testing.T) {
acl.ApplyFiltering(fwRules)
if len(acl.rulesPairs) != 2 {
t.Errorf("firewall rules not applied: %v", acl.rulesPairs)
return
}
})
t.Run("add extra rules", func(t *testing.T) {
// remove first rule
fwRules = fwRules[1:]
fwRules = append(fwRules, &mgmProto.FirewallRule{
PeerIP: "10.93.0.3",
Direction: mgmProto.FirewallRule_IN,
Action: mgmProto.FirewallRule_DROP,
Protocol: mgmProto.FirewallRule_ICMP,
})
existedRulesID := map[string]struct{}{}
for id := range acl.rulesPairs {
existedRulesID[id] = struct{}{}
}
acl.ApplyFiltering(fwRules)
// we should have one old and one new rule in the existed rules
if len(acl.rulesPairs) != 2 {
t.Errorf("firewall rules not applied")
return
}
// check that old rules was removed
for id := range existedRulesID {
if _, ok := acl.rulesPairs[id]; ok {
t.Errorf("old rule was not removed")
return
}
}
})
}

View File

@ -0,0 +1,77 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/netbirdio/netbird/client/internal/acl (interfaces: IFaceMapper)
// Package mocks is a generated GoMock package.
package mocks
import (
reflect "reflect"
gomock "github.com/golang/mock/gomock"
iface "github.com/netbirdio/netbird/iface"
)
// MockIFaceMapper is a mock of IFaceMapper interface.
type MockIFaceMapper struct {
ctrl *gomock.Controller
recorder *MockIFaceMapperMockRecorder
}
// MockIFaceMapperMockRecorder is the mock recorder for MockIFaceMapper.
type MockIFaceMapperMockRecorder struct {
mock *MockIFaceMapper
}
// NewMockIFaceMapper creates a new mock instance.
func NewMockIFaceMapper(ctrl *gomock.Controller) *MockIFaceMapper {
mock := &MockIFaceMapper{ctrl: ctrl}
mock.recorder = &MockIFaceMapperMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockIFaceMapper) EXPECT() *MockIFaceMapperMockRecorder {
return m.recorder
}
// IsUserspaceBind mocks base method.
func (m *MockIFaceMapper) IsUserspaceBind() bool {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "IsUserspaceBind")
ret0, _ := ret[0].(bool)
return ret0
}
// IsUserspaceBind indicates an expected call of IsUserspaceBind.
func (mr *MockIFaceMapperMockRecorder) IsUserspaceBind() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsUserspaceBind", reflect.TypeOf((*MockIFaceMapper)(nil).IsUserspaceBind))
}
// Name mocks base method.
func (m *MockIFaceMapper) Name() string {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Name")
ret0, _ := ret[0].(string)
return ret0
}
// Name indicates an expected call of Name.
func (mr *MockIFaceMapperMockRecorder) Name() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*MockIFaceMapper)(nil).Name))
}
// SetFiltering mocks base method.
func (m *MockIFaceMapper) SetFiltering(arg0 iface.PacketFilter) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SetFiltering", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// SetFiltering indicates an expected call of SetFiltering.
func (mr *MockIFaceMapperMockRecorder) SetFiltering(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetFiltering", reflect.TypeOf((*MockIFaceMapper)(nil).SetFiltering), arg0)
}

View File

@ -17,6 +17,7 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"github.com/netbirdio/netbird/client/internal/acl"
"github.com/netbirdio/netbird/client/internal/dns" "github.com/netbirdio/netbird/client/internal/dns"
"github.com/netbirdio/netbird/client/internal/peer" "github.com/netbirdio/netbird/client/internal/peer"
"github.com/netbirdio/netbird/client/internal/proxy" "github.com/netbirdio/netbird/client/internal/proxy"
@ -114,6 +115,7 @@ type Engine struct {
statusRecorder *peer.Status statusRecorder *peer.Status
routeManager routemanager.Manager routeManager routemanager.Manager
acl acl.Manager
dnsServer dns.Server dnsServer dns.Server
} }
@ -222,6 +224,12 @@ func (e *Engine) Start() error {
e.routeManager = routemanager.NewManager(e.ctx, e.config.WgPrivateKey.PublicKey().String(), e.wgInterface, e.statusRecorder) e.routeManager = routemanager.NewManager(e.ctx, e.config.WgPrivateKey.PublicKey().String(), e.wgInterface, e.statusRecorder)
if acl, err := acl.Create(e.wgInterface); err != nil {
log.Errorf("failed to create ACL manager, policy will not work: %s", err.Error())
} else {
e.acl = acl
}
if e.dnsServer == nil { if e.dnsServer == nil {
// todo fix custom address // todo fix custom address
dnsServer, err := dns.NewDefaultServer(e.ctx, e.wgInterface, e.config.CustomDNSAddress) dnsServer, err := dns.NewDefaultServer(e.ctx, e.wgInterface, e.config.CustomDNSAddress)
@ -622,6 +630,9 @@ func (e *Engine) updateNetworkMap(networkMap *mgmProto.NetworkMap) error {
log.Errorf("failed to update dns server, err: %v", err) log.Errorf("failed to update dns server, err: %v", err)
} }
if e.acl != nil {
e.acl.ApplyFiltering(networkMap.FirewallRules)
}
e.networkSerial = serial e.networkSerial = serial
return nil return nil
} }
@ -1005,6 +1016,9 @@ func (e *Engine) close() {
e.dnsServer.Stop() e.dnsServer.Stop()
} }
if e.acl != nil {
e.acl.Stop()
}
} }
func findIPFromInterfaceName(ifaceName string) (net.IP, error) { func findIPFromInterfaceName(ifaceName string) (net.IP, error) {

View File

@ -14,9 +14,6 @@ import (
"time" "time"
"github.com/pion/transport/v2/stdnet" "github.com/pion/transport/v2/stdnet"
"github.com/netbirdio/netbird/iface/bind"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -31,6 +28,7 @@ import (
"github.com/netbirdio/netbird/client/system" "github.com/netbirdio/netbird/client/system"
nbdns "github.com/netbirdio/netbird/dns" nbdns "github.com/netbirdio/netbird/dns"
"github.com/netbirdio/netbird/iface" "github.com/netbirdio/netbird/iface"
"github.com/netbirdio/netbird/iface/bind"
mgmt "github.com/netbirdio/netbird/management/client" mgmt "github.com/netbirdio/netbird/management/client"
mgmtProto "github.com/netbirdio/netbird/management/proto" mgmtProto "github.com/netbirdio/netbird/management/proto"
"github.com/netbirdio/netbird/management/server" "github.com/netbirdio/netbird/management/server"

14
go.mod
View File

@ -36,6 +36,7 @@ require (
github.com/getlantern/systray v1.2.1 github.com/getlantern/systray v1.2.1
github.com/gliderlabs/ssh v0.3.4 github.com/gliderlabs/ssh v0.3.4
github.com/godbus/dbus/v5 v5.1.0 github.com/godbus/dbus/v5 v5.1.0
github.com/golang/mock v1.6.0
github.com/google/go-cmp v0.5.9 github.com/google/go-cmp v0.5.9
github.com/google/gopacket v1.1.19 github.com/google/gopacket v1.1.19
github.com/google/nftables v0.0.0-20220808154552-2eca00135732 github.com/google/nftables v0.0.0-20220808154552-2eca00135732
@ -48,7 +49,6 @@ require (
github.com/miekg/dns v1.1.43 github.com/miekg/dns v1.1.43
github.com/mitchellh/hashstructure/v2 v2.0.2 github.com/mitchellh/hashstructure/v2 v2.0.2
github.com/okta/okta-sdk-golang/v2 v2.18.0 github.com/okta/okta-sdk-golang/v2 v2.18.0
github.com/open-policy-agent/opa v0.49.0
github.com/patrickmn/go-cache v2.1.0+incompatible github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pion/logging v0.2.2 github.com/pion/logging v0.2.2
github.com/pion/stun v0.4.0 github.com/pion/stun v0.4.0
@ -71,14 +71,13 @@ require (
require ( require (
github.com/BurntSushi/toml v1.2.1 // indirect github.com/BurntSushi/toml v1.2.1 // indirect
github.com/OneOfOne/xxhash v1.2.8 // indirect
github.com/XiaoMi/pegasus-go-client v0.0.0-20210427083443-f3b6b08bc4c2 // indirect github.com/XiaoMi/pegasus-go-client v0.0.0-20210427083443-f3b6b08bc4c2 // indirect
github.com/agnivade/levenshtein v1.1.1 // indirect
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d // indirect github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgraph-io/ristretto v0.1.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 // indirect github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect
@ -88,14 +87,12 @@ require (
github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 // indirect github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 // indirect
github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 // indirect github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 // indirect
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f // indirect github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f // indirect github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f // indirect
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211024062804-40e447a793be // indirect github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211024062804-40e447a793be // indirect
github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-redis/redis/v8 v8.11.5 // indirect github.com/go-redis/redis/v8 v8.11.5 // indirect
github.com/go-stack/stack v1.8.0 // indirect github.com/go-stack/stack v1.8.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff // indirect github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff // indirect
github.com/hashicorp/go-uuid v1.0.2 // indirect github.com/hashicorp/go-uuid v1.0.2 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
@ -116,16 +113,11 @@ require (
github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect
github.com/rogpeppe/go-internal v1.8.0 // indirect github.com/rogpeppe/go-internal v1.8.0 // indirect
github.com/spf13/cast v1.5.0 // indirect github.com/spf13/cast v1.5.0 // indirect
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564 // indirect github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564 // indirect
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9 // indirect github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9 // indirect
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/yashtewari/glob-intersection v0.1.0 // indirect
github.com/yuin/goldmark v1.4.13 // indirect github.com/yuin/goldmark v1.4.13 // indirect
go.opentelemetry.io/otel/sdk v1.11.1 // indirect go.opentelemetry.io/otel/sdk v1.11.1 // indirect
go.opentelemetry.io/otel/trace v1.11.1 // indirect go.opentelemetry.io/otel/trace v1.11.1 // indirect
@ -133,10 +125,12 @@ require (
golang.org/x/mod v0.8.0 // indirect golang.org/x/mod v0.8.0 // indirect
golang.org/x/oauth2 v0.8.0 // indirect golang.org/x/oauth2 v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect golang.org/x/text v0.9.0 // indirect
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
golang.org/x/tools v0.6.0 // indirect golang.org/x/tools v0.6.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
google.golang.org/appengine v1.6.7 // indirect google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 // indirect google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 // indirect gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 // indirect

40
go.sum
View File

@ -53,16 +53,12 @@ github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpz
github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/XiaoMi/pegasus-go-client v0.0.0-20210427083443-f3b6b08bc4c2 h1:pami0oPhVosjOu/qRHepRmdjD6hGILF7DBr+qQZeP10= github.com/XiaoMi/pegasus-go-client v0.0.0-20210427083443-f3b6b08bc4c2 h1:pami0oPhVosjOu/qRHepRmdjD6hGILF7DBr+qQZeP10=
github.com/XiaoMi/pegasus-go-client v0.0.0-20210427083443-f3b6b08bc4c2/go.mod h1:jNIx5ykW1MroBuaTja9+VpglmaJOUzezumfhLlER3oY= github.com/XiaoMi/pegasus-go-client v0.0.0-20210427083443-f3b6b08bc4c2/go.mod h1:jNIx5ykW1MroBuaTja9+VpglmaJOUzezumfhLlER3oY=
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@ -73,8 +69,6 @@ github.com/allegro/bigcache/v3 v3.0.2 h1:AKZCw+5eAaVyNTBmI2fgyPVJhHkdWder3O9Irpr
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/bazelbuild/rules_go v0.30.0/go.mod h1:MC23Dc/wkXEyk3Wpq6lCqz0ZAYOZDw2DR5y3N1q2i7M= github.com/bazelbuild/rules_go v0.30.0/go.mod h1:MC23Dc/wkXEyk3Wpq6lCqz0ZAYOZDw2DR5y3N1q2i7M=
@ -84,7 +78,6 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d h1:pVrfxiGfwelyab6n21ZBkbkmbevaf+WvMIiR7sr97hw= github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d h1:pVrfxiGfwelyab6n21ZBkbkmbevaf+WvMIiR7sr97hw=
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 h1:3uZCA/BLTIu+DqCfguByNMJa2HVHpXvjfy0Dy7g6fuA=
github.com/c-robinson/iplib v1.0.3 h1:NG0UF0GoEsrC1/vyfX1Lx2Ss7CySWl3KqqXh3q4DdPU= github.com/c-robinson/iplib v1.0.3 h1:NG0UF0GoEsrC1/vyfX1Lx2Ss7CySWl3KqqXh3q4DdPU=
github.com/c-robinson/iplib v1.0.3/go.mod h1:i3LuuFL1hRT5gFpBRnEydzw8R6yhGkF4szNDIbF8pgo= github.com/c-robinson/iplib v1.0.3/go.mod h1:i3LuuFL1hRT5gFpBRnEydzw8R6yhGkF4szNDIbF8pgo=
github.com/cenkalti/backoff v1.1.1-0.20190506075156-2146c9339422/go.mod h1:b6Nc7NRH5C4aCISLry0tLnTjcuTEvoiqcWDdsU0sOGM= github.com/cenkalti/backoff v1.1.1-0.20190506075156-2146c9339422/go.mod h1:b6Nc7NRH5C4aCISLry0tLnTjcuTEvoiqcWDdsU0sOGM=
@ -92,7 +85,6 @@ github.com/cenkalti/backoff/v4 v4.1.0/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInq
github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4=
github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
@ -149,14 +141,13 @@ github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgraph-io/badger/v3 v3.2103.5 h1:ylPa6qzbjYRQMU6jokoj4wzcaweHylt//CH0AKt0akg=
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g=
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
@ -178,7 +169,6 @@ github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi
github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/foxcpp/go-mockdns v0.0.0-20210729171921-fb145fc6f897 h1:E52jfcE64UG42SwLmrW0QByONfGynWuzBvm86BoB9z8=
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 h1:FDqhDm7pcsLhhWl1QtD8vlzI4mm59llRvNzrFg6/LAA= github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 h1:FDqhDm7pcsLhhWl1QtD8vlzI4mm59llRvNzrFg6/LAA=
@ -201,7 +191,6 @@ github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55/go.mod h1:6mmzY2
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f h1:wrYrQttPS8FHIRSlsrcuKazukx/xqO/PpLZzZXsF+EA= github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f h1:wrYrQttPS8FHIRSlsrcuKazukx/xqO/PpLZzZXsF+EA=
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA= github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do= github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
@ -246,8 +235,6 @@ github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
@ -257,7 +244,6 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff h1:W71vTCKoxtdXgnm1ECDFkfQnpdqAO00zzGXLA5yaEX8= github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff h1:W71vTCKoxtdXgnm1ECDFkfQnpdqAO00zzGXLA5yaEX8=
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw= github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw=
@ -270,7 +256,6 @@ github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4er
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
@ -279,6 +264,7 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -298,12 +284,10 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@ -412,7 +396,6 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@ -519,8 +502,6 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
github.com/open-policy-agent/opa v0.49.0 h1:TIlpCT1B5FSm8Dqo/a4t23gKmHkQysC3+7W77F99P4k=
github.com/open-policy-agent/opa v0.49.0/go.mod h1:WTLWtu498/QNTDkiHx76Xj7jaJUPvLJAPtdMkCcst0w=
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
@ -594,8 +575,6 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ=
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
@ -657,8 +636,6 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes=
github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
@ -672,14 +649,8 @@ github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYp
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg= github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yashtewari/glob-intersection v0.1.0 h1:6gJvMYQlTDOL3dMsPF6J0+26vwX9MB8/1q3uAdhmTrg=
github.com/yashtewari/glob-intersection v0.1.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@ -696,7 +667,6 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opentelemetry.io/otel v1.11.1 h1:4WLLAmcfkmDk2ukNXJyq3/kiz/3UzCaYq6PskJsaou4= go.opentelemetry.io/otel v1.11.1 h1:4WLLAmcfkmDk2ukNXJyq3/kiz/3UzCaYq6PskJsaou4=
go.opentelemetry.io/otel v1.11.1/go.mod h1:1nNhXBbWSD0nsL38H6btgnFN2k4i0sNLHNNMZMSbUGE= go.opentelemetry.io/otel v1.11.1/go.mod h1:1nNhXBbWSD0nsL38H6btgnFN2k4i0sNLHNNMZMSbUGE=
@ -954,6 +924,7 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -986,6 +957,7 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -1034,6 +1006,7 @@ golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
@ -1153,6 +1126,7 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=

90
iface/device_wrapper.go Normal file
View File

@ -0,0 +1,90 @@
package iface
import (
"net"
"sync"
"golang.zx2c4.com/wireguard/tun"
)
// PacketFilter interface for firewall abilities
type PacketFilter interface {
// DropOutgoing filter outgoing packets from host to external destinations
DropOutgoing(packetData []byte) bool
// DropIncoming filter incoming packets from external sources to host
DropIncoming(packetData []byte) bool
// SetNetwork of the wireguard interface to which filtering applied
SetNetwork(*net.IPNet)
}
// DeviceWrapper to override Read or Write of packets
type DeviceWrapper struct {
tun.Device
filter PacketFilter
mutex sync.RWMutex
}
// newDeviceWrapper constructor function
func newDeviceWrapper(device tun.Device) *DeviceWrapper {
return &DeviceWrapper{
Device: device,
}
}
// Read wraps read method with filtering feature
func (d *DeviceWrapper) Read(bufs [][]byte, sizes []int, offset int) (n int, err error) {
if n, err = d.Device.Read(bufs, sizes, offset); err != nil {
return 0, err
}
d.mutex.RLock()
filter := d.filter
d.mutex.RUnlock()
if filter == nil {
return
}
for i := 0; i < n; i++ {
if filter.DropOutgoing(bufs[i][offset : offset+sizes[i]]) {
bufs = append(bufs[:i], bufs[i+1:]...)
sizes = append(sizes[:i], sizes[i+1:]...)
n--
i--
}
}
return n, nil
}
// Write wraps write method with filtering feature
func (d *DeviceWrapper) Write(bufs [][]byte, offset int) (int, error) {
d.mutex.RLock()
filter := d.filter
d.mutex.RUnlock()
if filter == nil {
return d.Device.Write(bufs, offset)
}
filteredBufs := make([][]byte, 0, len(bufs))
dropped := 0
for _, buf := range bufs {
if !filter.DropIncoming(buf[offset:]) {
filteredBufs = append(filteredBufs, buf)
dropped++
}
}
n, err := d.Device.Write(filteredBufs, offset)
n += dropped
return n, err
}
// SetFiltering sets packet filter to device
func (d *DeviceWrapper) SetFiltering(filter PacketFilter) {
d.mutex.Lock()
d.filter = filter
d.mutex.Unlock()
}

View File

@ -0,0 +1,216 @@
package iface
import (
"net"
"testing"
"github.com/golang/mock/gomock"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
mocks "github.com/netbirdio/netbird/iface/mocks"
)
func TestDeviceWrapperRead(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
tun := mocks.NewMockDevice(ctrl)
filter := mocks.NewMockPacketFilter(ctrl)
mockBufs := [][]byte{{}}
mockSizes := []int{0}
mockOffset := 0
t.Run("read ICMP", func(t *testing.T) {
ipLayer := &layers.IPv4{
Version: 4,
TTL: 64,
Protocol: layers.IPProtocolICMPv4,
SrcIP: net.IP{192, 168, 0, 1},
DstIP: net.IP{100, 200, 0, 1},
}
icmpLayer := &layers.ICMPv4{
TypeCode: layers.CreateICMPv4TypeCode(layers.ICMPv4TypeEchoRequest, 0),
Id: 1,
Seq: 1,
}
buffer := gopacket.NewSerializeBuffer()
err := gopacket.SerializeLayers(buffer, gopacket.SerializeOptions{},
ipLayer,
icmpLayer,
)
if err != nil {
t.Errorf("serialize packet: %v", err)
return
}
tun.EXPECT().Read(mockBufs, mockSizes, mockOffset).
DoAndReturn(func(bufs [][]byte, sizes []int, offset int) (int, error) {
bufs[0] = buffer.Bytes()
sizes[0] = len(bufs[0])
return 1, nil
})
wrapped := newDeviceWrapper(tun)
bufs := [][]byte{{}}
sizes := []int{0}
offset := 0
n, err := wrapped.Read(bufs, sizes, offset)
if err != nil {
t.Errorf("unexpeted error: %v", err)
return
}
if n != 1 {
t.Errorf("expected n=1, got %d", n)
return
}
})
t.Run("write TCP", func(t *testing.T) {
ipLayer := &layers.IPv4{
Version: 4,
TTL: 64,
Protocol: layers.IPProtocolICMPv4,
SrcIP: net.IP{100, 200, 0, 9},
DstIP: net.IP{100, 200, 0, 10},
}
// create TCP layer packet
tcpLayer := &layers.TCP{
SrcPort: layers.TCPPort(34423),
DstPort: layers.TCPPort(8080),
}
buffer := gopacket.NewSerializeBuffer()
err := gopacket.SerializeLayers(buffer, gopacket.SerializeOptions{},
ipLayer,
tcpLayer,
)
if err != nil {
t.Errorf("serialize packet: %v", err)
return
}
mockBufs[0] = buffer.Bytes()
tun.EXPECT().Write(mockBufs, 0).Return(1, nil)
wrapped := newDeviceWrapper(tun)
bufs := [][]byte{buffer.Bytes()}
n, err := wrapped.Write(bufs, 0)
if err != nil {
t.Errorf("unexpeted error: %v", err)
return
}
if n != 1 {
t.Errorf("expected n=1, got %d", n)
return
}
})
t.Run("drop write UDP package", func(t *testing.T) {
ipLayer := &layers.IPv4{
Version: 4,
TTL: 64,
Protocol: layers.IPProtocolICMPv4,
SrcIP: net.IP{100, 200, 0, 11},
DstIP: net.IP{100, 200, 0, 20},
}
// create TCP layer packet
tcpLayer := &layers.UDP{
SrcPort: layers.UDPPort(27278),
DstPort: layers.UDPPort(53),
}
buffer := gopacket.NewSerializeBuffer()
err := gopacket.SerializeLayers(buffer, gopacket.SerializeOptions{},
ipLayer,
tcpLayer,
)
if err != nil {
t.Errorf("serialize packet: %v", err)
return
}
mockBufs = [][]byte{}
tun.EXPECT().Write(mockBufs, 0).Return(0, nil)
filter.EXPECT().DropOutput(gomock.Any()).Return(true)
wrapped := newDeviceWrapper(tun)
wrapped.filter = filter
bufs := [][]byte{buffer.Bytes()}
n, err := wrapped.Write(bufs, 0)
if err != nil {
t.Errorf("unexpeted error: %v", err)
return
}
if n != 0 {
t.Errorf("expected n=1, got %d", n)
return
}
})
t.Run("drop read UDP package", func(t *testing.T) {
ipLayer := &layers.IPv4{
Version: 4,
TTL: 64,
Protocol: layers.IPProtocolICMPv4,
SrcIP: net.IP{100, 200, 0, 11},
DstIP: net.IP{100, 200, 0, 20},
}
// create TCP layer packet
tcpLayer := &layers.UDP{
SrcPort: layers.UDPPort(19243),
DstPort: layers.UDPPort(1024),
}
buffer := gopacket.NewSerializeBuffer()
err := gopacket.SerializeLayers(buffer, gopacket.SerializeOptions{},
ipLayer,
tcpLayer,
)
if err != nil {
t.Errorf("serialize packet: %v", err)
return
}
mockBufs := [][]byte{{}}
mockSizes := []int{0}
mockOffset := 0
tun.EXPECT().Read(mockBufs, mockSizes, mockOffset).
DoAndReturn(func(bufs [][]byte, sizes []int, offset int) (int, error) {
bufs[0] = buffer.Bytes()
sizes[0] = len(bufs[0])
return 1, nil
})
filter.EXPECT().DropInput(gomock.Any()).Return(true)
wrapped := newDeviceWrapper(tun)
wrapped.filter = filter
bufs := [][]byte{{}}
sizes := []int{0}
offset := 0
n, err := wrapped.Read(bufs, sizes, offset)
if err != nil {
t.Errorf("unexpeted error: %v", err)
return
}
if n != 0 {
t.Errorf("expected n=0, got %d", n)
return
}
})
}

View File

@ -1,6 +1,7 @@
package iface package iface
import ( import (
"fmt"
"net" "net"
"sync" "sync"
"time" "time"
@ -118,3 +119,17 @@ func (w *WGIface) Close() error {
defer w.mu.Unlock() defer w.mu.Unlock()
return w.tun.Close() return w.tun.Close()
} }
// SetFiltering sets packet filters for the userspace impelemntation
func (w *WGIface) SetFiltering(filter PacketFilter) error {
w.mu.Lock()
defer w.mu.Unlock()
if w.tun.wrapper == nil {
return fmt.Errorf("userspace packet filtering not handled on this device")
}
filter.SetNetwork(w.tun.address.Network)
w.tun.wrapper.SetFiltering(filter)
return nil
}

75
iface/mocks/filter.go Normal file
View File

@ -0,0 +1,75 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/netbirdio/netbird/iface (interfaces: PacketFilter)
// Package mocks is a generated GoMock package.
package mocks
import (
net "net"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
)
// MockPacketFilter is a mock of PacketFilter interface.
type MockPacketFilter struct {
ctrl *gomock.Controller
recorder *MockPacketFilterMockRecorder
}
// MockPacketFilterMockRecorder is the mock recorder for MockPacketFilter.
type MockPacketFilterMockRecorder struct {
mock *MockPacketFilter
}
// NewMockPacketFilter creates a new mock instance.
func NewMockPacketFilter(ctrl *gomock.Controller) *MockPacketFilter {
mock := &MockPacketFilter{ctrl: ctrl}
mock.recorder = &MockPacketFilterMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockPacketFilter) EXPECT() *MockPacketFilterMockRecorder {
return m.recorder
}
// DropInput mocks base method.
func (m *MockPacketFilter) DropOutgoing(arg0 []byte) bool {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DropOutgoing", arg0)
ret0, _ := ret[0].(bool)
return ret0
}
// DropInput indicates an expected call of DropInput.
func (mr *MockPacketFilterMockRecorder) DropInput(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropOutgoing", reflect.TypeOf((*MockPacketFilter)(nil).DropOutgoing), arg0)
}
// DropOutput mocks base method.
func (m *MockPacketFilter) DropIncoming(arg0 []byte) bool {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DropIncoming", arg0)
ret0, _ := ret[0].(bool)
return ret0
}
// DropOutput indicates an expected call of DropOutput.
func (mr *MockPacketFilterMockRecorder) DropOutput(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropIncoming", reflect.TypeOf((*MockPacketFilter)(nil).DropIncoming), arg0)
}
// SetNetwork mocks base method.
func (m *MockPacketFilter) SetNetwork(arg0 *net.IPNet) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "SetNetwork", arg0)
}
// SetNetwork indicates an expected call of SetNetwork.
func (mr *MockPacketFilterMockRecorder) SetNetwork(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetNetwork", reflect.TypeOf((*MockPacketFilter)(nil).SetNetwork), arg0)
}

152
iface/mocks/tun.go Normal file
View File

@ -0,0 +1,152 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: golang.zx2c4.com/wireguard/tun (interfaces: Device)
// Package mocks is a generated GoMock package.
package mocks
import (
os "os"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
tun "golang.zx2c4.com/wireguard/tun"
)
// MockDevice is a mock of Device interface.
type MockDevice struct {
ctrl *gomock.Controller
recorder *MockDeviceMockRecorder
}
// MockDeviceMockRecorder is the mock recorder for MockDevice.
type MockDeviceMockRecorder struct {
mock *MockDevice
}
// NewMockDevice creates a new mock instance.
func NewMockDevice(ctrl *gomock.Controller) *MockDevice {
mock := &MockDevice{ctrl: ctrl}
mock.recorder = &MockDeviceMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockDevice) EXPECT() *MockDeviceMockRecorder {
return m.recorder
}
// BatchSize mocks base method.
func (m *MockDevice) BatchSize() int {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "BatchSize")
ret0, _ := ret[0].(int)
return ret0
}
// BatchSize indicates an expected call of BatchSize.
func (mr *MockDeviceMockRecorder) BatchSize() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BatchSize", reflect.TypeOf((*MockDevice)(nil).BatchSize))
}
// Close mocks base method.
func (m *MockDevice) Close() error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Close")
ret0, _ := ret[0].(error)
return ret0
}
// Close indicates an expected call of Close.
func (mr *MockDeviceMockRecorder) Close() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockDevice)(nil).Close))
}
// Events mocks base method.
func (m *MockDevice) Events() <-chan tun.Event {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Events")
ret0, _ := ret[0].(<-chan tun.Event)
return ret0
}
// Events indicates an expected call of Events.
func (mr *MockDeviceMockRecorder) Events() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Events", reflect.TypeOf((*MockDevice)(nil).Events))
}
// File mocks base method.
func (m *MockDevice) File() *os.File {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "File")
ret0, _ := ret[0].(*os.File)
return ret0
}
// File indicates an expected call of File.
func (mr *MockDeviceMockRecorder) File() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "File", reflect.TypeOf((*MockDevice)(nil).File))
}
// MTU mocks base method.
func (m *MockDevice) MTU() (int, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "MTU")
ret0, _ := ret[0].(int)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// MTU indicates an expected call of MTU.
func (mr *MockDeviceMockRecorder) MTU() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MTU", reflect.TypeOf((*MockDevice)(nil).MTU))
}
// Name mocks base method.
func (m *MockDevice) Name() (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Name")
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Name indicates an expected call of Name.
func (mr *MockDeviceMockRecorder) Name() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*MockDevice)(nil).Name))
}
// Read mocks base method.
func (m *MockDevice) Read(arg0 [][]byte, arg1 []int, arg2 int) (int, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Read", arg0, arg1, arg2)
ret0, _ := ret[0].(int)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Read indicates an expected call of Read.
func (mr *MockDeviceMockRecorder) Read(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*MockDevice)(nil).Read), arg0, arg1, arg2)
}
// Write mocks base method.
func (m *MockDevice) Write(arg0 [][]byte, arg1 int) (int, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Write", arg0, arg1)
ret0, _ := ret[0].(int)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Write indicates an expected call of Write.
func (mr *MockDeviceMockRecorder) Write(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*MockDevice)(nil).Write), arg0, arg1)
}

View File

@ -22,6 +22,7 @@ type tunDevice struct {
name string name string
device *device.Device device *device.Device
iceBind *bind.ICEBind iceBind *bind.ICEBind
wrapper *DeviceWrapper
} }
func newTunDevice(address WGAddress, mtu int, routes []string, tunAdapter TunAdapter, transportNet transport.Net) *tunDevice { func newTunDevice(address WGAddress, mtu int, routes []string, tunAdapter TunAdapter, transportNet transport.Net) *tunDevice {
@ -49,9 +50,10 @@ func (t *tunDevice) Create() error {
return err return err
} }
t.name = name t.name = name
t.wrapper = newDeviceWrapper(tunDevice)
log.Debugf("attaching to interface %v", name) log.Debugf("attaching to interface %v", name)
t.device = device.NewDevice(tunDevice, t.iceBind, device.NewLogger(device.LogLevelSilent, "[wiretrustee] ")) t.device = device.NewDevice(t.wrapper, t.iceBind, device.NewLogger(device.LogLevelSilent, "[wiretrustee] "))
// without this property mobile devices can discover remote endpoints if the configured one was wrong. // without this property mobile devices can discover remote endpoints if the configured one was wrong.
// this helps with support for the older NetBird clients that had a hardcoded direct mode // this helps with support for the older NetBird clients that had a hardcoded direct mode
//t.device.DisableSomeRoamingForBrokenMobileSemantics() //t.device.DisableSomeRoamingForBrokenMobileSemantics()

View File

@ -23,6 +23,7 @@ type tunDevice struct {
netInterface NetInterface netInterface NetInterface
iceBind *bind.ICEBind iceBind *bind.ICEBind
uapi net.Listener uapi net.Listener
wrapper *DeviceWrapper
close chan struct{} close chan struct{}
} }
@ -90,9 +91,14 @@ func (c *tunDevice) createWithUserspace() (NetInterface, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
c.wrapper = newDeviceWrapper(tunIface)
// We need to create a wireguard-go device and listen to configuration requests // We need to create a wireguard-go device and listen to configuration requests
tunDev := device.NewDevice(tunIface, c.iceBind, device.NewLogger(device.LogLevelSilent, "[netbird] ")) tunDev := device.NewDevice(
c.wrapper,
c.iceBind,
device.NewLogger(device.LogLevelSilent, "[netbird] "),
)
err = tunDev.Up() err = tunDev.Up()
if err != nil { if err != nil {
_ = tunIface.Close() _ = tunIface.Close()

View File

@ -23,6 +23,7 @@ type tunDevice struct {
iceBind *bind.ICEBind iceBind *bind.ICEBind
mtu int mtu int
uapi net.Listener uapi net.Listener
wrapper *DeviceWrapper
close chan struct{} close chan struct{}
} }
@ -52,6 +53,8 @@ func (c *tunDevice) createWithUserspace() (NetInterface, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
c.wrapper = newDeviceWrapper(tunIface)
// We need to create a wireguard-go device and listen to configuration requests // We need to create a wireguard-go device and listen to configuration requests
tunDev := device.NewDevice(tunIface, c.iceBind, device.NewLogger(device.LogLevelSilent, "[netbird] ")) tunDev := device.NewDevice(tunIface, c.iceBind, device.NewLogger(device.LogLevelSilent, "[netbird] "))
err = tunDev.Up() err = tunDev.Up()

View File

@ -119,6 +119,153 @@ func (DeviceAuthorizationFlowProvider) EnumDescriptor() ([]byte, []int) {
return file_management_proto_rawDescGZIP(), []int{17, 0} return file_management_proto_rawDescGZIP(), []int{17, 0}
} }
type FirewallRuleDirection int32
const (
FirewallRule_IN FirewallRuleDirection = 0
FirewallRule_OUT FirewallRuleDirection = 1
)
// Enum value maps for FirewallRuleDirection.
var (
FirewallRuleDirection_name = map[int32]string{
0: "IN",
1: "OUT",
}
FirewallRuleDirection_value = map[string]int32{
"IN": 0,
"OUT": 1,
}
)
func (x FirewallRuleDirection) Enum() *FirewallRuleDirection {
p := new(FirewallRuleDirection)
*p = x
return p
}
func (x FirewallRuleDirection) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (FirewallRuleDirection) Descriptor() protoreflect.EnumDescriptor {
return file_management_proto_enumTypes[2].Descriptor()
}
func (FirewallRuleDirection) Type() protoreflect.EnumType {
return &file_management_proto_enumTypes[2]
}
func (x FirewallRuleDirection) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use FirewallRuleDirection.Descriptor instead.
func (FirewallRuleDirection) EnumDescriptor() ([]byte, []int) {
return file_management_proto_rawDescGZIP(), []int{25, 0}
}
type FirewallRuleAction int32
const (
FirewallRule_ACCEPT FirewallRuleAction = 0
FirewallRule_DROP FirewallRuleAction = 1
)
// Enum value maps for FirewallRuleAction.
var (
FirewallRuleAction_name = map[int32]string{
0: "ACCEPT",
1: "DROP",
}
FirewallRuleAction_value = map[string]int32{
"ACCEPT": 0,
"DROP": 1,
}
)
func (x FirewallRuleAction) Enum() *FirewallRuleAction {
p := new(FirewallRuleAction)
*p = x
return p
}
func (x FirewallRuleAction) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (FirewallRuleAction) Descriptor() protoreflect.EnumDescriptor {
return file_management_proto_enumTypes[3].Descriptor()
}
func (FirewallRuleAction) Type() protoreflect.EnumType {
return &file_management_proto_enumTypes[3]
}
func (x FirewallRuleAction) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use FirewallRuleAction.Descriptor instead.
func (FirewallRuleAction) EnumDescriptor() ([]byte, []int) {
return file_management_proto_rawDescGZIP(), []int{25, 1}
}
type FirewallRuleProtocol int32
const (
FirewallRule_UNKNOWN FirewallRuleProtocol = 0
FirewallRule_ALL FirewallRuleProtocol = 1
FirewallRule_TCP FirewallRuleProtocol = 2
FirewallRule_UDP FirewallRuleProtocol = 3
FirewallRule_ICMP FirewallRuleProtocol = 4
)
// Enum value maps for FirewallRuleProtocol.
var (
FirewallRuleProtocol_name = map[int32]string{
0: "UNKNOWN",
1: "ALL",
2: "TCP",
3: "UDP",
4: "ICMP",
}
FirewallRuleProtocol_value = map[string]int32{
"UNKNOWN": 0,
"ALL": 1,
"TCP": 2,
"UDP": 3,
"ICMP": 4,
}
)
func (x FirewallRuleProtocol) Enum() *FirewallRuleProtocol {
p := new(FirewallRuleProtocol)
*p = x
return p
}
func (x FirewallRuleProtocol) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (FirewallRuleProtocol) Descriptor() protoreflect.EnumDescriptor {
return file_management_proto_enumTypes[4].Descriptor()
}
func (FirewallRuleProtocol) Type() protoreflect.EnumType {
return &file_management_proto_enumTypes[4]
}
func (x FirewallRuleProtocol) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use FirewallRuleProtocol.Descriptor instead.
func (FirewallRuleProtocol) EnumDescriptor() ([]byte, []int) {
return file_management_proto_rawDescGZIP(), []int{25, 2}
}
type EncryptedMessage struct { type EncryptedMessage struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@ -995,6 +1142,8 @@ type NetworkMap struct {
DNSConfig *DNSConfig `protobuf:"bytes,6,opt,name=DNSConfig,proto3" json:"DNSConfig,omitempty"` DNSConfig *DNSConfig `protobuf:"bytes,6,opt,name=DNSConfig,proto3" json:"DNSConfig,omitempty"`
// RemotePeerConfig represents a list of remote peers that the receiver can connect to // RemotePeerConfig represents a list of remote peers that the receiver can connect to
OfflinePeers []*RemotePeerConfig `protobuf:"bytes,7,rep,name=offlinePeers,proto3" json:"offlinePeers,omitempty"` OfflinePeers []*RemotePeerConfig `protobuf:"bytes,7,rep,name=offlinePeers,proto3" json:"offlinePeers,omitempty"`
// FirewallRule represents a list of firewall rules to be applied to peer
FirewallRules []*FirewallRule `protobuf:"bytes,8,rep,name=FirewallRules,proto3" json:"FirewallRules,omitempty"`
} }
func (x *NetworkMap) Reset() { func (x *NetworkMap) Reset() {
@ -1078,6 +1227,13 @@ func (x *NetworkMap) GetOfflinePeers() []*RemotePeerConfig {
return nil return nil
} }
func (x *NetworkMap) GetFirewallRules() []*FirewallRule {
if x != nil {
return x.FirewallRules
}
return nil
}
// RemotePeerConfig represents a configuration of a remote peer. // RemotePeerConfig represents a configuration of a remote peer.
// The properties are used to configure WireGuard Peers sections // The properties are used to configure WireGuard Peers sections
type RemotePeerConfig struct { type RemotePeerConfig struct {
@ -1849,6 +2005,86 @@ func (x *NameServer) GetPort() int64 {
return 0 return 0
} }
// FirewallRule represents a firewall rule
type FirewallRule struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
PeerIP string `protobuf:"bytes,1,opt,name=PeerIP,proto3" json:"PeerIP,omitempty"`
Direction FirewallRuleDirection `protobuf:"varint,2,opt,name=Direction,proto3,enum=management.FirewallRuleDirection" json:"Direction,omitempty"`
Action FirewallRuleAction `protobuf:"varint,3,opt,name=Action,proto3,enum=management.FirewallRuleAction" json:"Action,omitempty"`
Protocol FirewallRuleProtocol `protobuf:"varint,4,opt,name=Protocol,proto3,enum=management.FirewallRuleProtocol" json:"Protocol,omitempty"`
Port string `protobuf:"bytes,5,opt,name=Port,proto3" json:"Port,omitempty"`
}
func (x *FirewallRule) Reset() {
*x = FirewallRule{}
if protoimpl.UnsafeEnabled {
mi := &file_management_proto_msgTypes[25]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *FirewallRule) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*FirewallRule) ProtoMessage() {}
func (x *FirewallRule) ProtoReflect() protoreflect.Message {
mi := &file_management_proto_msgTypes[25]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use FirewallRule.ProtoReflect.Descriptor instead.
func (*FirewallRule) Descriptor() ([]byte, []int) {
return file_management_proto_rawDescGZIP(), []int{25}
}
func (x *FirewallRule) GetPeerIP() string {
if x != nil {
return x.PeerIP
}
return ""
}
func (x *FirewallRule) GetDirection() FirewallRuleDirection {
if x != nil {
return x.Direction
}
return FirewallRule_IN
}
func (x *FirewallRule) GetAction() FirewallRuleAction {
if x != nil {
return x.Action
}
return FirewallRule_ACCEPT
}
func (x *FirewallRule) GetProtocol() FirewallRuleProtocol {
if x != nil {
return x.Protocol
}
return FirewallRule_UNKNOWN
}
func (x *FirewallRule) GetPort() string {
if x != nil {
return x.Port
}
return ""
}
var File_management_proto protoreflect.FileDescriptor var File_management_proto protoreflect.FileDescriptor
var file_management_proto_rawDesc = []byte{ var file_management_proto_rawDesc = []byte{
@ -1966,7 +2202,7 @@ var file_management_proto_rawDesc = []byte{
0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x18, 0x04, 0x20, 0x01,
0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x22, 0xee, 0x02, 0x0a, 0x0a, 0x4e, 0x65, 0x74, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x22, 0xae, 0x03, 0x0a, 0x0a, 0x4e, 0x65, 0x74,
0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x69, 0x61, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x69, 0x61,
0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x12, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x12,
0x36, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x36, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20,
@ -1989,7 +2225,11 @@ var file_management_proto_rawDesc = []byte{
0x6e, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6e, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e,
0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74,
0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x6f, 0x66, 0x66, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x6f, 0x66, 0x66,
0x6c, 0x69, 0x6e, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x22, 0x97, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x3e, 0x0a, 0x0d, 0x46, 0x69, 0x72,
0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x69,
0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x46, 0x69, 0x72, 0x65,
0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x97, 0x01, 0x0a, 0x10, 0x52, 0x65,
0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a,
0x0a, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x0a, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x6c, 0x52, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x6c,
@ -2083,32 +2323,55 @@ var file_management_proto_rawDesc = []byte{
0x76, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x76, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x02, 0x49, 0x50, 0x12, 0x16, 0x0a, 0x06, 0x4e, 0x53, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x02, 0x49, 0x50, 0x12, 0x16, 0x0a, 0x06, 0x4e, 0x53, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20,
0x01, 0x28, 0x03, 0x52, 0x06, 0x4e, 0x53, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x01, 0x28, 0x03, 0x52, 0x06, 0x4e, 0x53, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x50,
0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x32, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x22,
0xf7, 0x02, 0x0a, 0x11, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, 0xf0, 0x02, 0x0a, 0x0c, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65,
0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x45, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1c, 0x12, 0x16, 0x0a, 0x06, 0x50, 0x65, 0x65, 0x72, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x06, 0x50, 0x65, 0x65, 0x72, 0x49, 0x50, 0x12, 0x40, 0x0a, 0x09, 0x44, 0x69, 0x72, 0x65,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x6d, 0x61,
0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c,
0x6c, 0x52, 0x75, 0x6c, 0x65, 0x2e, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52,
0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x06, 0x41, 0x63,
0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x6d, 0x61, 0x6e,
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c,
0x52, 0x75, 0x6c, 0x65, 0x2e, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x41, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x12, 0x3d, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18,
0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
0x6e, 0x74, 0x2e, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63,
0x6f, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09,
0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x22, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x12, 0x06, 0x0a, 0x02, 0x49, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4f,
0x55, 0x54, 0x10, 0x01, 0x22, 0x1e, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0a,
0x0a, 0x06, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x52,
0x4f, 0x50, 0x10, 0x01, 0x22, 0x3c, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a,
0x03, 0x41, 0x4c, 0x4c, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x02, 0x12,
0x07, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x43, 0x4d, 0x50,
0x10, 0x04, 0x32, 0xf7, 0x02, 0x0a, 0x11, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e,
0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x45, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69,
0x6e, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45,
0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a,
0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63,
0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12,
0x46, 0x0a, 0x04, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65,
0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73,
0x61, 0x67, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x53, 0x65,
0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x6d, 0x61, 0x6e,
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65,
0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x09, 0x69,
0x73, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67,
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x11, 0x2e, 0x6d, 0x61,
0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00,
0x12, 0x5a, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74,
0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x1c,
0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72,
0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d,
0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70,
0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x46, 0x0a, 0x04, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06,
0x53, 0x79, 0x6e, 0x63, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61,
0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
0x22, 0x00, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65,
0x72, 0x4b, 0x65, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e,
0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x09, 0x69, 0x73, 0x48, 0x65,
0x61, 0x6c, 0x74, 0x68, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67,
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x5a, 0x0a,
0x1a, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x1c, 0x2e, 0x6d, 0x61,
0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74,
0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61,
0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64,
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (
@ -2123,81 +2386,89 @@ func file_management_proto_rawDescGZIP() []byte {
return file_management_proto_rawDescData return file_management_proto_rawDescData
} }
var file_management_proto_enumTypes = make([]protoimpl.EnumInfo, 2) var file_management_proto_enumTypes = make([]protoimpl.EnumInfo, 5)
var file_management_proto_msgTypes = make([]protoimpl.MessageInfo, 25) var file_management_proto_msgTypes = make([]protoimpl.MessageInfo, 26)
var file_management_proto_goTypes = []interface{}{ var file_management_proto_goTypes = []interface{}{
(HostConfig_Protocol)(0), // 0: management.HostConfig.Protocol (HostConfig_Protocol)(0), // 0: management.HostConfig.Protocol
(DeviceAuthorizationFlowProvider)(0), // 1: management.DeviceAuthorizationFlow.provider (DeviceAuthorizationFlowProvider)(0), // 1: management.DeviceAuthorizationFlow.provider
(*EncryptedMessage)(nil), // 2: management.EncryptedMessage (FirewallRuleDirection)(0), // 2: management.FirewallRule.direction
(*SyncRequest)(nil), // 3: management.SyncRequest (FirewallRuleAction)(0), // 3: management.FirewallRule.action
(*SyncResponse)(nil), // 4: management.SyncResponse (FirewallRuleProtocol)(0), // 4: management.FirewallRule.protocol
(*LoginRequest)(nil), // 5: management.LoginRequest (*EncryptedMessage)(nil), // 5: management.EncryptedMessage
(*PeerKeys)(nil), // 6: management.PeerKeys (*SyncRequest)(nil), // 6: management.SyncRequest
(*PeerSystemMeta)(nil), // 7: management.PeerSystemMeta (*SyncResponse)(nil), // 7: management.SyncResponse
(*LoginResponse)(nil), // 8: management.LoginResponse (*LoginRequest)(nil), // 8: management.LoginRequest
(*ServerKeyResponse)(nil), // 9: management.ServerKeyResponse (*PeerKeys)(nil), // 9: management.PeerKeys
(*Empty)(nil), // 10: management.Empty (*PeerSystemMeta)(nil), // 10: management.PeerSystemMeta
(*WiretrusteeConfig)(nil), // 11: management.WiretrusteeConfig (*LoginResponse)(nil), // 11: management.LoginResponse
(*HostConfig)(nil), // 12: management.HostConfig (*ServerKeyResponse)(nil), // 12: management.ServerKeyResponse
(*ProtectedHostConfig)(nil), // 13: management.ProtectedHostConfig (*Empty)(nil), // 13: management.Empty
(*PeerConfig)(nil), // 14: management.PeerConfig (*WiretrusteeConfig)(nil), // 14: management.WiretrusteeConfig
(*NetworkMap)(nil), // 15: management.NetworkMap (*HostConfig)(nil), // 15: management.HostConfig
(*RemotePeerConfig)(nil), // 16: management.RemotePeerConfig (*ProtectedHostConfig)(nil), // 16: management.ProtectedHostConfig
(*SSHConfig)(nil), // 17: management.SSHConfig (*PeerConfig)(nil), // 17: management.PeerConfig
(*DeviceAuthorizationFlowRequest)(nil), // 18: management.DeviceAuthorizationFlowRequest (*NetworkMap)(nil), // 18: management.NetworkMap
(*DeviceAuthorizationFlow)(nil), // 19: management.DeviceAuthorizationFlow (*RemotePeerConfig)(nil), // 19: management.RemotePeerConfig
(*ProviderConfig)(nil), // 20: management.ProviderConfig (*SSHConfig)(nil), // 20: management.SSHConfig
(*Route)(nil), // 21: management.Route (*DeviceAuthorizationFlowRequest)(nil), // 21: management.DeviceAuthorizationFlowRequest
(*DNSConfig)(nil), // 22: management.DNSConfig (*DeviceAuthorizationFlow)(nil), // 22: management.DeviceAuthorizationFlow
(*CustomZone)(nil), // 23: management.CustomZone (*ProviderConfig)(nil), // 23: management.ProviderConfig
(*SimpleRecord)(nil), // 24: management.SimpleRecord (*Route)(nil), // 24: management.Route
(*NameServerGroup)(nil), // 25: management.NameServerGroup (*DNSConfig)(nil), // 25: management.DNSConfig
(*NameServer)(nil), // 26: management.NameServer (*CustomZone)(nil), // 26: management.CustomZone
(*timestamppb.Timestamp)(nil), // 27: google.protobuf.Timestamp (*SimpleRecord)(nil), // 27: management.SimpleRecord
(*NameServerGroup)(nil), // 28: management.NameServerGroup
(*NameServer)(nil), // 29: management.NameServer
(*FirewallRule)(nil), // 30: management.FirewallRule
(*timestamppb.Timestamp)(nil), // 31: google.protobuf.Timestamp
} }
var file_management_proto_depIdxs = []int32{ var file_management_proto_depIdxs = []int32{
11, // 0: management.SyncResponse.wiretrusteeConfig:type_name -> management.WiretrusteeConfig 14, // 0: management.SyncResponse.wiretrusteeConfig:type_name -> management.WiretrusteeConfig
14, // 1: management.SyncResponse.peerConfig:type_name -> management.PeerConfig 17, // 1: management.SyncResponse.peerConfig:type_name -> management.PeerConfig
16, // 2: management.SyncResponse.remotePeers:type_name -> management.RemotePeerConfig 19, // 2: management.SyncResponse.remotePeers:type_name -> management.RemotePeerConfig
15, // 3: management.SyncResponse.NetworkMap:type_name -> management.NetworkMap 18, // 3: management.SyncResponse.NetworkMap:type_name -> management.NetworkMap
7, // 4: management.LoginRequest.meta:type_name -> management.PeerSystemMeta 10, // 4: management.LoginRequest.meta:type_name -> management.PeerSystemMeta
6, // 5: management.LoginRequest.peerKeys:type_name -> management.PeerKeys 9, // 5: management.LoginRequest.peerKeys:type_name -> management.PeerKeys
11, // 6: management.LoginResponse.wiretrusteeConfig:type_name -> management.WiretrusteeConfig 14, // 6: management.LoginResponse.wiretrusteeConfig:type_name -> management.WiretrusteeConfig
14, // 7: management.LoginResponse.peerConfig:type_name -> management.PeerConfig 17, // 7: management.LoginResponse.peerConfig:type_name -> management.PeerConfig
27, // 8: management.ServerKeyResponse.expiresAt:type_name -> google.protobuf.Timestamp 31, // 8: management.ServerKeyResponse.expiresAt:type_name -> google.protobuf.Timestamp
12, // 9: management.WiretrusteeConfig.stuns:type_name -> management.HostConfig 15, // 9: management.WiretrusteeConfig.stuns:type_name -> management.HostConfig
13, // 10: management.WiretrusteeConfig.turns:type_name -> management.ProtectedHostConfig 16, // 10: management.WiretrusteeConfig.turns:type_name -> management.ProtectedHostConfig
12, // 11: management.WiretrusteeConfig.signal:type_name -> management.HostConfig 15, // 11: management.WiretrusteeConfig.signal:type_name -> management.HostConfig
0, // 12: management.HostConfig.protocol:type_name -> management.HostConfig.Protocol 0, // 12: management.HostConfig.protocol:type_name -> management.HostConfig.Protocol
12, // 13: management.ProtectedHostConfig.hostConfig:type_name -> management.HostConfig 15, // 13: management.ProtectedHostConfig.hostConfig:type_name -> management.HostConfig
17, // 14: management.PeerConfig.sshConfig:type_name -> management.SSHConfig 20, // 14: management.PeerConfig.sshConfig:type_name -> management.SSHConfig
14, // 15: management.NetworkMap.peerConfig:type_name -> management.PeerConfig 17, // 15: management.NetworkMap.peerConfig:type_name -> management.PeerConfig
16, // 16: management.NetworkMap.remotePeers:type_name -> management.RemotePeerConfig 19, // 16: management.NetworkMap.remotePeers:type_name -> management.RemotePeerConfig
21, // 17: management.NetworkMap.Routes:type_name -> management.Route 24, // 17: management.NetworkMap.Routes:type_name -> management.Route
22, // 18: management.NetworkMap.DNSConfig:type_name -> management.DNSConfig 25, // 18: management.NetworkMap.DNSConfig:type_name -> management.DNSConfig
16, // 19: management.NetworkMap.offlinePeers:type_name -> management.RemotePeerConfig 19, // 19: management.NetworkMap.offlinePeers:type_name -> management.RemotePeerConfig
17, // 20: management.RemotePeerConfig.sshConfig:type_name -> management.SSHConfig 30, // 20: management.NetworkMap.FirewallRules:type_name -> management.FirewallRule
1, // 21: management.DeviceAuthorizationFlow.Provider:type_name -> management.DeviceAuthorizationFlow.provider 20, // 21: management.RemotePeerConfig.sshConfig:type_name -> management.SSHConfig
20, // 22: management.DeviceAuthorizationFlow.ProviderConfig:type_name -> management.ProviderConfig 1, // 22: management.DeviceAuthorizationFlow.Provider:type_name -> management.DeviceAuthorizationFlow.provider
25, // 23: management.DNSConfig.NameServerGroups:type_name -> management.NameServerGroup 23, // 23: management.DeviceAuthorizationFlow.ProviderConfig:type_name -> management.ProviderConfig
23, // 24: management.DNSConfig.CustomZones:type_name -> management.CustomZone 28, // 24: management.DNSConfig.NameServerGroups:type_name -> management.NameServerGroup
24, // 25: management.CustomZone.Records:type_name -> management.SimpleRecord 26, // 25: management.DNSConfig.CustomZones:type_name -> management.CustomZone
26, // 26: management.NameServerGroup.NameServers:type_name -> management.NameServer 27, // 26: management.CustomZone.Records:type_name -> management.SimpleRecord
2, // 27: management.ManagementService.Login:input_type -> management.EncryptedMessage 29, // 27: management.NameServerGroup.NameServers:type_name -> management.NameServer
2, // 28: management.ManagementService.Sync:input_type -> management.EncryptedMessage 2, // 28: management.FirewallRule.Direction:type_name -> management.FirewallRule.direction
10, // 29: management.ManagementService.GetServerKey:input_type -> management.Empty 3, // 29: management.FirewallRule.Action:type_name -> management.FirewallRule.action
10, // 30: management.ManagementService.isHealthy:input_type -> management.Empty 4, // 30: management.FirewallRule.Protocol:type_name -> management.FirewallRule.protocol
2, // 31: management.ManagementService.GetDeviceAuthorizationFlow:input_type -> management.EncryptedMessage 5, // 31: management.ManagementService.Login:input_type -> management.EncryptedMessage
2, // 32: management.ManagementService.Login:output_type -> management.EncryptedMessage 5, // 32: management.ManagementService.Sync:input_type -> management.EncryptedMessage
2, // 33: management.ManagementService.Sync:output_type -> management.EncryptedMessage 13, // 33: management.ManagementService.GetServerKey:input_type -> management.Empty
9, // 34: management.ManagementService.GetServerKey:output_type -> management.ServerKeyResponse 13, // 34: management.ManagementService.isHealthy:input_type -> management.Empty
10, // 35: management.ManagementService.isHealthy:output_type -> management.Empty 5, // 35: management.ManagementService.GetDeviceAuthorizationFlow:input_type -> management.EncryptedMessage
2, // 36: management.ManagementService.GetDeviceAuthorizationFlow:output_type -> management.EncryptedMessage 5, // 36: management.ManagementService.Login:output_type -> management.EncryptedMessage
32, // [32:37] is the sub-list for method output_type 5, // 37: management.ManagementService.Sync:output_type -> management.EncryptedMessage
27, // [27:32] is the sub-list for method input_type 12, // 38: management.ManagementService.GetServerKey:output_type -> management.ServerKeyResponse
27, // [27:27] is the sub-list for extension type_name 13, // 39: management.ManagementService.isHealthy:output_type -> management.Empty
27, // [27:27] is the sub-list for extension extendee 5, // 40: management.ManagementService.GetDeviceAuthorizationFlow:output_type -> management.EncryptedMessage
0, // [0:27] is the sub-list for field type_name 36, // [36:41] is the sub-list for method output_type
31, // [31:36] is the sub-list for method input_type
31, // [31:31] is the sub-list for extension type_name
31, // [31:31] is the sub-list for extension extendee
0, // [0:31] is the sub-list for field type_name
} }
func init() { file_management_proto_init() } func init() { file_management_proto_init() }
@ -2506,14 +2777,26 @@ func file_management_proto_init() {
return nil return nil
} }
} }
file_management_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*FirewallRule); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
} }
type x struct{} type x struct{}
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_management_proto_rawDesc, RawDescriptor: file_management_proto_rawDesc,
NumEnums: 2, NumEnums: 5,
NumMessages: 25, NumMessages: 26,
NumExtensions: 0, NumExtensions: 0,
NumServices: 1, NumServices: 1,
}, },

View File

@ -186,6 +186,9 @@ message NetworkMap {
// RemotePeerConfig represents a list of remote peers that the receiver can connect to // RemotePeerConfig represents a list of remote peers that the receiver can connect to
repeated RemotePeerConfig offlinePeers = 7; repeated RemotePeerConfig offlinePeers = 7;
// FirewallRule represents a list of firewall rules to be applied to peer
repeated FirewallRule FirewallRules = 8;
} }
// RemotePeerConfig represents a configuration of a remote peer. // RemotePeerConfig represents a configuration of a remote peer.
@ -297,4 +300,29 @@ message NameServer {
string IP = 1; string IP = 1;
int64 NSType = 2; int64 NSType = 2;
int64 Port = 3; int64 Port = 3;
} }
// FirewallRule represents a firewall rule
message FirewallRule {
string PeerIP = 1;
direction Direction = 2;
action Action = 3;
protocol Protocol = 4;
string Port = 5;
enum direction {
IN = 0;
OUT = 1;
}
enum action {
ACCEPT = 0;
DROP = 1;
}
enum protocol {
UNKNOWN = 0;
ALL = 1;
TCP = 2;
UDP = 3;
ICMP = 4;
}
}

View File

@ -286,7 +286,7 @@ func (a *Account) GetGroup(groupID string) *Group {
// GetPeerNetworkMap returns a group by ID if exists, nil otherwise // GetPeerNetworkMap returns a group by ID if exists, nil otherwise
func (a *Account) GetPeerNetworkMap(peerID, dnsDomain string) *NetworkMap { func (a *Account) GetPeerNetworkMap(peerID, dnsDomain string) *NetworkMap {
aclPeers := a.getPeersByACL(peerID) aclPeers, firewallRules := a.getPeerConnectionResources(peerID)
// exclude expired peers // exclude expired peers
var peersToConnect []*Peer var peersToConnect []*Peer
var expiredPeers []*Peer var expiredPeers []*Peer
@ -317,11 +317,12 @@ func (a *Account) GetPeerNetworkMap(peerID, dnsDomain string) *NetworkMap {
} }
return &NetworkMap{ return &NetworkMap{
Peers: peersToConnect, Peers: peersToConnect,
Network: a.Network.Copy(), Network: a.Network.Copy(),
Routes: routesUpdate, Routes: routesUpdate,
DNSConfig: dnsUpdate, DNSConfig: dnsUpdate,
OfflinePeers: expiredPeers, OfflinePeers: expiredPeers,
FirewallRules: firewallRules,
} }
} }

View File

@ -919,17 +919,14 @@ func TestAccountManager_NetworkUpdates(t *testing.T) {
Enabled: true, Enabled: true,
Rules: []*PolicyRule{ Rules: []*PolicyRule{
{ {
Enabled: true, Enabled: true,
Sources: []string{"group-id"}, Sources: []string{"group-id"},
Destinations: []string{"group-id"}, Destinations: []string{"group-id"},
Action: PolicyTrafficActionAccept, Bidirectional: true,
Action: PolicyTrafficActionAccept,
}, },
}, },
} }
if err := policy.UpdateQueryFromRules(); err != nil {
t.Errorf("update policy query from rules: %v", err)
return
}
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
t.Run("save group update", func(t *testing.T) { t.Run("save group update", func(t *testing.T) {

View File

@ -129,12 +129,11 @@ func restore(file string) (*FileStore, error) {
store.PrivateDomain2AccountID[account.Domain] = accountID store.PrivateDomain2AccountID[account.Domain] = accountID
} }
// TODO: policy query generated from the Go template and rule object. // TODO: delete this block after migration
// We need to refactor this part to avoid using templating for policies queries building
// and drop this migration part.
policies := make(map[string]int, len(account.Policies)) policies := make(map[string]int, len(account.Policies))
for i, policy := range account.Policies { for i, policy := range account.Policies {
policies[policy.ID] = i policies[policy.ID] = i
policy.UpgradeAndFix()
} }
if account.Policies == nil { if account.Policies == nil {
account.Policies = make([]*Policy, 0) account.Policies = make([]*Policy, 0)
@ -145,9 +144,9 @@ func restore(file string) (*FileStore, error) {
log.Errorf("unable to migrate rule to policy: %v", err) log.Errorf("unable to migrate rule to policy: %v", err)
continue continue
} }
if i, ok := policies[policy.ID]; ok { // don't update policies from rules, rules deprecated,
account.Policies[i] = policy // only append not existed rules as part of the migration process
} else { if _, ok := policies[policy.ID]; !ok {
account.Policies = append(account.Policies, policy) account.Policies = append(account.Policies, policy)
} }
} }

View File

@ -285,10 +285,7 @@ func TestRestorePolicies_Migration(t *testing.T) {
require.Equal(t, policy.Description, require.Equal(t, policy.Description,
"This is a default rule that allows connections between all the resources", "This is a default rule that allows connections between all the resources",
"failed to restore a FileStore file - missing Account Policies Description") "failed to restore a FileStore file - missing Account Policies Description")
expectedPolicy := policy.Copy()
err = expectedPolicy.UpdateQueryFromRules()
require.NoError(t, err, "failed to upldate query") require.NoError(t, err, "failed to upldate query")
require.Equal(t, policy.Query, expectedPolicy.Query, "failed to restore a FileStore file - missing Account Policies Query")
require.Len(t, policy.Rules, 1, "failed to restore a FileStore file - missing Account Policy Rules") require.Len(t, policy.Rules, 1, "failed to restore a FileStore file - missing Account Policy Rules")
require.Equal(t, policy.Rules[0].Action, PolicyTrafficActionAccept, "failed to restore a FileStore file - missing Account Policies Action") require.Equal(t, policy.Rules[0].Action, PolicyTrafficActionAccept, "failed to restore a FileStore file - missing Account Policies Action")
require.Equal(t, policy.Rules[0].Destinations, require.Equal(t, policy.Rules[0].Destinations,

View File

@ -436,6 +436,8 @@ func toSyncResponse(config *Config, peer *Peer, turnCredentials *TURNCredentials
offlinePeers := toRemotePeerConfig(networkMap.OfflinePeers, dnsName) offlinePeers := toRemotePeerConfig(networkMap.OfflinePeers, dnsName)
firewallRules := toProtocolFirewallRules(networkMap.FirewallRules)
return &proto.SyncResponse{ return &proto.SyncResponse{
WiretrusteeConfig: wtConfig, WiretrusteeConfig: wtConfig,
PeerConfig: pConfig, PeerConfig: pConfig,
@ -449,6 +451,7 @@ func toSyncResponse(config *Config, peer *Peer, turnCredentials *TURNCredentials
RemotePeersIsEmpty: len(remotePeers) == 0, RemotePeersIsEmpty: len(remotePeers) == 0,
Routes: routesUpdate, Routes: routesUpdate,
DNSConfig: dnsUpdate, DNSConfig: dnsUpdate,
FirewallRules: firewallRules,
}, },
} }
} }

View File

@ -551,49 +551,91 @@ components:
required: required:
- sources - sources
- destinations - destinations
PolicyRule: PolicyRuleMinimum:
type: object type: object
properties: properties:
id: id:
description: Rule ID description: Policy rule ID
type: string type: string
example: ch8i4ug6lnn4g9hqv7mg example: ch8i4ug6lnn4g9hqv7mg
name: name:
description: Rule name identifier description: Policy rule name identifier
type: string type: string
example: Default example: Default
description: description:
description: Rule friendly description description: Policy rule friendly description
type: string type: string
example: This is a default rule that allows connections between all the resources example: This is a default rule that allows connections between all the resources
enabled: enabled:
description: Rules status description: Policy rule status
type: boolean type: boolean
example: true
sources:
description: policy source groups
type: array
items:
$ref: '#/components/schemas/GroupMinimum'
destinations:
description: policy destination groups
type: array
items:
$ref: '#/components/schemas/GroupMinimum'
action: action:
description: policy accept or drops packets description: Policy rule accept or drops packets
type: string type: string
enum: ["accept","drop"] enum: ["accept","drop"]
example: accept bidirectional:
description: Define if the rule is applicable in both directions, sources, and destinations.
type: boolean
example: true
protocol:
description: Policy rule type of the traffic
type: string
enum: ["all", "tcp", "udp", "icmp"]
example: "tcp"
ports:
description: Policy rule affected ports or it ranges list
type: array
items:
type: string
example: [80,443]
required: required:
- name - name
- sources
- destinations
- action
- enabled - enabled
- bidirectional
- protocol
- action
PolicyRuleUpdate:
allOf:
- $ref: '#/components/schemas/PolicyRuleMinimum'
- type: object
properties:
sources:
description: Policy rule source groups
type: array
items:
type: string
destinations:
description: Policy rule destination groups
type: array
items:
type: string
required:
- sources
- destinations
PolicyRule:
allOf:
- $ref: '#/components/schemas/PolicyRuleMinimum'
- type: object
properties:
sources:
description: Policy rule source groups
type: array
items:
$ref: '#/components/schemas/GroupMinimum'
destinations:
description: Policy rule destination groups
type: array
items:
$ref: '#/components/schemas/GroupMinimum'
required:
- sources
- destinations
PolicyMinimum: PolicyMinimum:
type: object type: object
properties: properties:
id:
description: Policy ID
type: string
name: name:
description: Policy name identifier description: Policy name identifier
type: string type: string
@ -609,29 +651,35 @@ components:
query: query:
description: Policy Rego query description: Policy Rego query
type: string type: string
example: package netbird\n\nall[rule] {\n is_peer_in_any_group([\"ch8i4ug6lnn4g9hqv7m0\",\"ch8i4ug6lnn4g9hqv7m0\"])\n rule := {\n rules_from_group(\"ch8i4ug6lnn4g9hqv7m0\", \"dst\", \"accept\", \"\"),\n rules_from_group(\"ch8i4ug6lnn4g9hqv7m0\", \"src\", \"accept\", \"\"),\n }[_][_]\n}\n
rules:
description: Policy rule object for policy UI editor
type: array
items:
$ref: '#/components/schemas/PolicyRule'
required: required:
- name - name
- description - description
- enabled - enabled
- query - query
- rules PolicyUpdate:
allOf:
- $ref: '#/components/schemas/PolicyMinimum'
- type: object
properties:
rules:
description: Policy rule object for policy UI editor
type: array
items:
$ref: '#/components/schemas/PolicyRuleUpdate'
required:
- rules
Policy: Policy:
allOf: allOf:
- $ref: '#/components/schemas/PolicyMinimum' - $ref: '#/components/schemas/PolicyMinimum'
- type: object - type: object
properties: properties:
id: rules:
description: Policy ID description: Policy rule object for policy UI editor
type: string type: array
example: ch8i4ug6lnn4g9hqv7mg items:
$ref: '#/components/schemas/PolicyRule'
required: required:
- id - rules
RouteRequest: RouteRequest:
type: object type: object
properties: properties:
@ -884,7 +932,7 @@ security:
paths: paths:
/api/accounts: /api/accounts:
get: get:
summary: List all Accounts summary: List all accounts
description: Returns a list of accounts of a user. Always returns a list of one account. description: Returns a list of accounts of a user. Always returns a list of one account.
tags: [ Accounts ] tags: [ Accounts ]
security: security:
@ -909,7 +957,7 @@ paths:
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
/api/accounts/{accountId}: /api/accounts/{accountId}:
put: put:
summary: Update an Account summary: Update an account
description: Update information about an account description: Update information about an account
tags: [ Accounts ] tags: [ Accounts ]
security: security:
@ -950,7 +998,7 @@ paths:
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
/api/users: /api/users:
get: get:
summary: List all Users summary: List all users
description: Returns a list of all users description: Returns a list of all users
tags: [ Users ] tags: [ Users ]
security: security:
@ -980,7 +1028,7 @@ paths:
'500': '500':
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
post: post:
summary: Create a User summary: Create a user
description: Creates a new service user or sends an invite to a regular user description: Creates a new service user or sends an invite to a regular user
tags: [ Users ] tags: [ Users ]
security: security:
@ -1009,7 +1057,7 @@ paths:
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
/api/users/{userId}: /api/users/{userId}:
put: put:
summary: Update a User summary: Update a user
description: Update information about a User description: Update information about a User
tags: [ Users ] tags: [ Users ]
security: security:
@ -1044,8 +1092,8 @@ paths:
'500': '500':
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
delete: delete:
summary: Delete a User summary: Delete a user
description: Delete a User description: Delete a user
tags: [ Users ] tags: [ Users ]
security: security:
- BearerAuth: [ ] - BearerAuth: [ ]
@ -1071,7 +1119,7 @@ paths:
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
/api/users/{userId}/tokens: /api/users/{userId}/tokens:
get: get:
summary: List all Tokens summary: List all tokens
description: Returns a list of all tokens for a user description: Returns a list of all tokens for a user
tags: [ Tokens ] tags: [ Tokens ]
security: security:
@ -1102,7 +1150,7 @@ paths:
'500': '500':
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
post: post:
summary: Create a Token summary: Create a token
description: Create a new token for a user description: Create a new token for a user
tags: [ Tokens ] tags: [ Tokens ]
security: security:
@ -1138,7 +1186,7 @@ paths:
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
/api/users/{userId}/tokens/{tokenId}: /api/users/{userId}/tokens/{tokenId}:
get: get:
summary: Retrieve a Token summary: Retrieve a token
description: Returns a specific token for a user description: Returns a specific token for a user
tags: [ Tokens ] tags: [ Tokens ]
security: security:
@ -1173,7 +1221,7 @@ paths:
'500': '500':
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
delete: delete:
summary: Delete a Token summary: Delete a token
description: Delete a token for a user description: Delete a token for a user
tags: [ Tokens ] tags: [ Tokens ]
security: security:
@ -1206,7 +1254,7 @@ paths:
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
/api/peers: /api/peers:
get: get:
summary: List all Peers summary: List all peers
description: Returns a list of all peers description: Returns a list of all peers
tags: [ Peers ] tags: [ Peers ]
security: security:
@ -1231,7 +1279,7 @@ paths:
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
/api/peers/{peerId}: /api/peers/{peerId}:
get: get:
summary: Retrieve a Peer summary: Retrieve a peer
description: Get information about a peer description: Get information about a peer
tags: [ Peers ] tags: [ Peers ]
security: security:
@ -1260,7 +1308,7 @@ paths:
'500': '500':
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
put: put:
summary: Update a Peer summary: Update a peer
description: Update information about a peer description: Update information about a peer
tags: [ Peers ] tags: [ Peers ]
security: security:
@ -1295,7 +1343,7 @@ paths:
'500': '500':
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
delete: delete:
summary: Delete a Peer summary: Delete a peer
description: Delete a peer description: Delete a peer
tags: [ Peers ] tags: [ Peers ]
security: security:
@ -1322,7 +1370,7 @@ paths:
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
/api/setup-keys: /api/setup-keys:
get: get:
summary: List all Setup Keys summary: List all setup keys
description: Returns a list of all Setup Keys description: Returns a list of all Setup Keys
tags: [ Setup Keys ] tags: [ Setup Keys ]
security: security:
@ -1346,8 +1394,8 @@ paths:
'500': '500':
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
post: post:
summary: Create a Setup Key summary: Create a setup key
description: Creates a Setup Key description: Creates a setup key
tags: [ Setup Keys ] tags: [ Setup Keys ]
security: security:
- BearerAuth: [ ] - BearerAuth: [ ]
@ -1375,8 +1423,8 @@ paths:
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
/api/setup-keys/{keyId}: /api/setup-keys/{keyId}:
get: get:
summary: Retrieve a Setup Key summary: Retrieve a setup key
description: Get information about a Setup Key description: Get information about a setup key
tags: [ Setup Keys ] tags: [ Setup Keys ]
security: security:
- BearerAuth: [ ] - BearerAuth: [ ]
@ -1404,8 +1452,8 @@ paths:
'500': '500':
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
put: put:
summary: Update a Setup Key summary: Update a setup key
description: Update information about a Setup Key description: Update information about a setup key
tags: [ Setup Keys ] tags: [ Setup Keys ]
security: security:
- BearerAuth: [ ] - BearerAuth: [ ]
@ -1440,8 +1488,8 @@ paths:
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
/api/groups: /api/groups:
get: get:
summary: List all Groups summary: List all groups
description: Returns a list of all Groups description: Returns a list of all groups
tags: [ Groups ] tags: [ Groups ]
security: security:
- BearerAuth: [ ] - BearerAuth: [ ]
@ -1464,8 +1512,8 @@ paths:
'500': '500':
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
post: post:
summary: Create a Group summary: Create a group
description: Creates a Group description: Creates a group
tags: [ Groups ] tags: [ Groups ]
security: security:
- BearerAuth: [ ] - BearerAuth: [ ]
@ -1493,8 +1541,8 @@ paths:
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
/api/groups/{groupId}: /api/groups/{groupId}:
get: get:
summary: Retrieve a Group summary: Retrieve a group
description: Get information about a Group description: Get information about a group
tags: [ Groups ] tags: [ Groups ]
security: security:
- BearerAuth: [ ] - BearerAuth: [ ]
@ -1522,8 +1570,8 @@ paths:
'500': '500':
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
put: put:
summary: Update a Group summary: Update a group
description: Update/Replace a Group description: Update/Replace a group
tags: [ Groups ] tags: [ Groups ]
security: security:
- BearerAuth: [ ] - BearerAuth: [ ]
@ -1558,7 +1606,7 @@ paths:
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
delete: delete:
summary: Delete a Group summary: Delete a Group
description: Delete a Group description: Delete a group
tags: [ Groups ] tags: [ Groups ]
security: security:
- BearerAuth: [ ] - BearerAuth: [ ]
@ -1584,8 +1632,8 @@ paths:
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
/api/rules: /api/rules:
get: get:
summary: List all Rules summary: List all rules
description: Returns a list of all Rules description: Returns a list of all rules
tags: [ Rules ] tags: [ Rules ]
security: security:
- BearerAuth: [ ] - BearerAuth: [ ]
@ -1608,8 +1656,8 @@ paths:
'500': '500':
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
post: post:
summary: Create a Rule summary: Create a rule
description: Creates a Rule description: Creates a rule
tags: [ Rules ] tags: [ Rules ]
security: security:
- BearerAuth: [ ] - BearerAuth: [ ]
@ -1629,8 +1677,8 @@ paths:
$ref: '#/components/schemas/Rule' $ref: '#/components/schemas/Rule'
/api/rules/{ruleId}: /api/rules/{ruleId}:
get: get:
summary: Retrieve a Rule summary: Retrieve a rule
description: Get information about a Rules description: Get information about a rules
tags: [ Rules ] tags: [ Rules ]
security: security:
- BearerAuth: [ ] - BearerAuth: [ ]
@ -1658,8 +1706,8 @@ paths:
'500': '500':
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
put: put:
summary: Update a Rule summary: Update a rule
description: Update/Replace a Rule description: Update/Replace a rule
tags: [ Rules ] tags: [ Rules ]
security: security:
- BearerAuth: [ ] - BearerAuth: [ ]
@ -1693,8 +1741,8 @@ paths:
'500': '500':
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
delete: delete:
summary: Delete a Rule summary: Delete a rule
description: Delete a Rule description: Delete a rule
tags: [ Rules ] tags: [ Rules ]
security: security:
- BearerAuth: [ ] - BearerAuth: [ ]
@ -1720,8 +1768,8 @@ paths:
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
/api/policies: /api/policies:
get: get:
summary: List all Policies summary: List all policies
description: Returns a list of all Policies description: Returns a list of all policies
tags: [ Policies ] tags: [ Policies ]
security: security:
- BearerAuth: [ ] - BearerAuth: [ ]
@ -1744,8 +1792,8 @@ paths:
'500': '500':
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
post: post:
summary: Create a Policy summary: Create a policy
description: Creates a Policy description: Creates a policy
tags: [ Policies ] tags: [ Policies ]
security: security:
- BearerAuth: [ ] - BearerAuth: [ ]
@ -1755,7 +1803,7 @@ paths:
content: content:
'application/json': 'application/json':
schema: schema:
$ref: '#/components/schemas/PolicyMinimum' $ref: '#/components/schemas/PolicyUpdate'
responses: responses:
'200': '200':
description: A Policy Object description: A Policy Object
@ -1765,7 +1813,7 @@ paths:
$ref: '#/components/schemas/Policy' $ref: '#/components/schemas/Policy'
/api/policies/{policyId}: /api/policies/{policyId}:
get: get:
summary: Retrieve a Policy summary: Retrieve a policy
description: Get information about a Policies description: Get information about a Policies
tags: [ Policies ] tags: [ Policies ]
security: security:
@ -1794,7 +1842,7 @@ paths:
'500': '500':
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
put: put:
summary: Update a Policy summary: Update a policy
description: Update/Replace a Policy description: Update/Replace a Policy
tags: [ Policies ] tags: [ Policies ]
security: security:
@ -1812,7 +1860,7 @@ paths:
content: content:
'application/json': 'application/json':
schema: schema:
$ref: '#/components/schemas/PolicyMinimum' $ref: '#/components/schemas/PolicyUpdate'
responses: responses:
'200': '200':
description: A Policy object description: A Policy object
@ -1830,7 +1878,7 @@ paths:
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
delete: delete:
summary: Delete a Policy summary: Delete a Policy
description: Delete a Policy description: Delete a policy
tags: [ Policies ] tags: [ Policies ]
security: security:
- BearerAuth: [ ] - BearerAuth: [ ]
@ -1856,7 +1904,7 @@ paths:
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
/api/routes: /api/routes:
get: get:
summary: List all Routes summary: List all routes
description: Returns a list of all routes description: Returns a list of all routes
tags: [ Routes ] tags: [ Routes ]
security: security:
@ -1880,7 +1928,7 @@ paths:
'500': '500':
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
post: post:
summary: Create a Route summary: Create a route
description: Creates a Route description: Creates a Route
tags: [ Routes ] tags: [ Routes ]
security: security:
@ -1910,7 +1958,7 @@ paths:
/api/routes/{routeId}: /api/routes/{routeId}:
get: get:
summary: Retrieve a Route summary: Retrieve a route
description: Get information about a Routes description: Get information about a Routes
tags: [ Routes ] tags: [ Routes ]
security: security:
@ -1939,7 +1987,7 @@ paths:
'500': '500':
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
put: put:
summary: Update a Route summary: Update a route
description: Update/Replace a Route description: Update/Replace a Route
tags: [ Routes ] tags: [ Routes ]
security: security:
@ -1975,7 +2023,7 @@ paths:
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
delete: delete:
summary: Delete a Route summary: Delete a Route
description: Delete a Route description: Delete a route
tags: [ Routes ] tags: [ Routes ]
security: security:
- BearerAuth: [ ] - BearerAuth: [ ]
@ -2001,7 +2049,7 @@ paths:
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
/api/dns/nameservers: /api/dns/nameservers:
get: get:
summary: List all Nameserver Groups summary: List all nameserver groups
description: Returns a list of all Nameserver Groups description: Returns a list of all Nameserver Groups
tags: [ DNS ] tags: [ DNS ]
security: security:
@ -2025,7 +2073,7 @@ paths:
'500': '500':
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
post: post:
summary: Create a Nameserver Group summary: Create a nameserver group
description: Creates a Nameserver Group description: Creates a Nameserver Group
tags: [ DNS ] tags: [ DNS ]
security: security:
@ -2052,9 +2100,10 @@ paths:
"$ref": "#/components/responses/forbidden" "$ref": "#/components/responses/forbidden"
'500': '500':
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
/api/dns/nameservers/{nsgroupId}: /api/dns/nameservers/{nsgroupId}:
get: get:
summary: Retrieve a Nameserver Group summary: Retrieve a nameserver group
description: Get information about a Nameserver Groups description: Get information about a Nameserver Groups
tags: [ DNS ] tags: [ DNS ]
security: security:
@ -2083,7 +2132,7 @@ paths:
'500': '500':
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
put: put:
summary: Update a Nameserver Group summary: Update a nameserver group
description: Update/Replace a Nameserver Group description: Update/Replace a Nameserver Group
tags: [ DNS ] tags: [ DNS ]
security: security:
@ -2118,7 +2167,7 @@ paths:
'500': '500':
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
delete: delete:
summary: Delete a Nameserver Group summary: Delete a nameserver group
description: Delete a Nameserver Group description: Delete a Nameserver Group
tags: [ DNS ] tags: [ DNS ]
security: security:
@ -2143,9 +2192,10 @@ paths:
"$ref": "#/components/responses/forbidden" "$ref": "#/components/responses/forbidden"
'500': '500':
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
/api/dns/settings: /api/dns/settings:
get: get:
summary: Retrieve DNS Settings summary: Retrieve DNS settings
description: Returns a DNS settings object description: Returns a DNS settings object
tags: [ DNS ] tags: [ DNS ]
security: security:
@ -2168,7 +2218,7 @@ paths:
'500': '500':
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
put: put:
summary: Update DNS Settings summary: Update DNS settings
description: Updates a DNS settings object description: Updates a DNS settings object
tags: [ DNS ] tags: [ DNS ]
security: security:
@ -2197,7 +2247,7 @@ paths:
"$ref": "#/components/responses/internal_error" "$ref": "#/components/responses/internal_error"
/api/events: /api/events:
get: get:
summary: List all Events summary: List all events
description: Returns a list of all events description: Returns a list of all events
tags: [ Events ] tags: [ Events ]
security: security:

View File

@ -72,6 +72,42 @@ const (
PolicyRuleActionDrop PolicyRuleAction = "drop" PolicyRuleActionDrop PolicyRuleAction = "drop"
) )
// Defines values for PolicyRuleProtocol.
const (
PolicyRuleProtocolAll PolicyRuleProtocol = "all"
PolicyRuleProtocolIcmp PolicyRuleProtocol = "icmp"
PolicyRuleProtocolTcp PolicyRuleProtocol = "tcp"
PolicyRuleProtocolUdp PolicyRuleProtocol = "udp"
)
// Defines values for PolicyRuleMinimumAction.
const (
PolicyRuleMinimumActionAccept PolicyRuleMinimumAction = "accept"
PolicyRuleMinimumActionDrop PolicyRuleMinimumAction = "drop"
)
// Defines values for PolicyRuleMinimumProtocol.
const (
PolicyRuleMinimumProtocolAll PolicyRuleMinimumProtocol = "all"
PolicyRuleMinimumProtocolIcmp PolicyRuleMinimumProtocol = "icmp"
PolicyRuleMinimumProtocolTcp PolicyRuleMinimumProtocol = "tcp"
PolicyRuleMinimumProtocolUdp PolicyRuleMinimumProtocol = "udp"
)
// Defines values for PolicyRuleUpdateAction.
const (
PolicyRuleUpdateActionAccept PolicyRuleUpdateAction = "accept"
PolicyRuleUpdateActionDrop PolicyRuleUpdateAction = "drop"
)
// Defines values for PolicyRuleUpdateProtocol.
const (
PolicyRuleUpdateProtocolAll PolicyRuleUpdateProtocol = "all"
PolicyRuleUpdateProtocolIcmp PolicyRuleUpdateProtocol = "icmp"
PolicyRuleUpdateProtocolTcp PolicyRuleUpdateProtocol = "tcp"
PolicyRuleUpdateProtocolUdp PolicyRuleUpdateProtocol = "udp"
)
// Defines values for UserStatus. // Defines values for UserStatus.
const ( const (
UserStatusActive UserStatus = "active" UserStatusActive UserStatus = "active"
@ -344,7 +380,7 @@ type Policy struct {
Enabled bool `json:"enabled"` Enabled bool `json:"enabled"`
// Id Policy ID // Id Policy ID
Id string `json:"id"` Id *string `json:"id,omitempty"`
// Name Policy name identifier // Name Policy name identifier
Name string `json:"name"` Name string `json:"name"`
@ -364,6 +400,138 @@ type PolicyMinimum struct {
// Enabled Policy status // Enabled Policy status
Enabled bool `json:"enabled"` Enabled bool `json:"enabled"`
// Id Policy ID
Id *string `json:"id,omitempty"`
// Name Policy name identifier
Name string `json:"name"`
// Query Policy Rego query
Query string `json:"query"`
}
// PolicyRule defines model for PolicyRule.
type PolicyRule struct {
// Action Policy rule accept or drops packets
Action PolicyRuleAction `json:"action"`
// Bidirectional Define if the rule is applicable in both directions, sources, and destinations.
Bidirectional bool `json:"bidirectional"`
// Description Policy rule friendly description
Description *string `json:"description,omitempty"`
// Destinations Policy rule destination groups
Destinations []GroupMinimum `json:"destinations"`
// Enabled Policy rule status
Enabled bool `json:"enabled"`
// Id Policy rule ID
Id *string `json:"id,omitempty"`
// Name Policy rule name identifier
Name string `json:"name"`
// Ports Policy rule affected ports or it ranges list
Ports *[]string `json:"ports,omitempty"`
// Protocol Policy rule type of the traffic
Protocol PolicyRuleProtocol `json:"protocol"`
// Sources Policy rule source groups
Sources []GroupMinimum `json:"sources"`
}
// PolicyRuleAction Policy rule accept or drops packets
type PolicyRuleAction string
// PolicyRuleProtocol Policy rule type of the traffic
type PolicyRuleProtocol string
// PolicyRuleMinimum defines model for PolicyRuleMinimum.
type PolicyRuleMinimum struct {
// Action Policy rule accept or drops packets
Action PolicyRuleMinimumAction `json:"action"`
// Bidirectional Define if the rule is applicable in both directions, sources, and destinations.
Bidirectional bool `json:"bidirectional"`
// Description Policy rule friendly description
Description *string `json:"description,omitempty"`
// Enabled Policy rule status
Enabled bool `json:"enabled"`
// Id Policy rule ID
Id *string `json:"id,omitempty"`
// Name Policy rule name identifier
Name string `json:"name"`
// Ports Policy rule affected ports or it ranges list
Ports *[]string `json:"ports,omitempty"`
// Protocol Policy rule type of the traffic
Protocol PolicyRuleMinimumProtocol `json:"protocol"`
}
// PolicyRuleMinimumAction Policy rule accept or drops packets
type PolicyRuleMinimumAction string
// PolicyRuleMinimumProtocol Policy rule type of the traffic
type PolicyRuleMinimumProtocol string
// PolicyRuleUpdate defines model for PolicyRuleUpdate.
type PolicyRuleUpdate struct {
// Action Policy rule accept or drops packets
Action PolicyRuleUpdateAction `json:"action"`
// Bidirectional Define if the rule is applicable in both directions, sources, and destinations.
Bidirectional bool `json:"bidirectional"`
// Description Policy rule friendly description
Description *string `json:"description,omitempty"`
// Destinations Policy rule destination groups
Destinations []string `json:"destinations"`
// Enabled Policy rule status
Enabled bool `json:"enabled"`
// Id Policy rule ID
Id *string `json:"id,omitempty"`
// Name Policy rule name identifier
Name string `json:"name"`
// Ports Policy rule affected ports or it ranges list
Ports *[]string `json:"ports,omitempty"`
// Protocol Policy rule type of the traffic
Protocol PolicyRuleUpdateProtocol `json:"protocol"`
// Sources Policy rule source groups
Sources []string `json:"sources"`
}
// PolicyRuleUpdateAction Policy rule accept or drops packets
type PolicyRuleUpdateAction string
// PolicyRuleUpdateProtocol Policy rule type of the traffic
type PolicyRuleUpdateProtocol string
// PolicyUpdate defines model for PolicyUpdate.
type PolicyUpdate struct {
// Description Policy friendly description
Description string `json:"description"`
// Enabled Policy status
Enabled bool `json:"enabled"`
// Id Policy ID
Id *string `json:"id,omitempty"`
// Name Policy name identifier // Name Policy name identifier
Name string `json:"name"` Name string `json:"name"`
@ -371,36 +539,9 @@ type PolicyMinimum struct {
Query string `json:"query"` Query string `json:"query"`
// Rules Policy rule object for policy UI editor // Rules Policy rule object for policy UI editor
Rules []PolicyRule `json:"rules"` Rules []PolicyRuleUpdate `json:"rules"`
} }
// PolicyRule defines model for PolicyRule.
type PolicyRule struct {
// Action policy accept or drops packets
Action PolicyRuleAction `json:"action"`
// Description Rule friendly description
Description *string `json:"description,omitempty"`
// Destinations policy destination groups
Destinations []GroupMinimum `json:"destinations"`
// Enabled Rules status
Enabled bool `json:"enabled"`
// Id Rule ID
Id *string `json:"id,omitempty"`
// Name Rule name identifier
Name string `json:"name"`
// Sources policy source groups
Sources []GroupMinimum `json:"sources"`
}
// PolicyRuleAction policy accept or drops packets
type PolicyRuleAction string
// Route defines model for Route. // Route defines model for Route.
type Route struct { type Route struct {
// Description Route description // Description Route description
@ -680,10 +821,10 @@ type PutApiGroupsGroupIdJSONRequestBody = GroupRequest
type PutApiPeersPeerIdJSONRequestBody = PeerRequest type PutApiPeersPeerIdJSONRequestBody = PeerRequest
// PostApiPoliciesJSONRequestBody defines body for PostApiPolicies for application/json ContentType. // PostApiPoliciesJSONRequestBody defines body for PostApiPolicies for application/json ContentType.
type PostApiPoliciesJSONRequestBody = PolicyMinimum type PostApiPoliciesJSONRequestBody = PolicyUpdate
// PutApiPoliciesPolicyIdJSONRequestBody defines body for PutApiPoliciesPolicyId for application/json ContentType. // PutApiPoliciesPolicyIdJSONRequestBody defines body for PutApiPoliciesPolicyId for application/json ContentType.
type PutApiPoliciesPolicyIdJSONRequestBody = PolicyMinimum type PutApiPoliciesPolicyIdJSONRequestBody = PolicyUpdate
// PostApiRoutesJSONRequestBody defines body for PostApiRoutes for application/json ContentType. // PostApiRoutesJSONRequestBody defines body for PostApiRoutes for application/json ContentType.
type PostApiRoutesJSONRequestBody = RouteRequest type PostApiRoutesJSONRequestBody = RouteRequest

View File

@ -6,7 +6,6 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/rs/xid" "github.com/rs/xid"
log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/management/server" "github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/http/api" "github.com/netbirdio/netbird/management/server/http/api"
@ -47,7 +46,17 @@ func (h *Policies) GetAllPolicies(w http.ResponseWriter, r *http.Request) {
return return
} }
util.WriteJSONObject(w, accountPolicies) policies := []*api.Policy{}
for _, policy := range accountPolicies {
resp := toPolicyResponse(account, policy)
if len(resp.Rules) == 0 {
util.WriteError(status.Errorf(status.Internal, "no rules in the policy"), w)
return
}
policies = append(policies, resp)
}
util.WriteJSONObject(w, policies)
} }
// UpdatePolicy handles update to a policy identified by a given ID // UpdatePolicy handles update to a policy identified by a given ID
@ -78,63 +87,7 @@ func (h *Policies) UpdatePolicy(w http.ResponseWriter, r *http.Request) {
return return
} }
var req api.PutApiPoliciesPolicyIdJSONRequestBody h.savePolicy(w, r, account, user, policyID)
err = json.NewDecoder(r.Body).Decode(&req)
if err != nil {
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
return
}
if req.Name == "" {
util.WriteError(status.Errorf(status.InvalidArgument, "policy name shouldn't be empty"), w)
return
}
policy := server.Policy{
ID: policyID,
Name: req.Name,
Enabled: req.Enabled,
Description: req.Description,
Query: req.Query,
}
if req.Rules != nil {
for _, r := range req.Rules {
pr := server.PolicyRule{
Destinations: groupMinimumsToStrings(account, r.Destinations),
Sources: groupMinimumsToStrings(account, r.Sources),
Name: r.Name,
}
pr.Enabled = r.Enabled
if r.Description != nil {
pr.Description = *r.Description
}
if r.Id != nil {
pr.ID = *r.Id
}
switch r.Action {
case api.PolicyRuleActionAccept:
pr.Action = server.PolicyTrafficActionAccept
case api.PolicyRuleActionDrop:
pr.Action = server.PolicyTrafficActionDrop
default:
util.WriteError(status.Errorf(status.InvalidArgument, "unknown action type"), w)
return
}
policy.Rules = append(policy.Rules, &pr)
}
}
if err := policy.UpdateQueryFromRules(); err != nil {
log.Errorf("failed to update policy query: %v", err)
util.WriteError(err, w)
return
}
if err = h.accountManager.SavePolicy(account.Id, user.Id, &policy); err != nil {
util.WriteError(err, w)
return
}
util.WriteJSONObject(w, toPolicyResponse(account, &policy))
} }
// CreatePolicy handles policy creation request // CreatePolicy handles policy creation request
@ -146,9 +99,19 @@ func (h *Policies) CreatePolicy(w http.ResponseWriter, r *http.Request) {
return return
} }
var req api.PostApiPoliciesJSONRequestBody h.savePolicy(w, r, account, user, "")
err = json.NewDecoder(r.Body).Decode(&req) }
if err != nil {
// savePolicy handles policy creation and update
func (h *Policies) savePolicy(
w http.ResponseWriter,
r *http.Request,
account *server.Account,
user *server.User,
policyID string,
) {
var req api.PutApiPoliciesPolicyIdJSONRequestBody
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w) util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
return return
} }
@ -158,49 +121,97 @@ func (h *Policies) CreatePolicy(w http.ResponseWriter, r *http.Request) {
return return
} }
policy := &server.Policy{ if len(req.Rules) == 0 {
ID: xid.New().String(), util.WriteError(status.Errorf(status.InvalidArgument, "policy rules shouldn't be empty"), w)
return
}
if policyID == "" {
policyID = xid.New().String()
}
policy := server.Policy{
ID: policyID,
Name: req.Name, Name: req.Name,
Enabled: req.Enabled, Enabled: req.Enabled,
Description: req.Description, Description: req.Description,
Query: req.Query,
} }
for _, r := range req.Rules {
pr := server.PolicyRule{
ID: policyID, //TODO: when policy can contain multiple rules, need refactor
Name: r.Name,
Destinations: groupMinimumsToStrings(account, r.Destinations),
Sources: groupMinimumsToStrings(account, r.Sources),
Bidirectional: r.Bidirectional,
}
if req.Rules != nil { pr.Enabled = r.Enabled
for _, r := range req.Rules { if r.Description != nil {
pr := server.PolicyRule{ pr.Description = *r.Description
ID: xid.New().String(), }
Destinations: groupMinimumsToStrings(account, r.Destinations),
Sources: groupMinimumsToStrings(account, r.Sources), switch r.Action {
Name: r.Name, case api.PolicyRuleUpdateActionAccept:
} pr.Action = server.PolicyTrafficActionAccept
pr.Enabled = r.Enabled case api.PolicyRuleUpdateActionDrop:
if r.Description != nil { pr.Action = server.PolicyTrafficActionDrop
pr.Description = *r.Description default:
} util.WriteError(status.Errorf(status.InvalidArgument, "unknown action type"), w)
switch r.Action { return
case api.PolicyRuleActionAccept: }
pr.Action = server.PolicyTrafficActionAccept
case api.PolicyRuleActionDrop: switch r.Protocol {
pr.Action = server.PolicyTrafficActionDrop case api.PolicyRuleUpdateProtocolAll:
default: pr.Protocol = server.PolicyRuleProtocolALL
util.WriteError(status.Errorf(status.InvalidArgument, "unknown action type"), w) case api.PolicyRuleUpdateProtocolTcp:
pr.Protocol = server.PolicyRuleProtocolTCP
case api.PolicyRuleUpdateProtocolUdp:
pr.Protocol = server.PolicyRuleProtocolUDP
case api.PolicyRuleUpdateProtocolIcmp:
pr.Protocol = server.PolicyRuleProtocolICMP
default:
util.WriteError(status.Errorf(status.InvalidArgument, "unknown protocol type: %v", r.Protocol), w)
return
}
if r.Ports != nil && len(*r.Ports) != 0 {
ports := *r.Ports
pr.Ports = ports[:]
}
// validate policy object
switch pr.Protocol {
case server.PolicyRuleProtocolALL, server.PolicyRuleProtocolICMP:
if len(pr.Ports) != 0 {
util.WriteError(status.Errorf(status.InvalidArgument, "for ALL or ICMP protocol ports is not allowed"), w)
return
}
if !pr.Bidirectional {
util.WriteError(status.Errorf(status.InvalidArgument, "for ALL or ICMP protocol type flow can be only bi-directional"), w)
return
}
case server.PolicyRuleProtocolTCP, server.PolicyRuleProtocolUDP:
if !pr.Bidirectional && len(pr.Ports) == 0 {
util.WriteError(status.Errorf(status.InvalidArgument, "for ALL or ICMP protocol type flow can be only bi-directional"), w)
return return
} }
policy.Rules = append(policy.Rules, &pr)
} }
policy.Rules = append(policy.Rules, &pr)
} }
if err := policy.UpdateQueryFromRules(); err != nil {
if err := h.accountManager.SavePolicy(account.Id, user.Id, &policy); err != nil {
util.WriteError(err, w) util.WriteError(err, w)
return return
} }
if err = h.accountManager.SavePolicy(account.Id, user.Id, policy); err != nil { resp := toPolicyResponse(account, &policy)
util.WriteError(err, w) if len(resp.Rules) == 0 {
util.WriteError(status.Errorf(status.Internal, "no rules in the policy"), w)
return return
} }
util.WriteJSONObject(w, toPolicyResponse(account, policy)) util.WriteJSONObject(w, resp)
} }
// DeletePolicy handles policy deletion request // DeletePolicy handles policy deletion request
@ -252,7 +263,13 @@ func (h *Policies) GetPolicy(w http.ResponseWriter, r *http.Request) {
return return
} }
util.WriteJSONObject(w, toPolicyResponse(account, policy)) resp := toPolicyResponse(account, policy)
if len(resp.Rules) == 0 {
util.WriteError(status.Errorf(status.Internal, "no rules in the policy"), w)
return
}
util.WriteJSONObject(w, resp)
default: default:
util.WriteError(status.Errorf(status.NotFound, "method not found"), w) util.WriteError(status.Errorf(status.NotFound, "method not found"), w)
} }
@ -261,22 +278,24 @@ func (h *Policies) GetPolicy(w http.ResponseWriter, r *http.Request) {
func toPolicyResponse(account *server.Account, policy *server.Policy) *api.Policy { func toPolicyResponse(account *server.Account, policy *server.Policy) *api.Policy {
cache := make(map[string]api.GroupMinimum) cache := make(map[string]api.GroupMinimum)
ap := &api.Policy{ ap := &api.Policy{
Id: policy.ID, Id: &policy.ID,
Name: policy.Name, Name: policy.Name,
Description: policy.Description, Description: policy.Description,
Enabled: policy.Enabled, Enabled: policy.Enabled,
Query: policy.Query,
} }
if len(policy.Rules) == 0 {
return ap
}
for _, r := range policy.Rules { for _, r := range policy.Rules {
rule := api.PolicyRule{ rule := api.PolicyRule{
Id: &r.ID, Id: &r.ID,
Name: r.Name, Name: r.Name,
Enabled: r.Enabled, Enabled: r.Enabled,
Description: &r.Description, Description: &r.Description,
Bidirectional: r.Bidirectional,
Protocol: api.PolicyRuleProtocol(r.Protocol),
Action: api.PolicyRuleAction(r.Action),
}
if len(r.Ports) != 0 {
portsCopy := r.Ports[:]
rule.Ports = &portsCopy
} }
for _, gid := range r.Sources { for _, gid := range r.Sources {
_, ok := cache[gid] _, ok := cache[gid]
@ -314,13 +333,13 @@ func toPolicyResponse(account *server.Account, policy *server.Policy) *api.Polic
return ap return ap
} }
func groupMinimumsToStrings(account *server.Account, gm []api.GroupMinimum) []string { func groupMinimumsToStrings(account *server.Account, gm []string) []string {
result := make([]string, 0, len(gm)) result := make([]string, 0, len(gm))
for _, gm := range gm { for _, g := range gm {
if _, ok := account.Groups[gm.Id]; ok { if _, ok := account.Groups[g]; !ok {
continue continue
} }
result = append(result, gm.Id) result = append(result, g)
} }
return result return result
} }

View File

@ -0,0 +1,315 @@
package http
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/status"
"github.com/gorilla/mux"
"github.com/netbirdio/netbird/management/server/jwtclaims"
"github.com/magiconair/properties/assert"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/mock_server"
)
func initPoliciesTestData(policies ...*server.Policy) *Policies {
testPolicies := make(map[string]*server.Policy, len(policies))
for _, policy := range policies {
testPolicies[policy.ID] = policy
}
return &Policies{
accountManager: &mock_server.MockAccountManager{
GetPolicyFunc: func(_, policyID, _ string) (*server.Policy, error) {
policy, ok := testPolicies[policyID]
if !ok {
return nil, status.Errorf(status.NotFound, "policy not found")
}
return policy, nil
},
SavePolicyFunc: func(_, _ string, policy *server.Policy) error {
if !strings.HasPrefix(policy.ID, "id-") {
policy.ID = "id-was-set"
policy.Rules[0].ID = "id-was-set"
}
return nil
},
SaveRuleFunc: func(_, _ string, rule *server.Rule) error {
if !strings.HasPrefix(rule.ID, "id-") {
rule.ID = "id-was-set"
}
return nil
},
GetRuleFunc: func(_, ruleID, _ string) (*server.Rule, error) {
if ruleID != "idoftherule" {
return nil, fmt.Errorf("not found")
}
return &server.Rule{
ID: "idoftherule",
Name: "Rule",
Source: []string{"idofsrcrule"},
Destination: []string{"idofdestrule"},
Flow: server.TrafficFlowBidirect,
}, nil
},
GetAccountFromTokenFunc: func(claims jwtclaims.AuthorizationClaims) (*server.Account, *server.User, error) {
user := server.NewAdminUser("test_user")
return &server.Account{
Id: claims.AccountId,
Domain: "hotmail.com",
Policies: []*server.Policy{
{ID: "id-existed"},
},
Groups: map[string]*server.Group{
"F": {ID: "F"},
"G": {ID: "G"},
},
Users: map[string]*server.User{
"test_user": user,
},
}, user, nil
},
},
claimsExtractor: jwtclaims.NewClaimsExtractor(
jwtclaims.WithFromRequestContext(func(r *http.Request) jwtclaims.AuthorizationClaims {
return jwtclaims.AuthorizationClaims{
UserId: "test_user",
Domain: "hotmail.com",
AccountId: "test_id",
}
}),
),
}
}
func TestPoliciesGetPolicy(t *testing.T) {
tt := []struct {
name string
expectedStatus int
expectedBody bool
requestType string
requestPath string
requestBody io.Reader
}{
{
name: "GetPolicy OK",
expectedBody: true,
requestType: http.MethodGet,
requestPath: "/api/policies/idofthepolicy",
expectedStatus: http.StatusOK,
},
{
name: "GetPolicy not found",
requestType: http.MethodGet,
requestPath: "/api/policies/notexists",
expectedStatus: http.StatusNotFound,
},
}
policy := &server.Policy{
ID: "idofthepolicy",
Name: "Rule",
Rules: []*server.PolicyRule{
{ID: "idoftherule", Name: "Rule"},
},
}
p := initPoliciesTestData(policy)
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
recorder := httptest.NewRecorder()
req := httptest.NewRequest(tc.requestType, tc.requestPath, tc.requestBody)
router := mux.NewRouter()
router.HandleFunc("/api/policies/{policyId}", p.GetPolicy).Methods("GET")
router.ServeHTTP(recorder, req)
res := recorder.Result()
defer res.Body.Close()
if status := recorder.Code; status != tc.expectedStatus {
t.Errorf("handler returned wrong status code: got %v want %v",
status, tc.expectedStatus)
return
}
if !tc.expectedBody {
return
}
content, err := io.ReadAll(res.Body)
if err != nil {
t.Fatalf("I don't know what I expected; %v", err)
}
var got api.Policy
if err = json.Unmarshal(content, &got); err != nil {
t.Fatalf("Sent content is not in correct json format; %v", err)
}
assert.Equal(t, *got.Id, policy.ID)
assert.Equal(t, got.Name, policy.Name)
})
}
}
func TestPoliciesWritePolicy(t *testing.T) {
str := func(s string) *string { return &s }
tt := []struct {
name string
expectedStatus int
expectedBody bool
expectedPolicy *api.Policy
requestType string
requestPath string
requestBody io.Reader
}{
{
name: "WritePolicy POST OK",
requestType: http.MethodPost,
requestPath: "/api/policies",
requestBody: bytes.NewBuffer(
[]byte(`{
"Name":"Default POSTed Policy",
"Rules":[
{
"Name":"Default POSTed Policy",
"Description": "Description",
"Protocol": "tcp",
"Action": "accept",
"Bidirectional":true
}
]}`)),
expectedStatus: http.StatusOK,
expectedBody: true,
expectedPolicy: &api.Policy{
Id: str("id-was-set"),
Name: "Default POSTed Policy",
Rules: []api.PolicyRule{
{
Id: str("id-was-set"),
Name: "Default POSTed Policy",
Description: str("Description"),
Protocol: "tcp",
Action: "accept",
Bidirectional: true,
},
},
},
},
{
name: "WritePolicy POST Invalid Name",
requestType: http.MethodPost,
requestPath: "/api/policies",
requestBody: bytes.NewBuffer(
[]byte(`{"Name":""}`)),
expectedStatus: http.StatusUnprocessableEntity,
expectedBody: false,
},
{
name: "WritePolicy PUT OK",
requestType: http.MethodPut,
requestPath: "/api/policies/id-existed",
requestBody: bytes.NewBuffer(
[]byte(`{
"ID": "id-existed",
"Name":"Default POSTed Policy",
"Rules":[
{
"ID": "id-existed",
"Name":"Default POSTed Policy",
"Description": "Description",
"Protocol": "tcp",
"Action": "accept",
"Bidirectional":true
}
]}`)),
expectedStatus: http.StatusOK,
expectedBody: true,
expectedPolicy: &api.Policy{
Id: str("id-existed"),
Name: "Default POSTed Policy",
Rules: []api.PolicyRule{
{
Id: str("id-existed"),
Name: "Default POSTed Policy",
Description: str("Description"),
Protocol: "tcp",
Action: "accept",
Bidirectional: true,
},
},
},
},
{
name: "WritePolicy PUT Invalid Name",
requestType: http.MethodPut,
requestPath: "/api/policies/id-existed",
requestBody: bytes.NewBuffer(
[]byte(`{"ID":"id-existed","Name":"","Rules":[{"ID":"id-existed"}]}`)),
expectedStatus: http.StatusUnprocessableEntity,
},
}
p := initPoliciesTestData(&server.Policy{
ID: "id-existed",
Name: "Default POSTed Rule",
Rules: []*server.PolicyRule{
{
ID: "id-existed",
Name: "Default POSTed Rule",
Bidirectional: true,
},
},
})
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
recorder := httptest.NewRecorder()
req := httptest.NewRequest(tc.requestType, tc.requestPath, tc.requestBody)
router := mux.NewRouter()
router.HandleFunc("/api/policies", p.CreatePolicy).Methods("POST")
router.HandleFunc("/api/policies/{policyId}", p.UpdatePolicy).Methods("PUT")
router.ServeHTTP(recorder, req)
res := recorder.Result()
defer res.Body.Close()
content, err := io.ReadAll(res.Body)
if err != nil {
t.Fatalf("I don't know what I expected; %v", err)
return
}
if status := recorder.Code; status != tc.expectedStatus {
t.Errorf("handler returned wrong status code: got %v want %v, content: %s",
status, tc.expectedStatus, string(content))
return
}
if !tc.expectedBody {
return
}
expected, err := json.Marshal(tc.expectedPolicy)
if err != nil {
t.Fatalf("marshal expected policy: %v", err)
return
}
assert.Equal(t, strings.Trim(string(content), " \n"), string(expected), "content mismatch")
})
}
}

View File

@ -112,10 +112,6 @@ func (h *RulesHandler) UpdateRule(w http.ResponseWriter, r *http.Request) {
policy.Rules[0].Destinations = reqDestinations policy.Rules[0].Destinations = reqDestinations
policy.Rules[0].Enabled = !req.Disabled policy.Rules[0].Enabled = !req.Disabled
policy.Rules[0].Description = req.Description policy.Rules[0].Description = req.Description
if err := policy.UpdateQueryFromRules(); err != nil {
util.WriteError(err, w)
return
}
switch req.Flow { switch req.Flow {
case server.TrafficFlowBidirectString: case server.TrafficFlowBidirectString:

View File

@ -30,9 +30,6 @@ func initRulesTestData(rules ...*server.Rule) *RulesHandler {
if err != nil { if err != nil {
panic(err) panic(err)
} }
if err := policy.UpdateQueryFromRules(); err != nil {
panic(err)
}
testPolicies[policy.ID] = policy testPolicies[policy.ID] = policy
} }
return &RulesHandler{ return &RulesHandler{

View File

@ -25,11 +25,12 @@ const (
) )
type NetworkMap struct { type NetworkMap struct {
Peers []*Peer Peers []*Peer
Network *Network Network *Network
Routes []*route.Route Routes []*route.Route
DNSConfig nbdns.Config DNSConfig nbdns.Config
OfflinePeers []*Peer OfflinePeers []*Peer
FirewallRules []*FirewallRule
} }
type Network struct { type Network struct {

View File

@ -225,8 +225,7 @@ func (am *DefaultAccountManager) GetPeers(accountID, userID string) ([]*Peer, er
// fetch all the peers that have access to the user's peers // fetch all the peers that have access to the user's peers
for _, peer := range peers { for _, peer := range peers {
// TODO: use firewall rules aclPeers, _ := account.getPeerConnectionResources(peer.ID)
aclPeers := account.getPeersByACL(peer.ID)
for _, p := range aclPeers { for _, p := range aclPeers {
peersMap[p.ID] = p peersMap[p.ID] = p
} }
@ -865,7 +864,7 @@ func (am *DefaultAccountManager) GetPeer(accountID, peerID, userID string) (*Pee
} }
for _, p := range userPeers { for _, p := range userPeers {
aclPeers := account.getPeersByACL(p.ID) aclPeers, _ := account.getPeerConnectionResources(p.ID)
for _, aclPeer := range aclPeers { for _, aclPeer := range aclPeers {
if aclPeer.ID == peerID { if aclPeer.ID == peerID {
return peer, nil return peer, nil
@ -884,98 +883,6 @@ func updatePeerMeta(peer *Peer, meta PeerSystemMeta, account *Account) (*Peer, b
return peer, false return peer, false
} }
// GetPeerRules returns a list of source or destination rules of a given peer.
func (a *Account) GetPeerRules(peerID string) (srcRules []*Rule, dstRules []*Rule) {
// Rules are group based so there is no direct access to peers.
// First, find all groups that the given peer belongs to
peerGroups := make(map[string]struct{})
for s, group := range a.Groups {
for _, peer := range group.Peers {
if peerID == peer {
peerGroups[s] = struct{}{}
break
}
}
}
// Second, find all rules that have discovered source and destination groups
srcRulesMap := make(map[string]*Rule)
dstRulesMap := make(map[string]*Rule)
for _, rule := range a.Rules {
for _, g := range rule.Source {
if _, ok := peerGroups[g]; ok && srcRulesMap[rule.ID] == nil {
srcRules = append(srcRules, rule)
srcRulesMap[rule.ID] = rule
}
}
for _, g := range rule.Destination {
if _, ok := peerGroups[g]; ok && dstRulesMap[rule.ID] == nil {
dstRules = append(dstRules, rule)
dstRulesMap[rule.ID] = rule
}
}
}
return srcRules, dstRules
}
// getPeersByACL returns all peers that given peer has access to.
func (a *Account) getPeersByACL(peerID string) []*Peer {
var peers []*Peer
srcRules, dstRules := a.GetPeerRules(peerID)
groups := map[string]*Group{}
for _, r := range srcRules {
if r.Disabled {
continue
}
if r.Flow == TrafficFlowBidirect {
for _, gid := range r.Destination {
if group, ok := a.Groups[gid]; ok {
groups[gid] = group
}
}
}
}
for _, r := range dstRules {
if r.Disabled {
continue
}
if r.Flow == TrafficFlowBidirect {
for _, gid := range r.Source {
if group, ok := a.Groups[gid]; ok {
groups[gid] = group
}
}
}
}
peersSet := make(map[string]struct{})
for _, g := range groups {
for _, pid := range g.Peers {
peer, ok := a.Peers[pid]
if !ok {
log.Warnf(
"peer %s found in group %s but doesn't belong to account %s",
pid,
g.ID,
a.Id,
)
continue
}
// exclude original peer
if _, ok := peersSet[peer.ID]; peer.ID != peerID && !ok {
peersSet[peer.ID] = struct{}{}
peers = append(peers, peer.Copy())
}
}
}
return peers
}
// updateAccountPeers updates all peers that belong to an account. // updateAccountPeers updates all peers that belong to an account.
// Should be called when changes have to be synced to peers. // Should be called when changes have to be synced to peers.
func (am *DefaultAccountManager) updateAccountPeers(account *Account) error { func (am *DefaultAccountManager) updateAccountPeers(account *Account) error {

View File

@ -227,16 +227,13 @@ func TestAccountManager_GetNetworkMapWithPolicy(t *testing.T) {
policy.Enabled = true policy.Enabled = true
policy.Rules = []*PolicyRule{ policy.Rules = []*PolicyRule{
{ {
Enabled: true, Enabled: true,
Sources: []string{group1.ID}, Sources: []string{group1.ID},
Destinations: []string{group2.ID}, Destinations: []string{group2.ID},
Action: PolicyTrafficActionAccept, Bidirectional: true,
Action: PolicyTrafficActionAccept,
}, },
} }
if err := policy.UpdateQueryFromRules(); err != nil {
t.Errorf("expecting policy to be updated, got failure %v", err)
return
}
err = manager.SavePolicy(account.Id, userID, &policy) err = manager.SavePolicy(account.Id, userID, &policy)
if err != nil { if err != nil {
t.Errorf("expecting rule to be added, got failure %v", err) t.Errorf("expecting rule to be added, got failure %v", err)

View File

@ -1,18 +1,13 @@
package server package server
import ( import (
"bytes"
"context"
_ "embed" _ "embed"
"fmt" "fmt"
"html/template"
"strings" "strings"
"github.com/netbirdio/netbird/management/proto"
"github.com/netbirdio/netbird/management/server/activity" "github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/status" "github.com/netbirdio/netbird/management/server/status"
"github.com/open-policy-agent/opa/rego"
log "github.com/sirupsen/logrus"
) )
// PolicyUpdateOperationType operation type // PolicyUpdateOperationType operation type
@ -21,6 +16,12 @@ type PolicyUpdateOperationType int
// PolicyTrafficActionType action type for the firewall // PolicyTrafficActionType action type for the firewall
type PolicyTrafficActionType string type PolicyTrafficActionType string
// PolicyRuleProtocolType type of traffic
type PolicyRuleProtocolType string
// PolicyRuleDirection direction of traffic
type PolicyRuleDirection string
const ( const (
// PolicyTrafficActionAccept indicates that the traffic is accepted // PolicyTrafficActionAccept indicates that the traffic is accepted
PolicyTrafficActionAccept = PolicyTrafficActionType("accept") PolicyTrafficActionAccept = PolicyTrafficActionType("accept")
@ -28,21 +29,35 @@ const (
PolicyTrafficActionDrop = PolicyTrafficActionType("drop") PolicyTrafficActionDrop = PolicyTrafficActionType("drop")
) )
const (
// PolicyRuleProtocolALL type of traffic
PolicyRuleProtocolALL = PolicyRuleProtocolType("all")
// PolicyRuleProtocolTCP type of traffic
PolicyRuleProtocolTCP = PolicyRuleProtocolType("tcp")
// PolicyRuleProtocolUDP type of traffic
PolicyRuleProtocolUDP = PolicyRuleProtocolType("udp")
// PolicyRuleProtocolICMP type of traffic
PolicyRuleProtocolICMP = PolicyRuleProtocolType("icmp")
)
const (
// PolicyRuleFlowDirect allows trafic from source to destination
PolicyRuleFlowDirect = PolicyRuleDirection("direct")
// PolicyRuleFlowBidirect allows traffic to both directions
PolicyRuleFlowBidirect = PolicyRuleDirection("bidirect")
)
const (
firewallRuleDirectionIN = 0
firewallRuleDirectionOUT = 1
)
// PolicyUpdateOperation operation object with type and values to be applied // PolicyUpdateOperation operation object with type and values to be applied
type PolicyUpdateOperation struct { type PolicyUpdateOperation struct {
Type PolicyUpdateOperationType Type PolicyUpdateOperationType
Values []string Values []string
} }
//go:embed rego/default_policy_module.rego
var defaultPolicyModule string
//go:embed rego/default_policy.rego
var defaultPolicyText string
// defaultPolicyTemplate is a template for the default policy
var defaultPolicyTemplate = template.Must(template.New("policy").Parse(defaultPolicyText))
// PolicyRule is the metadata of the policy // PolicyRule is the metadata of the policy
type PolicyRule struct { type PolicyRule struct {
// ID of the policy rule // ID of the policy rule
@ -65,18 +80,30 @@ type PolicyRule struct {
// Sources policy source groups // Sources policy source groups
Sources []string Sources []string
// Bidirectional define if the rule is applicable in both directions, sources, and destinations
Bidirectional bool
// Protocol type of the traffic
Protocol PolicyRuleProtocolType
// Ports or it ranges list
Ports []string
} }
// Copy returns a copy of a policy rule // Copy returns a copy of a policy rule
func (pm *PolicyRule) Copy() *PolicyRule { func (pm *PolicyRule) Copy() *PolicyRule {
return &PolicyRule{ return &PolicyRule{
ID: pm.ID, ID: pm.ID,
Name: pm.Name, Name: pm.Name,
Description: pm.Description, Description: pm.Description,
Enabled: pm.Enabled, Enabled: pm.Enabled,
Action: pm.Action, Action: pm.Action,
Destinations: pm.Destinations[:], Destinations: pm.Destinations[:],
Sources: pm.Sources[:], Sources: pm.Sources[:],
Bidirectional: pm.Bidirectional,
Protocol: pm.Protocol,
Ports: pm.Ports[:],
} }
} }
@ -107,9 +134,6 @@ type Policy struct {
// Enabled status of the policy // Enabled status of the policy
Enabled bool Enabled bool
// Query of Rego the policy
Query string
// Rules of the policy // Rules of the policy
Rules []*PolicyRule Rules []*PolicyRule
} }
@ -121,7 +145,6 @@ func (p *Policy) Copy() *Policy {
Name: p.Name, Name: p.Name,
Description: p.Description, Description: p.Description,
Enabled: p.Enabled, Enabled: p.Enabled,
Query: p.Query,
} }
for _, r := range p.Rules { for _, r := range p.Rules {
c.Rules = append(c.Rules, r.Copy()) c.Rules = append(c.Rules, r.Copy())
@ -134,214 +157,124 @@ func (p *Policy) EventMeta() map[string]any {
return map[string]any{"name": p.Name} return map[string]any{"name": p.Name}
} }
// UpdateQueryFromRules marshals policy rules to Rego string and set it to Query // UpgradeAndFix different version of policies to latest version
func (p *Policy) UpdateQueryFromRules() error { func (p *Policy) UpgradeAndFix() {
type templateVars struct {
All []string
Source []string
Destination []string
}
queries := []string{}
for _, r := range p.Rules { for _, r := range p.Rules {
if !r.Enabled { // start migrate from version v0.20.3
continue if r.Protocol == "" {
r.Protocol = PolicyRuleProtocolALL
} }
if r.Protocol == PolicyRuleProtocolALL && !r.Bidirectional {
buff := new(bytes.Buffer) r.Bidirectional = true
input := templateVars{
All: append(r.Destinations[:], r.Sources...),
Source: r.Sources,
Destination: r.Destinations,
} }
if err := defaultPolicyTemplate.Execute(buff, input); err != nil { // -- v0.20.4
return status.Errorf(status.BadRequest, "failed to update policy query: %v", err)
}
queries = append(queries, buff.String())
} }
p.Query = strings.Join(queries, "\n")
return nil
} }
// FirewallRule is a rule of the firewall. // FirewallRule is a rule of the firewall.
type FirewallRule struct { type FirewallRule struct {
// PeerID of the peer
PeerID string
// PeerIP of the peer // PeerIP of the peer
PeerIP string PeerIP string
// Direction of the traffic // Direction of the traffic
Direction string Direction int
// Action of the traffic // Action of the traffic
Action string Action string
// Protocol of the traffic
Protocol string
// Port of the traffic // Port of the traffic
Port string Port string
// id for internal purposes
id string
} }
// parseFromRegoResult parses the Rego result to a FirewallRule. // getPeerConnectionResources for a given peer
func (f *FirewallRule) parseFromRegoResult(value interface{}) error { //
object, ok := value.(map[string]interface{}) // This function returns the list of peers and firewall rules that are applicable to a given peer.
if !ok { func (a *Account) getPeerConnectionResources(peerID string) ([]*Peer, []*FirewallRule) {
return fmt.Errorf("invalid Rego query eval result") generateResources, getAccumulatedResources := a.connResourcesGenerator()
}
peerID, ok := object["ID"].(string) for _, policy := range a.Policies {
if !ok {
return fmt.Errorf("invalid Rego query eval result peer ID type")
}
peerIP, ok := object["IP"].(string)
if !ok {
return fmt.Errorf("invalid Rego query eval result peer IP type")
}
direction, ok := object["Direction"].(string)
if !ok {
return fmt.Errorf("invalid Rego query eval result peer direction type")
}
action, ok := object["Action"].(string)
if !ok {
return fmt.Errorf("invalid Rego query eval result peer action type")
}
port, ok := object["Port"].(string)
if !ok {
return fmt.Errorf("invalid Rego query eval result peer port type")
}
f.PeerID = peerID
f.PeerIP = peerIP
f.Direction = direction
f.Action = action
f.Port = port
// NOTE: update this id each time when new field added
f.id = peerID + peerIP + direction + action + port
return nil
}
// queryPeersAndFwRulesByRego returns a list associated Peers and firewall rules list for this peer.
func (a *Account) queryPeersAndFwRulesByRego(
peerID string,
queryNumber int,
query string,
) ([]*Peer, []*FirewallRule) {
input := map[string]interface{}{
"peer_id": peerID,
"peers": a.Peers,
"groups": a.Groups,
}
stmt, err := rego.New(
rego.Query("data.netbird.all"),
rego.Module("netbird", defaultPolicyModule),
rego.Module(fmt.Sprintf("netbird-%d", queryNumber), query),
).PrepareForEval(context.TODO())
if err != nil {
log.WithError(err).Error("get Rego query")
return nil, nil
}
evalResult, err := stmt.Eval(
context.TODO(),
rego.EvalInput(input),
)
if err != nil {
log.WithError(err).Error("eval Rego query")
return nil, nil
}
if len(evalResult) == 0 || len(evalResult[0].Expressions) == 0 {
log.Trace("empty Rego query eval result")
return nil, nil
}
expressions, ok := evalResult[0].Expressions[0].Value.([]interface{})
if !ok {
return nil, nil
}
dst := make(map[string]struct{})
src := make(map[string]struct{})
peers := make([]*Peer, 0, len(expressions))
rules := make([]*FirewallRule, 0, len(expressions))
for _, v := range expressions {
rule := &FirewallRule{}
if err := rule.parseFromRegoResult(v); err != nil {
log.WithError(err).Error("parse Rego query eval result")
continue
}
rules = append(rules, rule)
switch rule.Direction {
case "dst":
if _, ok := dst[rule.PeerID]; ok {
continue
}
dst[rule.PeerID] = struct{}{}
case "src":
if _, ok := src[rule.PeerID]; ok {
continue
}
src[rule.PeerID] = struct{}{}
default:
log.WithField("direction", rule.Direction).Error("invalid direction")
continue
}
}
added := make(map[string]struct{})
if _, ok := src[peerID]; ok {
for id := range dst {
if _, ok := added[id]; !ok && id != peerID {
added[id] = struct{}{}
}
}
}
if _, ok := dst[peerID]; ok {
for id := range src {
if _, ok := added[id]; !ok && id != peerID {
added[id] = struct{}{}
}
}
}
for id := range added {
peers = append(peers, a.Peers[id])
}
return peers, rules
}
// getPeersByPolicy returns all peers that given peer has access to.
func (a *Account) getPeersByPolicy(peerID string) (peers []*Peer, rules []*FirewallRule) {
peersSeen := make(map[string]struct{})
ruleSeen := make(map[string]struct{})
for i, policy := range a.Policies {
if !policy.Enabled { if !policy.Enabled {
continue continue
} }
p, r := a.queryPeersAndFwRulesByRego(peerID, i, policy.Query)
for _, peer := range p { for _, rule := range policy.Rules {
if _, ok := peersSeen[peer.ID]; ok { if !rule.Enabled {
continue continue
} }
peers = append(peers, peer)
peersSeen[peer.ID] = struct{}{} sourcePeers, peerInSources := getAllPeersFromGroups(a, rule.Sources, peerID)
} destinationPeers, peerInDestinations := getAllPeersFromGroups(a, rule.Destinations, peerID)
for _, rule := range r {
if _, ok := ruleSeen[rule.id]; ok { if rule.Bidirectional {
continue if peerInSources {
generateResources(rule, destinationPeers, firewallRuleDirectionIN)
}
if peerInDestinations {
generateResources(rule, sourcePeers, firewallRuleDirectionOUT)
}
}
if peerInSources {
generateResources(rule, destinationPeers, firewallRuleDirectionOUT)
}
if peerInDestinations {
generateResources(rule, sourcePeers, firewallRuleDirectionIN)
} }
rules = append(rules, rule)
ruleSeen[rule.id] = struct{}{}
} }
} }
return
return getAccumulatedResources()
}
// connResourcesGenerator returns generator and accumulator function which returns the result of generator calls
//
// The generator function is used to generate the list of peers and firewall rules that are applicable to a given peer.
// It safe to call the generator function multiple times for same peer and different rules no duplicates will be
// generated. The accumulator function returns the result of all the generator calls.
func (a *Account) connResourcesGenerator() (func(*PolicyRule, []*Peer, int), func() ([]*Peer, []*FirewallRule)) {
rulesExists := make(map[string]struct{})
peersExists := make(map[string]struct{})
rules := make([]*FirewallRule, 0)
peers := make([]*Peer, 0)
return func(rule *PolicyRule, groupPeers []*Peer, direction int) {
for _, peer := range groupPeers {
if _, ok := peersExists[peer.ID]; !ok {
peers = append(peers, peer)
peersExists[peer.ID] = struct{}{}
}
fwRule := FirewallRule{
PeerIP: peer.IP.String(),
Direction: direction,
Action: string(rule.Action),
Protocol: string(rule.Protocol),
}
ruleID := fmt.Sprintf("%s%d", peer.ID+peer.IP.String(), direction)
ruleID += string(rule.Protocol) + string(rule.Action) + strings.Join(rule.Ports, ",")
if _, ok := rulesExists[ruleID]; ok {
continue
}
rulesExists[ruleID] = struct{}{}
if len(rule.Ports) == 0 {
rules = append(rules, &fwRule)
continue
}
for _, port := range rule.Ports {
addRule := fwRule
addRule.Port = port
rules = append(rules, &addRule)
}
}
}, func() ([]*Peer, []*FirewallRule) {
return peers, rules
}
} }
// GetPolicy from the store // GetPolicy from the store
@ -475,3 +408,63 @@ func (am *DefaultAccountManager) savePolicy(account *Account, policy *Policy) (e
} }
return return
} }
func toProtocolFirewallRules(update []*FirewallRule) []*proto.FirewallRule {
result := make([]*proto.FirewallRule, len(update))
for i := range update {
direction := proto.FirewallRule_IN
if update[i].Direction == firewallRuleDirectionOUT {
direction = proto.FirewallRule_OUT
}
action := proto.FirewallRule_ACCEPT
if update[i].Action == string(PolicyTrafficActionDrop) {
action = proto.FirewallRule_DROP
}
protocol := proto.FirewallRule_UNKNOWN
switch PolicyRuleProtocolType(update[i].Protocol) {
case PolicyRuleProtocolALL:
protocol = proto.FirewallRule_ALL
case PolicyRuleProtocolTCP:
protocol = proto.FirewallRule_TCP
case PolicyRuleProtocolUDP:
protocol = proto.FirewallRule_UDP
case PolicyRuleProtocolICMP:
protocol = proto.FirewallRule_ICMP
}
result[i] = &proto.FirewallRule{
PeerIP: update[i].PeerIP,
Direction: direction,
Action: action,
Protocol: protocol,
Port: update[i].Port,
}
}
return result
}
// getAllPeersFromGroups for given peer ID and list of groups
//
// Returns list of peers and boolean indicating if peer is in any of the groups
func getAllPeersFromGroups(account *Account, groups []string, peerID string) ([]*Peer, bool) {
peerInGroups := false
filteredPeers := make([]*Peer, 0, len(groups))
for _, g := range groups {
group, ok := account.Groups[g]
if !ok {
continue
}
for _, p := range group.Peers {
peer := account.Peers[p]
if peer.ID == peerID {
peerInGroups = true
continue
}
filteredPeers = append(filteredPeers, peer)
}
}
return filteredPeers, peerInGroups
}

View File

@ -1,6 +1,7 @@
package server package server
import ( import (
"fmt"
"net" "net"
"testing" "testing"
@ -11,262 +12,412 @@ import (
func TestAccount_getPeersByPolicy(t *testing.T) { func TestAccount_getPeersByPolicy(t *testing.T) {
account := &Account{ account := &Account{
Peers: map[string]*Peer{ Peers: map[string]*Peer{
"cfif97at2r9s73au3q00": { "peerA": {
ID: "cfif97at2r9s73au3q00", ID: "peerA",
IP: net.ParseIP("100.65.14.88"), IP: net.ParseIP("100.65.14.88"),
}, },
"cfif97at2r9s73au3q0g": { "peerB": {
ID: "cfif97at2r9s73au3q0g", ID: "peerB",
IP: net.ParseIP("100.65.80.39"), IP: net.ParseIP("100.65.80.39"),
}, },
"cfif97at2r9s73au3q10": { "peerC": {
ID: "cfif97at2r9s73au3q10", ID: "peerC",
IP: net.ParseIP("100.65.254.139"), IP: net.ParseIP("100.65.254.139"),
}, },
"cfif97at2r9s73au3q20": { "peerD": {
ID: "cfif97at2r9s73au3q20", ID: "peerD",
IP: net.ParseIP("100.65.62.5"), IP: net.ParseIP("100.65.62.5"),
}, },
"cfj4tiqt2r9s73dmeun0": { "peerE": {
ID: "cfj4tiqt2r9s73dmeun0", ID: "peerE",
IP: net.ParseIP("100.65.32.206"), IP: net.ParseIP("100.65.32.206"),
}, },
"cg7h032t2r9s73cg5fk0": { "peerF": {
ID: "cg7h032t2r9s73cg5fk0", ID: "peerF",
IP: net.ParseIP("100.65.250.202"), IP: net.ParseIP("100.65.250.202"),
}, },
"cgcnkj2t2r9s73cg5vv0": { "peerG": {
ID: "cgcnkj2t2r9s73cg5vv0", ID: "peerG",
IP: net.ParseIP("100.65.13.186"), IP: net.ParseIP("100.65.13.186"),
}, },
"cgcol4qt2r9s73cg601g": { "peerH": {
ID: "cgcol4qt2r9s73cg601g", ID: "peerH",
IP: net.ParseIP("100.65.29.55"), IP: net.ParseIP("100.65.29.55"),
}, },
}, },
Groups: map[string]*Group{ Groups: map[string]*Group{
"cet9e92t2r9s7383ns20": { "GroupAll": {
ID: "cet9e92t2r9s7383ns20", ID: "GroupAll",
Name: "All", Name: "All",
Peers: []string{ Peers: []string{
"cfif97at2r9s73au3q0g", "peerB",
"cfif97at2r9s73au3q00", "peerA",
"cfif97at2r9s73au3q20", "peerD",
"cfif97at2r9s73au3q10", "peerC",
"cfj4tiqt2r9s73dmeun0", "peerE",
"cg7h032t2r9s73cg5fk0", "peerF",
"cgcnkj2t2r9s73cg5vv0", "peerG",
"cgcol4qt2r9s73cg601g", "peerH",
}, },
}, },
"cev90bat2r9s7383o150": { "GroupSwarm": {
ID: "cev90bat2r9s7383o150", ID: "GroupSwarm",
Name: "swarm", Name: "swarm",
Peers: []string{ Peers: []string{
"cfif97at2r9s73au3q0g", "peerB",
"cfif97at2r9s73au3q00", "peerA",
"cfif97at2r9s73au3q20", "peerD",
"cfj4tiqt2r9s73dmeun0", "peerE",
"cgcnkj2t2r9s73cg5vv0", "peerG",
"cgcol4qt2r9s73cg601g", "peerH",
}, },
}, },
}, },
Rules: map[string]*Rule{ Rules: map[string]*Rule{
"cet9e92t2r9s7383ns2g": { "RuleDefault": {
ID: "cet9e92t2r9s7383ns2g", ID: "RuleDefault",
Name: "Default", Name: "Default",
Description: "This is a default rule that allows connections between all the resources", Description: "This is a default rule that allows connections between all the resources",
Source: []string{ Source: []string{
"cet9e92t2r9s7383ns20", "GroupAll",
}, },
Destination: []string{ Destination: []string{
"cet9e92t2r9s7383ns20", "GroupAll",
}, },
}, },
"cev90bat2r9s7383o15g": { "RuleSwarm": {
ID: "cev90bat2r9s7383o15g", ID: "RuleSwarm",
Name: "Swarm", Name: "Swarm",
Description: "", Description: "",
Source: []string{ Source: []string{
"cev90bat2r9s7383o150", "GroupSwarm",
"cet9e92t2r9s7383ns20", "GroupAll",
}, },
Destination: []string{ Destination: []string{
"cev90bat2r9s7383o150", "GroupSwarm",
}, },
}, },
}, },
} }
rule1, err := RuleToPolicy(account.Rules["cet9e92t2r9s7383ns2g"]) rule1, err := RuleToPolicy(account.Rules["RuleDefault"])
assert.NoError(t, err) assert.NoError(t, err)
rule2, err := RuleToPolicy(account.Rules["cev90bat2r9s7383o15g"]) rule2, err := RuleToPolicy(account.Rules["RuleSwarm"])
assert.NoError(t, err) assert.NoError(t, err)
account.Policies = append(account.Policies, rule1, rule2) account.Policies = append(account.Policies, rule1, rule2)
t.Run("check that all peers get map", func(t *testing.T) { t.Run("check that all peers get map", func(t *testing.T) {
for _, p := range account.Peers { for _, p := range account.Peers {
peers, firewallRules := account.getPeersByPolicy(p.ID) peers, firewallRules := account.getPeerConnectionResources(p.ID)
assert.GreaterOrEqual(t, len(peers), 2, "mininum number peers should present") assert.GreaterOrEqual(t, len(peers), 2, "mininum number peers should present")
assert.GreaterOrEqual(t, len(firewallRules), 2, "mininum number of firewall rules should present") assert.GreaterOrEqual(t, len(firewallRules), 2, "mininum number of firewall rules should present")
} }
}) })
t.Run("check first peer map details", func(t *testing.T) { t.Run("check first peer map details", func(t *testing.T) {
peers, firewallRules := account.getPeersByPolicy("cfif97at2r9s73au3q0g") peers, firewallRules := account.getPeerConnectionResources("peerB")
assert.Len(t, peers, 7) assert.Len(t, peers, 7)
assert.Contains(t, peers, account.Peers["cfif97at2r9s73au3q00"]) assert.Contains(t, peers, account.Peers["peerA"])
assert.Contains(t, peers, account.Peers["cfif97at2r9s73au3q10"]) assert.Contains(t, peers, account.Peers["peerC"])
assert.Contains(t, peers, account.Peers["cfif97at2r9s73au3q20"]) assert.Contains(t, peers, account.Peers["peerD"])
assert.Contains(t, peers, account.Peers["cfj4tiqt2r9s73dmeun0"]) assert.Contains(t, peers, account.Peers["peerE"])
assert.Contains(t, peers, account.Peers["cg7h032t2r9s73cg5fk0"]) assert.Contains(t, peers, account.Peers["peerF"])
epectedFirewallRules := []*FirewallRule{ epectedFirewallRules := []*FirewallRule{
{ {
PeerID: "cfif97at2r9s73au3q00",
PeerIP: "100.65.14.88", PeerIP: "100.65.14.88",
Direction: "src", Direction: firewallRuleDirectionIN,
Action: "accept", Action: "accept",
Protocol: "all",
Port: "", Port: "",
id: "cfif97at2r9s73au3q00100.65.14.88srcaccept",
}, },
{ {
PeerID: "cfif97at2r9s73au3q00",
PeerIP: "100.65.14.88", PeerIP: "100.65.14.88",
Direction: "dst", Direction: firewallRuleDirectionOUT,
Action: "accept", Action: "accept",
Protocol: "all",
Port: "", Port: "",
id: "cfif97at2r9s73au3q00100.65.14.88dstaccept",
},
{
PeerID: "cfif97at2r9s73au3q0g",
PeerIP: "100.65.80.39",
Direction: "dst",
Action: "accept",
Port: "",
id: "cfif97at2r9s73au3q0g100.65.80.39dstaccept",
}, },
{ {
PeerID: "cfif97at2r9s73au3q0g",
PeerIP: "100.65.80.39",
Direction: "src",
Action: "accept",
Port: "",
id: "cfif97at2r9s73au3q0g100.65.80.39srcaccept",
},
{
PeerID: "cfif97at2r9s73au3q10",
PeerIP: "100.65.254.139", PeerIP: "100.65.254.139",
Direction: "dst", Direction: firewallRuleDirectionOUT,
Action: "accept", Action: "accept",
Protocol: "all",
Port: "", Port: "",
id: "cfif97at2r9s73au3q10100.65.254.139dstaccept",
}, },
{ {
PeerID: "cfif97at2r9s73au3q10",
PeerIP: "100.65.254.139", PeerIP: "100.65.254.139",
Direction: "src", Direction: firewallRuleDirectionIN,
Action: "accept", Action: "accept",
Protocol: "all",
Port: "", Port: "",
id: "cfif97at2r9s73au3q10100.65.254.139srcaccept",
}, },
{ {
PeerID: "cfif97at2r9s73au3q20",
PeerIP: "100.65.62.5", PeerIP: "100.65.62.5",
Direction: "dst", Direction: firewallRuleDirectionOUT,
Action: "accept", Action: "accept",
Protocol: "all",
Port: "", Port: "",
id: "cfif97at2r9s73au3q20100.65.62.5dstaccept",
}, },
{ {
PeerID: "cfif97at2r9s73au3q20",
PeerIP: "100.65.62.5", PeerIP: "100.65.62.5",
Direction: "src", Direction: firewallRuleDirectionIN,
Action: "accept", Action: "accept",
Protocol: "all",
Port: "", Port: "",
id: "cfif97at2r9s73au3q20100.65.62.5srcaccept",
}, },
{ {
PeerID: "cfj4tiqt2r9s73dmeun0",
PeerIP: "100.65.32.206", PeerIP: "100.65.32.206",
Direction: "dst", Direction: firewallRuleDirectionOUT,
Action: "accept", Action: "accept",
Protocol: "all",
Port: "", Port: "",
id: "cfj4tiqt2r9s73dmeun0100.65.32.206dstaccept",
}, },
{ {
PeerID: "cfj4tiqt2r9s73dmeun0",
PeerIP: "100.65.32.206", PeerIP: "100.65.32.206",
Direction: "src", Direction: firewallRuleDirectionIN,
Action: "accept", Action: "accept",
Protocol: "all",
Port: "", Port: "",
id: "cfj4tiqt2r9s73dmeun0100.65.32.206srcaccept",
}, },
{ {
PeerID: "cg7h032t2r9s73cg5fk0",
PeerIP: "100.65.250.202", PeerIP: "100.65.250.202",
Direction: "dst", Direction: firewallRuleDirectionOUT,
Action: "accept", Action: "accept",
Protocol: "all",
Port: "", Port: "",
id: "cg7h032t2r9s73cg5fk0100.65.250.202dstaccept",
}, },
{ {
PeerID: "cg7h032t2r9s73cg5fk0",
PeerIP: "100.65.250.202", PeerIP: "100.65.250.202",
Direction: "src", Direction: firewallRuleDirectionIN,
Action: "accept", Action: "accept",
Protocol: "all",
Port: "", Port: "",
id: "cg7h032t2r9s73cg5fk0100.65.250.202srcaccept",
}, },
{ {
PeerID: "cgcnkj2t2r9s73cg5vv0",
PeerIP: "100.65.13.186", PeerIP: "100.65.13.186",
Direction: "dst", Direction: firewallRuleDirectionOUT,
Action: "accept", Action: "accept",
Protocol: "all",
Port: "", Port: "",
id: "cgcnkj2t2r9s73cg5vv0100.65.13.186dstaccept",
}, },
{ {
PeerID: "cgcnkj2t2r9s73cg5vv0",
PeerIP: "100.65.13.186", PeerIP: "100.65.13.186",
Direction: "src", Direction: firewallRuleDirectionIN,
Action: "accept", Action: "accept",
Protocol: "all",
Port: "", Port: "",
id: "cgcnkj2t2r9s73cg5vv0100.65.13.186srcaccept",
}, },
{ {
PeerID: "cgcol4qt2r9s73cg601g",
PeerIP: "100.65.29.55", PeerIP: "100.65.29.55",
Direction: "dst", Direction: firewallRuleDirectionOUT,
Action: "accept", Action: "accept",
Protocol: "all",
Port: "", Port: "",
id: "cgcol4qt2r9s73cg601g100.65.29.55dstaccept",
}, },
{ {
PeerID: "cgcol4qt2r9s73cg601g",
PeerIP: "100.65.29.55", PeerIP: "100.65.29.55",
Direction: "src", Direction: firewallRuleDirectionIN,
Action: "accept", Action: "accept",
Protocol: "all",
Port: "", Port: "",
id: "cgcol4qt2r9s73cg601g100.65.29.55srcaccept",
}, },
} }
assert.Len(t, firewallRules, len(epectedFirewallRules)) assert.Len(t, firewallRules, len(epectedFirewallRules))
slices.SortFunc(firewallRules, func(a, b *FirewallRule) bool { slices.SortFunc(epectedFirewallRules, sortFunc())
return a.PeerID < b.PeerID slices.SortFunc(firewallRules, sortFunc())
})
for i := range firewallRules { for i := range firewallRules {
assert.Equal(t, epectedFirewallRules[i], firewallRules[i]) assert.Equal(t, epectedFirewallRules[i], firewallRules[i])
} }
}) })
} }
func TestAccount_getPeersByPolicyDirect(t *testing.T) {
account := &Account{
Peers: map[string]*Peer{
"peerA": {
ID: "peerA",
IP: net.ParseIP("100.65.14.88"),
},
"peerB": {
ID: "peerB",
IP: net.ParseIP("100.65.80.39"),
},
"peerC": {
ID: "peerC",
IP: net.ParseIP("100.65.254.139"),
},
},
Groups: map[string]*Group{
"GroupAll": {
ID: "GroupAll",
Name: "All",
Peers: []string{
"peerB",
"peerA",
"peerC",
},
},
"GroupSwarm": {
ID: "GroupSwarm",
Name: "swarm",
Peers: []string{
"peerB",
},
},
"peerF": {
ID: "peerF",
Name: "dmz",
Peers: []string{
"peerC",
},
},
},
Rules: map[string]*Rule{
"RuleDefault": {
ID: "RuleDefault",
Name: "Default",
Disabled: true,
Description: "This is a default rule that allows connections between all the resources",
Source: []string{
"GroupAll",
},
Destination: []string{
"GroupAll",
},
},
"RuleSwarm": {
ID: "RuleSwarm",
Name: "Swarm",
Description: "",
Source: []string{
"GroupSwarm",
},
Destination: []string{
"peerF",
},
},
},
}
rule1, err := RuleToPolicy(account.Rules["RuleDefault"])
assert.NoError(t, err)
rule2, err := RuleToPolicy(account.Rules["RuleSwarm"])
assert.NoError(t, err)
account.Policies = append(account.Policies, rule1, rule2)
t.Run("check first peer map", func(t *testing.T) {
peers, firewallRules := account.getPeerConnectionResources("peerB")
assert.Contains(t, peers, account.Peers["peerC"])
epectedFirewallRules := []*FirewallRule{
{
PeerIP: "100.65.254.139",
Direction: firewallRuleDirectionIN,
Action: "accept",
Protocol: "all",
Port: "",
},
{
PeerIP: "100.65.254.139",
Direction: firewallRuleDirectionOUT,
Action: "accept",
Protocol: "all",
Port: "",
},
}
assert.Len(t, firewallRules, len(epectedFirewallRules))
slices.SortFunc(epectedFirewallRules, sortFunc())
slices.SortFunc(firewallRules, sortFunc())
for i := range firewallRules {
assert.Equal(t, epectedFirewallRules[i], firewallRules[i])
}
})
t.Run("check second peer map", func(t *testing.T) {
peers, firewallRules := account.getPeerConnectionResources("peerC")
assert.Contains(t, peers, account.Peers["peerB"])
epectedFirewallRules := []*FirewallRule{
{
PeerIP: "100.65.80.39",
Direction: firewallRuleDirectionIN,
Action: "accept",
Protocol: "all",
Port: "",
},
{
PeerIP: "100.65.80.39",
Direction: firewallRuleDirectionOUT,
Action: "accept",
Protocol: "all",
Port: "",
},
}
assert.Len(t, firewallRules, len(epectedFirewallRules))
slices.SortFunc(epectedFirewallRules, sortFunc())
slices.SortFunc(firewallRules, sortFunc())
for i := range firewallRules {
assert.Equal(t, epectedFirewallRules[i], firewallRules[i])
}
})
account.Policies[1].Rules[0].Bidirectional = false
t.Run("check first peer map directional only", func(t *testing.T) {
peers, firewallRules := account.getPeerConnectionResources("peerB")
assert.Contains(t, peers, account.Peers["peerC"])
epectedFirewallRules := []*FirewallRule{
{
PeerIP: "100.65.254.139",
Direction: firewallRuleDirectionOUT,
Action: "accept",
Protocol: "all",
Port: "",
},
}
assert.Len(t, firewallRules, len(epectedFirewallRules))
slices.SortFunc(epectedFirewallRules, sortFunc())
slices.SortFunc(firewallRules, sortFunc())
for i := range firewallRules {
assert.Equal(t, epectedFirewallRules[i], firewallRules[i])
}
})
t.Run("check second peer map directional only", func(t *testing.T) {
peers, firewallRules := account.getPeerConnectionResources("peerC")
assert.Contains(t, peers, account.Peers["peerB"])
epectedFirewallRules := []*FirewallRule{
{
PeerIP: "100.65.80.39",
Direction: firewallRuleDirectionIN,
Action: "accept",
Protocol: "all",
Port: "",
},
}
assert.Len(t, firewallRules, len(epectedFirewallRules))
slices.SortFunc(epectedFirewallRules, sortFunc())
slices.SortFunc(firewallRules, sortFunc())
for i := range firewallRules {
assert.Equal(t, epectedFirewallRules[i], firewallRules[i])
}
})
}
func sortFunc() func(a *FirewallRule, b *FirewallRule) bool {
return func(a, b *FirewallRule) bool {
return a.PeerIP+fmt.Sprintf("%d", a.Direction) < b.PeerIP+fmt.Sprintf("%d", b.Direction)
}
}

View File

@ -1,9 +0,0 @@
package netbird
all[rule] {
is_peer_in_any_group([{{range $i, $e := .All}}{{if $i}},{{end}}"{{$e}}"{{end}}])
rule := {
{{range $i, $e := .Destination}}rules_from_group("{{$e}}", "dst", "accept", ""),{{end}}
{{range $i, $e := .Source}}rules_from_group("{{$e}}", "src", "accept", ""),{{end}}
}[_][_]
}

View File

@ -1,34 +0,0 @@
package netbird
import future.keywords.if
import future.keywords.in
import future.keywords.contains
# get_rule builds a netbird rule object from given parameters
get_rule(peer_id, direction, action, port) := rule if {
peer := input.peers[_]
peer.ID == peer_id
rule := {
"ID": peer.ID,
"IP": peer.IP,
"Direction": direction,
"Action": action,
"Port": port,
}
}
# netbird_rules_from_group returns a list of netbird rules for a given group_id
rules_from_group(group_id, direction, action, port) := rules if {
group := input.groups[_]
group.ID == group_id
rules := [get_rule(peer, direction, action, port) | peer := group.Peers[_]]
}
# is_peer_in_any_group checks that input peer present at least in one group
is_peer_in_any_group(groups) := count([group_id]) > 0 if {
group_id := groups[_]
group := input.groups[_]
group.ID == group_id
peer := group.Peers[_]
peer == input.peer_id
}

View File

@ -911,8 +911,6 @@ func TestGetNetworkMap_RouteSync(t *testing.T) {
newPolicy.Name = "peer1 only" newPolicy.Name = "peer1 only"
newPolicy.Rules[0].Sources = []string{newGroup.ID} newPolicy.Rules[0].Sources = []string{newGroup.ID}
newPolicy.Rules[0].Destinations = []string{newGroup.ID} newPolicy.Rules[0].Destinations = []string{newGroup.ID}
err = newPolicy.UpdateQueryFromRules()
require.NoError(t, err)
err = am.SavePolicy(account.Id, userID, newPolicy) err = am.SavePolicy(account.Id, userID, newPolicy)
require.NoError(t, err) require.NoError(t, err)

View File

@ -67,13 +67,15 @@ func (r *Rule) ToPolicyRule() *PolicyRule {
return nil return nil
} }
return &PolicyRule{ return &PolicyRule{
ID: r.ID, ID: r.ID,
Name: r.Name, Name: r.Name,
Enabled: !r.Disabled, Enabled: !r.Disabled,
Description: r.Description, Description: r.Description,
Action: PolicyTrafficActionAccept, Destinations: r.Destination,
Destinations: r.Destination, Sources: r.Source,
Sources: r.Source, Bidirectional: true,
Protocol: PolicyRuleProtocolALL,
Action: PolicyTrafficActionAccept,
} }
} }
@ -82,15 +84,11 @@ func RuleToPolicy(rule *Rule) (*Policy, error) {
if rule == nil { if rule == nil {
return nil, fmt.Errorf("rule is empty") return nil, fmt.Errorf("rule is empty")
} }
policy := &Policy{ return &Policy{
ID: rule.ID, ID: rule.ID,
Name: rule.Name, Name: rule.Name,
Description: rule.Description, Description: rule.Description,
Enabled: !rule.Disabled, Enabled: !rule.Disabled,
Rules: []*PolicyRule{rule.ToPolicyRule()}, Rules: []*PolicyRule{rule.ToPolicyRule()},
} }, nil
if err := policy.UpdateQueryFromRules(); err != nil {
return nil, err
}
return policy, nil
} }